diff --git a/.gitignore b/.gitignore index 67854e2..3775d0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ vendor/ build/ composer.lock +.idea/ +*.DS_Store +.phpunit.result.cache diff --git a/src/Commands/CreatePermission.php b/src/Commands/CreatePermission.php index 6cdf78d..477dc93 100644 --- a/src/Commands/CreatePermission.php +++ b/src/Commands/CreatePermission.php @@ -3,7 +3,8 @@ namespace Maklad\Permission\Commands; use Illuminate\Console\Command; -use Maklad\Permission\Contracts\PermissionInterface as Permission; +use function app; +use function config; /** * Class CreatePermission @@ -17,15 +18,15 @@ class CreatePermission extends Command protected $description = 'Create a permission'; - public function handle(): void + public function handle() { - $permissionClass = \app(\config('permission.models.permission')); + $permissionClass = app(config('permission.models.permission')); $permission = $permissionClass::create([ 'name' => $this->argument('name'), 'guard_name' => $this->argument('guard') ]); - $this->info("Permission `{$permission->name}` created"); + $this->info("Permission `$permission->name` created"); } } diff --git a/src/Commands/CreateRole.php b/src/Commands/CreateRole.php index 110cdaa..aba014e 100644 --- a/src/Commands/CreateRole.php +++ b/src/Commands/CreateRole.php @@ -3,7 +3,8 @@ namespace Maklad\Permission\Commands; use Illuminate\Console\Command; -use Maklad\Permission\Contracts\RoleInterface as Role; +use function app; +use function config; /** * Class CreateRole @@ -18,9 +19,9 @@ class CreateRole extends Command protected $description = 'Create a role'; - public function handle(): void + public function handle() { - $roleClass = \app(\config('permission.models.role')); + $roleClass = app(config('permission.models.role')); $name = $this->argument('name'); $guard = $this->argument('guard'); @@ -31,10 +32,10 @@ public function handle(): void 'guard_name' => $guard ]); - $this->info("Role `{$role->name}` created"); + $this->info("Role `$role->name` created"); $role->givePermissionTo($permissions); $permissionsStr = $role->permissions->implode('name', '`, `'); - $this->info("Permissions `{$permissionsStr}` has been given to role `{$role->name}`"); + $this->info("Permissions `$permissionsStr` has been given to role `$role->name`"); } } diff --git a/src/Contracts/PermissionInterface.php b/src/Contracts/PermissionInterface.php index e31aac3..ba87e5c 100644 --- a/src/Contracts/PermissionInterface.php +++ b/src/Contracts/PermissionInterface.php @@ -2,7 +2,6 @@ namespace Maklad\Permission\Contracts; -use Jenssegers\Mongodb\Relations\BelongsToMany; use Maklad\Permission\Exceptions\PermissionDoesNotExist; /** @@ -13,9 +12,8 @@ interface PermissionInterface { /** * A permission can be applied to roles. - * @return BelongsToMany */ - public function roles(): BelongsToMany; + public function rolesQuery(); /** * Find a permission by its name. diff --git a/src/Contracts/RoleInterface.php b/src/Contracts/RoleInterface.php index d3d641b..e30d0d0 100644 --- a/src/Contracts/RoleInterface.php +++ b/src/Contracts/RoleInterface.php @@ -2,7 +2,6 @@ namespace Maklad\Permission\Contracts; -use Jenssegers\Mongodb\Relations\BelongsToMany; use Maklad\Permission\Exceptions\RoleDoesNotExist; /** @@ -13,9 +12,8 @@ interface RoleInterface { /** * A role may be given various permissions. - * @return BelongsToMany */ - public function permissions(): BelongsToMany; + public function permissionsQuery(); /** * Find a role by its name and guard name. diff --git a/src/Directives/PermissionDirectives.php b/src/Directives/PermissionDirectives.php index d100a7a..b3ec3d9 100644 --- a/src/Directives/PermissionDirectives.php +++ b/src/Directives/PermissionDirectives.php @@ -3,6 +3,7 @@ namespace Maklad\Permission\Directives; use Illuminate\View\Compilers\BladeCompiler; +use function explode; /** * Class PermissionDirectives @@ -25,7 +26,7 @@ public function roleDirective(): void $this->bladeCompiler->directive('role', function ($arguments) { list($role, $guard) = $this->extractRoleGuard($arguments); - return "check() && auth({$guard})->user()->hasRole({$role})): ?>"; + return "check() && auth($guard)->user()->hasRole($role)): ?>"; }); $this->bladeCompiler->directive('endrole', function () { @@ -41,7 +42,7 @@ public function hasroleDirective(): void $this->bladeCompiler->directive('hasrole', function ($arguments) { list($role, $guard) = $this->extractRoleGuard($arguments); - return "check() && auth({$guard})->user()->hasRole({$role})): ?>"; + return "check() && auth($guard)->user()->hasRole($role)): ?>"; }); $this->bladeCompiler->directive('endhasrole', function () { return ''; @@ -56,7 +57,7 @@ public function hasanyroleDirective(): void $this->bladeCompiler->directive('hasanyrole', function ($arguments) { list($roles, $guard) = $this->extractRoleGuard($arguments); - return "check() && auth({$guard})->user()->hasAnyRole({$roles})): ?>"; + return "check() && auth($guard)->user()->hasAnyRole($roles)): ?>"; }); $this->bladeCompiler->directive('endhasanyrole', function () { return ''; @@ -71,7 +72,7 @@ public function hasallrolesDirective(): void $this->bladeCompiler->directive('hasallroles', function ($arguments) { list($roles, $guard) = $this->extractRoleGuard($arguments); - return "check() && auth({$guard})->user()->hasAllRoles({$roles})): ?>"; + return "check() && auth($guard)->user()->hasAllRoles($roles)): ?>"; }); $this->bladeCompiler->directive('endhasallroles', function () { return ''; @@ -85,8 +86,8 @@ public function hasallrolesDirective(): void */ private function extractRoleGuard($arguments): array { - $arguments = preg_replace('(\(|\)| )', '', $arguments); + $arguments = preg_replace('([() ])', '', $arguments); - return \explode(',', $arguments . ','); + return explode(',', $arguments . ','); } } diff --git a/src/Exceptions/MakladException.php b/src/Exceptions/MakladException.php index 5e50d15..26251f2 100644 --- a/src/Exceptions/MakladException.php +++ b/src/Exceptions/MakladException.php @@ -4,6 +4,8 @@ use InvalidArgumentException; use Throwable; +use function app; +use function config; /** * Class MakladException @@ -22,8 +24,8 @@ public function __construct(string $message = null, int $code = 0, Throwable $pr { parent::__construct($message, $code, $previous); - if (\config('permission.log_registration_exception')) { - $logger = \app('log'); + if (config('permission.log_registration_exception')) { + $logger = app('log'); $logger->alert($message); } } diff --git a/src/Exceptions/UnauthorizedException.php b/src/Exceptions/UnauthorizedException.php index 04d9251..312085e 100644 --- a/src/Exceptions/UnauthorizedException.php +++ b/src/Exceptions/UnauthorizedException.php @@ -3,6 +3,8 @@ namespace Maklad\Permission\Exceptions; use Symfony\Component\HttpKernel\Exception\HttpException; +use function app; +use function config; /** * Class UnauthorizedException @@ -10,8 +12,8 @@ */ class UnauthorizedException extends HttpException { - private array $requiredRoles = []; - private array $requiredPermissions = []; + private array $requiredRoles; + private array $requiredPermissions; /** * UnauthorizedException constructor. @@ -29,8 +31,8 @@ public function __construct( ) { parent::__construct($statusCode, $message); - if (\config('permission.log_registration_exception')) { - $logger = \app('log'); + if (config('permission.log_registration_exception')) { + $logger = app('log'); $logger->alert($message); } diff --git a/src/Guard.php b/src/Guard.php index 50666d3..bc4a8a9 100644 --- a/src/Guard.php +++ b/src/Guard.php @@ -3,6 +3,10 @@ namespace Maklad\Permission; use Illuminate\Support\Collection; +use ReflectionClass; +use ReflectionException; +use function get_class; +use function is_object; /** * Class Guard @@ -12,25 +16,25 @@ class Guard { /** * return collection of (guard_name) property if exist on class or object - * otherwise will return collection of guards names that exists in config/auth.php. + * otherwise will return collection of guards names that exist in config/auth.php. * * @param $model * * @return Collection - * @throws \ReflectionException + * @throws ReflectionException */ public function getNames($model) : Collection { $guardName = null; $class = null; - if (\is_object($model)) { + if (is_object($model)) { $guardName = $model->guard_name ?? null; } if ($guardName === null) { - $class = \is_object($model) ? \get_class($model) : $model; - $guardName = (new \ReflectionClass($class))->getDefaultProperties()['guard_name'] ?? null; + $class = is_object($model) ? get_class($model) : $model; + $guardName = (new ReflectionClass($class))->getDefaultProperties()['guard_name'] ?? null; } if ($guardName) { @@ -56,7 +60,7 @@ public function getNames($model) : Collection * @param $class * * @return string - * @throws \ReflectionException + * @throws ReflectionException */ public function getDefaultName($class): string { diff --git a/src/Helpers.php b/src/Helpers.php index 6e118ed..99875a1 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -3,6 +3,8 @@ namespace Maklad\Permission; use Illuminate\Support\Collection; +use function collect; +use function config; /** * Class Helpers @@ -17,9 +19,9 @@ class Helpers */ public function getModelForGuard(string $guard): ?string { - return \collect(\config('auth.guards')) + return collect(config('auth.guards')) ->map(function ($guard) { - return \config("auth.providers.{$guard['provider']}.model"); + return config("auth.providers.{$guard['provider']}.model"); })->get($guard); } @@ -31,7 +33,8 @@ public function getModelForGuard(string $guard): ?string */ public function getGuardDoesNotMatchMessage(Collection $expected, string $given): string { - return "The given role or permission should use guard `{$expected->implode(', ')}` instead of `{$given}`."; + $expectedStr = $expected->implode(', '); + return "The given role or permission should use guard `$expectedStr` instead of `$given`."; } /** @@ -42,7 +45,7 @@ public function getGuardDoesNotMatchMessage(Collection $expected, string $given) */ public function getPermissionAlreadyExistsMessage(string $name, string $guardName): string { - return "A permission `{$name}` already exists for guard `{$guardName}`."; + return "A permission `$name` already exists for guard `$guardName`."; } /** @@ -53,7 +56,7 @@ public function getPermissionAlreadyExistsMessage(string $name, string $guardNam */ public function getPermissionDoesNotExistMessage(string $name, string $guardName): string { - return "There is no permission named `{$name}` for guard `{$guardName}`."; + return "There is no permission named `$name` for guard `$guardName`."; } /** @@ -64,7 +67,7 @@ public function getPermissionDoesNotExistMessage(string $name, string $guardName */ public function getRoleAlreadyExistsMessage(string $name, string $guardName): string { - return "A role `{$name}` already exists for guard `{$guardName}`."; + return "A role `$name` already exists for guard `$guardName`."; } /** @@ -76,7 +79,7 @@ public function getRoleAlreadyExistsMessage(string $name, string $guardName): st */ public function getRoleDoesNotExistMessage(string $name, string $guardName): string { - return "There is no role named `{$name}` for guard `{$guardName}`."; + return "There is no role named `$name` for guard `$guardName`."; } /** @@ -86,7 +89,7 @@ public function getRoleDoesNotExistMessage(string $name, string $guardName): str */ public function getUnauthorizedRoleMessage(string $roles): string { - $message = "User does not have the right roles `{$roles}`."; + $message = "User does not have the right roles `$roles`."; if (! config('permission.display_permission_in_exception')) { $message = 'User does not have the right roles.'; } @@ -101,7 +104,7 @@ public function getUnauthorizedRoleMessage(string $roles): string */ public function getUnauthorizedPermissionMessage(string $permissions): string { - $message = "User does not have the right permissions `{$permissions}`."; + $message = "User does not have the right permissions `$permissions`."; if (! config('permission.display_permission_in_exception')) { $message = 'User does not have the right permissions.'; } diff --git a/src/Middlewares/PermissionMiddleware.php b/src/Middlewares/PermissionMiddleware.php index 8b542c3..25b771a 100644 --- a/src/Middlewares/PermissionMiddleware.php +++ b/src/Middlewares/PermissionMiddleware.php @@ -3,11 +3,12 @@ namespace Maklad\Permission\Middlewares; use Closure; -use Illuminate\Http\Request; use Maklad\Permission\Exceptions\UnauthorizedException; use Maklad\Permission\Exceptions\UnauthorizedPermission; use Maklad\Permission\Exceptions\UserNotLoggedIn; use Maklad\Permission\Helpers; +use function explode; +use function is_array; /** * Class PermissionMiddleware @@ -16,21 +17,21 @@ class PermissionMiddleware { /** - * @param Request $request + * @param $request * @param Closure $next - * @param array|string $permission + * @param $permission * * @return mixed * @throws UnauthorizedException */ - public function handle(Request $request, Closure $next, array|string $permission): mixed + public function handle($request, Closure $next, $permission): mixed { if (app('auth')->guest()) { $helpers = new Helpers(); throw new UserNotLoggedIn(403, $helpers->getUserNotLoggedINMessage()); } - $permissions = \is_array($permission) ? $permission : \explode('|', $permission); + $permissions = is_array($permission) ? $permission : explode('|', $permission); if (! app('auth')->user()->hasAnyPermission($permissions)) { diff --git a/src/Middlewares/RoleMiddleware.php b/src/Middlewares/RoleMiddleware.php index d5cb492..f5427b7 100644 --- a/src/Middlewares/RoleMiddleware.php +++ b/src/Middlewares/RoleMiddleware.php @@ -3,10 +3,12 @@ namespace Maklad\Permission\Middlewares; use Closure; -use Illuminate\Http\Request; +use Maklad\Permission\Exceptions\UnauthorizedException; use Maklad\Permission\Exceptions\UnauthorizedRole; use Maklad\Permission\Exceptions\UserNotLoggedIn; use Maklad\Permission\Helpers; +use function explode; +use function is_array; /** * Class RoleMiddleware @@ -15,21 +17,21 @@ class RoleMiddleware { /** - * @param Request $request + * @param $request * @param Closure $next - * @param array|string $role + * @param $role * * @return mixed - * @throws \Maklad\Permission\Exceptions\UnauthorizedException + * @throws UnauthorizedException */ - public function handle(Request $request, Closure $next, array|string $role): mixed + public function handle($request, Closure $next, $role): mixed { if (app('auth')->guest()) { $helpers = new Helpers(); throw new UserNotLoggedIn(403, $helpers->getUserNotLoggedINMessage()); } - $roles = \is_array($role) ? $role : \explode('|', $role); + $roles = is_array($role) ? $role : explode('|', $role); if (! app('auth')->user()->hasAnyRole($roles)) { $helpers = new Helpers(); diff --git a/src/Models/Permission.php b/src/Models/Permission.php index 62e877f..b23f4dd 100644 --- a/src/Models/Permission.php +++ b/src/Models/Permission.php @@ -4,7 +4,6 @@ use Illuminate\Support\Collection; use Jenssegers\Mongodb\Eloquent\Model; -use Jenssegers\Mongodb\Relations\BelongsToMany; use Maklad\Permission\Contracts\PermissionInterface; use Maklad\Permission\Exceptions\PermissionAlreadyExists; use Maklad\Permission\Exceptions\PermissionDoesNotExist; @@ -13,9 +12,12 @@ use Maklad\Permission\PermissionRegistrar; use Maklad\Permission\Traits\HasRoles; use Maklad\Permission\Traits\RefreshesPermissionCache; +use ReflectionException; +use function app; /** * Class Permission + * @property string $_id * @package Maklad\Permission\Models */ class Permission extends Model implements PermissionInterface @@ -31,7 +33,7 @@ class Permission extends Model implements PermissionInterface * * @param array $attributes * - * @throws \ReflectionException + * @throws ReflectionException */ public function __construct(array $attributes = []) { @@ -49,11 +51,11 @@ public function __construct(array $attributes = []) * * @param array $attributes * - * @return $this|mixed - * @throws \Maklad\Permission\Exceptions\PermissionAlreadyExists - * @throws \ReflectionException + * @return $this|\Illuminate\Database\Eloquent\Model + * @throws PermissionAlreadyExists + * @throws ReflectionException */ - public static function create(array $attributes = []) + public static function create(array $attributes = []): \Illuminate\Database\Eloquent\Model|static { $helpers = new Helpers(); $attributes['guard_name'] = $attributes['guard_name'] ?? (new Guard())->getDefaultName(static::class); @@ -67,7 +69,7 @@ public static function create(array $attributes = []) throw new PermissionAlreadyExists($helpers->getPermissionAlreadyExistsMessage($name, $guardName)); } - return $helpers->checkVersion() ? parent::create($attributes) : static::query()->create($attributes); + return static::query()->create($attributes); } /** @@ -77,8 +79,7 @@ public static function create(array $attributes = []) * @param string|null $guardName * * @return PermissionInterface - * @throws \Maklad\Permission\Exceptions\PermissionAlreadyExists - * @throws \ReflectionException + * @throws ReflectionException */ public static function findOrCreate(string $name, string $guardName = null): PermissionInterface { @@ -97,20 +98,40 @@ public static function findOrCreate(string $name, string $guardName = null): Per /** * A permission can be applied to roles. - * @return BelongsToMany + * @return mixed */ - public function roles(): BelongsToMany + public function rolesQuery(): mixed { - return $this->belongsToMany(config('permission.models.role')); + $roleClass = $this->getRoleClass(); + return $roleClass->query()->where('permission_ids', 'all', [$this->_id]); + } + + /** + * A permission can be applied to roles. + * @return mixed + */ + public function getRolesAttribute(): mixed + { + return $this->rolesQuery()->get(); + } + + /** + * A permission belongs to some users of the model associated with its guard. + * @return mixed + */ + public function usersQuery(): mixed + { + $usersClass = app($this->helpers->getModelForGuard($this->attributes['guard_name'])); + return $usersClass->query()->where('permission_ids', 'all', [$this->_id]); } /** * A permission belongs to some users of the model associated with its guard. - * @return BelongsToMany + * @return mixed */ - public function users(): BelongsToMany + public function getUsersAttribute(): mixed { - return $this->belongsToMany($this->helpers->getModelForGuard($this->attributes['guard_name'])); + return $this->usersQuery()->get(); } /** @@ -120,8 +141,7 @@ public function users(): BelongsToMany * @param string|null $guardName * * @return PermissionInterface - * @throws PermissionDoesNotExist - * @throws \ReflectionException + * @throws ReflectionException */ public static function findByName(string $name, string $guardName = null): PermissionInterface { @@ -145,6 +165,6 @@ public static function findByName(string $name, string $guardName = null): Permi */ protected static function getPermissions(): Collection { - return \app(PermissionRegistrar::class)->getPermissions(); + return app(PermissionRegistrar::class)->getPermissions(); } } diff --git a/src/Models/Role.php b/src/Models/Role.php index 7f7d4d2..ac1c76e 100644 --- a/src/Models/Role.php +++ b/src/Models/Role.php @@ -2,8 +2,8 @@ namespace Maklad\Permission\Models; +use Illuminate\Database\Eloquent\Builder; use Jenssegers\Mongodb\Eloquent\Model; -use Jenssegers\Mongodb\Relations\BelongsToMany; use Maklad\Permission\Contracts\PermissionInterface; use Maklad\Permission\Contracts\RoleInterface; use Maklad\Permission\Exceptions\GuardDoesNotMatch; @@ -14,9 +14,11 @@ use Maklad\Permission\Traits\HasPermissions; use Maklad\Permission\Traits\RefreshesPermissionCache; use ReflectionException; +use function is_string; /** * Class Role + * @property string $_id * @package Maklad\Permission\Models */ class Role extends Model implements RoleInterface @@ -32,11 +34,11 @@ class Role extends Model implements RoleInterface * * @param array $attributes * - * @throws \ReflectionException + * @throws ReflectionException */ public function __construct(array $attributes = []) { - $attributes['guard_name'] ??= (new Guard())->getDefaultName(static::class); + $attributes['guard_name'] = $attributes['guard_name'] ?? (new Guard())->getDefaultName(static::class); parent::__construct($attributes); @@ -48,24 +50,23 @@ public function __construct(array $attributes = []) /** * @param array $attributes * - * @return $this|mixed - * @throws RoleAlreadyExists - * @internal param array $attributesĀ§ + * @return Builder|\Illuminate\Database\Eloquent\Model|RoleInterface * - * @throws \ReflectionException + * @throws RoleAlreadyExists + * @throws ReflectionException */ - public static function create(array $attributes = []) + public static function create(array $attributes = []): \Illuminate\Database\Eloquent\Model|RoleInterface|Builder { - $attributes['guard_name'] ??= (new Guard())->getDefaultName(static::class); + $attributes['guard_name'] = $attributes['guard_name'] ?? (new Guard())->getDefaultName(static::class); $helpers = new Helpers(); - if (static::where('name', $attributes['name'])->where('guard_name', $attributes['guard_name'])->first()) { + if (static::query()->where('name', $attributes['name'])->where('guard_name', $attributes['guard_name'])->first()) { $name = (string)$attributes['name']; $guardName = (string)$attributes['guard_name']; throw new RoleAlreadyExists($helpers->getRoleAlreadyExistsMessage($name, $guardName)); } - return $helpers->checkVersion() ? parent::create($attributes) : static::query()->create($attributes); + return static::query()->create($attributes); } /** @@ -75,14 +76,15 @@ public static function create(array $attributes = []) * @param string|null $guardName * * @return RoleInterface - * @throws \Maklad\Permission\Exceptions\RoleAlreadyExists - * @throws \ReflectionException + * @throws RoleAlreadyExists + * @throws ReflectionException */ - public static function findOrCreate(string $name, string $guardName = null): RoleInterface + public static function findOrCreate(string $name, string $guardName = null): Role { $guardName = $guardName ?? (new Guard())->getDefaultName(static::class); - $role = static::where('name', $name) + $role = static::query() + ->where('name', $name) ->where('guard_name', $guardName) ->first(); @@ -101,13 +103,14 @@ public static function findOrCreate(string $name, string $guardName = null): Rol * * @return RoleInterface * @throws RoleDoesNotExist - * @throws \ReflectionException + * @throws ReflectionException */ public static function findByName(string $name, string $guardName = null): RoleInterface { $guardName = $guardName ?? (new Guard())->getDefaultName(static::class); - $role = static::where('name', $name) + $role = static::query() + ->where('name', $name) ->where('guard_name', $guardName) ->first(); @@ -120,12 +123,22 @@ public static function findByName(string $name, string $guardName = null): RoleI } /** - * A role belongs to some users of the model associated with its guard. - * @return BelongsToMany + * A permission belongs to some users of the model associated with its guard. + * @return mixed + */ + public function usersQuery(): mixed + { + $usersClass = app($this->helpers->getModelForGuard($this->attributes['guard_name'])); + return $usersClass->query()->where('role_ids', 'all', [$this->_id]); + } + + /** + * A permission belongs to some users of the model associated with its guard. + * @return mixed */ - public function users(): BelongsToMany + public function getUsersAttribute(): mixed { - return $this->belongsToMany($this->helpers->getModelForGuard($this->attributes['guard_name'])); + return $this->usersQuery()->get(); } /** @@ -140,7 +153,7 @@ public function users(): BelongsToMany */ public function hasPermissionTo(string|PermissionInterface $permission): bool { - if (\is_string($permission)) { + if (is_string($permission)) { $permission = $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName()); } @@ -151,6 +164,6 @@ public function hasPermissionTo(string|PermissionInterface $permission): bool throw new GuardDoesNotMatch($this->helpers->getGuardDoesNotMatchMessage($expected, $given)); } - return $this->permissions->contains('id', $permission->id); + return in_array($permission->_id, $this->permission_ids ?? [], true); } } diff --git a/src/PermissionRegistrar.php b/src/PermissionRegistrar.php index cf4ee48..f6b2ddd 100644 --- a/src/PermissionRegistrar.php +++ b/src/PermissionRegistrar.php @@ -21,9 +21,9 @@ class PermissionRegistrar protected string $cacheKey = 'maklad.permission.cache'; - protected mixed $permissionClass; + protected string $permissionClass; - protected mixed $roleClass; + protected string $roleClass; /** * PermissionRegistrar constructor. @@ -70,7 +70,7 @@ public function forgetCachedPermissions(): void public function getPermissions(): Collection { return $this->cache->remember($this->cacheKey, config('permission.cache_expiration_time'), function () { - return $this->getPermissionClass()->with('roles')->get(); + return $this->getPermissionClass()->get(); }); } diff --git a/src/PermissionServiceProvider.php b/src/PermissionServiceProvider.php index 8881ec5..1060fdf 100644 --- a/src/PermissionServiceProvider.php +++ b/src/PermissionServiceProvider.php @@ -2,12 +2,12 @@ namespace Maklad\Permission; +use Illuminate\Support\Facades\DB; use Illuminate\Support\ServiceProvider; use Illuminate\View\Compilers\BladeCompiler; use Maklad\Permission\Contracts\PermissionInterface as Permission; use Maklad\Permission\Contracts\RoleInterface as Role; use Maklad\Permission\Directives\PermissionDirectives; -use Illuminate\Support\Facades\DB; /** * Class PermissionServiceProvider @@ -41,11 +41,8 @@ public function boot() $this->registerModelBindings(); - try { - DB::connection()->getPdo(); - app(PermissionRegistrar::class)->registerPermissions(); - } catch (\Exception $e) { - } + DB::connection()->getPdo(); + app(PermissionRegistrar::class)->registerPermissions(); } public function register() diff --git a/src/Traits/HasPermissions.php b/src/Traits/HasPermissions.php index 303bb23..a373f5d 100644 --- a/src/Traits/HasPermissions.php +++ b/src/Traits/HasPermissions.php @@ -5,15 +5,16 @@ use Illuminate\Support\Collection; use Jenssegers\Mongodb\Eloquent\Builder; use Jenssegers\Mongodb\Eloquent\Model; -use Jenssegers\Mongodb\Relations\BelongsToMany; +use Maklad\Permission\Contracts\PermissionInterface; use Maklad\Permission\Contracts\PermissionInterface as Permission; use Maklad\Permission\Exceptions\GuardDoesNotMatch; use Maklad\Permission\Guard; use Maklad\Permission\Helpers; -use Maklad\Permission\Models\Role; use Maklad\Permission\PermissionRegistrar; -use Monolog\Handler\StreamHandler; -use Monolog\Logger; +use ReflectionException; +use function collect; +use function is_array; +use function is_string; /** * Trait HasPermissions @@ -23,17 +24,6 @@ trait HasPermissions { private $permissionClass; - public static function bootHasPermissions() - { - static::deleting(function (Model $model) { - if (isset($model->forceDeleting) && !$model->forceDeleting) { - return; - } - - $model->permissions()->sync([]); - }); - } - public function getPermissionClass() { if ($this->permissionClass === null) { @@ -43,35 +33,37 @@ public function getPermissionClass() } /** - * A role may be given various permissions. - * @return BelongsToMany + * Query the permissions + */ + public function permissionsQuery() + { + $permission = $this->getPermissionClass(); + return $permission::whereIn('_id', $this->permission_ids ?? []); + } + + /** + * gets the permissions Attribute */ - public function permissions(): BelongsToMany + public function getPermissionsAttribute() { - return $this->belongsToMany(config('permission.models.permission')); + return $this->permissionsQuery()->get(); } /** * Grant the given permission(s) to a role. * - * @param string|array|Permission|\Illuminate\Support\Collection $permissions + * @param string|array|Permission|Collection $permissions * * @return $this * @throws GuardDoesNotMatch */ public function givePermissionTo(...$permissions): self { - $permissions = collect($permissions) - ->flatten() - ->map(function ($permission) { - return $this->getStoredPermission($permission); - }) - ->each(function ($permission) { - $this->ensureModelSharesGuard($permission); - }) + $this->permission_ids = collect($this->permission_ids ?? []) + ->merge($this->getPermissionIds($permissions)) ->all(); - $this->permissions()->saveMany($permissions); + $this->save(); $this->forgetCachedPermissions(); @@ -81,36 +73,38 @@ public function givePermissionTo(...$permissions): self /** * Remove all current permissions and set the given ones. * - * @param string|array|Permission|\Illuminate\Support\Collection $permissions + * @param string|array|Permission|Collection $permissions * * @return $this * @throws GuardDoesNotMatch */ public function syncPermissions(...$permissions): self { - $this->permissions()->sync([]); + $this->permission_ids = $this->getPermissionIds($permissions); + $this->save(); return $this->givePermissionTo($permissions); } /** * Revoke the given permission. * - * @param string|array|Permission|\Illuminate\Support\Collection $permissions + * @param string|array|Permission|Collection $permissions * * @return $this - * @throws \Maklad\Permission\Exceptions\GuardDoesNotMatch + * @throws GuardDoesNotMatch */ public function revokePermissionTo(...$permissions): self { - collect($permissions) - ->flatten() - ->map(function ($permission) { - $permission = $this->getStoredPermission($permission); - $this->permissions()->detach($permission); + $permissions = $this->getPermissionIds($permissions); + + $this->permission_ids = collect($this->permission_ids ?? []) + ->filter(function ($permission) use ($permissions) { + return ! in_array($permission, $permissions, true); + }) + ->all(); - return $permission; - }); + $this->save(); $this->forgetCachedPermissions(); @@ -121,11 +115,11 @@ public function revokePermissionTo(...$permissions): self * @param string|Permission $permission * * @return Permission - * @throws \ReflectionException + * @throws ReflectionException */ - protected function getStoredPermission(string|Permission $permission): Permission + protected function getStoredPermission($permission): Permission { - if (\is_string($permission)) { + if (is_string($permission)) { return $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName()); } @@ -136,7 +130,7 @@ protected function getStoredPermission(string|Permission $permission): Permissio * @param Model $roleOrPermission * * @throws GuardDoesNotMatch - * @throws \ReflectionException + * @throws ReflectionException */ protected function ensureModelSharesGuard(Model $roleOrPermission): void { @@ -151,7 +145,7 @@ protected function ensureModelSharesGuard(Model $roleOrPermission): void /** * @return Collection - * @throws \ReflectionException + * @throws ReflectionException */ protected function getGuardNames(): Collection { @@ -160,7 +154,7 @@ protected function getGuardNames(): Collection /** * @return string - * @throws \ReflectionException + * @throws ReflectionException */ protected function getDefaultGuardName(): string { @@ -178,13 +172,13 @@ public function forgetCachedPermissions(): void /** * Convert to Permission Models * - * @param mixed $permissions + * @param array|string|Collection $permissions * * @return Collection */ - private function convertToPermissionModels(mixed $permissions): Collection + private function convertToPermissionModels($permissions): Collection { - if (\is_array($permissions)) { + if (is_array($permissions)) { $permissions = collect($permissions); } @@ -212,10 +206,12 @@ public function getPermissionNames(): Collection */ public function getPermissionsViaRoles(): Collection { - return $this->load('roles', 'roles.permissions') + $permissionIds = $this->roles->pluck('permission_ids')->flatten()->unique()->values(); + return $this->getPermissionClass()->query()->whereIn('_id', $permissionIds)->get(); + /*return $this->load('roles', 'roles.permissions') ->roles->flatMap(function (Role $role) { return $role->permissions; - })->sort()->values(); + })->sort()->values();*/ } /** @@ -232,15 +228,14 @@ public function getAllPermissions(): Collection /** * Determine if the model may perform the given permission. * - * @param string|Permission $permission + * @param string|PermissionInterface $permission * @param string|null $guardName - * * @return bool - * @throws \ReflectionException + * @throws ReflectionException */ - public function hasPermissionTo(string|Permission $permission, string $guardName = null): bool + public function hasPermissionTo($permission, string $guardName = null): bool { - if (\is_string($permission)) { + if (is_string($permission)) { $permission = $this->getPermissionClass()->findByName( $permission, $guardName ?? $this->getDefaultGuardName() @@ -256,11 +251,11 @@ public function hasPermissionTo(string|Permission $permission, string $guardName * @param array ...$permissions * * @return bool - * @throws \ReflectionException + * @throws ReflectionException */ public function hasAnyPermission(...$permissions): bool { - if (\is_array($permissions[0])) { + if (is_array($permissions[0])) { $permissions = $permissions[0]; } @@ -274,12 +269,12 @@ public function hasAnyPermission(...$permissions): bool } /** - * Determine if the model has all of the given permissions(s). + * Determine if the model has all the given permissions(s). * * @param $permissions * * @return bool - * @throws \ReflectionException + * @throws ReflectionException */ public function hasAllPermissions(...$permissions): bool { @@ -312,15 +307,15 @@ protected function hasPermissionViaRole(Permission $permission): bool * @param string|Permission $permission * * @return bool - * @throws \ReflectionException + * @throws ReflectionException */ - public function hasDirectPermission(string|Permission $permission): bool + public function hasDirectPermission($permission): bool { - if (\is_string($permission)) { + if (is_string($permission)) { $permission = $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName()); } - return $this->permissions->contains('id', $permission->id); + return $this->permissions->contains('_id', $permission->_id); } /** @@ -335,15 +330,15 @@ public function getDirectPermissions(): Collection * Scope the model query to certain permissions only. * * @param Builder $query - * @param array|string|\Maklad\Permission\Contracts\PermissionInterface|Collection $permissions + * @param array|string|Permission|Collection $permissions * * @return Builder */ - public function scopePermission(Builder $query, array|string|Collection|Permission $permissions): Builder + public function scopePermission(Builder $query, $permissions): Builder { $permissions = $this->convertToPermissionModels($permissions); - $roles = \collect([]); + $roles = collect([]); foreach ($permissions as $permission) { $roles = $roles->merge($permission->roles); @@ -353,4 +348,20 @@ public function scopePermission(Builder $query, array|string|Collection|Permissi return $query->orWhereIn('permission_ids', $permissions->pluck('_id')) ->orWhereIn('role_ids', $roles->pluck('_id')); } + + /** + * @param string|array|Permission|Collection $permissions + * @return array + */ + protected function getPermissionIds(...$permissions): array + { + return collect($permissions) + ->flatten() + ->map(function ($permission) { + $permission = $this->getStoredPermission($permission); + $this->ensureModelSharesGuard($permission); + return $permission->_id; + }) + ->all(); + } } diff --git a/src/Traits/HasRoles.php b/src/Traits/HasRoles.php index 899e06f..dfb0421 100644 --- a/src/Traits/HasRoles.php +++ b/src/Traits/HasRoles.php @@ -5,10 +5,12 @@ use Illuminate\Support\Collection; use Jenssegers\Mongodb\Eloquent\Builder; use Jenssegers\Mongodb\Eloquent\Model; +use Jenssegers\Mongodb\Relations\BelongsToMany; use Maklad\Permission\Contracts\RoleInterface as Role; use Maklad\Permission\Helpers; use Maklad\Permission\PermissionRegistrar; use ReflectionException; +use function collect; /** * Trait HasRoles @@ -20,7 +22,7 @@ trait HasRoles private $roleClass; - public static function bootHasRoles() + public static function bootHasRoles(): void { static::deleting(function (Model $model) { if (isset($model->forceDeleting) && !$model->forceDeleting) { @@ -42,7 +44,7 @@ public function getRoleClass() /** * A model may have multiple roles. */ - public function roles() + public function roles(): BelongsToMany|\Illuminate\Database\Eloquent\Relations\BelongsToMany { return $this->belongsToMany(config('permission.models.role')); } @@ -51,11 +53,11 @@ public function roles() * Scope the model query to certain roles only. * * @param Builder $query - * @param array|string|Collection|Role $roles + * @param string|array|Role|Collection $roles * * @return Builder */ - public function scopeRole(Builder $query, Role|array|string|Collection $roles): Builder + public function scopeRole(Builder $query, $roles): Builder { $roles = $this->convertToRoleModels($roles); @@ -68,10 +70,11 @@ public function scopeRole(Builder $query, Role|array|string|Collection $roles): * @param array|string|Role ...$roles * * @return array|Role|string + * @throws ReflectionException */ - public function assignRole(...$roles): array|Role|string + public function assignRole(...$roles) { - $roles = \collect($roles) + $roles = collect($roles) ->flatten() ->map(function ($role) { return $this->getStoredRole($role); @@ -95,9 +98,9 @@ public function assignRole(...$roles): array|Role|string * * @return array|Role|string */ - public function removeRole(...$roles): array|Role|string + public function removeRole(...$roles) { - \collect($roles) + collect($roles) ->flatten() ->map(function ($role) { $role = $this->getStoredRole($role); @@ -117,8 +120,9 @@ public function removeRole(...$roles): array|Role|string * @param array ...$roles * * @return array|Role|string + * @throws ReflectionException */ - public function syncRoles(...$roles): array|Role|string + public function syncRoles(...$roles): Role|array|string { $this->roles()->sync([]); @@ -128,13 +132,13 @@ public function syncRoles(...$roles): array|Role|string /** * Determine if the model has (one of) the given role(s). * - * @param array|string|Collection|Role $roles + * @param string|array|Role|Collection $roles * * @return bool */ - public function hasRole(Role|array|string|Collection $roles): bool + public function hasRole($roles): bool { - if (\is_string($roles) && false !== \strpos($roles, '|')) { + if (\is_string($roles) && str_contains($roles, '|')) { $roles = \explode('|', $roles); } @@ -142,7 +146,7 @@ public function hasRole(Role|array|string|Collection $roles): bool return $this->roles->contains('name', $roles->name ?? $roles); } - $roles = \collect()->make($roles)->map(function ($role) { + $roles = collect()->make($roles)->map(function ($role) { return $role instanceof Role ? $role->name : $role; }); @@ -152,17 +156,17 @@ public function hasRole(Role|array|string|Collection $roles): bool /** * Determine if the model has any of the given role(s). * - * @param array|string|Collection|Role $roles + * @param string|array|Role|Collection $roles * * @return bool */ - public function hasAnyRole(Role|array|string|Collection $roles): bool + public function hasAnyRole($roles): bool { return $this->hasRole($roles); } /** - * Determine if the model has all of the given role(s). + * Determine if the model has all the given role(s). * * @param $roles * @@ -189,7 +193,7 @@ public function hasAllRoles(...$roles): bool * @return Role * @throws ReflectionException */ - protected function getStoredRole(Role|string $role): Role + protected function getStoredRole($role): Role { if (\is_string($role)) { return $this->getRoleClass()->findByName($role, $this->getDefaultGuardName()); @@ -225,10 +229,8 @@ private function convertToRoleModels($roles): Collection $roles = collect([$roles]); } - $roles = $roles->map(function ($role) { + return $roles->map(function ($role) { return $this->getStoredRole($role); }); - - return $roles; } } diff --git a/src/Traits/RefreshesPermissionCache.php b/src/Traits/RefreshesPermissionCache.php index 49463c4..77e89c0 100644 --- a/src/Traits/RefreshesPermissionCache.php +++ b/src/Traits/RefreshesPermissionCache.php @@ -2,7 +2,8 @@ namespace Maklad\Permission\Traits; -use Maklad\Permission\PermissionRegistrar; +use function app; +use function config; /** * Trait RefreshesPermissionCache @@ -10,14 +11,19 @@ */ trait RefreshesPermissionCache { - public static function bootRefreshesPermissionCache() + /** + * Refresh Permission Cache + * + * @return void + */ + public static function bootRefreshesPermissionCache(): void { static::saved(function () { - \app(\config('permission.models.permission'))->forgetCachedPermissions(); + app(config('permission.models.permission'))->forgetCachedPermissions(); }); static::deleted(function () { - \app(\config('permission.models.permission'))->forgetCachedPermissions(); + app(config('permission.models.permission'))->forgetCachedPermissions(); }); } } diff --git a/tests/Admin.php b/tests/Admin.php index a910cb4..297891d 100644 --- a/tests/Admin.php +++ b/tests/Admin.php @@ -3,11 +3,11 @@ namespace Maklad\Permission\Test; use Illuminate\Auth\Authenticatable; +use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; +use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; +use Illuminate\Foundation\Auth\Access\Authorizable; use Jenssegers\Mongodb\Eloquent\Model; use Maklad\Permission\Traits\HasRoles; -use Illuminate\Foundation\Auth\Access\Authorizable; -use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; -use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; class Admin extends Model implements AuthorizableContract, AuthenticatableContract { diff --git a/tests/CacheTest.php b/tests/CacheTest.php index 67615ff..d2216d4 100644 --- a/tests/CacheTest.php +++ b/tests/CacheTest.php @@ -3,9 +3,9 @@ namespace Maklad\Permission\Test; use Illuminate\Support\Facades\DB; +use Maklad\Permission\Models\Permission; use Maklad\Permission\Models\Role; use Maklad\Permission\PermissionRegistrar; -use Maklad\Permission\Models\Permission; class CacheTest extends TestCase { @@ -25,7 +25,7 @@ public function setUp(): void $this->registrar->registerPermissions(); - $this->assertCount(2, DB::getQueryLog()); + $this->assertCount(1, DB::getQueryLog()); DB::flushQueryLog(); } @@ -45,20 +45,20 @@ public function permission_creation_and_updating_and_deleting_should_flush_the_c $this->assertCount(1, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(3, DB::getQueryLog()); + $this->assertCount(2, DB::getQueryLog()); $permission->name = 'other name'; $permission->save(); - $this->assertCount(4, DB::getQueryLog()); + $this->assertCount(3, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(6, DB::getQueryLog()); + $this->assertCount(4, DB::getQueryLog()); $permission->delete(); - $this->assertCount(7, DB::getQueryLog()); + $this->assertCount(5, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(9, DB::getQueryLog()); + $this->assertCount(6, DB::getQueryLog()); } /** @test */ @@ -68,20 +68,20 @@ public function role_creation_and_updating_and_deleting_should_flush_the_cache() $this->assertCount(2, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(4, DB::getQueryLog()); + $this->assertCount(3, DB::getQueryLog()); $role->name = 'other name'; $role->save(); - $this->assertCount(5, DB::getQueryLog()); + $this->assertCount(4, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(7, DB::getQueryLog()); + $this->assertCount(5, DB::getQueryLog()); $role->delete(); - $this->assertCount(8, DB::getQueryLog()); + $this->assertCount(6, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(10, DB::getQueryLog()); + $this->assertCount(7, DB::getQueryLog()); } /** @test */ @@ -98,10 +98,10 @@ public function user_creation_should_not_flush_the_cache() public function adding_a_permission_to_a_role_should_flush_the_cache() { $this->testUserRole->givePermissionTo($this->testUserPermission); - $this->assertCount(2, DB::getQueryLog()); + $this->assertCount(1, DB::getQueryLog()); $this->registrar->registerPermissions(); - $this->assertCount(4, DB::getQueryLog()); + $this->assertCount(2, DB::getQueryLog()); } /** @test */ @@ -109,13 +109,13 @@ public function has_permission_to_should_use_the_cache() { $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']); $this->testUser->assignRole('testRole'); - $this->assertCount(7, DB::getQueryLog()); + $this->assertCount(4, DB::getQueryLog()); $this->assertTrue($this->testUser->hasPermissionTo('edit-articles')); - $this->assertCount(11, DB::getQueryLog()); + $this->assertCount(8, DB::getQueryLog()); $this->assertTrue($this->testUser->hasPermissionTo('edit-news')); - $this->assertCount(11, DB::getQueryLog()); + $this->assertCount(10, DB::getQueryLog()); $this->assertTrue($this->testUser->hasPermissionTo('edit-articles')); $this->assertCount(11, DB::getQueryLog()); diff --git a/tests/HasPermissionsTest.php b/tests/HasPermissionsTest.php index 6ace968..1817c65 100644 --- a/tests/HasPermissionsTest.php +++ b/tests/HasPermissionsTest.php @@ -6,7 +6,6 @@ use Maklad\Permission\Exceptions\PermissionDoesNotExist; use Maklad\Permission\Models\Permission; use Maklad\Permission\Models\Role; -use Monolog\Handler\StreamHandler; use Monolog\Logger; class HasPermissionsTest extends TestCase @@ -183,9 +182,9 @@ public function it_doesnt_detach_permissions_when_soft_deleting() public function it_can_give_and_revoke_multiple_permissions() { $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']); - $this->assertEquals(2, $this->testUserRole->permissions()->count()); + $this->assertEquals(2, $this->testUserRole->permissionsQuery()->count()); $this->testUserRole->revokePermissionTo(['edit-articles', 'edit-news']); - $this->assertEquals(0, $this->testUserRole->permissions()->count()); + $this->assertEquals(0, $this->testUserRole->permissionsQuery()->count()); } /** @test */ diff --git a/tests/PermissionTest.php b/tests/PermissionTest.php index 463bdc7..bfa33c0 100644 --- a/tests/PermissionTest.php +++ b/tests/PermissionTest.php @@ -54,7 +54,7 @@ public function it_has_user_models_of_the_right_class() $this->assertInstanceOf(User::class, $this->testUserPermission->users->first()); $this->testUser->delete(); - $this->assertEquals(0, $this->testUserPermission->users()->count()); + $this->assertEquals(0, $this->testUserPermission->usersQuery()->count()); } /** @test */ diff --git a/tests/RoleTest.php b/tests/RoleTest.php index 564d9a6..9ff0b00 100644 --- a/tests/RoleTest.php +++ b/tests/RoleTest.php @@ -2,11 +2,11 @@ namespace Maklad\Permission\Test; -use Maklad\Permission\Models\Role; -use Maklad\Permission\Models\Permission; use Maklad\Permission\Exceptions\GuardDoesNotMatch; -use Maklad\Permission\Exceptions\RoleAlreadyExists; use Maklad\Permission\Exceptions\PermissionDoesNotExist; +use Maklad\Permission\Exceptions\RoleAlreadyExists; +use Maklad\Permission\Models\Permission; +use Maklad\Permission\Models\Role; use Monolog\Logger; class RoleTest extends TestCase @@ -32,7 +32,7 @@ public function it_has_user_models_of_the_right_class() $this->assertInstanceOf(User::class, $this->testUserRole->users->first()); $this->testUser->delete(); - $this->assertEquals(0, $this->testUserRole->users()->count()); + $this->assertEquals(0, $this->testUserRole->usersQuery()->count()); } /** @test */ diff --git a/tests/TestCase.php b/tests/TestCase.php index 0d528bf..0495d1a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -10,7 +10,6 @@ use Maklad\Permission\Models\Role; use Maklad\Permission\PermissionRegistrar; use Maklad\Permission\PermissionServiceProvider; -use Monolog\Handler\StreamHandler; use Monolog\Handler\TestHandler; use Orchestra\Testbench\TestCase as Orchestra; diff --git a/tests/TestSeeder.php b/tests/TestSeeder.php index 21e965c..6c5473f 100644 --- a/tests/TestSeeder.php +++ b/tests/TestSeeder.php @@ -9,7 +9,6 @@ class TestSeeder extends Seeder { - private Application $app; public function __construct(Application $app) diff --git a/tests/User.php b/tests/User.php index b63453d..c62d9a5 100644 --- a/tests/User.php +++ b/tests/User.php @@ -3,11 +3,11 @@ namespace Maklad\Permission\Test; use Illuminate\Auth\Authenticatable; +use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; +use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; +use Illuminate\Foundation\Auth\Access\Authorizable; use Jenssegers\Mongodb\Eloquent\Model; use Maklad\Permission\Traits\HasRoles; -use Illuminate\Foundation\Auth\Access\Authorizable; -use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; -use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; /** * Class User