Optimized pre-joint-permission logic efficiency

This commit is contained in:
Dan Brown 2022-07-10 13:45:04 +01:00
parent a721405202
commit c5e9dfa168
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
1 changed files with 67 additions and 41 deletions

View File

@ -37,7 +37,7 @@ class PermissionService
protected $db; protected $db;
/** /**
* @var array * @var array<string, array<int, Entity>>
*/ */
protected $entityCache; protected $entityCache;
@ -68,10 +68,12 @@ class PermissionService
foreach ($entities as $entity) { foreach ($entities as $entity) {
$class = get_class($entity); $class = get_class($entity);
if (!isset($this->entityCache[$class])) { if (!isset($this->entityCache[$class])) {
$this->entityCache[$class] = collect(); $this->entityCache[$class] = [];
} }
$this->entityCache[$class]->put($entity->id, $entity);
$this->entityCache[$class][$entity->getRawAttribute('id')] = $entity;
} }
} }
@ -80,8 +82,8 @@ class PermissionService
*/ */
protected function getBook(int $bookId): ?Book protected function getBook(int $bookId): ?Book
{ {
if (isset($this->entityCache[Book::class]) && $this->entityCache[Book::class]->has($bookId)) { if ($this->entityCache[Book::class][$bookId] ?? false) {
return $this->entityCache[Book::class]->get($bookId); return $this->entityCache[Book::class][$bookId];
} }
return Book::query()->withTrashed()->find($bookId); return Book::query()->withTrashed()->find($bookId);
@ -92,8 +94,8 @@ class PermissionService
*/ */
protected function getChapter(int $chapterId): ?Chapter protected function getChapter(int $chapterId): ?Chapter
{ {
if (isset($this->entityCache[Chapter::class]) && $this->entityCache[Chapter::class]->has($chapterId)) { if ($this->entityCache[Chapter::class][$chapterId] ?? false) {
return $this->entityCache[Chapter::class]->get($chapterId); return $this->entityCache[Chapter::class][$chapterId];
} }
return Chapter::query() return Chapter::query()
@ -206,7 +208,7 @@ class PermissionService
$entities = [$entity]; $entities = [$entity];
if ($entity instanceof Book) { if ($entity instanceof Book) {
$books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get(); $books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get();
$this->buildJointPermissionsForBooks($books, Role::query()->get()->all(), true); $this->buildJointPermissionsForBooks($books, Role::query()->with('permissions')->get()->all(), true);
return; return;
} }
@ -270,7 +272,7 @@ class PermissionService
} }
/** /**
* Delete all of the entity jointPermissions for a list of entities. * Delete all the entity jointPermissions for a list of entities.
* *
* @param Role[] $roles * @param Role[] $roles
*/ */
@ -283,19 +285,7 @@ class PermissionService
} }
/** /**
* Delete the entity jointPermissions for a particular entity. * Delete all the entity jointPermissions for a list of entities.
*
* @param Entity $entity
*
* @throws Throwable
*/
public function deleteJointPermissionsForEntity(Entity $entity)
{
$this->deleteManyJointPermissionsForEntities([$entity]);
}
/**
* Delete all of the entity jointPermissions for a list of entities.
* *
* @param Entity[] $entities * @param Entity[] $entities
* *
@ -303,20 +293,16 @@ class PermissionService
*/ */
protected function deleteManyJointPermissionsForEntities(array $entities) protected function deleteManyJointPermissionsForEntities(array $entities)
{ {
if (count($entities) === 0) { $idsByType = $this->entitiesToTypeIdMap($entities);
return;
}
$this->db->transaction(function () use ($entities) { $this->db->transaction(function () use ($idsByType) {
foreach (array_chunk($entities, 1000) as $entityChunk) { foreach ($idsByType as $type => $ids) {
$query = $this->db->table('joint_permissions'); foreach (array_chunk($ids, 1000) as $idChunk) {
foreach ($entityChunk as $entity) { $this->db->table('joint_permissions')
$query->orWhere(function (QueryBuilder $query) use ($entity) { ->where('entity_type', '=', $type)
$query->where('entity_id', '=', $entity->id) ->whereIn('entity_id', $idChunk)
->where('entity_type', '=', $entity->getMorphClass()); ->delete();
});
} }
$query->delete();
} }
}); });
} }
@ -334,16 +320,14 @@ class PermissionService
$this->readyEntityCache($entities); $this->readyEntityCache($entities);
$jointPermissions = []; $jointPermissions = [];
// Fetch Entity Permissions and create a mapping of entity restricted statuses // Create a mapping of entity restricted statuses
$entityRestrictedMap = []; $entityRestrictedMap = [];
$permissionFetch = EntityPermission::query();
foreach ($entities as $entity) { foreach ($entities as $entity) {
$entityRestrictedMap[$entity->getMorphClass() . ':' . $entity->id] = boolval($entity->getRawAttribute('restricted')); $entityRestrictedMap[$entity->getMorphClass() . ':' . $entity->getRawAttribute('id')] = boolval($entity->getRawAttribute('restricted'));
$permissionFetch->orWhere(function ($query) use ($entity) {
$query->where('restrictable_id', '=', $entity->id)->where('restrictable_type', '=', $entity->getMorphClass());
});
} }
$permissions = $permissionFetch->get();
// Fetch related entity permissions
$permissions = $this->getEntityPermissionsForEntities($entities);
// Create a mapping of explicit entity permissions // Create a mapping of explicit entity permissions
$permissionMap = []; $permissionMap = [];
@ -377,6 +361,48 @@ class PermissionService
}); });
} }
/**
* From the given entity list, provide back a mapping of entity types to
* the ids of that given type. The type used is the DB morph class.
* @param Entity[] $entities
* @return array<string, int[]>
*/
protected function entitiesToTypeIdMap(array $entities): array
{
$idsByType = [];
foreach ($entities as $entity) {
$type = $entity->getMorphClass();
if (!isset($idsByType[$type])) {
$idsByType[$type] = [];
}
$idsByType[$type][] = $entity->getRawAttribute('id');
}
return $idsByType;
}
/**
* Get the entity permissions for all the given entities
* @param Entity[] $entities
* @return EloquentCollection
*/
protected function getEntityPermissionsForEntities(array $entities)
{
$idsByType = $this->entitiesToTypeIdMap($entities);
$permissionFetch = EntityPermission::query();
foreach ($idsByType as $type => $ids) {
$permissionFetch->orWhere(function (Builder $query) use ($type, $ids) {
$query->where('restrictable_type', '=', $type)->whereIn('restrictable_id', $ids);
});
}
return $permissionFetch->get();
}
/** /**
* Get the actions related to an entity. * Get the actions related to an entity.
*/ */