文化集团/自定义关系

轻松实现自定义 Eloquent 关系

v1.3.0 2024-10-03 06:36 UTC

This package is auto-updated.

Last update: 2024-10-03 06:42:28 UTC


README

Latest Version on Packagist Total Downloads Github Actions

此包提供了一种简单的方法来实现 Eloquent 模型之间的自定义关系

安装

通过 Composer

$ composer require culturegr/custom-relation

Laravel 5.5+ 中,包的服务提供者应该 自动发现,因此您不需要注册它。如果出于某种原因需要手动注册,可以在 config/app.php 中的提供者数组中添加它

'providers' => [
    // ...
    CultureGr\CustomRelation\CustomRelationServiceProvider::class,
],

使用

假设我们有一个实现了简单 ACL(访问控制列表)层的 Laravel 应用程序:有用户被分配了一些角色,每个角色又包含了许多权限。简化的数据库结构可能如下所示

Alt text

存在一个 User 模型,它与一个 Role 模型之间存在多对多关系,该模型又与一个 Permission 模型之间存在多对多关系。

现在假设在某个时刻,我们需要访问分配给特定用户的所有权限。让我们通过创建一个将用于定义 UserPermission 模型之间关系的 CustomRelation 类来实现这一点。

创建自定义关系类

自定义关系类应简化所有需要的逻辑,用于连接 userspermissions 表,以及支持关系 预加载。可以通过运行 make:relation Artisan 命令来创建它

$ php artisan make:relation UserPermissionRelation

这将生成一个新的 CustomRelation 类,名为 UserPermissionRelation,位于 app/Eloquent/CustomRelations 目录中,并包含所有必要的样板代码

<?php

namespace App\Eloquent\CustomRelations;

use CultureGr\CustomRelation\CustomRelation;
use Illuminate\Database\Eloquent\Collection;

class UserPermissionRelation extends CustomRelation
{
   /**
    * The Eloquent query builder instance.
    *
    * @var \Illuminate\Database\Eloquent\Builder
    */
   protected $query;


   /**
    * The parent model instance.
    *
    * @var \Illuminate\Database\Eloquent\Model
    */
   protected $parent;


   /**
    * Set the base constraints on the relation query.
    *
    * @return void
    */
   public function addConstraints()
   {
       // ...
   }


   /**
    * Set the constraints for an eager load of the relation.
    *
    * @param  array  $apps  An array of parent models
    * @return void
    */
   public function addEagerConstraints(array $apps)
   {
       // ...
   }


   /**
    * Match the eagerly loaded results to their parents.
    *
    * @param  array  $apps  An array of parent models
    * @param  \Illuminate\Database\Eloquent\Collection  $results  The result of the query executed by our relation class.
    * @param  string  $relation  The name of the relation
    * @return array
    */
   public function match(array $apps, Collection $results, $relation)
   {
       // ...
   }
}

实现自定义关系类

UserPermissionRelation 类初始化了两个属性

  • $this->query 提供了对相关 Permission 模型查询构建实例的访问
  • $this->parent 提供了对父 User 模型的访问

为了定义用户/权限关系,应该实现以下方法

addConstraints

设置关系查询的基本约束。在我们的例子中

public function addConstraints()
{
    $this->query
        ->join('permission_role', 'permission_role.permission_id', '=', 'permissions.id')
        ->join('roles', 'permission_role.role_id', '=', 'roles.id')
        ->join('role_user', 'role_user.role_id', '=', 'roles.id');

    // If relation is not eager loaded
    if (!is_null($this->parent->getAttribute('id'))) {
        $this->query->where('role_user.user_id', '=', $this->parent->getAttribute('id'));
    }
}

addEagerConstraints

设置关系预加载的约束。在我们的例子中

public function addEagerConstraints(array $users)
{
    $this->query
        ->whereIn('role_user.user_id', collect($users)->pluck('id'))
        ->with('roles.users'); // To avoid N+1 problem when eager loading
}

match

将预加载的结果与它们的父对象匹配。在我们的例子中

public function match(array $users, Collection $results, $relation)
{
    if ($results->isEmpty()) {
        return $users;
    }

    foreach ($users as $user) {
        $user->setRelation(
            $relation,
            $results->unique()->filter(function (Permission $permission) use ($user) {
                return in_array($user->id, $permission->roles->pluck('users.*.id')->flatten()->toArray());
            })->values()
        );
    }

    return $users;
}

使用自定义关系类

一旦实现了 UserPermissionRelation 类,就可以使用它通过 relatesTo 方法定义 UserPermission 模型之间新的自定义关系,该方法通过 HasCustomRelation 特性对模型可用

use CultureGr\CustomRelation\HasCustomRelation;

class User extends Model
{
    use HasCustomRelation;

    // ...

    public function permissions(): UserPermissionRelation
    {
        return $this->relatesTo(Permission::class, UserPermissionRelation::class);
    }
}

这就完成了 🔥!现在我们可以像使用任何常规的 Eloquent 关系 一样使用我们的新自定义 permissions 关系

// Use relationship as a method
$userPermissions = User::find('id')->permissions()->get();

// Use relationship as a dynamic property
$userPermissions = User::find('id')->permissions;

// Eager loading
$user = User::with('permissions')->where(/* ... */)->get();

// Lazy eager loading
$user = User::find('id');
$user->load('permissions');

测试

composer test

许可证

请参阅 许可证文件 以获取更多信息。

鸣谢

  • 出色的 Laravel/PHP 社区