php - Laravel 使用 BelongsToMany 中关联的类别过滤用户
问题描述
在我们的应用程序中,每个类别和用户都可以关联在一起。
每个类别可以关联一个或多个用户,每个用户可以关联一个或多个类别
例如你假设我们有这个类别结构:
application
web
php
laravel
lumen
web_design
html
css
js
mobile
java
flutter
在这种结构中,每个类别都可以有一个或多个孩子,我们parent_id
在数据库结构中使用它们来实现它们。
现在,这些类别中的每一个或某些类别都与一个或多个用户相关联,例如:
application (Alfred)
web (Alfred)
php (Alfred)
laravel (Alfred)
lumen (Alfred)
web_design (Alfred)
html (Alfred)
css (Alfred)
js (Alfred)
mobile (Alfred)
java (Alfred)
flutter (Alfred)
此结构中与该用户关联的所有类别:(Alfred)
并且每个类别都可以与另一个用户关联,例如:
application (Alfred)
web (Alfred)
php (Alfred,Ella)
laravel (Alfred,Ella)
lumen (Alfred,Ella)
web_design (Alfred,Elizabeth)
html (Alfred,Elizabeth)
css (Alfred,Elizabeth)
js (Alfred,Elizabeth)
mobile (Alfred,Jack)
java (Alfred,Jack)
flutter (Alfred,Jack)
或者换句话说,你可以假设我们有这样的结构:
application (Alfred)
web (Alfred)
php (Alfred, Ella)
laravel (Alfred, Ella)
lumen (Alfred,Ella ,Linda)
web_design (Alfred, Elizabeth)
html (Alfred,Elizabeth)
css (Alfred,Elizabeth)
js (Alfred,Elizabeth, Scarlett)
mobile (Alfred, Jack)
java (Alfred, Jack)
flutter (Alfred, Jack, Jim)
此用户BelongsToMany
与Role
我们同步到数据库中
- Alfred : is-portal-manager
- Ella : is-manager
- 伊丽莎白:是经理
- 杰克:是编辑
- 斯嘉丽:是作家
- 吉姆:是作家
我们使用category_user
表来同步这个结构来关联类别和用户
那么我们有五个表:users
,,categories
和roles
中间表:category_user
和role_user
migrations
数据库和表:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained();
$table->string('name');
$table->string('family');
$table->string('username');
//...
});
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('parent_id')->nullable();
$table->string('name');
//...
});
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('label');
$table->timestamps();
});
Schema::create('category_user', function (Blueprint $table) {
$table->foreignId('category_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->primary(['category_id', 'user_id']);
});
Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('role_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->primary(['role_id', 'user_id']);
});
最后,我们的问题是什么:
我们想要获取与category_user
表关联和定义的登录用户的类别,这意味着当Alfred
登录系统时应该具有与之关联的所有类别。(一个或多个类别),另一个用户应该具有相同的策略,
- Alfred : is-portal-manager (所有类别都有子类别: application,web,php,laravel,lumen,web_design,html,css,js,mobile,java,flutter)
- Ella : is-manager (php,laravel,lumen)
- 伊丽莎白 : is-manager (web_design,html,css,js)
- Jack : is-editor (mobile,java,flutter)
- 斯嘉丽:是作家(js)
- 吉姆:是作家(颤抖)
完整的结构:
application (Alfred: is-portal-manager)
web (Alfred: is-portal-manager)
php (Alfred: is-portal-manager, Ella: is-manager)
laravel (Alfred: is-portal-manager, Ella: is-manager)
lumen (Alfred: is-portal-manager,Ella: is-manager ,Linda)
web_design (Alfred: is-portal-manager, Elizabeth: is-manager)
html (Alfred: is-portal-manager,Elizabeth: is-manager)
css (Alfred: is-portal-manager,Elizabeth: is-manager)
js (Alfred: is-portal-manager,Elizabeth: is-manager, Scarlett: is-writer)
mobile (Alfred: is-portal-manager, Jack: is-editor)
java (Alfred: is-portal-manager, Jack: is-editor)
flutter (Alfred: is-portal-manager, Jack: is-editor, Jim: is-writer)
楷模:
class Category extends Model
{
public function parent(): BelongsTo
{
return $this->belongsTo(Category::class, 'parent_id', 'id', 'parent');
}
public function subcategories(): HasMany
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}
class Role extends Model
{
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}
class User extends Authenticatable
{
use Notifiable;
/**
* Get the parent User to which the current User belongs.
*/
public function parent(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Get all Users which belong to the current User.
*/
public function kids(): HasMany
{
return $this->hasMany(User::class);
}
public function categories(): BelongsToMany
{
$categories = $this->belongsToMany(Category::class);
return $categories;
}
/**
* Determine whether the current user has prime Role of Portal Manager.
*/
public function isPortalManager(): Boolean
{
return $this->roles->contains('label', 'is-portal-manager');
}
/**
* Determine whether the current user has prime Role of Manager.
*/
public function isManager(): Boolean
{
return $this->roles->contains('label', 'is-manager');
}
/**
* Determine whether the current user has prime Role of Editor.
*/
public function isEditor()
{
return $this->roles->contains('label', 'is-editor');
}
/**
* Determine whether the current user has prime Role of Writer.
*/
public function isWriter()
{
return $this->roles->contains('label', 'is-writer');
}
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
/**
* Determine whether current User has the given role.
* Given role can be a Role object or string or int
*
* @param Role|string|int $role
* @return boolean
*/
public function hasRole($role)
{
//dd('a');
/** When $role is an object of class Role */
if ($role instanceof Role) {
return !!$role->intersect($this->roles)->count();
}
/** When $role is an integer */
if (is_int(($role))) {
return $this->roles->contains('id', $role);
}
/**
* When $role is string
* - Check against id (in case id is uuid stored as string)
* - Check against name
* - Check against label
*/
if (is_string($role)) {
return !!(
$this->roles->contains('id', $role) ||
$this->roles->contains('name', $role) ||
$this->roles->contains('label', $role)
);
}
}
public function hasRoleByName($role)
{
if ($role == null) {
return false;
}
if (is_string($role)) {
return $this->roles->contains('name', $role) || $this->roles->contains('label', $role);
} else {
return !!$role->intersect($this->roles)->count();
}
}
}
我们希望使用以下代码为每个用户获取所有类别(一个或多个):
$user = User::find(1);
return $user->categoires();
解决方案
您可以尝试以下特征
namespace App\Concerns;
use App\Models\Category;
use Illuminate\Database\Eloquent\Collection;
trait HasCategories
{
/**
* Get all Categories associated with the User in nested tree structure
*/
public function availableCategories(): Collection
{
$categories = $this->categories;
$parents = $categories->filter(fn($cat) =>
!in_array($cat->parent_id, $categories->pluck('id')->all()) || is_null($cat->parent_id)
);
$parents->map(fn($parent) => $this->setNested($parent, $categories));
return $parents;
}
/**
* Set the nested structure for the given $parent with relation.
*/
protected function setNested($parent, $categories)
{
$parent->setRelation('subcategories', $categories->where('parent_id', $parent->id));
$parent->subcategories->map(function($sub) use($categories){
if($categories->contains('parent_id', $sub->id)) {
$this->setNested($sub, $categories);
}
return $sub;
});
return $parent;
}
}
推荐阅读
- iis - 使用 IIS10 URL 重写从 MediaWiki URL 中删除命名空间前缀
- mysql - 设计师不只处理 MySQL 关系
- http - 某些网络的重定向失败
- kentico - Kentico - 仅将相关页面限制为本地化页面
- amazon-web-services - 通过 CloudFormation 中的 SecretsManager 获取 AWS::RDS::DBCluster 的 MasterUserPassword
- xamarin - 可以创建贝塞尔半圆 Xamarin Forms SkiaSharp
- sql - 有子查询与只是加入表?
- javascript - 需要帮助保存开发者工具日志
- php - 在 HTML 输入 = 文本字段 onchange 的更改上运行 MySQL 查询
- c# - 如何使用 linq 将对象列表分成两半,然后将它们分组