ExpressionEngine Docs

Relating Models

Relationship Metadata

All relationships are stored in a relationships metadata array:

protected static $_relationships = array();

Each key in this array should be the name of a relationship. The values should be arrays describing the relationship:

protected static $_relationships = array(
  'Template' => array( ... ),
  'Authors' => array( ... )

The relationship names are used for accessing the related data. They must be uppercased and valid php variable names:

// as declared above
$template = $my_model->Template;
$authors = $my_model->Authors;

The array describing each relationship contains the information that allows ExpressionEngine to correctly identify the related data. By default, only the the relationship type is required.


The relationship type describes how two items are related. There are four relationship types:

These are declared by name:

protected static $_relationships = array(
  'Template' => array(
    'type' => 'BelongsTo'

When figuring out how two items are related, consider how you would talk about them in plain english. For example, a Template Group has many Templates, but a Template belongs to a Template Group.


The relationship model declares the shortname of the model you are relating to. This is optional and will default to the relationship name:

protected static $_relationships = array(
  'Authors' => array(
    'type' => 'hasMany',
    'model' => 'ee:Member'

from_key & to_key

To specify which fields to use when matching up two models, you can specify a from_key for the current model and a to_key for the model being related:

protected static $_relationships = array(
  'Authors' => array(
    'type' => 'hasMany',
    'model' => 'ee:Member'
    'from_key' => 'author_id',
    'to_key' => 'member_id'

The keys are optional. ExpressionEngine will attempt to divine the correct columns to use based on the type of relationship and primary key names. For example, if a Comment belongsTo an Entry, and the entry has a primary key called entry_id, ExpressionEngine will look for a field called entry_id in the comment. The following table describes these automatic assumptions:

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


When a model belongsTo another model, deleting the parent model will trigger a deletion of the child model. This is in line with the human expectation of how these types of relationships work.

If you wish to disable this behavior, you can specify the relationship as weak. A weak relationship will zero out the foreign key column on deletion, but it will not cascade the delete:

protected static $_relationships = array(
  'LastAuthor' => array(
    'type' => 'belongsTo',
    'model' => 'ee:Member'
    'from_key' => 'author_id'
    'weak' => TRUE


When dealing with HasAndBelongsToMany relationships you will need to declare a pivot table that contains the relationship information:

'pivot' => 'mytable'

By default the to_key and from_key are used to determine pivot table column names. If your table uses a different name for the pivot columns, you can specify them individually:

'pivot' => array(
  'table' => 'mytable',
  'left' => 'some_key',
  'right' => 'another_key'

Here left describes the pivot table key connecting the current model and right describes the key connecting the pivot table to the related model. Picture it like a row of dominoes with the pivot in the center:


Inverse Relationships

All relationships must be declared in both associated models. Thif tmproves code consistency and predictability by allowing both sides of the relationship to be altered without creating orphaned objects. This is particularly important when creating parent-child relationships that cascade their deletions automatically.

When relating to models that are not your own, this becomes problematic since your addon cannot directly add relationships to native models. To get around this problem and to allow for native events to cascade to your models, you must declare all external dependencies in your addon.setup.php file:

'models.dependencies' => array(
  'MyModel'   => array(

You can then declare the relationship as normal, but with the addition of an inverse key describing how it will behave on the other model:

'Author' => array(
  'type'     => 'belongsTo',
  'from_key' => 'author_id',
  'to_key'   => 'member_id',
  'model'    => 'ee:Member',
  'weak'     => TRUE,
  'inverse' => array(
    'name' => 'AuthoredMyModels',
    'type' => 'hasMany'

The inverse key is not guaranteed to be unique across all addons, so it is automatically prefixed with your prefix. In order to access the AuthoredMyModels relationship on a member object, we must first alias it to a valid value:

$member = ee('Model')->get('Member')->first();
$member->alias('mymodel:AuthoredMyModels', 'Lastauthored');

$last_authored = $member->Lastauthored;

Caution: Aliases are mutable, you should reassign them before use if your code has shared access to the object.