Skip to content

Commit cb86fd8

Browse files
authored
Support custom fields on cache (#2091)
1 parent 55f96fb commit cb86fd8

File tree

2 files changed

+83
-25
lines changed

2 files changed

+83
-25
lines changed

src/PermissionRegistrar.php

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ class PermissionRegistrar
5252
/** @var array */
5353
private $cachedRoles = [];
5454

55+
/** @var array */
56+
private $alias = [];
57+
58+
/** @var array */
59+
private $except = [];
60+
5561
/**
5662
* PermissionRegistrar constructor.
5763
*
@@ -174,18 +180,20 @@ private function loadPermissions()
174180
});
175181

176182
// fallback for old cache method, must be removed on next mayor version
177-
if (! isset($this->permissions['permissions'])) {
183+
if (! isset($this->permissions['alias'])) {
178184
$this->forgetCachedPermissions();
179185
$this->loadPermissions();
180186

181187
return;
182188
}
183189

190+
$this->alias = $this->permissions['alias'];
191+
184192
$this->hydrateRolesCache();
185193

186194
$this->permissions = $this->getHydratedPermissionCollection();
187195

188-
$this->cachedRoles = [];
196+
$this->cachedRoles = $this->alias = $this->except = [];
189197
}
190198

191199
/**
@@ -267,27 +275,55 @@ public function getCacheStore(): Store
267275
return $this->cache->getStore();
268276
}
269277

278+
/**
279+
* Changes array keys with alias
280+
*
281+
* @return array
282+
*/
283+
private function aliasedArray($model): array
284+
{
285+
return collect(is_array($model) ? $model : $model->getAttributes())->except($this->except)
286+
->keyBy(function ($value, $key) {
287+
return $this->alias[$key] ?? $key;
288+
})->all();
289+
}
290+
291+
/**
292+
* Array for cache alias
293+
*/
294+
private function aliasModelFields($newKeys = []): void
295+
{
296+
$i = 0;
297+
$alphas = ! count($this->alias) ? range('a', 'h') : range('j', 'p');
298+
299+
foreach (array_keys($newKeys->getAttributes()) as $value) {
300+
if (! isset($this->alias[$value])) {
301+
$this->alias[$value] = $alphas[$i++] ?? $value;
302+
}
303+
}
304+
305+
$this->alias = array_diff_key($this->alias, array_flip($this->except));
306+
}
307+
270308
/*
271309
* Make the cache smaller using an array with only required fields
272310
*/
273311
private function getSerializedPermissionsForCache()
274312
{
275-
$roleClass = $this->getRoleClass();
276-
$roleKey = (new $roleClass())->getKeyName();
277-
278-
$permissionClass = $this->getPermissionClass();
279-
$permissionKey = (new $permissionClass())->getKeyName();
313+
$this->except = config('permission.cache.column_names_except', ['created_at','updated_at', 'deleted_at']);
280314

281-
$permissions = $permissionClass
282-
->select($permissionKey, "$permissionKey as i", 'name as n', 'guard_name as g')
283-
->with("roles:$roleKey,$roleKey as i,name as n,guard_name as g")->get()
315+
$permissions = $this->getPermissionClass()->select()->with('roles')->get()
284316
->map(function ($permission) {
285-
return $permission->only('i', 'n', 'g') + $this->getSerializedRoleRelation($permission);
317+
if (! $this->alias) {
318+
$this->aliasModelFields($permission);
319+
}
320+
321+
return $this->aliasedArray($permission) + $this->getSerializedRoleRelation($permission);
286322
})->all();
287323
$roles = array_values($this->cachedRoles);
288324
$this->cachedRoles = [];
289325

290-
return compact('permissions', 'roles');
326+
return ['alias' => array_flip($this->alias)] + compact('permissions', 'roles');
291327
}
292328

293329
private function getSerializedRoleRelation($permission)
@@ -296,13 +332,18 @@ private function getSerializedRoleRelation($permission)
296332
return [];
297333
}
298334

335+
if (! isset($this->alias['roles'])) {
336+
$this->alias['roles'] = 'r';
337+
$this->aliasModelFields($permission->roles[0]);
338+
}
339+
299340
return [
300341
'r' => $permission->roles->map(function ($role) {
301-
if (! isset($this->cachedRoles[$role->i])) {
302-
$this->cachedRoles[$role->i] = $role->only('i', 'n', 'g');
342+
if (! isset($this->cachedRoles[$role->getKey()])) {
343+
$this->cachedRoles[$role->getKey()] = $this->aliasedArray($role);
303344
}
304345

305-
return $role->i;
346+
return $role->getKey();
306347
})->all(),
307348
];
308349
}
@@ -315,11 +356,7 @@ private function getHydratedPermissionCollection()
315356
return Collection::make(
316357
array_map(function ($item) use ($permissionInstance) {
317358
return $permissionInstance
318-
->newFromBuilder([
319-
$permissionInstance->getKeyName() => $item['i'],
320-
'name' => $item['n'],
321-
'guard_name' => $item['g'],
322-
])
359+
->newFromBuilder($this->aliasedArray(array_diff_key($item, ['r' => 0])))
323360
->setRelation('roles', $this->getHydratedRoleCollection($item['r'] ?? []));
324361
}, $this->permissions['permissions'])
325362
);
@@ -338,11 +375,8 @@ private function hydrateRolesCache()
338375
$roleInstance = new $roleClass();
339376

340377
array_map(function ($item) use ($roleInstance) {
341-
$this->cachedRoles[$item['i']] = $roleInstance->newFromBuilder([
342-
$roleInstance->getKeyName() => $item['i'],
343-
'name' => $item['n'],
344-
'guard_name' => $item['g'],
345-
]);
378+
$role = $roleInstance->newFromBuilder($this->aliasedArray($item));
379+
$this->cachedRoles[$role->getKey()] = $role;
346380
}, $this->permissions['roles']);
347381
}
348382
}

tests/HasPermissionsWithCustomModelsTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Spatie\Permission\Test;
44

5+
use DB;
6+
use Spatie\Permission\PermissionRegistrar;
7+
58
class HasPermissionsWithCustomModelsTest extends HasPermissionsTest
69
{
710
/** @var bool */
@@ -12,4 +15,25 @@ public function it_can_use_custom_model_permission()
1215
{
1316
$this->assertSame(get_class($this->testUserPermission), Permission::class);
1417
}
18+
19+
/** @test */
20+
public function it_can_use_custom_fields_from_cache()
21+
{
22+
DB::connection()->getSchemaBuilder()->table(config('permission.table_names.roles'), function ($table) {
23+
$table->string('type')->default('R');
24+
});
25+
DB::connection()->getSchemaBuilder()->table(config('permission.table_names.permissions'), function ($table) {
26+
$table->string('type')->default('P');
27+
});
28+
29+
$this->testUserRole->givePermissionTo($this->testUserPermission);
30+
app(PermissionRegistrar::class)->getPermissions();
31+
32+
DB::enableQueryLog();
33+
$this->assertSame('P', Permission::findByName('edit-articles')->type);
34+
$this->assertSame('R', Permission::findByName('edit-articles')->roles[0]->type);
35+
DB::disableQueryLog();
36+
37+
$this->assertSame(0, count(DB::getQueryLog()));
38+
}
1539
}

0 commit comments

Comments
 (0)