自定义 Laravel 关系?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39213022/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 14:22:59  来源:igfitidea点击:

Custom Laravel Relations?

phplaraveleloquent

提问by Johnny

Hypothetical situation: Let's say we have 3 models:

假设情况:假设我们有 3 个模型:

  • User
  • Role
  • Permission
  • User
  • Role
  • Permission

Let's also say Userhas a many-to-many relation with Role, and Rolehas a many-to-many relation with Permission.

假设与具有User多对多关系Role,并且与Role具有多对多关系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 Permissions for a User?There isn't a BelongsToManyThrough.

如果您想获得 a 的所有PermissionsUser怎么办?没有一个BelongsToManyThrough

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').

似乎您在做一些感觉不太正确并且不适用于诸如User::with('permissions')或 之类的事情时陷入困境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;
    }
}

This example is, just one example, don't read too much into it. The point is, how can you define a customrelationship? 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?

这个例子只是一个例子,不要过多阅读。关键是,如何定义自定义关系?另一个例子可能是 facebook 评论和作者的母亲之间的关系。奇怪,我知道,但希望你能明白。自定义关系。如何?

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.

在我看来,一个好的解决方案是用类似于 Laravel 中描述任何其他关系的方式来描述这种关系。返回 Eloquent 的东西Relation

class User
{
    public function permissions() {
        return $this->customRelation(Permission::class, ...);
    }
}

Does something like this already exist?

像这样的东西已经存在了吗?

回答by Johnny

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 constraintsadded by the Relationand manually add any new constraintsI needed.

最接近解决方案的是@biship在评论中发布的内容。您将在哪里手动修改现有Relation. 这在某些情况下可能很有效。确实,在某些情况下它可能是正确的解决方案。但是,我发现我不得不删除所有constraints添加的内容Relation并手动添加constraints我需要的任何新内容。

My thinking is this... If you're going to be stripping down the constraintseach time so that the Relationis just "bare". Why not make a custom Relationthat doesn't add any constraintsitself and takes a Closureto help facilitate adding constraints?

我的想法是......如果你constraints每次都要剥离,以便它Relation只是“裸露”。为什么不制作一个Relation不添加任何内容constraints并需要一个Closure帮助促进添加的自定义constraints

Solution

解决方案

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. :)

尚未实现的方法用于预先加载并且必须声明为抽象方法。我还没有那么远。:)

And a trait to make this new CustomRelation easier to use.

以及使这个新CustomRelation 更易于使用的特性。

trait HasCustomRelations
{
    public function custom($related, Closure $baseConstraints)
    {
        $instance = new $related;
        $query = $instance->newQuery();

        return new Custom($query, $this, $baseConstraints);
    }
}

Usage

用法

// 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

I went a ahead and put all this on Githubjust 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. :)

我继续前进并将所有这些都放在 Github上,以防有更多人对这样的事情感兴趣。在我看来,这仍然是一种科学实验。但是,嘿,我们可以一起解决这个问题。:)

回答by choy vallejo

I believe this concept already exists. You may choose on using Laravel ACL Roles and Permissions or Gate, or a package known as Entrust by zizaco.

我相信这个概念已经存在。你可以选择使用 Laravel ACL Roles and Permissions 或 Gate,或者一个名为 Entrust by zizaco 的包。

Zizaco - Entrust

Zizaco - 委托

Laracast - watch video 13 and 14

Laracast - 观看视频 13 和 14

Good luck!

祝你好运!

回答by pseudoanime

Have you looked into the hasManyThroughrelationship that Laravel offers?

你有没有研究过hasManyThroughLaravel 提供的关系?

Laravel HasManyThrough

Laravel HasManyThrough

It should help you retrieve all the permissions for a user.

它应该可以帮助您检索用户的所有权限。