【问题标题】:Custom Laravel Relations?自定义 Laravel 关系?
【发布时间】:2017-01-05 21:13:40
【问题描述】:

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

  • User
  • Role
  • Permission

假设UserRole 具有多对多关系,RolePermission 具有多对多关系。

所以他们的模型可能看起来像这样。 (我故意让他们简短。)

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 获取所有Permissions 怎么办?没有BelongsToManyThrough

您似乎在做一些感觉不太正确的事情,并且不适用于 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;
    }
}

这个例子只是一个例子,不要过多解读。关键是,您如何定义 custom 关系?另一个例子可能是 Facebook 评论与作者母亲之间的关系。很奇怪,我知道,但希望你能明白。自定义关系。怎么样?

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

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

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

【问题讨论】:

  • 是的,返回一个关系,而不是一个物化数组。您可以从现有关系开始并添加到它。 See this example.
  • @bishop 有趣!我没有想过要操纵现有的关系。 Laravel 在文档中需要一个“食谱”页面来处理此类内容。 :)

标签: php laravel eloquent


【解决方案1】:

你是否研究过 Laravel 提供的 hasManyThrough 关系?

Laravel HasManyThrough

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

【讨论】:

  • 我有。实际上,对于我的示例,我相信 BelongsToManyThrough 将是票,但不存在这种关系。这就是我的帖子的重点 - 当没有股票 laravel 关系来描述你的关系时,你会怎么做?
【解决方案2】:

最接近解决方案的是@biship posted in the comments。您可以手动修改现有Relation 的属性。这在某些情况下可能效果很好。确实,在某些情况下,这可能是正确的解决方案。但是,我发现我不得不删除由Relation 添加的所有constraints,并手动添加我需要的任何新constraints

我的想法是……如果您每次都将剥离constraints 以使Relation 只是“裸露的”。为什么不创建一个不添加任何 constraints 本身并采用 Closure 来帮助添加 constraints 的自定义 Relation

解决方案

这样的事情似乎对我很有效。至少,这是基本概念:

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();
    }
}

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

还有一个特性让这个新的Custom 关系更易于使用。

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

我先写了all this on Github,以防有更多人对这样的事情感兴趣。在我看来,这仍然是一种科学实验。但是,嘿,我们可以一起解决这个问题。 :)

【讨论】:

  • 你需要在initRelation方法中返回$models,否则->with('customrelation')->load('customrelation')将不起作用,像这样:``` public function initRelation(array $models, $关系){返回$模型; } ```
【解决方案3】:

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

Zizaco - Entrust

Laracast - watch video 13 and 14

祝你好运!

【讨论】:

  • 对不起。我没有询问任何特定于 ACL 的内容。只是关于自定义关系。使用用户、角色和权限可能会产生误导。 ://
猜你喜欢
  • 1970-01-01
  • 2018-10-16
  • 2021-11-09
  • 2023-04-01
  • 1970-01-01
  • 2018-05-05
  • 2019-02-09
  • 2019-03-22
相关资源
最近更新 更多