Laravel 使用 BelongsToMany 中关联的类别过滤用户
Laravel filtering users with categories associeted in 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 : 是经理
- 伊丽莎白:是经理
- 杰克:是编辑
- Scarlett : 是作家
- 吉姆:是作家
我们使用 category_user
table 将此结构同步到关联类别和用户
然后我们有五个 table:users
、categories
、roles
和中间的 table:category_user
和 role_user
migrations
数据库和 tables:
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
table 关联和定义的已登录用户类别,这意味着当 Alfred
登录系统时应该具有与其关联的所有类别。(一个或多个类别),其他用户应该有相同的策略,
- Alfred:is-portal-manager(具有子类别的所有类别:应用程序、网络、php、laravel、流明、web_design、html、css,js,移动,java,flutter)
- Ella : 是经理 (php,laravel,lumen)
- 伊丽莎白:是经理 (web_design,html,css,js)
- Jack : is-editor (mobile,java,flutter)
- Scarlett : 作家 (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;
}
}
在我们的应用程序中,每个类别和用户都可以关联在一起。
每个类别可以关联一个或多个用户,每个用户可以关联一个或多个类别
例如,您假设我们有这样的类别结构:
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 : 是经理
- 伊丽莎白:是经理
- 杰克:是编辑
- Scarlett : 是作家
- 吉姆:是作家
我们使用 category_user
table 将此结构同步到关联类别和用户
然后我们有五个 table:users
、categories
、roles
和中间的 table:category_user
和 role_user
migrations
数据库和 tables:
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
table 关联和定义的已登录用户类别,这意味着当 Alfred
登录系统时应该具有与其关联的所有类别。(一个或多个类别),其他用户应该有相同的策略,
- Alfred:is-portal-manager(具有子类别的所有类别:应用程序、网络、php、laravel、流明、web_design、html、css,js,移动,java,flutter)
- Ella : 是经理 (php,laravel,lumen)
- 伊丽莎白:是经理 (web_design,html,css,js)
- Jack : is-editor (mobile,java,flutter)
- Scarlett : 作家 (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;
}
}