文化集团 / 自定义关系
轻松实现自定义 Eloquent 关系
Requires
- php: ^8.1|^8.2|^8.3
- illuminate/support: ^9.0|^10.0|^11.0
Requires (Dev)
- laravel/legacy-factories: ^1.4
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.26
- phpunit/phpunit: ^10.0
README
此包提供了一种简单的方法来实现 Eloquent 模型之间的自定义关系
安装
通过 Composer
$ composer require culturegr/custom-relation
Laravel 5.5+ 中,包的服务提供者应该 自动发现,因此您不需要注册它。如果出于某种原因需要手动注册,可以在 config/app.php
中的提供者数组中添加它
'providers' => [ // ... CultureGr\CustomRelation\CustomRelationServiceProvider::class, ],
使用
假设我们有一个实现了简单 ACL(访问控制列表)层的 Laravel 应用程序:有用户被分配了一些角色,每个角色又包含了许多权限。简化的数据库结构可能如下所示
存在一个 User
模型,它与一个 Role
模型之间存在多对多关系,该模型又与一个 Permission
模型之间存在多对多关系。
现在假设在某个时刻,我们需要访问分配给特定用户的所有权限。让我们通过创建一个将用于定义 User
和 Permission
模型之间关系的 CustomRelation
类来实现这一点。
创建自定义关系类
自定义关系类应简化所有需要的逻辑,用于连接 users
和 permissions
表,以及支持关系 预加载。可以通过运行 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
方法定义 User
和 Permission
模型之间新的自定义关系,该方法通过 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 社区