ExpressionEngine Docs

# Building your own Models

- Registering Models
- Model Skeleton
- Properties
- Metadata
- Internal Events
- Relationships
- Validation
- Getters and Setters
- Typed Columns
- Composite Types
- Model Methods

This section gives a detailed description on how to implement your own models.

Note: Please review the basic Model Usage before attempting to write your own. Creating a model does not automatically create the corresponding database tables if they do not exist; you should use DB Forge to create your add-on’s tables.

## Registering Models

All models must be registered in your addon.setup.php file. They should be in an array called `models`, where the key is their name and the value is the class of the model relative to your namespace. By convention this should be inside a `Model` directory:

    'models' => array(
      'Author' => 'Model\Author',
      'MyModel' => 'Model\MyModel'
    )

Note: Your addon prefix will be prepended automatically.

## Model Skeleton

All models must declare a primary key, a table name, and a set of properties. The basic skeleton looks as follows:

    namespace My\Namespace\Model\MyModel\Model;

    use EllisLab\ExpressionEngine\Service\Model\Model;

    class MyModel extends Model {

      protected static $_primary_key = 'id';
      protected static $_table_name = 'my_awesome_table';

      protected $id;
      protected $name;
      protected $email;
      // ... more properties

    }

## Properties

The column names of the table should be reflected in the models properties. These must be declared as `protected`. If you require class properties for internal use, you should prefix them with an underscore:

    // treated as table columns
    protected $title;
    protected $description;

    // ignored - prefixed with an underscore
    protected $_cache;

## Metadata

All models have static metadata attached to them. Metadata must be declared as `protected static` and must begin with an underscore to avoid any potential confusion with column names. It can be retrieved with the `getMetadata()` method:

    protected static $_events = array();

    // accessing it:
    MyModel::getMetaData('events');

    // or on an instance
    $my_model_instance->getMetaData('events');

## Internal Events

As covered in the usage section all models can emit events that can be subscribed to:

    $my_model->on('boom', function($model)
    {
      echo 'boom event happened on '.$model->getName();
    });

    $my_model->emit('boom');

Unfortunately this local event behavior is not very convenient for internal event handling. To solve this problem, all model objects are subscribers to their own events. This means you can automatically bind to events and receive the callback on a regular method.

To do so, first create a public method with the format `on<EventName>`, and then listen to the event by creating an `$_events` metadata array that lists the events you want to subscribe to:

    protected static $_events = array('beforeSave');

    public function onBeforeSave()
    {
      echo 'about to save!';
    }

There are some events that are emitted when handling things in bulk so that you may do things in efficient batches. These event handlers need to be implemented as static methods as your model class will not represent any one record:

    public static function onAfterBulkDelete($delete_ids)
    {
      // Handle deleted items, do extra clean-up, etc.
    }

Note: Event names typically start with a lowercase letter, but the method name will have them as uppercase due to the `on` prefix.

### Default Events

Event Name | When | Parameters
--- | --- | ---
afterLoad | After a model is fetched | None
beforeInsert | Before saving a new model | None
afterInsert | After saving a new model | None
beforeUpdate | Before saving an existing model | Array of changed values
afterUpdate | After saving an existing model | Array of changed values
beforeSave | Before saving a model | None
afterSave | After saving a model | None
beforeValidate | Before validating | None
afterValidate | After validating | None
beforeDelete | Before deleting | None
afterDelete | After deleting | None
beforeBulkDelete | Before bulk deleting | Array of IDs being deleted
afterBulkDelete | After deleting | Array of IDs deleted

## Relationships

In-Depth Documentation: Relating Models)

Models can be related to other models using just a little bit of metadata. They are defined on a `$_relationships` metadata array. The array keys should be the relationship names, and their values will be $1 $2escription of the relationship:

    protected static $_relationships = array(
      'Author' => array(
        'model' => 'Member',
        'type' => 'BelongsTo'
      )
    );

    $my_model->Author; // fetches member that created this entry

The type name is required, all other fields have predictable defaults. The `model` key will default to the relationship name. You may need to modify the key names that are used to match the relationship. To do so, specify a `from_key` and a `to_key`, where the `from_key` is a property name on this model, and the `to_key` is a property name on the related model:

    protected static $_relationships = array(
      'Author' => array(
        'model' => 'Member',
        'type' => 'BelongsTo',
        'from_key' => 'author_id',
        'to_key' => 'member_id'
      )
    );

The following types are available:

Type | Default from_key | Default to_key
--- | --- | ---
HasOne | This primary key name | This primary key
HasMany | This primary key name | This primary key
BelongsTo | Related primary key name | Related primary key name
HasAndBelongsToMany | This primary key name | Related primary key name

### HasAndBelongsToMany

The keys for the `HasAndBelongsToMany` relationship work slightly differently from the rest. This is because this relationship type requires a pivot table to work. The `from_key` and `to_key` still specify a property on the models, but there is an additional `pivot` key for the pivot table name:

    protected static $_relationships = array(
      'Editors' => array(
        'model' => 'Member',
        'type' => 'HasAndBelongsToMany',
        'pivot' => 'my_model_editors',
        'from_key' => 'editor_id',
        'to_key' => 'member_id'
      )
    );

If your pivot table key names do not match the primary key names, you can specify them as well, by turning the pivot item into an array:

    'pivot' => array(
      'table' => 'my_model_editors',
      'left' => 'editor_id',
      'right' => 'member_id'
    )

The `left` column will be matched to your `from_key` property and the `right` column will be matched to your `to_key` property.

## Validation

Validation rules are added using the same format as the Validation Service. They should be added to a metadata item called `$_validation_rules`:

    protected static $_validation_rules = array(
      'name'  => 'required',
      'email' => 'required|email'
    );

You can also create your own local validation rules. These one-off rules can be added directly to the model class by creating a public method whose name starts with `validate`. The method will be treated as a custom validation rule. On validation it will receive the property name, current value, rule parameters, and the rule object. The method should return `TRUE` on success, and an error string or language key on failure:

    protected static $_validation_rules = array(
      'even_number' => 'validateMultipleOf[2]'
      'decade' => 'validateMultipleOf[10]'
    );

    public function validateMultipleOf($name, $value, $params, $object)
    {
      if ($value % $params[0] != 0)
      {
        return 'This field must be a multiple of '.$params[0];
      }

      return TRUE;
    }

## Getters and Setters

By default setting and getting of properties behaves the way it does for any object. However, you can create your own modifying getters and setters by creating methods in the format of `get__<property>` or `set__<property>`:

    protected $first_name;
    protected $last_name;

    protected function get__name()
    {
      return $this->first_name.' '.$this->last_name;
    }

    protected function set__name($value)
    {
      list($first, $last) = explode(' ', $value);

      $this->setRawProperty('first_name', $first);
      $this->setRawProperty('last_name', $last);
    }

    $my_model->name = 'Bob Bobson';
    $my_model->first_name; // Bob

Note: These methods break the camelCase naming convention in order to match the snake_case property names. It also serves as a clear indicator that these methods should not be called directly, even internally. When setting properties manually you should take care to use setRawProperty, which will correctly track changes to the property.

## Typed Columns

Model properties can have basic type constraints set on them. These constraints allow for simple get/set typecasting of common values. They are defined in a metadata array called `$_typed_columns`:

    protected static $_typed_columns = array(
      'model_id' => 'int',
      'created_at' => 'timestamp'
    );

    $my_model->model_id = '5'; // set to int 5
    $my_model->model_id; // always returns an integer

    $my_model->created_at; // returns a DateTime object
    $my_model->created_at = new DateTime('2015-01-30'); // sets an int timestamp
    $my_model->created_at = 1421558529; // also ok

The available options include:

Name | Setting | Getting
--- | --- | ---
bool | Cast to boolean | Cast to boolean
int | Cast to integer | Cast to integer
float | Cast to float | Cast to float
double | Cast to float | Cast to float
string | Cast to string | Cast to string
boolString | Cast to y/n | Cast to boolean
yesNo | Cast to y/n | Cast to boolean
timestamp | Cast to timestamp | Cast to DateTime

## Composite Types

Sometimes a database column may contain serialized data. Instead of treating this data merely as a string or using getters and setters, you can automatically turn it into an array or object. These are also defined in the `$_typed_columns` metadata array:

    protected static $_typed_columns = array(
      'settings' => 'json'
    );

    $my_model->settings = array('name' => 'Bob'); // stores: {"name": "Bob"}
    $my_model->settings['name']; // 'Bob'

The following composite types are included:

Name | Serialization
--- | ---
base64 | base64_encode($data)
json | json_encode($data)
serialized | serialize($data)
base64Serialized | base64_encode(serialize($data))

## Model Methods

class `EllisLab\ExpressionEngine\Service\Model\Model`

- `getId()`
- `getName()`
- `getPrimaryKey()`
- `getProperty($name)`
- `setProperty($name, $value)`
- `getRawProperty($name)`
- `setRawProperty($name, $value)`

### `getId()`

Get the primary key value

Parameter | Type | Description
--- | --- | ---
Returns | `Int`(or null if not yet saved) | The primary key value

### `getName()`

Get the model alias that was registered with ExpressionEngine.

Parameter | Type | Description
--- | --- | ---
Returns | `String` | The model alias

### `getPrimaryKey()`

Get the primary key name

Parameter | Type | Description
--- | --- | ---
Returns | `String` | The primary key name

### `getProperty($name)`

Get a model property, calling any getters that were defined by the model.

Parameter | Type | Description
--- | --- | ---
$name | `String` | The name of the property
Returns | `Mixed` | The value of the property

### `setProperty($name, $value)`

Set a model property and track its dirty state. Calls any setters that were defined by the model.

Parameter | Type | Description
--- | --- | ---
$name | `String` | The name of the property
$value | `Mixed` | The value to set the property to
Returns | `Model` | $this

### `getRawProperty($name)`

Get a model property, without calling any getters that were defined by the model.

Parameter | Type | Description
--- | --- | ---
$name | `String` | The name of the property
Returns | `Mixed` | The value of the property

### `setRawProperty($name, $value)`

Set a model property and track its dirty state. Does not call any setters that were defined by the model.

Parameter | Type | Description
--- | --- | ---
$name | `String` | The name of the property
$value | `Mixed` | The value to set the property to
Returns | `Model` | $this

- Relating Models