Skip to content

Basic usage

Eager loading

First, we have to define our relations. In the below example we will have UserModel, which have one-to-one relation with ProfileModel.

class UserModel extends Model
{
    use HasRelations;

    // ...

    protected function initialize()
    {
        $this->initRelations();
    }

    public function profile(): Relation
    {
        return $this->hasOne(ProfileModel::class);
    }
}

To eagerly load the data, we have to use with() method and specify relation we want to use as a first parameter.

model(UserModel::class)->with('profile')->findAll();

This will perform two queries. One for all users, and one to fetch all the profiles for these users.

The data for the relation will be available under the relation name, in this case $user->profile. Data format will respect the $returnType set in the ProfileModel class.

Note

You can still use your model as usual. If you omit the with() part, your model will work like a normal model.

Deep relations

We can also call deep relations. This will query all posts for user and then all the comments for posts:

model(UserModel::class)->with('posts')->with('posts.comments')->find(1);

The 'posts.comments' relation mean that we will be looking for comments relation in the PostModel class.

We can go even further and get only comments that were created by user we're looking for:

model(UserModel::class)->with('posts')->with('posts.comments', static function (Model $model) {
    $model->where('comments.user_id', 1);
})->find(1);

This will again query all posts for user but then will get only comments for posts that were created by user with ID 1.

Writing with relation

For simple relations like hasOne or hasMany, you can also save the entire object that contains relations inside.

So this is also possible:

$userModel = model(UserModel::class);
$user      = $userModel->with('profile')->find(1);

$user->profile->favorite_pet = 'Cat';

$userModel->with('profile')->save($user);

Note

If you use validation in the model, you should use a useTransactions() method to make sure that if something goes wrong, all changes are rolled back.

$userModel->with('profile')->useTransactions()->save($user);

In general, if you are saving an object with a relation, you should always use this method.

Lazy loading

Here we also have to specify our relations, just like in eager loading. The only difference is that we are required to use an Entity for our $returnType. That's because the entity will be responsible for triggering the relation request.

class UserModel extends Model
{
    use HasRelations;

    // ...

    protected $returnType = User::class;

    // ...

    protected function initialize()
    {
        $this->initRelations();
    }

    public function profile(): Relation
    {
        return $this->hasOne(ProfileModel::class);
    }
}

The entity class we use have to use hasLazyRelations trait.

class User extends Entity
{
    use HasLazyRelations;

    // ...
}

With lazy loading, data is fetched on demand when we access given relation property.

$users = model(UserModel::class)->findAll();
foreach ($users as $user) {
    var_dump($user->profile);
}

This will perform n+1 queries. First one to get all the users and then one for each profile we want to access.