自定义Laravel关系? [英] Custom Laravel Relations?
问题描述
假设情况:假设我们有3种模型:
Hypothetical situation: Let's say we have 3 models:
-
User
-
Role
-
Permission
User
Role
Permission
我们也可以说User
与Role
具有多对多关系,而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);
}
}
如果要获取User
的所有Permission
,怎么办?没有BelongsToManyThrough
.
似乎您在做一些感觉不正确的事情,并且不适用于User::with('permissions')
或User::has('permissions')
之类的事情.
It seems as though you are sort of stuck doing something that doesn't feel quite right and doesn't work with things like User::with('permissions')
or 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;
}
}
这个例子只是一个例子,请不要过多地阅读它.关键是,如何定义 custom 关系?另一个例子可能是Facebook评论与作者的母亲之间的关系.很奇怪,我知道,但希望您能明白.自定义关系.怎么样?
This example is, just one example, don't read too much into it. The point is, how can you define a custom relationship? Another example could be the relationship between a facebook comment and the author's mother. Weird, I know, but hopefully you get the idea. Custom Relationships. How?
在我看来,一个好的解决方案是以与描述Laravel中任何其他关系类似的方式描述这种关系.返回Eloquent Relation
的东西.
In my mind, a good solution would be for that relationship to be described in a similar way to how describe any other relationship in Laravel. Something that returns an Eloquent Relation
.
class User
{
public function permissions() {
return $this->customRelation(Permission::class, ...);
}
}
这样的东西已经存在吗?
推荐答案
The closest thing to a solution was what @biship posted in the comments. Where you would manually modify the properties of an existing Relation
. This might work well in some scenarios. Really, it may be the right solution in some cases. However, I found I was having to strip down all of the constraints
added by the Relation
and manually add any new constraints
I needed.
我的想法是...如果您要每次剥离constraints
,以使Relation
只是裸露".为什么不创建一个自定义Relation
,该自定义Relation
本身不添加任何constraints
并使用一个Closure
来帮助促进添加constraints
?
My thinking is this... If you're going to be stripping down the constraints
each time so that the Relation
is just "bare". Why not make a custom Relation
that doesn't add any constraints
itself and takes a Closure
to help facilitate adding constraints
?
类似的事情对我来说似乎很好.至少,这是基本概念:
Something like this seems to be working well for me. At least, this is the basic concept:
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();
}
}
尚未实现的方法用于急切加载,并且必须声明为抽象方法.我还没走呢:)
The methods not implemented yet are used for eager loading and must be declared as they are abstract. I haven't that far yet. :)
以及使此新Custom
关系更易于使用的特征.
And a trait to make this new Custom
Relation easier to use.
trait HasCustomRelations
{
public function custom($related, Closure $baseConstraints)
{
$instance = new $related;
$query = $instance->newQuery();
return new Custom($query, $this, $baseConstraints);
}
}
用法
// 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
我走了一步,把所有这些放在Github上,以防万一还有更多对这样的事情感兴趣的人.在我看来,这仍然是一种科学实验.但是,嘿,我们可以一起解决这个问题. :)
Github
I went a ahead and put all this on Github just in case there are more people who are interested in something like this. This is still sort of a science experiment in my opinion. But, hey, we can figure this out together. :)
这篇关于自定义Laravel关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!