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.
type
The relationship type
describes how two items are related. There are four relationship types:
- HasOne
- HasMany
- BelongsTo
- HasAndBelongsToMany
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.
model
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 |
weak
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
)
);
pivot
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:
ThisModel::from_key
–left
::pivot::right
–to_key
::RelatedModel
Inverse Relationships
All relationships must be declared in both associated models. This improves 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 add-on 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(
'ee:Member'
)
),
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.