Relations between models

Extending another model

A model can extend another, just like a class can extend another in PHP. Fields are inherited and the primary key of the parent is used to link records together. When the model is queried, the tables are joined. When values are inserted or updated, they are split to update the various tables. Also, the connection of the parent model is inherited.

The EXTENDING attribute specifies the model to extend.

<?php

use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\ActiveRecord\ModelCollection;
use ICanBoogie\DateTime;

/* @var $connections \ICanBoogie\ActiveRecord\ConnectionCollection */

$models = new ModelCollection($connections, [

    'nodes' => [

        Model::SCHEMA => [

            'nid' => 'serial',
            'title' => 'varchar'

        ]
    ],

    'contents' => [

        Model::EXTENDING => 'nodes',
        Model::SCHEMA => [

            'body' => 'text',
            'date' => 'date'

        ]
    ],

    'news' => [

        Model::EXTENDING => 'contents'

    ]
]);

$models['news']->save([

    'title' => "Testing!",
    'body' => "Testing...",
    'date' => DateTime::now()

]);

Contrary to tables, models are not required to define a schema if they extend another model, but they may end with different parents.

In the following example the parent table of news is nodes but its parent model is contents. That's because news doesn't define a schema and thus inherits the schema and some properties of its parent model.

<?php

/* @var $news \ICanBoogie\ActiveRecord\Model */

echo $news->parent->id;       // nodes
echo $news->parent_model->id; // contents

One-to-one relation (belongs_to)

Records of a model can belong to records of other models. For instance, a news article belonging to a user. The relation is specified with the BELONGS_TO attribute. When the belongs to relation is specified, a getter is automatically added to the prototype of the records. For instance, if records of a news model belong to records of a users model, than the get_user getter is added to the prototype of the records of the news model. The user of a news record can then by obtained using the magic property user.

<?php

use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\ActiveRecord\ModelCollection;

/* @var $connections \ICanBoogie\ActiveRecord\ConnectionCollection */

$models = new ModelCollection($connections, [

    'news' => [

        Model::BELONGS_TO => 'users',
        Model::SCHEMA => [

            'news_id' => 'serial',
            'uid' => 'foreign'
            // …

        ]
    ],

    'users' => [

        Model::SCHEMA => [

            'uid' => 'serial',
            'name' => 'varchar'
            // …

        ]
    ]

]);

/* @var $news Model */

$record = $news->one;

echo "{$record->title} belongs to {$record->user->name}.";

One-to-many relation (has_many)

A one-to-many relation can be established between two models. For instance, an article having many comments. The relation is specified with the HAS_MANY attribute or the has_many() method of the parent model. A getter is added to the active record class of the model and returns a Query instance when it is accessed.

The following example demonstrates how a one-to-many relation can be established between the "articles" and "comments" models, while creating the models:

<?php

use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\ActiveRecord\ModelCollection;

// …

/* @var $connections \ICanBoogie\ActiveRecord\ConnectionCollection */

$models = new ModelCollection($connections, [

    'comments' => [

        Model::ACTIVERECORD_CLASS => Comment::class,
        Model::SCHEMA => [

            'comment_id' => 'serial',
            'article_id' => 'foreign',
            'body' => 'text'

        ]
    ],

    'articles' => [

        Model::ACTIVERECORD_CLASS => Article::class,
        Model::HAS_MANY => 'comments',
        Model::SCHEMA => [

            'article_id' => 'serial',
            'title' => 'varchar'

        ]
    ]

]);

The relation can also be established after the models are created using the has_many method:

<?php

/* @var $models \ICanBoogie\ActiveRecord\ModelCollection */

$articles = $models['articles'];
$comments = $models['comments'];

$articles->has_many($comments);
# or, if the model can be obtained with `get_model()`
$articles->has_many('comments');

# The local and foreign keys can be specified if they cannot be obtained from the models
$articles->has_many('comments', [ 'local_key' => 'article_id', 'related_key' => 'comment_id' ]);

# The name of the magic property can also be specified
$articles->has_many('comments', [ 'as' => 'article_comments' ]);

The following example demonstrates how all the comments of the author called "me" can be retrieved in order from an article:

<?php

/* @var $articles \ICanBoogie\ActiveRecord\Model */

$comments = $articles->one->comments->filter_by_author("me")->ordered->all;