vd/app/Models/Permission/Traits/HasPermissions.php
2018-11-05 09:26:30 +08:00

297 lines
8.1 KiB
PHP

<?php
namespace App\Models\Permission\Traits;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use App\Models\Permission\Permission;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Auth\Access\Gate;
use App\Domains\Permission\Services\PermissionService;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
trait HasPermissions
{
public static function bootHasPermissions()
{
static::deleting(function ($model) {
if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {
return;
}
$model->permissions()->detach();
});
}
/**
* A model may have direct permissions.
*/
public function permissions(): BelongsToMany
{
return $this->belongsToMany(Permission::class, 'role_has_permissions', 'role_id', 'permission_id');
}
/**
* Scope the model query to certain permissions only.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string|array|Permission|Collection $permissions
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePermission(Builder $query, $permissions): Builder
{
$permissions = $this->convertToPermissionModels($permissions);
$rolesWithPermissions = array_unique(array_reduce($permissions, function ($result, $permission) {
return array_merge($result, $permission->roles->all());
}, []));
return $query->where(function ($query) use ($permissions, $rolesWithPermissions) {
$query->whereHas('permissions', function ($query) use ($permissions) {
$query->where(function ($query) use ($permissions) {
foreach ($permissions as $permission) {
$query->orWhere('permissions.id', $permission->id);
}
});
});
if (count($rolesWithPermissions) > 0) {
$query->orWhereHas('roles', function ($query) use ($rolesWithPermissions) {
$query->where(function ($query) use ($rolesWithPermissions) {
foreach ($rolesWithPermissions as $role) {
$query->orWhere('roles.id', $role->id);
}
});
});
}
});
}
/**
* @param string|array|Permission|Collection $permissions
*
* @return array
*/
protected function convertToPermissionModels($permissions): array
{
if ($permissions instanceof Collection) {
$permissions = $permissions->all();
}
$permissions = array_wrap($permissions);
return array_map(function ($permission) {
if ($permission instanceof Permission) {
return $permission;
}
return app(Permission::class)->findByName($permission);
}, $permissions);
}
/**
* Determine if the model may perform the given permission.
*
* @param string|Permission $permission
* @param string|null $guardName
*
* @return bool
*/
public function hasPermissionTo($permission): bool
{
if (is_string($permission)) {
$permission = app(Permission::class)->findByName($permission);
}
if (is_int($permission)) {
$permission = app(Permission::class)->findById($permission);
}
return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
}
/**
* Determine if the model has any of the given permissions.
*
* @param array ...$permissions
*
* @return bool
*/
public function hasAnyPermission(...$permissions): bool
{
if (is_array($permissions[0])) {
$permissions = $permissions[0];
}
foreach ($permissions as $permission) {
if ($this->hasPermissionTo($permission)) {
return true;
}
}
return false;
}
/**
* Determine if the model has all of the given permissions.
*
* @param array ...$permissions
*
* @return bool
*/
public function hasAllPermissions(...$permissions): bool
{
if (is_array($permissions[0])) {
$permissions = $permissions[0];
}
foreach ($permissions as $permission) {
if (! $this->hasPermissionTo($permission)) {
return false;
}
}
return true;
}
/**
* Determine if the model has, via roles, the given permission.
*
* @param Permission $permission
*
* @return bool
*/
protected function hasPermissionViaRole(Permission $permission): bool
{
return $this->hasRole($permission->roles);
}
/**
* Determine if the model has the given permission.
*
* @param string|Permission $permission
*
* @return bool
*/
public function hasDirectPermission($permission): bool
{
if (is_string($permission)) {
$permission = app(Permission::class)->findByName($permission);
if (!$permission) {
return false;
}
}
if (is_int($permission)) {
$permission = app(Permission::class)->findById($permission);
if (!$permission) {
return false;
}
}
return $this->permissions->contains('id', $permission->id);
}
/**
* Return all the permissions the model has via roles.
*/
public function getPermissionsViaRoles(): Collection
{
return $this->load('roles', 'roles.permissions')->roles->flatMap(function ($role) {
return $role->permissions;
})->sort()->values();
}
/**
* Return all the permissions the model has, both directly and via roles.
*/
public function getAllPermissions(): Collection
{
return $this->permissions->merge($this->getPermissionsViaRoles())->sort()->values();
}
/**
* Grant the given permission(s) to a role.
*
* @param string|array|Permission|Collection $permissions
*
* @return $this
*/
public function givePermissionTo(...$permissions)
{
$permissions = collect($permissions)->flatten()->map(function ($permission) {
return $this->getStoredPermission($permission);
})->filter(function ($permission) {
return $permission instanceof Permission;
})->all();
$this->permissions()->saveMany($permissions);
$this->forgetCachedPermissions();
return $this;
}
/**
* Remove all current permissions and set the given ones.
*
* @param string|array|Permission|Collection $permissions
*
* @return $this
*/
public function syncPermissions(...$permissions)
{
return DB::transaction(function () use ($permissions) {
$this->permissions()->detach();
return $this->givePermissionTo($permissions);
});
}
/**
* Revoke the given permission.
*
* @param Permission|Permission[]|string|string[] $permission
*
* @return $this
*/
public function revokePermissionTo($permission)
{
$this->permissions()->detach($this->getStoredPermission($permission));
$this->forgetCachedPermissions();
return $this;
}
/**
* @param string|array|Permission|Collection $permissions
*
* @return Permission|Permission[]|Collection
*/
protected function getStoredPermission($permissions)
{
if (is_numeric($permissions)) {
return app(Permission::class)->findById($permissions);
}
if (is_string($permissions)) {
return app(Permission::class)->findByName($permissions);
}
if (is_array($permissions)) {
return app(Permission::class)->whereIn('name', $permissions)->get();
}
return $permissions;
}
/**
* Forget the cached permissions.
*/
public function forgetCachedPermissions()
{
app(PermissionService::class)->forgetCachedPermissions();
}
}