Create your own custom relation for when stock relations aren't enough
[TOC]
- None of the stock Relations fit the bill. (BelongsToManyThrough, etc)
The recommended way to install is with composer:
composer require ardalanamini/laravel-custom-relation
Let's say we have 3 models:
User
Role
Permission
Let's also say User
has a many-to-many relation with Role
, and Role
has a many-to-many relation with Permission
.
So their models might look something like this. (I kept them brief on purpose.)
class User {
public function roles() {
return $this->belongsToMany(Role::class);
}
}
class Role {
public function users() {
return $this->belongsToMany(User::class);
}
public function permissions() {
return $this->belongsToMany(Permission::class);
}
}
class Permission {
public function roles() {
return $this->belongsToMany(Role::class);
}
}
What if you wanted to get all the Permission
s for a User
, or all the User
s with a particular Permission
? There no stock Relation in Laravel to descibe this. What we need is a BelongsToManyThrough
but no such thing exists in stock Laravel.
First, make sure your models are using the HasCustomRelations
trait. Then, define custom relations like this.
use LaravelCustomRelation\HasCustomRelations;
class User {
use HasCustomRelations;
/**
* Get the related permissions
*
* @return Illuminate\Database\Eloquent\Relations\Relation
*/
public function permissions() {
return $this->custom(
Permission::class,
// add constraints
function ($relation) {
$relation->getQuery()
// join the pivot table for permission and roles
->join('permission_role', 'permission_role.permission_id', '=', 'permissions.id')
// join the pivot table for users and roles
->join('role_user', 'role_user.role_id', '=', 'permission_role.role_id')
// for this user
->where('role_user.user_id', $this->id);
},
// add eager constraints
function ($relation, $models) {
$relation->getQuery()->whereIn(
'role_user.user_id',
collect($models)->map(function ($value) {
return $value->getKey();
})->values()->unique()->sort()->all()
);
}
);
}
}
use LaravelCustomRelation\HasCustomRelations;
class Permission {
use HasCustomRelations;
/**
* Get the related users
*
* @return Illuminate\Database\Eloquent\Relations\Relation
*/
public function users() {
return $this->custom(
User::class,
// constraints
function ($relation) {
$relation->getQuery()
// join the pivot table for users and roles
->join('role_user', 'role_user.user_id', '=', 'users.id')
// join the pivot table for permission and roles
->join('permission_role', 'permission_role.role_id', '=', 'role_user.role_id')
// for this permission
->where('permission_role.permission_id', $this->id);
},
// eager constraints
function ($relation, $models) {
$relation->getQuery()->whereIn(
'permission_role.permission_id',
collect($models)->map(function ($value) {
return $value->getKey();
})->values()->unique()->sort()->all()
);
}
);
}
}
You could now do all the normal stuff for relations without having to query in-between relations first.