You can define relationships between models to enable automatic loading of related data.
It's an optional feature; you can use Instarecord without relationships if you want to. This will give you greater control over the queries you execute, and can be more efficient if you don't need the related data.
Relationships can generally be categorized into two types:
- One-to-one: A single object of type
A
is related to a single object of typeB
. For example, aUser
has a singleProfile
. - One-to-many: A single object of type
A
is related to multiple objects of typeB
. For example, aUser
has manyPosts
.
Many-to-many relationships are implicitly supported; simply define a model that represents the connecting table and define two one-to-many relationships.
You can define a one-to-one relationship by simply adding a field that references another model:
<?php
use SoftwarePunt\Instarecord\Model;
class User extends Model
{
public int $id;
public string $name;
public Profile $profile; // will be backed by `profile_id`
}
The backing column will be determined automatically. In this example, the profile
property will be backed by a column named profile_id
.
Once you've defined the relationship, eager loading will be used automatically. In this example, when you query a User
, the related Profile
will be loaded and populated automatically.
When you query a model (or collection of models) with a one-to-one relationship, these are loaded automatically. This is called eager loading.
Eager loading will always cause extra queries to be executed, and can be quite inefficient if you don't need the relationships or have a lot of data.
If you use queryAllModels()
, Instarecord will batch the queries to load all relationships at once. If you fetch()
or otherwise load a single model, Instarecord will load the relationships one-by-one.
A ManyRelationship
utility is provided that allows you to more easily work with one-to-many relationships. It provides a cached, lazy-loaded wrapper around the collection that can handle the boring queries for you.
<?php
use SoftwarePunt\Instarecord\Model;
class User extends Model
{
public int $id;
public string $name;
public function posts(): ManyRelationship
{
return $this->hasMany(Post::class);
}
}
Rather than defining a property, you define a method that returns a ManyRelationship
instance. This instance will be cached and reused for the lifetime of the model.
You can optionally specify a foreign key column name as the second parameter to hasMany()
. Otherwise, it is automatically derived from the singularized host table name with an _id
suffix (so, in this example, user_id
).
You can use the query()
method on the relationship to get a query builder for the related model:
$user->posts()->query(); // ModelQuery<Post>
By default, the query results will be hooked. This means when your query loads any models, they are automatically added to the relationship's cache.
You can use the all()
method on the relationship to get an array of all results:
$user->posts()->all(); // Post[]
This may cause a large query, although only records that are not already cached will be loaded.
This list will be cached on the relationship, so subsequent calls will not cause additional queries to be executed.
You can use the fetch()
method on the relationship to get a single result:
$user->posts()->fetch(123); // Post|null
If the post is already cached (e.g. by an earlier all()
call or query),
You can invalidate and clear the cache for a relationship by calling reset()
:
$user->posts()->reset();
You can also manually add items to the cache:
$user->posts()->addLoaded($post);
$user->posts()->addLoadedArray($posts);