This document contains all of the class and API documentation, plus selected general documentation for Flourish r899. The Class Documentation and General Documentation links in the header provide for quick access to the table of contents. Any links preceeded by a small graphical arrow will leave this document.

Class Documentation

Database

ORM

Filesystem

Request

Session

Response

Utilities

Email

Date/Time

Numbers/Strings

Errors/Exceptions

General Documentation

fActiveRecord

Class Resources

Contents

ORM Classes

The fActiveRecord class is an abstract class that follows the active record pattern. It provides an object-oriented interface for creating, retrieving, storing and deleting a single row (or record) in a database. All interaction with the database is done automatically without the need to write any SQL.

In addition to providing an interface to the columns in a single table, data from other database tables related via FOREIGN KEY constraints can be easily and efficiently retrieved. To query for and return multiple fActiveRecord objects, please see the class fRecordSet.

The following discussion is built on top of the content of ORM Conventions. Topics include database schema structure, various notation standards and information about MySQL and SQLite databases.

Setup

In order to use the fActiveRecord class, a database table must exist to be modeled. Below is an example users table—please note that the table has been designed to demonstrate the features of the class, not as an example of a well-designed schema.

CREATE TABLE users (
    user_id INTEGER PRIMARY KEY AUTOINCREMENT,
    email VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    first_name VARCHAR(100) NOT NULL,
    middle_initial VARCHAR(5) NOT NULL DEFAULT '',
    last_name VARCHAR(100) NOT NULL,
    date_created TIMESTAMP NOT NULL,
    email_validated BOOLEAN NOT NULL DEFAULT FALSE,
    membership_fee DECIMAL(10,2) NOT NULL,
    profile TEXT NOT NULL DEFAULT '',
    status VARCHAR(20) NOT NULL CHECK(status IN ('Active', 'Inactive'))    
);

Once a table has been created, a PHP class will need to be made. The name of the class should be in UpperCamelCase notation and should be a singular form of the table name. Thus for the users table a class User would be created that extends fActiveRecord.

class User extends fActiveRecord
{
    protected function configure()
    {
    }
}

A blank extension of the configure() method has been included since that is where all class functionality configuration is placed. This method is called exactly once per script execution and is the preferred location to call code to extend or set up the class, but not the individual object.

In addition to the fActiveRecord class and database table, a method to connect to the database needs to be set up. To do this, an instance of the fDatabase class needs to be passed to fORMDatabase::attach() — this is commonly done in the site’s initilization script.

// Set up a SQLite database for use by fActiveRecord and fRecordSet
fORMDatabase::attach(
    new fDatabase('sqlite', '/path/to/database')
);

The fDatabase instance attached to fORMDatabase will also be used by fRecordSet.

Custom Class to Table Mapping

It is possible to change the class to table association for modeling an existing incompatible database. The static method fORM::mapClassToTable() will accept the $class and the $table to map to the class to.

This method should be called in the site-wide configuration and should not be called in the configure() method.

// Class to table mapping should occur before any classes are used
// such as when the database is attached via fORMDatabase::attach()
fORMDatabase::attach($db);
fORM::mapClassToTable('User', 'user');

Modeling Tables in Other Schemas

When a class models a table in a non-default schema (public for PostgreSQL, dbo for MSSQL and the username for Oracle and DB2), the static method fORM::mapClassToTable() should be called with first parameter, the $class to map and the second parameter, $table, should be in the format schema.table.

This method should be called in the site-wide configuration and should not be called in the configure() method.

// This maps the User class to the users table in the authorization schema
fORM::mapClassToTable('User', 'authorization.users');

Using Multiple Databases

When multiple databases are configured via fORMDatabase, classes can model tables on the non-default database by calling the method fORM::mapClassToDatabase(). The first parameter is the $class to map, and the second is the $name of the database set in fORMDatabase::attach().

// Attach a second database as "commerce_db" and have the User class model the users table in it
fORMDatabase::attach($db, 'commerce_db');
fORM::mapClassToDatabase('User', 'commerce_db');

Like fORM::mapClassToTable(), this method should be called in the site-wide configuration and should not be call in the configure() method. This method is not required for classes modeling tables in the default database — if no $name was provided to fORMDatabase::attach(), then the database is the default.

Creating and Loading Records

A new record can be created by simply creating an object without any parameters.

$new_user_1 = new User();
$new_user_2 = new User();

Existing records can be loaded from the database by passing the primary key value to the constructor. If a primary key has multiple columns, use an associative array with the keys being the columns.

// Loading a record with a single column primary key
$user = new User(2);
 
// Loading a record with a multiple column primary key
$permission = new Permission(array('user_id' => 2, 'resource_id' => 3));

It is also possible to load a record based on the values from columns in a UNIQUE constraint. When loading via a UNIQUE constraint, an associative array must be used, even if there is only a single column in the constraint.

// This loads a user by their unique email address
$user = new User(array('email' => 'will@flourishlib.com'));

Please note that an fActiveRecord object is a reference object. All objects of the same class will share the same data and any operations will affect all instances. Thus if the User object for user 3 has the first name changed, all other objects representing user 3 will also have the first name changed.

Column Operations

For every column in a record there are at least five different operations that can be performed. ORM plugins can change these default behaviors or add even more.

Action Description
get Retrieves the column’s value
set Sets a new value for the column - empty string '' are converted to NULL
encode Encodes all special HTML characters — should be used when content is not trusted or for displaying in HTML tag attributes
prepare Encodes all special HTML characters, but leaves HTML tags and entities unencoded — should only be used for trusted content
inspect Returns information about the column, including information such as the data type and valid values

These five operations are combined with the column name into a camelCase method name. Below are some examples:

$first_name = $user->getFirstName();
 
$user->setFirstName($first_name);
 
echo $user->encodeFirstName();
 
echo $user->prepareFirstName();
 
$max_length = $user->inspectFirstName('max_length');

As mentioned on the ORM Conventions page, all columns in the database should be created using underscore_notation. This assumes that numbers are separated from words by an underscore, such as address_2. If a number is not separated by an underscore, or you are having other notation conversion issues, you man need to customize the notation conversion using fGrammar.

Date, Time and Timestamp Columns

When dealing with date, time or timestamp columns, the prepare and encode methods require a single parameter, $date_formatting_string. The formatting string can be any valid date() formatting string, or a format name that was created with fTimestamp::defineFormat().

echo $user->prepareDateCreated('n/j/y');
echo $user->encodeDateCreated('my_date_format');

Float/Decimal Columns

Floating point columns without an explicit precision require a single parameter, $decimal_places, when calling the prepare and encode methods. Floating point columns with an explicit precision can optionally pass an integer for $decimal_places.

echo $user->prepareMembershipFee(2);

String Columns

Columns that are string columns (VARCHAR, CHAR and TEXT) can optionally pass TRUE to their prepare and encode methods to cause all email addresses and website addresses to be converted to HTML links and to cause content without block-level HTML to have newline characters converted to <br /> tags.

echo $user->prepareEmail(TRUE);
echo $user->prepareProfile(TRUE);

All Columns

Every column has an inspect method that will return an associative array of data about the column. It is also possible to retrieve a single value by passing the optional parameter, $element.

$column_info = $user->inspectFirstName();
$max_length  = $user->inspectFirstName('max_length');

Column Values and Objects

The fActiveRecord class supports storing both scalar values (strings, integers, booleans, etc) and objects in columns. The only special consideration with storing objects in columns is that they should have a __toString() method so that they can be converted to a scalar to be saved in the database.

All of the Flourish value objects include such a __toString() method, making them work perfectly with fActiveRecord. In fact, fActiveRecord even loads date, time and timestamp columns out of the database into fDate, fTime and fTimestamp objects respectively. Nothing needs to be called or configured to enable this functionality. Thus, when getting date, time or timestamp values from a record, be sure to treat them as fDate, fTime and fTimestamp objects.

// Chaining the fDate::format() method off of the get method
echo $user->getDateCreated()->format('n/j/y');
 
// Objects can be stored in a column as long as they have a __toString() method
$user->setLastLoginTimestamp(new fTimestamp());

Record Operations

The following methods allow manipulation of an active record object:

Method Description
store() Performs validate() and then executes an INSERT or UDPATE query
validate() Ensures a record can be successfully saved to the database without actually doing it
delete() Deletes a record from the database
load() Reloads a record’s values from the database
populate() Values from the HTTP request will automatically be set to the various columns of the record
replicate() / clone Creates a copy of the record, cloning all contained objects and removing any auto incrementing primary key, can also replicate related records
exists() Indicates if a record has already been stored in the database
reflect() Returns a string containing the method signature for every method of the record

store()

The store() method will ensure that the record can be properly saved in the database and will store it there. If any errors are found, an fValidationException will be thrown with a message that is suitable for display to end users. The validation is performed by store() calling validate().

store() will also automatically begin a database and filesystem transaction if they are not already in progress. This allows database and filesystem actions to be performed in extending code and child objects without the need to keep track of changes manually and revert them.

try {
    $user = new User();
    $user->setFirstName('Will');
    $user->setLastName('Bond');
    $user->store();
 
} catch (fExpectedException $e) {
    echo $e->printMessage();
}

There is a single optional parameter, $force_cascade, that affects how related records in one-to-many and one-to-one relationships are stored. See the Related Records Operations section for more details about how related records can be accessed and manipulated.

If related records (which we will call child records) have been set via an associate or populate method, and one or more of the original child records is no longer associated, and that child records has child records (grandchildren of the original) with an ON DELETE RESTRICT or ON DELETE NO ACTION clause in the FOREIGN KEY constraint, that child and the associated grandchildren records will all be deleted anyway. Normally an exception would be thrown indicating the child to be deleted had a grandchild record referencing it.

By default this parameter is set to FALSE to obey the restrictions in the database schema.

$user->populateFakeRelatedRecords();
$user->store(TRUE);

This forced cascade effect is accomplished by first finding the related records and explicitly deleting them before the originally associated record.

validate()

Validation of a record is performed based on the database schema and any additional validation rules set via the fORMValidation class, ORM plugins and fORM hooks.

What is Validated

The following rules are used to determine if a record is valid to store in the database:

  1. All data must be compatible with the data type of the column it is being stored in
  2. If a column has a NOT NULL constraint and does not have a DEFAULT value, a value other than an empty string must be set
  3. If a value has a FOREIGN KEY constraint, the value must reference a valid value
  4. If a column has a UNIQUE or PRIMARY KEY contraint, the value must be NULL or unique
  5. If a column has a CHECK constraint with an IN (...) expression, the value must be in the list - for MySQL this holds true for ENUM columns
  6. If a column is a VARCHAR or CHAR column, the value string length must be less than or equal to the size of the column

If any of these validation checks do not work out, an fValidationException will be thrown contains an error message suitable for end users.

There are many additional validation rules that can be added to a record via fORMValidation. If the desired functionality is not available via fORMValidation, an ORM hook can be used. Please see the Adding Functionality to fActiveRecord and Custom Validation Using a Hook section of the fORM page for details and example code.

Many of the various ORM plugins that come with Flourish (fORMColumn, fORMDate, fORMFile, fORMMoney and fORMOrdering) add additional validation rules.

Usage

Since this method is automatically called when executing store(), it will typically only be called in situations where storing is not possible, such as multi-page forms.

try {
    // ...
    $user->validate();
 
} catch (fValidationException $e) {
    echo $e->printMessage();
}

It is also possible to return an array of errors by passing TRUE to the first parameter of validate(), $return_messages. This prevents an exception from being thrown.

$errors = $user->validate(TRUE);

The returned array will have keys in the following format with the value being the error message.

Below is an example of a returned array:

array(
    // Regular columns in the users table
    'first_name'         => 'First Name: Please enter a value',
    'last_name'          => 'Last Name: Please enter a value',
    // A message involving multiple columns, from fORMValidate::addOneOrMoreRule()
    'email,phone'        => 'Email, Phone: Please enter at least one',
    // A message from fORMValidate::addManyToManyRule()
    'groups'             => 'Groups: Please select at least one',
    // A message from the bio column in the one-to-one related user_details table
    'user_details::bio'  => 'User Details Bio: Please enter a value',
    // Messages from one-to-many related favorites table
    'favorites[0]::name' => 'Favorite #1 Name: Please enter a value',
    'favorites[2]::name' => 'Favorite #3 Name: Please enter a value'
);

When passing TRUE to $return_messages, it is also possible to pass TRUE to the second parameter, $remove_column_names. This will remove the names of the columns from the error messages themselves, leaving the array with the regular keys, but anonymous messages useful for inclusion next to inputs.

$errors = $user->validate(TRUE, TRUE);

The returned array would look like:

array(
    // Regular columns in the users table
    'first_name'         => 'Please enter a value',
    'last_name'          => 'Please enter a value',
    // A message involving multiple columns, from fORMValidate::addOneOrMoreRule()
    'email,phone'        => 'Please enter at least one',
    // A message from fORMValidate::addManyToManyRule()
    'groups'             => 'Please select at least one',
    // A message from the bio column in the one-to-one related user_details table
    'user_details::bio'  => 'Please enter a value',
    // Messages from one-to-many related favorites table
    'favorites[0]::name' => 'Please enter a value',
    'favorites[2]::name' => 'Please enter a value'
);

delete()

The delete() method will remove the record from the database. Obviously, any cascading FOREIGN KEY constraints could cause other records to be deleted as well. This method will throw an fValidationException if the record is referenced by another record via a FOREIGN KEY constraint with an ON DELETE RESTRICT or ON DELETE NO ACTION clause.

try {
    $user->delete();
} catch (fValidationException $e) {
    echo $e->printMessage();
}

There is an optional parameter, $force_cascade, that when set to TRUE will delete all records that reference the record being deleted, even if they have an ON DELETE RESTRICT or ON DELETE NO ACTION clause in the FOREIGN KEY constraint. By default this is set to FALSE to obey the restrictions in the database schema.

$user->delete(TRUE);

This is accomplished by first finding the related records and explicitly deleting them before the original record.

load()

The load() method causes the values for the record to be reloaded from the database, overwriting any values that have been changed since the object was first loaded.

$user = new User(3);
$user->setFirstName('Joe');
 
// Reset the first name
$user->load();

populate()

The populate() method sets the values for a record from the fields and value contained in an HTTP request. For values to be pulled from the request, the HTML field names should be exactly the same as the database column names. Values from the form that are a blank strings are automatically converted to NULL.

The following HTML form when combined with the populate() method would cause the first_name, last_name and email column values to be set:

<form action="" method="post">
    <fieldset>
        <p>
            <label for="first_name">First Name</label>
            <input type="text" id="first_name" name="first_name" />
        </p>
        <p>
            <label for="last_name">Last Name</label>
            <input type="text" id="last_name" name="last_name" />
        </p>
        <p>
            <label for="email">Email</label>
            <input type="text" id="email" name="email" />
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>
try {
    $user = new User();
 
    // This will set first_name, last_name and email
    $user->populate();
    $user->store();
 
} catch (fExpectedException $e) {
    echo $e->printMessage();
}
Blank Strings

By default, populate() calls the individual set methods for each column in a record. This ensures that overridden methods are correctly called. The set methods in fActiveRecord will by default convert an empty string value '' to NULL. This treats empty HTML form input as if the user entered nothing.

Blank strings can be stored in a database columninstead of NULL by setting the column to NOT NULL and DEFAULT ''. When fActiveRecord finds a NOT NULL column with a NULL value and a non-NULL default, the default is substituted in place of the NULL.

Checkboxes

When using checkboxes in forms to be populated, it is best to preceed the checkbox input with a hidden input using the same name but a FALSE value. This way, if the checkbox is unchecked, PHP gets the value from the hidden input, changing the column to a FALSE value. When the checkbox is checked, PHP gets the value from the checkbox input, changing the column to a TRUE value.

<form action="" method="post">
    <fieldset>
        <p>
            <input type="hidden" name="enroll_me" value="0" />
            <label for="enroll_me">Enroll Me</label>
            <input type="checkbox" id="enroll_me" name="enroll_me" value="1" />
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>

Please note that there IS a difference between setting the hidden input to a blank string value and a FALSE value such as 0. As mentioned in the previous section, empty strings are converted to NULL, whereas a value such as 0 or false would not be.

Certain fORMValidation methods check to see if a column has a non-null value. If an empty string is used for the hidden input, the validation method would see that no value has been selected. If a non-null false value is used, the validation method would see that a non-null value has been selected. Depending on the requirements of the application, this distinction may be very important.

replicate() / clone

The replicate() method, passed no parameters is equivalent to a record being cloned. When an fActiveRecord object is cloned, all values and cache entries are copied (or cloned if the value is an object), all related records and old values are purged, any auto incrementing primary keys are erased and the record is changed to look as though it does not yet exist in the database.

$user = new User(1);
 
// Both $user2 and $user3 will have no $user_id and will return FALSE from ->exists()
$user2 = clone $user;
$user3 = $user->replicate();

It is also possible to replicate related records along with a record by passing plural class names into replicate(). There may be any number of class names and the classes must be for related records that are in a many-to-many or one-to-many relationship with the record. Below is an example of replicating a user and including all of their group memberships and favorites (see Related Records Operations for the database schema).

$user     = new User(1);
$new_user = $user->replicate('Groups', 'Favorites');

If a record is has more than one relationship route to the related record class, the route should be specified between curly braces ({ and }), like below:

$user     = new User(1);
$new_user = $user->replicate('Resources{read_permissions}');

exists()

The exists() method simply returns a boolean indicating if the record was loaded from the database.

if (!$user->exists()) {
    $user->sendWelcomeEmail();
}

This function is mostly useful when dealing with an object in a different scope than that which it was created. For instance, checking a record that has been passed into a function or an fActiveRecord hook callback.

Warning

Please note that this only checks to see if the object was constructed by passing a primary or unique key into the constructor. It can not be used with set methods to check and see if a record exists in the database.

The following code will NOT check to see if a user with the id 3 exists in the database. This call to exists will always return FALSE since the object was constructed without any parameters.

$user = new User();
$user->setUserId(3);
if ($user->exists()) {
    // …
}

The proper way to check if a record exists in the database is to try and create an instance, catching an fNotFoundException:

try {
    $user = new User(3);
} catch (fNotFoundException $e) {
    // …
}

From a technical perspective, for the first example to work, fActiveRecord object would have to have a mutable identity, meaning the object would change what row it represented over its lifetime. This would likely cause many other unintended side effect in common code.

reflect()

Since fActiveRecord classes have dynamic functionality based on the structure of a database table, sometimes it can be useful to check and see what exactly is available for a specific class. The reflect() method returns a preformatted text block containing the method signature for every method, concrete and dynamic, in the class.

echo '<pre>' . $user->reflect() . '</pre>';

would output something like

public function getFirstName();

public function setFirstName($first_name);

public function prepareFirstName();

public function encodeFirstName();

public function inspectFirstName($element=NULL);

It is also possible to pass a TRUE parameter to reflect() to also return the PHPDoc doc block for a method.

// Request the PHPDoc doc block too
echo $user->reflect(TRUE);

Related Records Operations

One of the useful features of fActiveRecord is that it automatically finds all related tables in a database schema by looking at FOREIGN KEY constraints. Absolutely no configuration is needed in the class.

The following tables definitions will be used for the examples below and are purposefully simple to help focus on the features, not the SQL.

CREATE TABLE groups (
    name VARCHAR(100) PRIMARY KEY
);
 
CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL
);
 
CREATE TABLE user_details (
    user_id INTEGER PRIMARY KEY REFERENCES users(user_id) ON DELETE CASCADE,
    photo VARCHAR(255) NOT NULL
);
 
CREATE TABLE users_groups (
    group VARCHAR(100) NOT NULL REFERENCES groups(name) ON DELETE CASCADE,
    user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    PRIMARY KEY (group, user_id)
);
 
CREATE TABLE favorites (
    favorite_id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    url VARCHAR(255) NOT NULL
);
 
CREATE TABLE resources (
    resource_id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    owner INTEGER NOT NULL REFERENCES users(user_id) ON DELETE RESTRICT
);
 
CREATE TABLE read_permissions (
    resource_id INTEGER NOT NULL REFERENCES resources(resource_id) ON DELETE CASCADE,
    user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    PRIMARY KEY (resource_id, user_id)
);

For the sake of the examples below, assume that the following classes have been defined to extend fActiveRecord:

Favorite
Group
Resource
User
UserDetail

Where there are multiple relationships between two table, such as from users to resources, fActiveRecord contains optional parameters for all related record methods that allows the proper relationship route to be specified. For details, please see the Relationship Routes section.

The detection of related tables allows for the following functionality to be provided:

*-to-one Relationships

When a column in a table contains a FOREIGN KEY constraint to another table, the two tables are in either a many-to-one or one-to-one relationship. The relationship would only be one-to-one if there was a UNIQUE constraint on the column with the FOREIGN KEY.

For instance, the resources table references the users table via the owner column. Because this FOREIGN KEY exists, it is possible to call a create action to instantiate the proper User object.

$resource = new Resource(1);
$user = $resource->createUser();

Since the user_details table references the user_id as a PRIMARY KEY, there can only be a single user_details record for each entry in users. This makes it a one-to-one relationship. For one-to-one relationships, it is also possible to call the populate and has actions just like *-to-many relationships. When working with one-to-one relationships, the related record name is singular rather than plural.

$user = new User(1);
$user->populateUserDetail();
if ($user->hasUserDetail()) {
    // …
}

*-to-many Relationships

When a column is referenced by a FOREIGN KEY constraint in another table, the two tables involved will end up being in either a one-to-many or many-to-many relationship. Many-to-many relationships happen when a joining table is used and the FOREIGN KEY constraints live in the joining table.

For the examples below the one-to-many relationship between the users table and the favorites table will be used along with the many-to-many relationship between the users and groups tables.

Building, Listing, Counting and Has

Each user on the system can have multiple favorites simply by creating new favorites and having each one reference the same user. The build action will create an fRecordSet of all records in such a relationship:

$favorites = $user->buildFavorites();

The $favorites record set may be empty, or it may contain quite a number of records.

In situations where the related records don’t need to be created, but a primary key will suffice, it is also possible to list related records. This will return an array of related primary keys.

$favorite_ids = $user->listFavorites();

The count action can be called for any related record and it will return the number of related records.

$number_of_favorites = $user->countFavorites();

If a number of records is not required, but just that related records exist, the has action can be used.

if ($user->hasFavorites()) {
    // …
}

Both one-to-many and many-to-many relationships support the build, count, list and has actions.

Associating

In many-to-many relationships, both types of records can exist without directly referencing each other. Thus often it is necessary to take one set of records and associate it with a specific record. The associate action will do this, such as below where a user is being associated with every group.

$groups = fRecordSet::build('Group');
$user->associateGroups($groups);
$user->store();

associate methods will accept an fRecordSet, an array of fActiveRecord objects or an array of primary keys. It is also possible to call associate methods to associate records in a one-to-many relationship, however the link action discussed next only works with many-to-many relationships.

It is also possible to parse associations from the fields in an HTTP request. In that situation the HTML form fields must be named in the format {plural_underscore_related_class}::{foreign_column}[]. The link action will grab values from the HTTP request and use them for associating records in a many-to-many relationship.

<form action="" method="post">
    <fieldset>
        <p>
            <label for="first_name">First Name</label>
            <input type="text" id="first_name" name="first_name" />
        </p>
        <p>
            <label for="last_name">Last Name</label>
            <input type="text" id="last_name" name="last_name" />
        </p>
        <p>
            <label for="email">Email</label>
            <input type="text" id="email" name="email" />
        </p>
        <p>
            <label for="group_a">Group A</label>
            <input type="checkbox" id="group_a" name="groups::name[]" value="Group A" />
        </p>
        <p>
            <label for="group_b">Group B</label>
            <input type="checkbox" id="group_b" name="groups::name[]" value="Group B" />
        </p>
        <p>
            <label for="group_c">Group C</label>
            <input type="checkbox" id="group_c" name="groups::name[]" value="Group C" />
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>
$user->linkGroups();
$user->store();

Please note that store() must be called to actually save the new relationships between the two tables.

Populating

For records that have "child" object in one-to-many relationships, the populate action will call the populate() method for each of a list of related records. In addition, when store() is called on the master record, all of the child records will be saved too.

Below is an example of the kind of HTML form that is needed for creating and populating child objects. Normally the HTML for the child object would be added and removed from the page on the fly using javascript, otherwise the validate() method from a child object could stop the records from being saved when a child object’s values are not complete. The initial printing of the HTML form elements is normally handled server side by iterating trough the fRecordSet of related records.

Each set of inputs for child object should always contain the PRIMARY KEY column and the column with the FOREIGN KEY constraint that references this table. Any other columns should only be included if new data is desired.

Each input for a related record needs to be prefixed with the plural underscore_notation version of the class name plus ::. In addition, each input should use array syntax at the end with a key shared for all inputs of the same record. The key can be any number or string, but must be the same for each input of the record. The example below uses 0, 1 and 2 as a simple example.

<form action="" method="post">
    <fieldset>
        <p>
            <label for="first_name">First Name</label>
            <input type="text" id="first_name" name="first_name" />
        </p>
        <p>
            <label for="last_name">Last Name</label>
            <input type="text" id="last_name" name="last_name" />
        </p>
        <p>
            <label for="email">Email</label>
            <input type="text" id="email" name="email" />
        </p>
        <p>
            <label for="fav_1">Favorite #1</label>
            <input type="name" id="fav_1" name="favorites::url[0]" value="" />
            <input type="hidden" name="favorites::user_id[0]" value="2" />
            <input type="hidden" name="favorites::favorite_id[0]" value="" />
        </p>
        <p>
            <label for="fav_2">Favorite #2</label>
            <input type="checkbox" id="fav_2" name="favorites::url[1]" value="" />
            <input type="hidden" name="favorites::user_id[1]" value="2" />
            <input type="hidden" name="favorites::favorite_id[1]" value="" />
        </p>
        <p>
            <label for="fav_3">Favorite #3</label>
            <input type="checkbox" id="fav_3" name="favorites::url[2]" value="C" />
            <input type="hidden" name="favorites::user_id[2]" value="2" />
            <input type="hidden" name="favorites::favorite_id[2]" value="" />
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>

The following PHP would actually create 3 Favorite objects and would set them to be saved when store() is called on the User object.

try {
    $user->populate();
    $user->populateFavorites();
    $user->store();
 
} catch (fExpectedException $e) {
    echo $e->printMessage();
}

Relationship Routes

When there are multiple one-to-many, many-to-many or *-to-one relationships for two tables, the proper route must be specified when calling the various related record methods. The appropriate route name can be determined by viewing the Relationship Routes section of the ORM Conventions page.

Routes can be specified in any of the following methods even if only one route exists, however that route will be automatically detected if not specified.

The build, count, create, link, list and populate action methods all optionally accept the route as the first parameter.

$record->createRelatedRecord('route');
 
$record->buildRelatedRecords('route');
 
$record->countRelatedRecords('route');
 
$record->linkRelatedRecords('route');
 
$record->listRelatedRecords('route');
 
$record->populateRelatedRecords('route');

associate action methods optionally accept the route as the second parameter.

$record->associateRelatedRecords($related_records, 'route');

When working with relationship routes in HTML forms, the relationship route name should be enclosed in {} directly after the foreign table name. This applies to forms being used with both the link and populate actions. Below is an example of a form for selecting the resources related to a user:

<p>
    <input type="checkbox" id="resource_1" name="resources{read_permissions}::resource_id[]" value="1" />
    <label for="resource_1">Resource 1</label>
</p>
<p>
    <input type="checkbox" id="resource_2" name="resources{read_permissions}::resource_id[]" value="2" />
    <label for="resource_2">Resource 2</label>
</p>

Configuration

As mentioned in the Setup section, the instance method configure() is called once per script execution for each classes that extends fActiveRecord. This method is designed to be the preferred place to execute any configuration code for an active record class.

class User extends fActiveRecord
{
    protected function configure()
    {
        // Configure the extra feature and overrides using the supporting ORM classes
    }
}

The following classes provide methods that extends and change the way that fActiveRecord classes work. Each class’s documentation page will include the necessary details on how to configure each bit of functionality and how it affects the standard use of active record classes.

fORM Provides functionality to override database table to class mapping, change the names used for records and column and extends fActiveRecord and fRecordSet through registering callbacks for various hooks
fORMColumn Allows changing the validation of columns to require email addresses or links, can configure columns to always create fNumber objects when loaded or have a column filled with a random string the first time a record is saved
fORMDate Can set columns to automatically save the date a record was created or last updated and can tie a timestamp column to another column to allow for saving timezones
fORMFile Provides functionality to automatically handle file or image uploads, including options to automatically duplicate and manipulate images
fORMJSON Extends both fActiveRecord and fRecordSet to have toJSON() methods
fORMMoney Can set columns to be loaded out of the database as fMoney objects and can tie fMoney columns to a second column to store currencies
fORMOrdering Allows configuring a column (either individually or in a group of columns) as the arbitrary ordering column for a class
fORMRelated Provides functionality to set the order in which related records are returned or modify what they are called (in context)
fORMValidation Allows setting additional validation rules (including conditional, one-or-more, many-to-many, etc), set the order for validation messages and configure UNIQUE constraints as case-insensitive

fActiveRecord API Referenceabstract classv1.0.0b67

An active record pattern base class

This class uses fORMSchema to inspect your database and provides an OO interface to a single database table. The class dynamically handles method calls for getting, setting and other operations on columns. It also dynamically handles retrieving and storing related records.

Changes:
1.0.0b67 Updated code to work with the new fORM API 8/6/10
1.0.0b66 Fixed a bug with store() and non-primary key auto-incrementing columns 7/5/10
1.0.0b65 Fixed bugs with inspect() making some min_value and max_value elements available for non-numeric types, fixed reflect() to list the min_value and max_value elements 6/8/10
1.0.0b64 BackwardsCompatibilityBreak - changed validate()'s returned messages array to have field name keys - added the option to validate() to remove field names from messages 5/26/10
1.0.0b63 Changed how is_subclass_of() is used to work around a bug in 5.2.x 4/6/10
1.0.0b62 Fixed a bug that could cause infinite recursion starting in v1.0.0b60 4/2/10
1.0.0b61 Fixed issues with handling populate actions when working with mapped classes 3/31/10
1.0.0b60 Fixed issues with handling associate and has actions when working with mapped classes, added validateClass() 3/30/10
1.0.0b59 Changed an extended UTF-8 arrow character into the correct -> 3/29/10
1.0.0b58 Fixed reflect() to specify the value returned from set methods 3/15/10
1.0.0b57 Added the post::loadFromIdentityMap() hook and fixed __construct() to always call the post::__construct() hook 3/14/10
1.0.0b56 Fixed $force_cascade in delete() to work even when the restricted relationship is once-removed through an unrestricted relationship 3/9/10
1.0.0b55 Fixed load() to that related records are cleared, requiring them to be loaded from the database 3/4/10
1.0.0b54 Fixed detection of route name for one-to-one relationships in delete() 3/3/10
1.0.0b53 Fixed a bug where related records with a primary key that contained a foreign key with an on update cascade clause would be deleted when changing the value of the column referenced by the foreign key 12/17/09
1.0.0b52 Backwards Compatibility Break - Added the $force_cascade parameter to delete() and store() - enabled calling prepare() and encode() for non-column get methods, added ::has{RelatedRecords}() methods 12/16/09
1.0.0b51 Made changed() properly recognize that a blank string and NULL are equivalent due to the way that set() casts values 11/14/09
1.0.0b50 Fixed a bug with trying to load by a multi-column primary key where one of the columns was not specified 11/13/09
1.0.0b49 Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09
1.0.0b48 Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b47 Changed ::associate{RelatedRecords}(), ::link{RelatedRecords}() and ::populate{RelatedRecords}() to allow for method chaining 10/22/09
1.0.0b46 Changed SQL statements to use value placeholders and identifier escaping 10/22/09
1.0.0b45 Added support for !~, &~, >< and OR comparisons to checkConditions(), made object handling in checkConditions() more robust 9/21/09
1.0.0b44 Updated code for new fValidationException API 9/18/09
1.0.0b43 Updated code for new fRecordSet API 9/16/09
1.0.0b42 Corrected a grammar bug in hash() 9/9/09
1.0.0b41 Fixed a bug in the last version that would cause issues with classes containing a custom class to table mapping 9/1/09
1.0.0b40 Added a check to the configuration part of __construct() to ensure modelled tables have primary keys 8/26/09
1.0.0b39 Changed set{ColumnName}() methods to return the record for method chaining, fixed a bug with loading by multi-column unique constraints, fixed a bug with load() 8/26/09
1.0.0b38 Updated changed() to do a strict comparison when at least one value is NULL 8/17/09
1.0.0b37 Changed __construct() to allow any Iterator object instead of just fResult 8/12/09
1.0.0b36 Fixed a bug with setting NULL values from v1.0.0b33 8/10/09
1.0.0b35 Fixed a bug with unescaping data in loadFromResult() from v1.0.0b33 8/10/09
1.0.0b34 Added the ability to compare fActiveRecord objects in checkConditions() 8/7/09
1.0.0b33 Performance enhancements to __call() and __construct() 8/7/09
1.0.0b32 Changed delete() to remove auto-incrementing primary keys after the post::delete() hook 7/29/09
1.0.0b31 Fixed a bug with loading a record by a multi-column primary key, fixed one-to-one relationship API 7/21/09
1.0.0b30 Updated reflect() for new fORM::callReflectCallbacks() API 7/13/09
1.0.0b29 Updated to use new fORM::callInspectCallbacks() method 7/13/09
1.0.0b28 Fixed a bug where records would break the identity map at the end of store() 7/9/09
1.0.0b27 Changed hash() from a protected method to a static public/internal method that requires the class name for non-fActiveRecord values 7/9/09
1.0.0b26 Added checkConditions() from fRecordSet 7/8/09
1.0.0b25 Updated validate() to use new fORMValidation API, including new message search/replace functionality 7/1/09
1.0.0b24 Changed validate() to remove duplicate validation messages 6/30/09
1.0.0b23 Updated code for new fORMValidation::validateRelated() API 6/26/09
1.0.0b22 Added support for the $formatting parameter to encode methods on char, text and varchar columns 6/19/09
1.0.0b21 Performance tweaks and updates for fORM and fORMRelated API changes 6/15/09
1.0.0b20 Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b19 Added list{RelatedRecords}() methods, updated code for new fORMRelated API 6/2/09
1.0.0b18 Changed store() to use new fORMRelated::store() method 6/2/09
1.0.0b17 Added some missing parameter information to reflect() 6/1/09
1.0.0b16 Fixed bugs in __clone() and replicate() related to recursive relationships 5/20/09
1.0.0b15 Fixed an incorrect variable reference in store() 5/6/09
1.0.0b14 store() no longer tries to get an auto-incrementing ID from the database if a value was set 5/2/09
1.0.0b13 delete(), load(), populate() and store() now return the record to allow for method chaining 3/23/09
1.0.0b12 set() now removes commas from integers and floats to prevent validation issues 3/22/09
1.0.0b11 encode() no longer adds commas to floats 3/22/09
1.0.0b10 __wakeup() no longer registers the record as the definitive copy in the identity map 3/22/09
1.0.0b9 Changed __construct() to populate database default values when a non-existing record is instantiated 1/12/09
1.0.0b8 Fixed exists() to properly detect cases when an existing record has one or more NULL values in the primary key 1/11/09
1.0.0b7 Fixed __construct() to not trigger the post::__construct() hook when force-configured 12/30/08
1.0.0b6 __construct() now accepts an associative array matching any unique key or primary key, fixed the post::__construct() hook to be called once for each record 12/26/08
1.0.0b5 Fixed replicate() to use plural record names for related records 12/12/08
1.0.0b4 Added replicate() to allow cloning along with related records 12/12/08
1.0.0b3 Changed __clone() to clone objects contains in the values and cache arrays 12/11/08
1.0.0b2 Added the __clone() method to properly duplicate a record 12/4/08
1.0.0b The initial implementation 8/4/07

Static Variables

::$callback_cacheprotected

Caches callbacks for methods

Type

array

::$configuredprotected

An array of flags indicating a class has been configured

Type

array

::$identity_mapprotected

Maps objects via their primary key

Type

array

::$method_name_cacheprotected

Caches method name parsings

Type

array

::$replicate_levelprotected

Keeps track of the recursive call level of replication so we can clear the map

Type

integer

::$replicate_mapprotected

Keeps a list of records that have been replicated

Type

array

::$unescape_mapprotected

Contains a list of what columns in each class need to be unescaped and what data type they are

Type

array

Variables

->cacheprotected

A data store for caching data related to a record, the structure of this is completely up to the developer using it

Type

array

->old_valuesprotected

The old values for this record

Column names are the keys, but a column key will only be present if a value has changed. The value associated with each key is an array of old values with the first entry being the oldest value. The static methods assign(), changed(), hasOld() and retrieveOld() are the best way to interact with this array.

Type

array

Records that are related to the current record via some relationship

This array is used to cache related records so that a database query is not required each time related records are accessed. The fORMRelated class handles most of the interaction with this array.

Type

array

->valuesprotected

The values for this record

This array always contains every column in the database table as a key with the value being the current value.

Type

array

Static Methods

::assign() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Sets a value to the $values array, preserving the old value in $old_values

Signature

void assign( array &$values, array &$old_values, string $column, mixed $value )

Parameters
array &$values The current values
array &$old_values The old values
string $column The column to set
mixed $value The value to set
::changed() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Checks to see if a value has changed

Signature

boolean changed( array &$values, array &$old_values, string $column )

Parameters
array &$values The current values
array &$old_values The old values
string $column The column to check
Returns

If the value for the column specified has changed

::checkClass() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Ensures a class extends fActiveRecord

Signature

boolean checkClass( string $class )

Parameters
string $class The class to check
Returns

If the class is an fActiveRecord descendant

::checkConditions() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Checks to see if a record matches all of the conditions

Signature

boolean checkConditions( fActiveRecord $record, array $conditions )

Parameters
fActiveRecord $record The record to check
array $conditions The conditions to check - see fRecordSet::filter() for format details
Returns

If the record meets all conditions

::compose() protected

Composes text using fText if loaded

Signatures

string compose( string $message, mixed $component )

string compose( mixed .. )

Parameters
string $message The message to compose
mixed $component A string or number to insert into the message
mixed .. ...
Returns

The composed and possible translated message

::forceConfigure() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Ensures that configure() has been called for the class

Signature

void forceConfigure( string $class )

Parameters
string $class The class to configure
::hash() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Takes a row of data or a primary key and makes a hash from the primary key

Signature

string|NULL hash( fActiveRecord|array|string|int $record, string $class=NULL )

Parameters
fActiveRecord|array|string|int $record An fActiveRecord object, an array of the records data, an array of primary key data or a scalar primary key value
string $class The class name, if $record isn't an fActiveRecord
Returns

A hash of the record's primary key value or NULL if the record doesn't exist yet

::hasOld() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Checks to see if an old value exists for a column

Signature

boolean hasOld( array &$old_values, string $column )

Parameters
array &$old_values The old values
string $column The column to set
Returns

If an old value for that column exists

::reset() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Resets the configuration of the class

Signature

void reset( )

::retrieveOld() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Retrieves the oldest value for a column or all old values

Signature

mixed retrieveOld( array &$old_values, string $column, mixed $default=NULL, boolean $return_all=FALSE )

Parameters
array &$old_values The old values
string $column The column to get
mixed $default The default value to return if no value exists
boolean $return_all Return the array of all old values for this column instead of just the oldest
Returns

The old value for the column

::validateClass() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Ensures a class extends fActiveRecord

Signature

void validateClass( string $class )

Parameters
string $class The class to verify

Methods

->__construct() public

Creates a new record or loads one from the database - if a primary key or unique key is provided the record will be loaded

Signature

fActiveRecord __construct( mixed $key=NULL )

Parameters
mixed $key The primary key or unique key value(s) - single column primary keys will accept a scalar value, all others must be an associative array of (string) {column} => (mixed) {value}
Throws
fNotFoundException
When the record specified by $key can not be found in the database
->__call() public

Handles all method calls for columns, related records and hook callbacks

Dynamically handles get, set, prepare, encode and inspect methods for each column in this record. Method names are in the form verbColumName().

This method also handles associate, build, count, has, and link verbs for records in many-to-many relationships; build, count, has and populate verbs for all related records in one-to-many relationships and create, has and populate verbs for all related records in one-to-one relationships, and the create verb for all related records in many-to-one relationships.

Method callbacks registered through fORM::registerActiveRecordMethod() will be delegated via this method.

Signature

mixed __call( string $method_name, array $parameters )

Parameters
string $method_name The name of the method called
array $parameters The parameters passed
Returns

The value returned by the method called

->__clone() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Creates a clone of a record

If the record has an auto incrementing primary key, the primary key will be erased in the clone. If the primary key is not auto incrementing, the primary key will be left as-is in the clone. In either situation the clone will return FALSE from the exists() method until store() is called.

Signature

fActiveRecord __clone( )

->__get() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

All requests that hit this method should be requests for callbacks

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__wakeup() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Configure itself when coming out of the session. Records from the session are NOT hooked into the identity map.

Signature

void __wakeup( )

->configure() protected

Allows the programmer to set features for the class

This method is only called once per page load for each class.

Signature

void configure( )

->constructInsertParams() protected

Creates the fDatabase::translatedQuery() insert statement params

Signature

array constructInsertParams( )

Returns

The parameters for an fDatabase::translatedQuery() SQL insert statement

->constructUpdateParams() protected

Creates the fDatabase::translatedQuery() update statement params

Signature

array constructUpdateParams( )

Returns

The parameters for an fDatabase::translatedQuery() SQL update statement

->delete() public

Deletes a record from the database, but does not destroy the object

This method will start a database transaction if one is not already active.

Signature

fActiveRecord delete( boolean $force_cascade=FALSE )

Parameters
boolean $force_cascade When TRUE, this will cause all child objects to be deleted, even if the ON DELETE clause is RESTRICT or NO ACTION
Returns

The record object, to allow for method chaining

->encode() protected

Retrieves a value from the record and prepares it for output into an HTML form element.

Below are the transformations performed:

  • varchar, char, text: will run through fHTML::encode(), if TRUE is passed the text will be run through fHTMLconvertNewLinks() and fHTML::makeLinks()
  • float: takes 1 parameter to specify the number of decimal places
  • date, time, timestamp: format() will be called on the fDate/fTime/fTimestamp object with the 1 parameter specified
  • objects: the object will be converted to a string by __toString() or a (string) cast and then will be run through fHTML::encode()
  • all other data types: the value will be run through fHTML::encode()
Signature

string encode( string $column, string $formatting=NULL )

Parameters
string $column The name of the column to retrieve
string $formatting The formatting string
Returns

The encoded value for the column specified

->exists() public

Checks to see if the record exists in the database

Signature

boolean exists( )

Returns

If the record exists in the database

->fetchResultFromUniqueKey() protected

Loads a record from the database based on a UNIQUE key

Signature

void fetchResultFromUniqueKey( array $values )

Parameters
array $values The UNIQUE key values to try and load with
Throws
fNotFoundException
->get() protected

Retrieves a value from the record

Signature

mixed get( string $column )

Parameters
string $column The name of the column to retrieve
Returns

The value for the column specified

->inspect() protected

Retrieves information about a column

Signature

mixed inspect( string $column, string $element=NULL )

Parameters
string $column The name of the column to inspect
string $element The metadata element to retrieve
Returns

The metadata array for the column, or the metadata element specified

->load() public

Loads a record from the database

Signature

fActiveRecord load( )

Returns

The record object, to allow for method chaining

Throws
fNotFoundException
When the record could not be found in the database
->loadFromIdentityMap() protected

Tries to load the object (via references to class vars) from the fORM identity map

Signature

boolean loadFromIdentityMap( array $row, string $hash )

Parameters
array $row The data source for the primary key values
string $hash The unique hash for this record
Returns

If the load was successful

->loadFromResult() protected

Loads a record from the database directly from a result object

Signature

boolean loadFromResult( Iterator $result, boolean $ignore_identity_map=FALSE )

Parameters
Iterator $result The result object to use for loading the current object
boolean $ignore_identity_map If the identity map should be ignored and the values loaded no matter what
Returns

If the record was loaded from the identity map

->populate() public

Sets the values for this record by getting values from the request through the fRequest class

Signature

fActiveRecord populate( )

Returns

The record object, to allow for method chaining

->prepare() protected

Retrieves a value from the record and prepares it for output into html.

Below are the transformations performed:

  • varchar, char, text: will run through fHTML::prepare(), if TRUE is passed the text will be run through fHTMLconvertNewLinks() and fHTML::makeLinks()
  • boolean: will return 'Yes' or 'No'
  • integer: will add thousands/millions/etc. separators
  • float: will add thousands/millions/etc. separators and takes 1 parameter to specify the number of decimal places
  • date, time, timestamp: format() will be called on the fDate/fTime/fTimestamp object with the 1 parameter specified
  • objects: the object will be converted to a string by __toString() or a (string) cast and then will be run through fHTML::prepare()
Signature

string prepare( string $column, mixed $formatting=NULL )

Parameters
string $column The name of the column to retrieve
mixed $formatting The formatting parameter, if applicable
Returns

The formatted value for the column specified

->reflect() public

Generates a pre-formatted block of text containing the method signatures for all methods (including dynamic ones)

Signature

string reflect( boolean $include_doc_comments=FALSE )

Parameters
boolean $include_doc_comments If the doc block comments for each method should be included
Returns

A preformatted block of text with the method signatures and optionally the doc comment

->replicate() public

Generates a clone of the current record, removing any auto incremented primary key value and allowing for replicating related records

This method will accept three different sets of parameters:

  • No parameters: this object will be cloned
  • A single TRUE value: this object plus all many-to-many associations and all child records (recursively) will be cloned
  • Any number of plural related record class names: the many-to-many associations or child records that correspond to the classes specified will be cloned

The class names specified can be a simple class name if there is only a single route between the two corresponding database tables. If there is more than one route between the two tables, the class name should be substituted with a string in the format 'RelatedClass{route}'.

Signatures

fActiveRecord replicate( string $related_class=NULL )

fActiveRecord replicate( string .. )

Parameters
string $related_class The plural related class to replicate - see method description for details
string .. ...
Returns

The cloned record

->set() protected

Sets a value to the record

Signature

fActiveRecord set( string $column, mixed $value )

Parameters
string $column The column to set the value to
mixed $value The value to set
Returns

This record, to allow for method chaining

->store() public

Stores a record in the database, whether existing or new

This method will start database and filesystem transactions if they have not already been started.

Signature

fActiveRecord store( boolean $force_cascade=FALSE )

Parameters
boolean $force_cascade When storing related records, this will force deleting child records even if they have their own children in a relationship with an RESTRICT or NO ACTION for the ON DELETE clause
Returns

The record object, to allow for method chaining

Throws
fValidationException
When validate() throws an exception
->validate() public

Validates the values of the record against the database and any additional validation rules

Signature

void|array validate( boolean $return_messages=FALSE, boolean $remove_column_names=FALSE )

Parameters
boolean $return_messages If an array of validation messages should be returned instead of an exception being thrown
boolean $remove_column_names If column names should be removed from the returned messages, leaving just the message itself
Returns

If $return_messages is TRUE, an array of validation messages will be returned

Throws
fValidationException
When the record, or one of the associated records, violates one of the validation rules for the class or can not be properly stored in the database

fAuthorization

Class Resources

Contents

The fAuthorization class is a static class provides functionality to restrict access to pages based on either simple a authorization level or more complex access control lists (ACLs).

Setup

Since the class is static, no instantiation is required, however to use the features some setup will need to be performed. The only setup common to using either authorization levels or ACLs is to set up a login page. For maintainability, I recommend you perform your setup in a common configuration like described on the Getting Started page:

// Set up a login page in your local.config.php
fAuthorization::setLoginPage('/login/');

The login page URL should be an absolute URL, relative to the domain name.

If need be, the login page URL can be retrieved with the static method getLoginPage().

fURL::redirect(fAuthorization::getLoginPage());

Authorization Levels

The simplest way to control access to pages is to use authorization levels. Each user is assigned a single authorization level and can view any page that requires that level or a level below.

After you have setup your login page, you are going to need to define the different authorization levels. Just like with the login page, you will probably want to place these in a common configuration file. Here is a simple example:

fAuthorization::setAuthLevels(
    array(
        'admin' => 100,
        'user'  => 50,
        'guest' => 25
    )
);

You’ll notice that each authorization level is assigned a number. If a user has a number that is the same or above the required level, they can view a page. If not, they will be redirected to the login page.

The setUserAuthLevel() method provides the functionality to assign an authorization level to a user when they log in:

// This would be executed after the username and password were verified
fAuthorization::setUserAuthLevel('admin');

To actually require an authorization level you will need to call requireAuthLevel() at the top of your page:

fAuthorization::requireAuthLevel('user');

If you wish to use a user’s authorization level to control other aspects of your site, you can use the checkAuthLevel() method:

if (fAuthorization::checkAuthLevel('admin')) {
    // Execute admin specific code
}

Last, but not least, if you need to get the user’s authorization level, that can be accomplished by calling getUserAuthLevel():

$auth_level = fAuthorization::getUserAuthLevel();

Access Control Lists

Access control lists (ACLs) allow for more fine-grained permissions than authorization levels. With ACLs you associate resource names with specific permissions. For a user to be able to access a page they need to have the required permission for the resource specified.

ACLs do not require any setup beyond assigning the user’s ACLs when they log in. Also note that the string '*' acts as a wildcard when doing resource and permission comparisons.

// This would be executed after the username and password were verified
fAuthorization::setUserACLs(
    array(
        'posts'  => array('*'),
        'users'  => array('add', 'edit', 'delete'),
        'groups' => array('add'),
        '*'      => array('list')
    )
);

The above user ACLs would imply the user has permissions to: do anything with posts; add, edit and delete users; add groups and list anything.

To require a user have a specific ACL to view a page, use the method requireACL() at the top of a page:

fAuthorization::requireACL('users', 'list');

The above code would require a user to have the list permission for the users resource in order to view the page.

In addition to control page views, ACLs can be useful for controlling access on a smaller level. If you wish to perform a conditional branch based on a user’s ACLs you can use the checkACL() method:

if (fAuthorization::checkACL('users', 'edit')) {
    // Code to be executed for users who can edit users
}

If you need to get a list of all ACLs assigned to the current user (in the same array format they are set), you can use the getUserACLs() method:

$user_acls = fAuthorization::getUserACLs();

Logging In

Once you have decided if you wish to go with authorization levels or ACLs you can move on to actually logging the user in.

Sometimes when a user visits the login page, they will have entered the URL manually, or will have followed a link. In this sort of situation you will need a default page to redirect them to. The rest of the time users will usually get directed to the login page because they tried to access a restricted page. You can get this information with the getRequestedURL() method.

getRequestedURL() requires a single parameter, $clear, which controls if the requested URL is erased when returned, or if it is to be left for later access. A second, optional, parameter is the default URL to use if the user was not redirected to the login page.

Here is an example of logging a user in and redirecting them to the requested page (or the home page if no page was requested):

if ('login' == $action && fRequest::isPost()) {
    if ($email == 'john@example.com' && sha1($password) == '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8') {
        fAuthorization::setUserAuthLevel('admin');
        fURL::redirect(fAuthorization::getRequestedURL(TRUE, '/'));
    }
}

Please note that the code above is simplified to demonstration, please check out the fCryptography Class for information on hashing passwords.

If for some reason you need to manually set the requested URL, that can be accomplished with the setRequestedURL() method.

In addition, it is usually necessary to remember some sort of information about the user that is logging in so you can retrieve their information on other pages. The setUserToken() and getUserToken() methods allow storing some sort of user identifier and getting it back later. Here is the above example with the user token code added:

if ('login' == $action && fRequest::isPost()) {
    if ($email == 'john@example.com' && sha1($password) == '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8') {
        fAuthorization::setUserAuthLevel('admin');
        fAuthorization::setUserToken('john@example.com');
        fURL::redirect(fAuthorization::getRequestedURL(TRUE, '/'));
    }
}

Checking Status

If you are using either authorization levels or access control lists, there are two simple methods that can be used to tell if a user is logged in. To require a user is logged in to view a page you will want to use the requireLoggedIn(), whereas if you want to create conditions based on whether or not the user is logged in you can use the checkLoggedIn() method:

// Require a user is logged in
fAuthorization::requireLoggedIn();
 
// Branch based on the user’s login status
if (fAuthorization::checkLoggedIn()) {
    // Code to execute if the user is logged in
}

Remote IP Checking

Sometimes it may be a requirement to only execute code if the remote connection is coming from a specific IP address or range. The method checkIP() provides this functionality. A single parameter $ip_ranges is required, which should like the IP address, CIDR range or IP/subnet-mask combination to check for. It is also possible to pass an array of any valid IP/range descriptors.

// Check for the IP 192.168.1.1
if (fAuthorization::checkIP('192.168.1.1')) {
    // ...
}
 
// Check for the IPs 192.168.1.1-255
if (fAuthorization::checkIP('192.168.1.0/24')) {
    // ...
}
 
// Check for the IPs 192.168.1.1-255
if (fAuthorization::checkIP('192.168.1.0/255.255.255.0')) {
    // ...
}
 
// Check for the IPs 192.168.1.1-255 or 192.168.2.1
if (fAuthorization::checkIP(array('192.168.1.0/24', '192.168.2.1'))) {
    // ...
}

It is also possible to create named IPs/ranges using the method addNamedIPRange(). Simply define the $name and $ip_ranges and then you can use that name with checkIP():

// Create a named IP/range
fAuthorization::addNamedIPRange('office', '192.168.1.0/24');
 
// Check the named IP/range
if (fAuthorization::checkIP('office')) {
    // ...
}

Logging Out

When a user logs out, it is important that their authorization level or ACLs are erased. This is quite simple to do using the destroyUserInfo() method:

fAuthorization::destroyUserInfo();

Preventing Privilege Escalation (Security)

One of the simplest ways for an attacker to gain unauthorized privileges is via session fixation. An attack would be executed by sending a link with a known session ID to a user and then waiting for them to log in. Once the user has logged in, the attacker can send the same session ID and receive the privileges of the user. Below is an example:

http://example.com/login.php?PHPSESSID=abcdef1234567890

By default fSession prevents such an attack by requiring that session IDs be passed in cookies, however it is theoretically possible runtime configuration of the session.use_only_cookies INI setting could be disabled.

To prevent attacks based on knowledge of the user’s session ID, fAuthorization automatically regenerates the session ID via session_regenerate_id() whenever setUserACLs(), setUserAuthLevel() or setUserToken() is called. The attacker won’t know the newly regenerated ID, and won’t be able to access the user’s account.

fAuthorization API Referencestatic classv1.0.0b5

Allows defining and checking user authentication via ACLs, authorization levels or a simple logged in/not logged in scheme

Changes:
1.0.0b5 Added getLoginPage() 3/9/10
1.0.0b4 Updated class to use new fSession API 10/23/09
1.0.0b3 Updated class to use new fSession API 5/8/09
1.0.0b2 Fixed a bug with using named IP ranges in checkIP() 1/10/09
1.0.0b The initial implementation 6/14/07

Static Methods

::addNamedIPRange() public

Adds a named IP address or range, or array of addresses and/or ranges

This method allows checkIP() to be called with a name instead of the actual IPs.

Signature

void addNamedIPRange( string $name, mixed $ip_ranges )

Parameters
string $name The name to give the IP addresses/ranges
mixed $ip_ranges This can be string (or array of strings) of the IPs or IP ranges to restrict to - please see checkIP() for format details
::checkACL() public

Checks to see if the logged in user meets the requirements of the ACL specified

Signature

boolean checkACL( string $resource, string $permission )

Parameters
string $resource The resource we are checking permissions for
string $permission The permission to require from the user
Returns

If the user has the required permissions

::checkAuthLevel() public

Checks to see if the logged in user has the specified auth level

Signature

boolean checkAuthLevel( string $level )

Parameters
string $level The level to check against the logged in user's level
Returns

If the user has the required auth level

::checkIP() public

Checks to see if the user is from the IPs or IP ranges specified

The $ip_ranges parameter can be either a single string, or an array of strings, each of which should be in one of the following formats:

  • A single IP address:
    • 192.168.1.1
    • 208.77.188.166
  • A CIDR range
    • 192.168.1.0/24
    • 208.77.188.160/28
  • An IP/subnet mask combination
    • 192.168.1.0/255.255.255.0
    • 208.77.188.160/255.255.255.240
Signature

boolean checkIP( mixed $ip_ranges )

Parameters
mixed $ip_ranges A string (or array of strings) of the IPs or IP ranges to restrict to - see method description for details
Returns

If the user is coming from (one of) the IPs or ranges specified

::checkLoggedIn() public

Checks to see if the user has an auth level or ACLs defined

Signature

boolean checkLoggedIn( )

Returns

If the user is logged in

::destroyUserInfo() public

Destroys the user's auth level and/or ACLs

Signature

void destroyUserInfo( )

::getLoginPage() public

Returns the login page set via setLoginPage()

Signature

string getLoginPage( )

Returns

The login page users are redirected to if they don't have the required authorization

::getRequestedURL() public

Returns the URL requested before the user was redirected to the login page

Signature

string getRequestedURL( boolean $clear, string $default_url=NULL )

Parameters
boolean $clear If the requested url should be cleared from the session after it is retrieved
string $default_url The default URL to return if the user was not redirected
Returns

The URL that was requested before they were redirected to the login page

::getUserACLs() public

Gets the ACLs for the logged in user

Signature

array getUserACLs( )

Returns

The logged in user's ACLs

::getUserAuthLevel() public

Gets the authorization level for the logged in user

Signature

string getUserAuthLevel( )

Returns

The logged in user's auth level

::getUserToken() public

Gets the value that was set as the user token, NULL if no token has been set

Signature

mixed getUserToken( )

Returns

The user token that had been set, NULL if none

::requireACL() public

Redirect the user to the login page if they do not have the permissions required

Signature

void requireACL( string $resource, string $permission )

Parameters
string $resource The resource we are checking permissions for
string $permission The permission to require from the user
::requireAuthLevel() public

Redirect the user to the login page if they do not have the auth level required

Signature

void requireAuthLevel( string $level )

Parameters
string $level The level to check against the logged in user's level
::requireLoggedIn() public

Redirect the user to the login page if they do not have an auth level or ACLs

Signature

void requireLoggedIn( )

::reset() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Resets the configuration of the class

Signature

void reset( )

::setAuthLevels() public

Sets the authorization levels to use for level checking

Signature

void setAuthLevels( array $levels )

Parameters
array $levels An associative array of (string) {level} => (integer) {value}, for each level
::setLoginPage() public

Sets the login page to redirect users to

Signature

void setLoginPage( string $url )

Parameters
string $url The URL of the login page
::setRequestedURL() public

Sets the restricted URL requested by the user

Signature

void setRequestedURL( string $url )

Parameters
string $url The URL to save as the requested URL
::setUserACLs() public

Sets the ACLs for the logged in user.

Array should be formatted like:

array (
    (string) {resource name} => array(
        (mixed) {permission}, ...
    ), ...
)

The resource name or the permission may be the single character '*' which acts as a wildcard.

Signature

void setUserACLs( array $acls )

Parameters
array $acls The logged in user's ACLs - see method description for format
::setUserAuthLevel() public

Sets the authorization level for the logged in user

Signature

void setUserAuthLevel( string $level )

Parameters
string $level The logged in user's auth level
::setUserToken() public

Sets some piece of information to use to identify the current user

Signature

void setUserToken( mixed $token )

Parameters
mixed $token The user's token. This could be a user id, an email address, a user object, etc.

fBuffer

Class Resources

Contents

The fBuffer class is a fairly straight-forward static class designed to make the output buffer functions in PHP a little more user-friendly. Only a single level of buffering is supported, however it has been supplemented with buffer capture support and replace functionality.

Output buffering is essential if you wish to change the headers a page sends after some of the output has been sent, and is also utilized by the fTemplating class for fully buffered output that allows changing values until right before the buffer is sent to the user.

Starting and Stopping

Normally when using the output buffer function you call ob_start() to start output buffering and one of the ob_end_*() methods to stop buffering. The fBuffer class instead uses the start() and stop() methods. Here is an example:

fBuffer::start();
// Execute code that produces output
// Send a header
// Execute more code that creates output
fBuffer::stop();

Gzip compression of the output buffer can be turned (if the zlib extension is installed) by passing TRUE to start().

// Start the output buffer with gzip compression
fBuffer::start(TRUE);

You can check to see if buffering has been started by calling isStarted():

if (!fBuffer::isStarted()) {
    fBuffer::start();
}

Getting, Erasing and Replacing

To get the current contents of the output buffer, simply call get():

$current_buffer = fBuffer::get();

If you wish to get rid of the buffered contents, call erase():

// Get rid of the current buffer contents
fBuffer::erase();

Also sometimes useful is the ability to replace a given string in the buffer with another. In this situation you can use the replace() method:

// Output stuff
 
// This would replace every instance of 'foo' with 'bar'
fBuffer::replace('foo', 'bar');

Capturing

Some of the built-in PHP functions (and other third party code) will only output content, as opposed to returning it for further processing. The fBuffer class provides two methods, startCapture() and stopCapture(), that make it easy to intercept such output.

// Begin capturing, everything passed to print or echo after here will be captured
fBuffer::startCapture();
 
// Execute code to output content
// ...
 
// Grab the captured output
$captured_content = fBuffer::stopCapture();

fBuffer API Referencestatic classv1.0.0b3

Used By

Provides a single, simplified interface for output buffering to prevent nested buffering issues and provide a more logical API

Changes:
1.0.0b3 Added a check to ensure the zlib extension is installd when doing gzipped buffering 5/20/10
1.0.0b2 Added the $gzip parameter to start() 5/19/10
1.0.0b The initial implementation 3/16/08

Static Methods

::erase() public

Erases the output buffer

Signature

void erase( )

::get() public

Returns the contents of output buffer

Signature

string get( )

Returns

The contents of the output buffer

::isStarted() public

Checks if buffering has been started

Signature

boolean isStarted( )

Returns

If buffering has been started

::replace() public

Replaces a value in the output buffer

Signature

void replace( string $find, string $replace )

Parameters
string $find The string to find
string $replace The string to replace
::reset() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Resets the configuration and buffer of the class

Signature

void reset( )

::start() public

Starts output buffering

Signature

void start( boolean $gzip=FALSE )

Parameters
boolean $gzip If the buffered output should be gzipped using ob_gzhandler()
::startCapture() public

Starts capturing output, should be used with stopCapture() to grab output from code that does not offer an option of returning a value instead of outputting it

Signature

void startCapture( )

::stop() public

Stops output buffering, flushing everything to the browser

Signature

void stop( )

::stopCapture() public

Stops capturing output, returning what was captured

Signature

string stopCapture( )

Returns

The captured output

fCRUD

Class Resources

Contents

The fCRUD class is a static class that provides functionality for the CRUD (create, read, update, delete) pages that power most dynamic websites and online applications. The various methods have been implemented to help reduce the amount of code needed to a standard page, letting the developer focus on the unique aspects of sites.

Sticky Search Values

When filtering the objects to display when executing the list action for a CRUD page, it will often times be required to remember the filtered values even after the user had edited or deleted an object. While it is possible to pass the filter values around in the URL, it leads to lots of extra code and more complex URLs.

As a solution, the getSearchValue() method will always return the last value selected by a user for a given GET or POST parameter. When a value is pulled out of GET or POST data, the value is saved in the user’s session. If the user then leaves the page and comes back without a value, the value will be looked up in the session. This method should be used in place of fRequest::get().

Here is an example of the usage:

// Get the parameter search_terms from GET, POST or the session
$search_terms = fCRUD::getSearchValue('search_terms');
 
// Here $search_terms would be used to filter the objects being displayed
// The SetCreator class is not a real class, but used as an example
$results = SetCreator::findUsers($search_terms);

In order to provide for accessibility and usability, it is recommended to redirect the user whenever values are pulled from a location other than GET data. This way the user can bookmark results, send a link via email, etc. The redirectWithLoadedValues() method will take the currently requested URL and will redirect the user to a new URL that includes all of the search values (and sorting values) that were pulled from the session. If no values are pulled from the session, no redirection will happen.

Here is the example above with the redirection code added:

// Get the parameter search_terms from GET, POST or the session
$search_terms = fCRUD::getSearchValue('search_terms');
 
// Redirect the user if any values were loaded from the session
fCRUD::redirectWithLoadedValues();
 
// Here $search_terms would be used to filter the objects being displayed
// The SetCreator class is not a real class, but used as an example
$results = SetCreator::findUsers($search_terms);

Sortable Columns (Sticky)

Having sortable columns on a CRUD page is often times be a huge usability boost. Unfortunately sortable columns can also be a pain to implement. The fCRUD class provides a few methods to help make it a little easier: getSortColumn(), getSortDirection(), printSortableColumn() and getColumnClass().

The getSortColumn() takes a single parameter, a $possible_columns array, and returns the one specified by the GET value for the parameter sort. The method also will save the last sort column and will reload it from the session if none is specified in the GET data. If neither of these methods can determine the sort column it will default to the first value in the $possible_columns array.

getSortDirection() takes a single parameter, $default_direction, and will return the sorting direction specified in the GET data for the parameter dir. The possible values for $default_direction (and the return value) are 'asc' and 'desc'. If no value is specified in the GET data, it will try to load the last sort direction from the session. If this can not be done the sort direction will default to the values specified in $default_direction.

These two methods work with the redirectWithLoadedValues() method the same way that getSearchValue() does. Here is an example of the three methods being used:

// Set the users to be sortable by name or email, defaulting to name
$sort = fCRUD::getSortColumn(array('name', 'email'));
 
// Set the sorting to default to ascending
$dir  = fCRUD::getSortDirection('asc');
 
// Redirect the user if one of the values was loaded from the session
fCURD::redirectWithLoadedValues();
 
// Use the sort column and direction in your code to load the objects in the proper order
$users = SetCreator::findUsers($sort, $dir);

The second step to getting sortable columns on CRUD pages is to create the links to allow sorting. The printSortableColumn() method accomplishes this task.

printSortableColumn() takes two parameters, with the second one being optional. The first is the $column to make sortable. This value will be returned when calling getSortColumn(). The second, optional, parameter $column_name allows you to specify a display name to be used for $column. If $column_name is not specified, the fGrammar::humanize()ed version of $column will be used instead.

printSortableColumn() prints out an a tag containing a link to the current URL with the current query string, except the sort and dir parameters will be changed to the correct values for the link. If a user clicks a sortable column link that is already sorting the object, the direction will be reversed. In addition, the a tag will have the CSS class sortable_column applied to it. If the link being created is for the column currently being sorted, a second CSS class asc or desc will be added to the a tag as appropriate. Here is an example of the PHP and corresponding HTML:

<table>
    <tr>
        <th><? fCRUD::printSortableColumn('name') ?></th>
        <th><? fCRUD::printSortableColumn('email', 'E-Mail') ?></th>
    </tr>
    ...
</table>
<!-- The following HTML is presented as if the name column is currently being sorted ascending -->
<table>
    <tr>
        <th><a href="{current_url}?sort=name&amp;dir=desc" class="sortable_column asc">Name</a></th>
        <th><a href="{current_url}?sort=email&amp;dir=asc" class="sortable_column">E-Mail</a></th>
    </tr>
    ...
</table>

Finally, when displaying the object information it might be nice to include a visual indication of which column is sorted on each row. This way if the user has scrolled so the column headers are out of view, they can still remember which column is being sorted. This can be accomplished by using the getColumnClass() method:

<tr>
    <td class="<? fCRUD::getColumnClass('name') ?>"><?php echo $object->getName() ?></td>
    <td class="<? fCRUD::getColumnClass('email') ?>"><?php echo $object->getEmail() ?></td>
</tr>
<!-- The following HTML is presented as if the name column is currently being sorted ascending -->
<tr>
    <td class="sorted">Will</th>
    <td class="">will@example.com</td>
</tr>

If you are using columns from related tables, simple include the table name and a . when passing the column name to printSortableColumn() and getColumnClass(). To sort by the name column in the groups table, just use groups.name.

Resetting Sticky Values

Since search values and sortable columns are saved in the sessions, whenever a user returns to the page the saved values will be loaded. If you want to allow the user to reset their stored values, simply add a reset parameter (without an = or a value) to the end of the query string. This will cause all stored values to be erased, and the user to be redirected to the same URL without the reset parameter. Here are a couple of examples:

<!--
All sticky search values and sorting information would be erased the the user would be redirected to /users/
-->
<a href="/users/?reset">Users</a>
 
<!--
In this case the user would be redirected to /galleries/?gallery_id=3
-->
<a href="/galleries/?gallery_id=3&reset">Gallery</a>
 
<!--
This would do nothing since reset is not at the end of the query string
-->
<a href="/galleries/?reset&gallery_id=3">Gallery</a>
 
<!--
This would do nothing since reset is followed by =. By not allowing an =, we prevent the possibility of
conflicting with a real query string parameter called reset.
-->
<a href="/galleries/?gallery_id=3&reset=">Gallery</a>

Row Colors

A common feature for list tables is to include alternating row colors (via CSS) to allow users to track rows across a table. The getRowClass() method provides functionality to accomplish this in a dynamic way that will also highlight a row that has just been added or updated.

The method takes two parameters, $row_value and $affected_value. When these two values are not equal the method will return 'odd' and 'even' on an alternating basis. When the two parameters are equal the method will return highlighted.

Here is an example of usage:

$user_id = fRequest::get('user_id');
$users   = SetCreator::findUsers();
 
foreach ($users as $user) {
    ?>
    <tr class="<?php echo fCRUD::getRowClass($user->getUserId(), $user_id) ?>">
        <td><?php echo $user->getName() ?></td>
        <td><?php echo $user->getEmail() ?></td>
    </tr>
    <?
}

The output from the above PHP would be:

<!-- The following HTML is presented as if $user_id = 1 and the user id for Will was 1 -->
<tr class="odd">
    <td>Joe</td>
    <td>joe@example.com</td>
</tr>
<tr class="highlighted">
    <td>Will</td>
    <td>will@example.com</td>
</tr>
<tr class="even">
    <td>Zach</td>
    <td>zach@example.com</td>
</tr>

fCRUD API Referencestatic classv1.0.0b5

Provides miscellaneous functionality for CRUD-like pages

Changes:
1.0.0b5 Updated class to use new fSession API 10/23/09
1.0.0b4 Updated class to use new fSession API 5/8/09
1.0.0b3 Backwards Compatiblity Break - moved printOption() to fHTML::printOption(), showChecked() to fHTMLshowChecked(), removeListItems() and reorderListItems() to fException::splitMessage(), generateRequestToken() to fRequestgenerateCSRFToken(), and validateRequestToken() to fRequest::validateCSRFToken() 5/8/09
1.0.0b2 Fixed a bug preventing loaded search values from being included in redirects 3/18/09
1.0.0b The initial implementation 6/14/07

Static Methods

::getColumnClass() public

Return the string 'sorted' if $column is the column that is currently being sorted by, otherwise returns ''

This method will only be useful if used with the other sort methods printSortableColumn(), getSortColumn() and getSortDirection().

Signature

string getColumnClass( string $column )

Parameters
string $column The column to check
Returns

The CSS class for the column, either '' or 'sorted'

::getRowClass() public

Returns a CSS class name for a row

Will return 'even', 'odd', or 'highlighted' if the two parameters are equal and not NULL. The first call to this method will return the appropriate class concatenated with ' first'.

Signature

string getRowClass( mixed $row_value=NULL, mixed $affected_value=NULL )

Parameters
mixed $row_value The value from the row
mixed $affected_value The value that was just added or updated
Returns

The css class

::getSearchValue() public

Gets the current value of a search field

If a value is an empty string and no cast to is specified, the value will become NULL.

If a query string of ?reset is passed, all previous search values will be erased.

Signature

mixed getSearchValue( string $column, string $cast_to=NULL, string $default=NULL )

Parameters
string $column The column that is being pulled back
string $cast_to The data type to cast to
string $default The default value
Returns

The current value

::getSortColumn() public

Gets the current column to sort by, defaults to first one specified

Signatures

string getSortColumn( string $possible_column )

string getSortColumn( string .. )

Parameters
string $possible_column The columns that can be sorted by, defaults to first
string .. ...
Returns

The column to sort by

::getSortDirection() public

Gets the current sort direction

Signature

string getSortDirection( string $default_direction )

Parameters
string $default_direction The default direction, 'asc' or 'desc'
Returns

The direction, 'asc' or 'desc'

::printSortableColumn() public

Prints a sortable column header a tag

The a tag will include the CSS class 'sortable_column' and the direction being sorted, 'asc' or 'desc'.

fCRUD::printSortableColumn('name', 'Name');

would create the following HTML based on the page context

<!-- If name is the current sort column in the asc direction, the output would be -->
<a href="?sort=name&dir=desc" class="sorted_column asc">Name</a>
<!-- If name is not the current sort column, the output would be -->
<a href="?sort-name&dir=asc" class="sorted_column">Name</a>
Signature

void printSortableColumn( string $column, string $column_name=NULL )

Parameters
string $column The column to create the sortable header for
string $column_name This will override the humanized version of the column
::redirectWithLoadedValues() public

Checks to see if any values (search or sort) were loaded from the session, and if so redirects the user to the current URL with those values added

Signature

void redirectWithLoadedValues( )

::reset() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Resets the configuration and data of the class

Signature

void reset( )

fCache

Class Resources

Contents

The fCache class provides a consistent caching interface that can use file, APC, memcached and XCache back-ends. It can be used cache any end-developer data, but is also integrated with the fDatabase, fSQLTranslation and fSchema classes.

APC and XCache are PHP op-code caches that also provide server-wide shared memory caches for user data. File caches use a single file and store serialized PHP data, while memcached is a standalone caching server that can be scaled extensively across machines.

Instantiation

Creating an instance of fCache just requires a $type and $data_store. The $type can be 'apc', 'file', 'memcache' or 'xcache'. Only 'file' and 'memcache' caches require a $data_store.

For 'file' caches, the $data_store should be a file path. For 'memcache' caches, the $data_store should be a PHP Memcache object. Please note there is also a PHP Memcached (note the d at the end) extension, however it is not currently supported as it is PHP 5.2+ only and is current in beta.

$apc_cache = new fCache('apc');
 
$file_cache = new fCache('file', '/path/to/cache');
 
$memcache = new Memcache();
$memcache->connect('localhost', 11211);
$memcache_cache = new fCache('memcache', $memcache);
 
$xcache_cache = new fCache('xcache');

Setting Values

The method set() accepts a $key, $value and optional $ttl (time-to-live). The $key should be a string of 250 characters or less, and the $value should be any PHP data type that can be serialized. The main PHP data type that can not be serialized in a resource.

The $key and $value combination will be stored in the cache permanently, unless a $ttl is provided. The $ttl is the number of seconds the cached $value will be accessible. Values with a $ttl will be cleaned up by whatever back-end is providing the cache. Warning: the APC back-end currently functions in such a way that the $ttl will be ignored when getting a value in the same script execution that it is set.

Please note that all PHP values are serialized before being stored in the cache. This ensures that the exact same value that goes into the cache will come back out, even if the back-end only supports basic types such as strings and integers.

// This value will last until explicitly deleted or the cache is cleared
$file_cache->set('computed_value', $computed_value);
 
// This value will last for 60 seconds
$file_cache->set('other_computed_value', $other_computed_value, 60);

It is also possible to only set a value if the $key does not already exist in the cache. This is performed using the add() method. add() takes the exact same parameters as set(), however it returns a boolean indicating if the value was added.

if ($file_cache->add('master_value', $master_value)) {
    // Compute a related value and store it also
}

Getting Values

The method get() takes the $key to retrieve the value for, and an optional $default to return if the $key is not set. If $default is not specified, NULL will be returned for any $key that is not currently set.

// This will return NULL if there is no value for 'computed_value'
$cached_value = $file_cache->get('computed_value');
 
// This will return 10 if there is no value for 'computed_value'
$cached_value = $file_cache->get('computed_value', 10);

Deleting Values

Values can be deleted individually from the cache by calling the method delete() and passing the $key to delete.

$file_cache->delete('computed_value');

Clearing the Cache

In addition to deleting specific cache entries, it is also possible to clear all of the entries in the cache. This will delete all key/value pairs in your cache, and depending on your cache type, may affect all other websites on the same server.

Please note that the XCache back-end may require an administrator login and password to clear the cache. This setting, and the login/password settings are controlled by ini settings.

$file_cache->clear();

fCache API Referenceclassv1.0.0b

A simple interface to cache data using different backends

Changes:
1.0.0b The initial implementation 4/28/09

Variables

->cacheprotected

The data cache, only used for file caches

The array structure is:

array(
    (string) {key} => array(
        'value'  => (mixed) {the key's value},
        'expire' => (integer) {the timestamp to expire at, 0 for none}
    )
)
Type

array

->data_storeprotected

The data store to use - the file path for a file cache, Memcache object for memcache

Type

mixed

->stateprotected

The data state, only used for file caches

The valid values are:

  • 'clean'
  • 'dirty'
Type

string

->typeprotected

The type of cache

The valid values are:

  • 'apc'
  • 'file'
  • 'memcache'
  • 'xcache'
Type

string

Methods

->__construct() public

Set the type and master key for the cache

A file cache uses a single file to store values in an associative array and is probably not suitable for a large number of keys.

Using an apc or xcache cache will have far better performance than a file or directory, however please remember that keys are shared server-wide.

Signature

fCache __construct( string $type, mixed $data_store=NULL )

Parameters
string $type The type of caching to use: 'apc', 'file', 'memcache', 'xcache'
mixed $data_store The path for a file cache, or an Memcache object for a memcache cache - not used for apc or xcache
->__destruct() internal public

Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code

Cleans up after the cache object

Signature

void __destruct( )

->add() public

Tries to set a value to the cache, but stops if a value already exists

Signature

boolean add( string $key, mixed $value, integer $ttl=0 )

Parameters
string $key The key to store as, this should not exceed 250 characters
mixed $value The value to store, this will be serialized
integer $ttl The number of seconds to keep the cache valid for, 0 for no limit
Returns

If the key/value pair were added successfully

->clear() public

Clears the WHOLE cache of every key, use with caution!

xcache may require a login or password depending on your ini settings.

Signature

void clear( )

->delete() public

Deletes a value from the cache

Signature

void delete( string $key )

Parameters
string $key The key to delete
->get() public

Returns a value from the cache

Signature

mixed get( string $key, mixed $default=NULL )

Parameters
string $key The key to return the value for
mixed $default The value to return if the key did not exist
Returns

The cached value or the default value if no cached value was found

->save() public

Only valid for file caches, saves the file to disk and will randomly clean up expired values

Signature

void save( )

->set() public

Sets a value to the cache, overriding any previous value

Signature

void set( string $key, mixed $value, integer $ttl=0 )

Parameters
string $key The key to store as, this should not exceed 250 characters
mixed $value The value to store, this will be serialized
integer $ttl The number of seconds to keep the cache valid for, 0 for no limit

fConnectivityException

Class Resources

Exception Classes

fConnectivityException is a sub-class of fUnexpectedException that indicates some sort of unrecoverable connection error has occurred. This could be used in situations where a database server is not reachable, a remote API could not be contacted, or an FTP connection timed out.

This space intentionally left blank

fConnectivityException API Referenceclassv1.0.0b

An exception caused by a connectivity error

Changes:
1.0.0b The initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fUnexpectedException
         |
         --fConnectivityException
Inherited Variables
Exception::$code
The exception code
Exception::$file
The file the exception was thrown in
Exception::$line
The line the exception was thrown on
Exception::$message
The exception message
Inherited Methods
fUnexpectedException::printMessage()
Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()<