Skip to content

Basic usage

This library works with both entities and simple arrays or objects. If your models use 'array' or 'object' as the return type, you can still use the with() method for eager loading relations. This prevents N+1 queries and works with all relation types.

However, features like lazy loading, writing relations through entities, and Entity Model Integration methods require entity classes with the HasLazyRelations trait. For the full feature set, using custom entities is recommended.

Eager loading

First, define your relations in the model. In the example below, UserModel has a one-to-one relation with ProfileModel.

use Michalsn\CodeIgniterRelations\Relations\HasOne;
use Michalsn\CodeIgniterRelations\Traits\HasRelations;

class UserModel extends Model
{
    use HasRelations;

    protected $table = 'users';
    protected $returnType = User::class;

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

To eagerly load the data, use the with() method and specify the relation name:

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

This performs two queries: one for all users, and one to fetch all profiles for these users. The relation data will be available under the relation name, in this case $user->profile. The data format will respect the $returnType set in the ProfileModel class.

Loading multiple relations

You can load multiple relations at once by passing an array:

$user = model(UserModel::class)->with(['profile', 'posts', 'roles'])->find(1);

Nested relations

You can load nested relations using dot notation. This will query all posts for the user and then all comments for those posts:

$user = model(UserModel::class)->with(['posts', 'posts.comments'])->find(1);

The 'posts.comments' relation means the comments relation will be looked up in the PostModel class.

Query constraints

You can apply constraints to relation queries using a callback:

$user = model(UserModel::class)
    ->with('posts', static function (Model $model) {
        $model->where('posts.published', 1);
        $model->orderBy('posts.created_at', 'DESC');
    })
    ->find(1);

This loads only published posts, ordered by creation date. Callbacks work with nested relations as well:

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

Lazy loading

Lazy loading requires using an entity class for the $returnType. The entity is responsible for triggering the relation request when you access the relation property.

Define your model with an entity return type:

use Michalsn\CodeIgniterRelations\Relations\HasOne;
use Michalsn\CodeIgniterRelations\Traits\HasRelations;

class UserModel extends Model
{
    use HasRelations;

    protected $table = 'users';
    protected $returnType = User::class;

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

The entity class must use the HasLazyRelations trait:

use CodeIgniter\Entity\Entity;
use Michalsn\CodeIgniterRelations\Traits\HasLazyRelations;

class User extends Entity
{
    use HasLazyRelations;
}

With lazy loading, data is fetched on demand when you access the relation property:

$users = model(UserModel::class)->findAll();
foreach ($users as $user) {
    // Profile is loaded here when accessed
    echo $user->profile->bio;
}

Note

This performs N+1 queries - one to get all users and then one for each profile accessed. For better performance, use eager loading with with() instead.

Writing relations

You can save related data directly through entities without referencing the model. This requires using entity classes with the HasLazyRelations trait.

Saving a single record

Use save() to create or update a single related record. You can pass an array or entity:

$user = model(UserModel::class)->find(1);

// Save with array
$user->posts()->save([
    'title' => 'My First Post',
    'content' => 'This is the content...',
]);

// Or save an entity
$post = new Post();
$post->title = 'My Second Post';
$post->content = 'More content...';
$user->posts()->save($post);

The save() method automatically sets the foreign key, inserts new records (if no ID is present), or updates existing records (if ID is present).

Saving multiple records

For HasMany and MorphMany relations, use saveMany() to save multiple records at once:

$user = model(UserModel::class)->find(1);

$user->posts()->saveMany([
    ['title' => 'Post 1', 'content' => 'Content 1'],
    ['title' => 'Post 2', 'content' => 'Content 2'],
]);

Updating existing relations

You can update already loaded related records using the entity's save() method:

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

// Update and save the profile
$user->profile->bio = 'Updated biography';
$user->profile->save();

More write operations

Different relation types support different operations like attach(), detach(), sync(), associate(), and dissociate(). See the Supported write operations table below and the Relations documentation for detailed information.

Reloading relations

When working with entities, you can reload data from the database using refresh() and load() methods.

refresh()

Reloads the entity's attributes and all previously loaded relations from the database:

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

// Make changes elsewhere...

// Reload everything
$user->refresh();

load()

Loads or reloads specific relations without touching the entity's attributes:

$user = model(UserModel::class)->find(1);

// Load relations on demand
$user->load('posts');
$user->load(['profile', 'roles']);

// Load with nested relations
$user->load('posts.comments');

// Load with callback
$user->load('posts', static function (Model $model) {
    $model->where('published', 1);
});

Supported write operations

Different relation types support different write operations:

Relation Type Available Methods Description
HasOne save() Save a single related record (insert or update)
HasMany save()
saveMany()
Save one or multiple related records (insert or update)
BelongsTo save()
associate()
dissociate()
Save the parent record, associate child with parent, or remove the association
BelongsToMany save()
saveMany()
attach()
detach()
sync()
Create and attach new records, or manage existing pivot table entries
HasOneThrough None Read-only relation (cannot write through intermediate models)
HasManyThrough None Read-only relation (cannot write through intermediate models)
MorphOne save() Save a single polymorphic related record (insert or update)
MorphMany save()
saveMany()
Save one or multiple polymorphic related records (insert or update)
MorphTo associate()
dissociate()
Associate child with any parent type, or remove the association

For detailed method descriptions, usage examples, and edge cases, see the Relations documentation.