['id', 'name', 'slug', 'book_id', 'chapter_id', 'text', 'draft'], 'chapter' => ['id', 'name', 'slug', 'book_id', 'description'], 'book' => ['id', 'name', 'slug', 'description'], 'bookshelf' => ['id', 'name', 'slug', 'description'], ]; public function __construct( protected EntityProvider $entityProvider ) { } /** * Efficiently load in entities for listing onto the given list * where entities are set as a relation via the given name. * This will look for a model id and type via 'name_id' and 'name_type'. * @param Model[] $relations */ public function loadIntoRelations(array $relations, string $relationName, bool $loadParents): void { $idsByType = []; foreach ($relations as $relation) { $type = $relation->getAttribute($relationName . '_type'); $id = $relation->getAttribute($relationName . '_id'); if (!isset($idsByType[$type])) { $idsByType[$type] = []; } $idsByType[$type][] = $id; } $modelMap = $this->idsByTypeToModelMap($idsByType, $loadParents); foreach ($relations as $relation) { $type = $relation->getAttribute($relationName . '_type'); $id = $relation->getAttribute($relationName . '_id'); $related = $modelMap[$type][strval($id)] ?? null; if ($related) { $relation->setRelation($relationName, $related); } } } /** * @param array $idsByType * @return array> */ protected function idsByTypeToModelMap(array $idsByType, bool $eagerLoadParents): array { $modelMap = []; foreach ($idsByType as $type => $ids) { if (!isset($this->listAttributes[$type])) { continue; } $instance = $this->entityProvider->get($type); $models = $instance->newQuery() ->select(array_merge($this->listAttributes[$type], $this->getSubSelectsForQuery($type))) ->scopes('visible') ->whereIn('id', $ids) ->with($eagerLoadParents ? $this->getRelationsToEagerLoad($type) : []) ->get(); if (count($models) > 0) { $modelMap[$type] = []; } foreach ($models as $model) { $modelMap[$type][strval($model->id)] = $model; } } return $modelMap; } protected function getRelationsToEagerLoad(string $type): array { $toLoad = []; $loadVisible = fn (Relation $query) => $query->scopes('visible'); if ($type === 'chapter' || $type === 'page') { $toLoad['book'] = $loadVisible; } if ($type === 'page') { $toLoad['chapter'] = $loadVisible; } return $toLoad; } protected function getSubSelectsForQuery(string $type): array { $subSelects = []; if ($type === 'chapter' || $type === 'page') { $subSelects['book_slug'] = function ($builder) { $builder->select('slug') ->from('books') ->whereColumn('books.id', '=', 'book_id'); }; } return $subSelects; } }