diff --git a/config/admin.php b/config/admin.php index d805463ee..7aaf2f120 100755 --- a/config/admin.php +++ b/config/admin.php @@ -230,7 +230,7 @@ 'menu' => [ 'cache' => [ // enable cache or not - 'enable' => false, + 'enable' => true, 'store' => 'file', ], diff --git a/src/Grid/Exporters/AbstractExporter.php b/src/Grid/Exporters/AbstractExporter.php index 8685a9e62..696a8ef4e 100755 --- a/src/Grid/Exporters/AbstractExporter.php +++ b/src/Grid/Exporters/AbstractExporter.php @@ -221,20 +221,20 @@ public function buildData(?int $page = null, ?int $perPage = null) * @param Collection $data * @return array */ - protected function normalize(Collection $data) + protected function normalize(Collection $data): array { - $data = $data->toArray(); - foreach ($data as &$row) { - $row = Arr::dot($row); - + $result = []; + foreach ($data as $item) { + $row = Arr::dot((array) $item); foreach ($row as &$v) { if (is_array($v) || is_object($v)) { $v = json_encode($v, JSON_UNESCAPED_UNICODE); } } + $result[] = $row; } - return $data; + return $result; } /** diff --git a/src/Models/Role.php b/src/Models/Role.php index bb729c64a..c0314cfb3 100755 --- a/src/Models/Role.php +++ b/src/Models/Role.php @@ -6,6 +6,7 @@ use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Support\Collection; class Role extends Model { @@ -79,12 +80,28 @@ public function menus(): BelongsToMany /** * Check user has permission. * - * @param $permission + * @param string|null $permission * @return bool */ public function can(?string $permission): bool { - return $this->permissions()->where('slug', $permission)->exists(); + if (! $permission) { + return false; + } + + return $this->getCachedPermissionsSlugs()->has($permission); + } + + /** + * Get cached permissions slugs collection. + * + * @return Collection + */ + protected function getCachedPermissionsSlugs(): Collection + { + return once(function () { + return $this->permissions()->pluck('slug')->flip(); + }); } /** @@ -106,24 +123,43 @@ public function cannot(?string $permission): bool */ public static function getPermissionId(array $roleIds) { - if (! $roleIds) { + if (empty($roleIds)) { return collect(); } - $related = config('admin.database.role_permissions_table'); - $model = new static(); - $keyName = $model->getKeyName(); + sort($roleIds); + $cacheKey = 'admin.role_permissions.'.md5(implode(',', $roleIds)); - return $model->newQuery() - ->leftJoin($related, $keyName, '=', 'role_id') - ->whereIn($keyName, $roleIds) - ->get(['permission_id', 'role_id']) - ->groupBy('role_id') - ->map(function ($v) { - $v = $v instanceof Arrayable ? $v->toArray() : $v; + return cache()->remember($cacheKey, 3600, function () use ($roleIds) { + $related = config('admin.database.role_permissions_table'); + $model = new static(); + $keyName = $model->getKeyName(); - return array_column($v, 'permission_id'); - }); + return $model->newQuery() + ->leftJoin($related, $keyName, '=', 'role_id') + ->whereIn($keyName, $roleIds) + ->get(['permission_id', 'role_id']) + ->groupBy('role_id') + ->map(function ($v) { + $v = $v instanceof Arrayable ? $v->toArray() : $v; + + return array_column($v, 'permission_id'); + }); + }); + } + + /** + * Clear role permissions cache. + * + * @return void + */ + public static function clearPermissionCache() + { + $store = cache()->getStore(); + + if (method_exists($store, 'forgetByPrefix')) { + $store->forgetByPrefix('admin.role_permissions.'); + } } /** @@ -149,5 +185,13 @@ protected static function boot() $model->permissions()->detach(); }); + + static::saved(function () { + static::clearPermissionCache(); + }); + + static::deleted(function () { + static::clearPermissionCache(); + }); } } diff --git a/src/Traits/HasPermissions.php b/src/Traits/HasPermissions.php index e8a7fdfb4..7d8017864 100644 --- a/src/Traits/HasPermissions.php +++ b/src/Traits/HasPermissions.php @@ -13,7 +13,7 @@ trait HasPermissions /** * Get all permissions of user. * - * @return mixed + * @return Collection */ public function allPermissions(): Collection { @@ -28,11 +28,49 @@ public function allPermissions(): Collection ->keyBy($this->getKeyName()); } + /** + * Get permissions map for fast lookup. + * + * @return array + */ + protected function getPermissionsMap(): array + { + return once(function () { + $slugs = []; + $ids = []; + foreach ($this->allPermissions() as $perm) { + $slugs[$perm->slug] = true; + $ids[$perm->id] = true; + } + + return ['slugs' => $slugs, 'ids' => $ids]; + }); + } + + /** + * Get roles map for fast lookup. + * + * @return array + */ + protected function getRolesMap(): array + { + return once(function () { + $slugs = []; + $ids = []; + foreach ($this->roles as $role) { + $slugs[$role->slug] = true; + $ids[$role->id] = true; + } + + return ['slugs' => $slugs, 'ids' => $ids]; + }); + } + /** * Check if user has permission. * - * @param $ability - * @param array|mixed $arguments + * @param string|int $ability + * @param array|mixed $paramters * @return bool */ public function can($ability, $paramters = []): bool @@ -45,18 +83,15 @@ public function can($ability, $paramters = []): bool return true; } - $permissions = $this->allPermissions(); + $map = $this->getPermissionsMap(); - return $permissions->pluck('slug')->contains($ability) ?: - $permissions - ->pluck('id') - ->contains($ability); + return isset($map['slugs'][$ability]) || isset($map['ids'][$ability]); } /** * Check if user has no permission. * - * @param $permission + * @param string $permission * @return bool */ public function cannot(string $permission): bool @@ -67,7 +102,7 @@ public function cannot(string $permission): bool /** * Check if user is administrator. * - * @return mixed + * @return bool */ public function isAdministrator(): bool { @@ -80,39 +115,45 @@ public function isAdministrator(): bool /** * Check if user is $role. * - * @param string $role - * @return mixed + * @param string|int $role + * @return bool */ - public function isRole(string $role): bool + public function isRole(string|int $role): bool { - /* @var Collection $roles */ - $roles = $this->roles; + $map = $this->getRolesMap(); - return $roles->pluck('slug')->contains($role) ?: - $roles->pluck('id')->contains($role); + return isset($map['slugs'][$role]) || isset($map['ids'][$role]); } /** * Check if user in $roles. * * @param string|array|Arrayable $roles - * @return mixed + * @return bool */ public function inRoles($roles = []): bool { - /* @var Collection $all */ - $all = $this->roles; - $roles = Helper::array($roles); - return $all->pluck('slug')->intersect($roles)->isNotEmpty() ?: - $all->pluck('id')->intersect($roles)->isNotEmpty(); + if (empty($roles)) { + return false; + } + + $map = $this->getRolesMap(); + + foreach ($roles as $role) { + if (isset($map['slugs'][$role]) || isset($map['ids'][$role])) { + return true; + } + } + + return false; } /** * If visible for roles. * - * @param $roles + * @param array $roles * @return bool */ public function visible($roles = []): bool