Relações De Laravel Personalizadas?
User
Role
Permission
User
tem uma relação de muitos para muitos com Role
, e Role
tem uma relação de muitos para muitos com Permission
.
Então, os modelos deles podem parecer-se com isto. (Mantive-os breves de propósito.)
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);
}
}
e se quisesses todos os Permission
para um User
?Não há nenhum.
parece que tu estão presos a fazer algo que não parece bem e não funciona com coisas como User::with('permissions')
ou User::has('permissions')
.
class User
{
public function permissions() {
$permissions = [];
foreach ($this->roles as $role) {
foreach ($role->permissions as $permission) {
$permissions = array_merge($permissions, $permission);
}
}
return $permissions;
}
}
Este exemplo é, apenas um exemplo, não leias muito nele. A questão é, como você pode definir uma relação customizada? Outro exemplo poderia ser a relação entre um comentário no facebook e a mãe do autor. Estranho, eu sei, mas espero que percebas a ideia. Relações Personalizadas. Como?
Na minha mente, uma boa solução seria para isso. relação a ser descrita de forma semelhante a como descrever qualquer outra relação em Laravel. Algo que retorna uma eloquenteRelation
.
class User
{
public function permissions() {
return $this->customRelation(Permission::class, ...);
}
}
já existe algo assim?
3 answers
O mais próximo de uma solução foi o que @biship postou nos comentários . Onde você modificaria manualmente as propriedades de um Relation
existente. Isto pode funcionar bem em alguns cenários. Realmente, pode ser a solução certa em alguns casos. No entanto, descobri que tinha de Despir todos os constraints
adicionados pelo Relation
e adicionar manualmente qualquer novo constraints
de que precisasse.
constraints
de cada vez para que o Relation
seja apenas "nua". Por que não fazer um costume Relation
que não adiciona nenhum constraints
e toma um Closure
para ajudar a facilitar a adição constraints
?
Solução
Algo assim parece estar a funcionar bem para mim. Pelo menos, este é o conceito básico:class Custom extends Relation
{
protected $baseConstraints;
public function __construct(Builder $query, Model $parent, Closure $baseConstraints)
{
$this->baseConstraints = $baseConstraints;
parent::__construct($query, $parent);
}
public function addConstraints()
{
call_user_func($this->baseConstraints, $this);
}
public function addEagerConstraints(array $models)
{
// not implemented yet
}
public function initRelation(array $models, $relation)
{
// not implemented yet
}
public function match(array $models, Collection $results, $relation)
{
// not implemented yet
}
public function getResults()
{
return $this->get();
}
}
Os métodos ainda não implementados são utilizados para o carregamento ansioso e devem ser declarados como abstractos. Ainda não cheguei tão longe. :)
E uma característica para tornar esta nova relação mais fácil de usar.trait HasCustomRelations
{
public function custom($related, Closure $baseConstraints)
{
$instance = new $related;
$query = $instance->newQuery();
return new Custom($query, $this, $baseConstraints);
}
}
Utilização
// app/User.php
class User
{
use HasCustomRelations;
public function permissions()
{
return $this->custom(Permission::class, 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);
});
}
}
// app/Permission.php
class Permission
{
use HasCustomRelations;
public function users()
{
return $this->custom(User::class, 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);
});
}
}
You could now do all the normal stuff for relations without having to query in-between relations first.
Github
Eu fui em frente e coloquei Tudo isto no Github só no caso de haver mais pessoas que estão interessadas em algo assim. Na minha opinião, isto ainda é uma espécie de experiência científica. Mas podemos resolver isto juntos. :)
Deve ajudá-lo a obter todas as permissões para um utilizador.