FUEL CMS User Guide : Version 1.5.2


Models

There are two main classes FUEL provides for creating models. The first, MY_Model, extends the CI_Model class to provide common methods for retrieving and manipulating data from the database usually specific to a table. It was developed to augment the CodeIgniter active record class and DOES NOT change any of those methods. The second, Base_module_model, extends MY_Model and provides simple module specific methods and is the class you will most likely want to extend for your models.

Both classes allow you to create simple to complex form interfaces with the model's MY_Model::form_fields() method, which gets passed to the Form_builder object to generate the forms in the CMS.

There's a lot to cover for model's. Below is an overview of the main features but it is recommended that you look at the API documentation for both table and record model classes (explained below).

Example

Below is an example of a quotes model (a simple module in the CMS), and the SQL for the table to house the quotes data.

CREATE TABLE `quotes` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `content` text COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `precedence` int(11) NOT NULL DEFAULT '0',
  `published` enum('yes','no') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'yes',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
require_once(FUEL_PATH.'models/Base_module_model.php');

class Quotes_model extends Base_module_model {

	public $required = array('content');

	function __construct()
	{
		parent::__construct('quotes'); // table name
	}

	function list_items($limit = NULL, $offset = NULL, $col = 'name', $order = 'asc')
	{
		$this->db->select('id, SUBSTRING(content, 1, 200) as content, name, title, precedence, published', FALSE);
		$data = parent::list_items($limit, $offset, $col, $order);
		return $data;
	}

	function _common_query()
	{
		parent::_common_query(); // to do active and published
		$this->db->order_by('precedence desc');
	}
}

class Quote_model extends Base_module_record {
	
	function get_quote_formatted()
	{
		return quote($this->_fields['content'], $this->name, $this->title);
	}

}
?>

For a tutorial on creating simple modules, click here.

MY_Model requires the following classes and helpers:

Table, Result, Record and Field Classes

Models are actually made up of 2-3 classes:

Initializing the Class

The Model class is initialized using the $this->load->model function:

$this->load->model('examples_model');

Once loaded, the Model object will be available using: $this->examples_model.

Configuring Model Information

Properties Reference

Property Default Value Description
public
auto_validate 1 Use auto-validation before saving
return_method auto Object, array, query, auto
auto_validate_fields
array('email|email_address' => 'valid_email', 'phone|phone_number' => 'valid_phone', )
fields to auto validate
required
array()
An array of required fields. If a key => val is provided, the key is name of the field and the value is the error message to display
default_required_message Please fill out the required field '%1s' The default required validator message
auto_date_add
array('date_added', 'entry_date')
Field names to automatically set the date when the value is NULL
auto_date_update
array('last_modified', 'last_updated')
Field names to automatically set the date on updates
date_use_gmt none Determines whether to use GMT time when storing dates and times
default_date none Default date value that get's passed to the model on save. Using 0000-00-00 will not work if it is a required field since it is not seen as an empty value
auto_trim 1 Will trim on clean
auto_encode_entities 1 Determines whether to automatically encode html entities. An array can be set instead of a boolean value for the names of the fields to perform the safe_htmlentities on
xss_clean none Determines whether automatically run the xss_clean. An array can be set instead of a boolean value for the names of the fields to perform the xss_clean on
readonly none Sets the model to readonly mode where you can't save or delete data
hidden_fields
array()
Fields to hide when creating a form
unique_fields
array()
Fields that are not IDs but are unique. Can also be an array of arrays for compound keys
linked_fields
array()
Fields that are linked meaning one value helps to determine another. Key is the field, value is a function name to transform it. (e.g. array('slug' => 'title'), or array('slug' => array('name' => 'strtolower')));
serialized_fields
array()
Fields that contain serialized data. This will automatically serialize before saving and unserialize data upon retrieving
default_serialization_method json The default serialization method. Options are 'json' and 'serialize'
boolean_fields
array()
Fields that are tinyint and should be treated as boolean
suffix _model The suffix used for the data record class
foreign_keys
array()
Map foreign keys to table models
has_many
array()
Keys are model, which can be a key value pair with the key being the module and the value being the model, module (if not specified in model parameter), relationships_model, foreign_key, candidate_key
belongs_to
array()
Keys are model, which can be a key value pair with the key being the module and the value being the model, module (if not specified in model parameter), relationships_model, foreign_key, candidate_key
representatives
array()
An array of fields that have arrays or regular expression values to match against different field types (e.g. 'number'=>'bigint|smallint|tinyint|int')
custom_fields
array()
An array of field names/types that map to a specific class
formatters
array()
An array of helper formatter functions related to a specific field type (e.g. string, datetime, number), or name (e.g. title, content) that can augment field results
protected
db N/A
table_name N/A
key_field N/A Usually the tables primary key(s)... can be an array if compound key
normalized_save_data N/A The saved data before it is cleaned
cleaned_data N/A Data after it is cleaned
dsn N/A The DSN string to connect to the database... if blank it will pull in from database config file
has_auto_increment N/A Does the table have auto_increment?
record_class N/A The name of the record class (if it can't be determined)
friendly_name N/A A friendlier name of the group of objects
singular_name N/A A friendly singular name of the object
rules N/A Validation rules
fields N/A Fields in the table
use_common_query N/A Include the _common_query method for each query
validator N/A The validator object
clear_related_on_save N/A Clears related records before saving
_tables N/A An array of table names with the key being the alias and the value being the actual table
_last_saved N/A A reference to the last saved object / ID of record;
_nested_errors N/A Used for capturing errors when saving multiple records (an array of records) on a single save method call

Extending Your Model

When extending MY_Model or Base_module_model, it is recommended to use a plural version of the objects name, in this example Examples, with the suffix of _model (e.g.Examples_model). The plural version is recommended because the singular version is often used for the custom record class (see below for more).

The __construct method can be passed the name of the table to map to as the first argument and then you can optionally set other properties in the second constructor argument (see above for additional properties).

class Examples_model extends MY_Model {

    function __construct()
    {
        parent::__construct('example', array('required' => 'name')); // table name, initialization params
    }
}

Most modules in FUEL extend the Base_module_model class, a child of MY_Model, but has some extended functionality needed for modules.

You can also define any of the class properties listed above like so:

class Examples_model extends MY_Model {

    public $required = array('name');
    public $record_class = 'Example_record';

    function __construct()
    {
        parent::__construct('example'); //table name
    }
}

Custom Record Objects

MY_Model adds another layer of control for your models by allowing you to define custom return objects from active record. This gives you more flexibility at the record level for your models. With custom record objects you can:

When creating a custom record class, it is recommended to use the singular version of the parent model class (so parent models should be plural).

class Examples_model extends MY_Model {

    public $required = array('name');

    function __construct()
    {
        parent::__construct('example'); //table name
    }
}


class Example_model extends MY_Model {

    Custom record model methods go here....

}

Custom record objects are not required. MY_Model will intelligently try and figure out the class name if one is not defined in the parent table model. If the class is not found it will return an array of arrays or an array of standard objects depending on the return_method property of the parent model class. Custom record objects must be defined in the same file that the parent table model is defined.

Create Derived Attributes

With a custom record object, you can derive attributes which means you can create new values from the existing fields or even other models. For example, you have a table with a text field named content that you need to filter and encode html entities and sometimes strip images before displaying on your webpage. Instead of applying the htmlentities and strip_image_tags function each time it is displayed, you can create a new derived attribute on your custom record object like so:

function get_content_formatted($strip_images = FALSE)
{
    $CI =& get_instance();
    if ($strip_images)
    {
        $CI->load->helper('security');
        $content = strip_image_tags($this->content);
    }
    $content = htmlentities($this->content);
    return $content;
}

Lazy Load Other Objects

Lazy loading of object is used when you don't want the overhead of queries to generate sub objects. For example, if you have a books model which has a foreign key to an authors model, you could create a method on the record class to lazy load that object like this:

function get_author()
{
    $author = $this->lazy_load(array('email' => 'hsolo@milleniumfalcon.com'), 'authors_model', FALSE);
    return $author;
}

Manipulate and Save at the Record Level

With custom record objects, you can update attributes and save the record object like so:

$foo = $this->examples_model->find_by_key(1);
$foo->bar = 'This is a test';
$foo->save();

Automatically Parse Field Values

In some cases, you may be saving text data that you want to parse the templating syntax upon retrieval. This can be done automatically by setting the $parsed_fields array property on the table class like so:

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
require_once(FUEL_PATH.'models/Base_module_model.php');

class Quotes_model extends Base_module_model {

	public $required = array('content');
	public $parsed_fields = array('content', 'content_formatted');
	
	function __construct()
	{
		parent::__construct('quotes'); // table name
	}

Note that both "content" and "content_formatted" were added to the $parsed_fields array. This is because they are treated as 2 separate fields even though the latter is magic method property.

Click here to view the function reference for custom record objects

Custom Fields Objects

Sometimes, but not as often, you may want a field to be a specific object instead just a string or integer value. MY_Model provides a custom_fields property that allows you to map specific classes to fields. The value can be an array with the keys being the field name, and the values can either be string values, referencing the class name, or an array specifying the module (key) and the class name (value). You can also pass in initialization parameters to the class with the init parameter. Below are some examples:

// loading from the fuel/application/libraries/custom_fields/My_asset_field.php
'image' => 'My_asset_field', 

// loading from the fuel/modules/my_module/libraries/custom_fields/My_text_field.php
'name' => array('my_module' => My_text_field'), 

// loading from the fuel/application/libraries/custom_fields/My_url_field.php and passing ina prefix_path of 'test'
'url' => array('model' => 'My_url_field', 'init' => array('prefix_path' => 'test')), 

Once created by a record, you will be able to reference the fields value as normal, but will also be able to call any methods on the custom field's object like so:

echo $record->image; // my_img.jpg

echo $record->image->my_func(); // my_func method's output from the My_asset_field object

By default, FUEL will look in the "{module}/libraries/custom_fields" folder for the the classes to load.

Magic Methods

MY_Model uses PHP's magic methods extensively. This allows for dynamically creating methods that aren't originally defined by the class. Magic methods are used both in the table and custom record classes. In custom record classes, any method prefixed with get_ can also be syntactically written to look like an instance property:

$record->get_content()

// can also be written as...
$record->content

There are also additional formatter variations you can use on a record property — such as {property}_formatted and {property}_stripped.

Using {property}_formatted will apply the auto_typography function if the property is a string value and if it is a date value will format the date by default to whatever is specified in the fuel/application/config/MY_config.php.

// returns content with <p> tags applied
echo $record->content_formatted;

Using {property}_stripped will apply the strip_tags PHP function and only applies to string values.

// returns content with HTML tags stripped (only applies to fields that return strings)
echo $record->content_stripped;

Additionally, you can use is_{property}() on any property that is a boolean type value or an enum value with 2 options.

if ($record->is_published())
{
	echo 'Published';
}
else
{
	echo 'Not Published';
}

To determine if a value exists for a certain property you can use has_{property}().

if ($record->has_image())
{
	echo '';
}
else
{
	echo 'No Image';
}

In the table class, magic methods are used to find records in interesting ways. For example, you can do something like this (where {} enclose areas where the developer should change to a proper field name):

// to find multiple items 
$this->examples_model->find_all_by_{column1}_and_{column2}('column1_val', 'column2_val');

// to find one item 
$this->examples_model->find_one_by_{column1}_or_{column2}('column1_val', 'column2_val');

Working with Active Record

MY_Model works alongside active record like so:

...
$this->db->where(array('published' => 'yes'))
$this->db->find_all()

// Is the same as...
$this->db->find_all(array('published' => 'yes'))

Relationships

FUEL CMS 1.0 provides several ways to form model relationships by adding one or more of the following properties to your model:

Note that both the has_many and belongs_to relationships use the on_after_save model hook to store relationship information. So, if you overwrite this method in your model and have one of these relationships assigned to your model, you must call parent::on_after_save($values); to properly save the relationship information.

Foreign Keys

The foreign_key model property will dynamically bind the foreign key's record object to the declared model's record object. So for example, say you have a products model. Each product can belong to only one category. In this case, we can use FUEL's built in Categories module to make the relationship like so:

class Products_model extends Base_module_model
{
  public $foreign_keys  = array('category_id' => array(FUEL_FOLDER => 'categories_model')));
  
  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

The constant FUEL_FOLDER is used as the array key above to point to the module in which to load the model (e.g. array(FUEL_FOLDER => 'categories_model'))). You can alternatively just use the name of the model as a string if the model exists in your application directory or you can use 'app' instead of FUEL_FOLDER


If your site uses categories within different contexts, you can add a where condition to target that context like so:

class Products_model extends Base_module_model
{
  public $foreign_keys  = array('category_id' => array(FUEL_FOLDER => 'categories_model', 'where' => array('context' => 'products')));
  
  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

Then in your front end code, you can access the object like so:

$id = uri_segment(3); // assumption here that the 3rd segment has the product id
$product = fuel_model('products', 'key', $id);
if (isset($product->id))
{
	echo $product->category->name;
}

Foreign Keys are automatically set as required fields when inputting int the CMS.

Has Many Relationship

The has_many model property allows you to assign many-to-many relationships between models without needing to setup a separate lookup table. FUEL will automatically save this relationship in the fuel_relationships table. If we continue with the products example above, a product may also need to be associated with multiple attributes, or tags, like regions your product belongs to, or specific features. For this, you can use FUEL's built in Tags module to create relationships like so:

class Products_model extends Base_module_model
{
  public $has_many = array('attributes' => array(array(FUEL_FOLDER => 'fuel_tags_model'));

  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

The constant FUEL_FOLDER is used as the array key above to point to the module in which to load the model (e.g. array(FUEL_FOLDER => 'categories_model'))). You can alternatively just use the name of the model as a string if the model exists in your application directory or you can use 'app' instead of FUEL_FOLDER


The long way:

class Products_model extends Base_module_model
{
  public $has_many = array('attributes' => array('model' => array(FUEL_FOLDER => 'fuel_tags_model'));

  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

Even longer:

class Products_model extends Base_module_model
{
  public $has_many = array('attributes' => array('model' => 'fuel_tags_model', 'module' => FUEL_FOLDER)); //NOTE THE 'module' key

  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

In this example, if you want to target only a specific set of tags that belong to a particular category, you can use the where condition to further filter the list of tags like so:

class Products_model extends Base_module_model
{
  public $has_many = array('attributes' => array(FUEL_FOLDER => 'fuel_tags_model', 'where' => 'category_id = 1'));

  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

Then in your front end code, you can access the object like so:

$id = uri_segment(3); // assumption here that the 3rd segment has the product id
$product = fuel_model('products', 'key', $id);
if (isset($product->id))
{
	foreach($product->attributes as $attribute)
	{
		echo $attribute->name;
	}
}

If you would like to do further active record filtering on the relationship before retrieving the data, you can call the property using it's full method form and passing TRUE to it. This will return the model object with the "find within" part of the query already set by active record:

$id = uri_segment(3); // assumption here that the 3rd segment has the product id
$product = fuel_model('products', 'key', $id);
if (isset($product->id))
{
	$tags_model = $product->get_attributes(TRUE); // NOTE THE DIFFERENCE HERE
	$attributes = $tags_model->find_all('category_id = 1');
	foreach($attributes as $attribute)
	{
		echo $attribute->name;
	}
}

Belongs To Relationship

The belongs_to model attribute is very similar to the has_many, but is done from the opposite perspective, which in this case would be from FUEL's Tags module. FUEL automatically creates the belongs_to relationship for the Tags module. This allows you to see which products belong to a specific tag in this example:

$slug = uri_segment(3); // assumption here that the 3rd segment has the slug
$tag = fuel_model('products', 'one', array('slug', $slug));
if (isset($tag->id))
{
	$products = $tag->products;
	foreach($products as $product)
	{
		echo $product->name;
	}
}

By default, FUEL uses the built-in relationships model to save relationship data. However, if you want to change the model, as well as the field names it uses to store the key information, you can pass the following parameters in your has_many or belongs_to properties array: relationships_model, foreign_key, candidate_key. An Example is below:

class Products_model extends Base_module_model
{
  public $has_many = array('attributes' => array('model' => array(FUEL_FOLDER => 'fuel_tags_model'), 'relationships_model' => 'my_relationship_model', 'foreign_key' => 'my_foreign_key', 'candidate_key' => 'candidate_key'));

  function __construct()
  {
    parent::__construct('products'); // table name
  }
}

Serialized Fields

FUEL CMS provides a way for you to automatically serialize and unserialize data in a field. To do this, you can assign the field name to the serialized_fields array. This will automatically serialize the data upon saving and unserialize it upon retrieval via a "find_*" method on the model. The default encoding is JSON but can be changed to use the PHP serialize/unserialize function by changing the default_serialization_method value to serialize.

class Products_model extends Base_module_model
{
  public $serialized_fields = array('attributes');

  function __construct()
  {
    parent::__construct('products'); // table name
  }
}
// then in your controller or elsewhere
$product = $this->products_model->create();
$product->attributes = array('color' => 'black', 'material' => 'metal');
$product->save(); // saves and serializes the data

Hooks

MY_Model provides hooks that you can overwrite with your own custom code to extend the functionality of your model. In most cases, the hooks accept an array of values to be processed and should return the processed array.

Table class hooks


Record class hooks

Formatters

Formatters allow you to easily modify a value by mapping a function to a suffix value of a property. An example of this is to use the "_formatted" suffix on a string type field which would apply the auto_typography.

echo $record->content;
// A long time ago...

echo $record->content_formatted;
// <p>A long time ago...</p>

Formatter function that can have additional parameters passed to them can be called as methods. An example would be the "wrap" filter:

$record->content_wrap(50);

Formatters can be applied to specific field types like datetime fields or string type fields, or they can be applied to field names (e.g. image, img, thumb). You can also apply multiple formatters to a field type by using the format function like so:

$record->format('content', 'stripped|formatted');
//OR
$record->format('content', 'stripped, formatted');
//OR
$record->format('content', array('stripped', 'formatted');

MY_Model has a formatters property in which Base_module_model extends to provide you by default with the following formatters for different field types:

Datetime and Date Field Examples

Below are examples of formatters that can be applied to a date field and links to the functions that they use.

String Field Examples

Below are examples of formatters that can be applied to a string fields (char, varchar, text) and links to the functions that they use.

Number Examples

Below are examples of formatters that can be applied to a number type fields (tinyint, smallint, int, bigint) and links to the functions that they use.

Special Field Name Examples

Below are examples of formatters that can be applied to a number type fields (tinyint, smallint, int, bigint) and links to the functions that they use.

Formatter aliases must not contain "_" (underscores). So for example, the function "word_limiter" has an alias of "wordlimiter".