Relationships¶
Rubik ORM supports defining relationships between models using a clean, declarative syntax.
Relationships are lazy-loaded on demand — meaning they are only queried when first accessed — and then cached for reuse.
Supported relationship types:
belongsTo
— a model references another one (foreign key on current table)hasOne
— a model owns a single related recordhasMany
— a model owns multiple related recordsbelongsToMany
— many-to-many relationship through a pivot table
⚙️ Defining Relationships¶
Each model can define its relationships by overriding the static relationships()
method:
use App\Models\User;
use App\Models\Post;
class Post extends Model
{
protected static string $table = 'posts';
protected static function fields(): array
{
return [
'id' => Column::Integer(primaryKey: true, autoincrement: true),
'user_id' => Column::Integer(
notNull: true,
foreignKey: Column::ForeignKey('id', 'users', 'CASCADE', 'CASCADE')
),
'title' => Column::Varchar(length: 255, notNull: true),
'body' => Column::Text(),
];
}
protected static function relationships(): array
{
return [
'author' => [
'type' => 'belongsTo',
'related' => User::class,
'foreignKey' => 'user_id',
'ownerKey' => 'id',
],
];
}
}
💡 Rubik automatically infers most keys when you follow naming conventions (
user_id
,post_id
, etc.), but you can override them explicitly.
🔁 belongsTo¶
Defines a reverse one-to-one or many-to-one relationship (current model has a foreign key pointing to another model).
SQL generated¶
Example¶
class User extends Model
{
protected static string $table = 'users';
}
class Post extends Model
{
protected static string $table = 'posts';
protected static function relationships(): array
{
return [
'author' => [
'type' => 'belongsTo',
'related' => User::class,
'foreignKey' => 'user_id',
],
];
}
}
🧩 hasOne¶
Defines a one-to-one relationship (the current model owns exactly one related record).
SQL generated¶
Example¶
class Profile extends Model
{
protected static string $table = 'profiles';
}
class User extends Model
{
protected static string $table = 'users';
protected static function relationships(): array
{
return [
'profile' => [
'type' => 'hasOne',
'related' => Profile::class,
'foreignKey' => 'user_id',
'localKey' => 'id',
],
];
}
}
🧮 hasMany¶
Defines a one-to-many relationship (the current model has multiple related records).
SQL generated¶
Example¶
class User extends Model
{
protected static string $table = 'users';
protected static function relationships(): array
{
return [
'posts' => [
'type' => 'hasMany',
'related' => Post::class,
'foreignKey' => 'user_id',
'localKey' => 'id',
],
];
}
}
⚡ The result of a
hasMany
relationship is always an array of models.
🔗 belongsToMany¶
Defines a many-to-many relationship using a pivot table.
SQL generated¶
SELECT tags.*
FROM tags
INNER JOIN post_tag ON post_tag.tag_id = tags.id
WHERE post_tag.post_id = :post_id;
Example¶
class Tag extends Model
{
protected static string $table = 'tags';
}
class Post extends Model
{
protected static string $table = 'posts';
protected static function relationships(): array
{
return [
'tags' => [
'type' => 'belongsToMany',
'related' => Tag::class,
'pivotTable' => 'post_tag',
'foreignKey' => 'post_id',
'relatedKey' => 'tag_id',
'localKey' => 'id',
'relatedOwnerKey' => 'id',
],
];
}
}
🧠 Lazy Loading and Caching¶
When you access a relationship property (like $user->posts
), Rubik executes the relationship query only once per model instance.
Subsequent accesses reuse the cached results:
$user = User::find(1);
// Executes one query
$posts = $user->posts;
// Uses cached data (no new query)
$postsAgain = $user->posts;
This improves performance without requiring eager loading.
🚀 Combining with Query Builder¶
Relationships return Query instances under the hood. You can modify them like any other builder before executing:
$user = User::find(1);
// Filter related posts
$recentPosts = $user->hasMany(Post::class, 'user_id')
->where('created_at', '>=', '2025-01-01')
->orderBy('id', 'DESC')
->limit(5)
->all();
Or start from a defined relationship key:
🧩 Summary of Relationship Types¶
Type | Direction | Example | Returns |
---|---|---|---|
belongsTo |
Reverse | $post->author |
Single model |
hasOne |
Forward | $user->profile |
Single model |
hasMany |
Forward | $user->posts |
Array of models |
belongsToMany |
Many-Many | $post->tags |
Array of models |
🧭 Next Steps¶
- Models — Define your models and fields
- Query Builder — Learn how to query related data