1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-08-07 23:03:00 +03:00

Input WYSIWYG: Added reference store & fetch handling

For book, shelves and chapters.
Made much of the existing handling generic to entity types.
Added new MixedEntityListLoader to help load lists somewhat efficiently.
Only manually tested so far.
This commit is contained in:
Dan Brown
2023-12-18 16:23:40 +00:00
parent c622b785a9
commit 307fae39c4
21 changed files with 219 additions and 97 deletions

View File

@@ -2,60 +2,62 @@
namespace BookStack\References;
use BookStack\Entities\Models\Page;
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Entity;
use Illuminate\Database\Eloquent\Collection;
class ReferenceStore
{
/**
* Update the outgoing references for the given page.
*/
public function updateForPage(Page $page): void
{
$this->updateForPages([$page]);
public function __construct(
protected EntityProvider $entityProvider
) {
}
/**
* Update the outgoing references for all pages in the system.
* Update the outgoing references for the given entity.
*/
public function updateForAllPages(): void
public function updateForEntity(Entity $entity): void
{
Reference::query()
->where('from_type', '=', (new Page())->getMorphClass())
->delete();
Page::query()->select(['id', 'html'])->chunk(100, function (Collection $pages) {
$this->updateForPages($pages->all());
});
$this->updateForEntities([$entity]);
}
/**
* Update the outgoing references for the pages in the given array.
* Update the outgoing references for all entities in the system.
*/
public function updateForAll(): void
{
Reference::query()->delete();
foreach ($this->entityProvider->all() as $entity) {
$entity->newQuery()->select(['id', $entity->htmlField])->chunk(100, function (Collection $entities) {
$this->updateForEntities($entities->all());
});
}
}
/**
* Update the outgoing references for the entities in the given array.
*
* @param Page[] $pages
* @param Entity[] $entities
*/
protected function updateForPages(array $pages): void
protected function updateForEntities(array $entities): void
{
if (count($pages) === 0) {
if (count($entities) === 0) {
return;
}
$parser = CrossLinkParser::createWithEntityResolvers();
$references = [];
$pageIds = array_map(fn (Page $page) => $page->id, $pages);
Reference::query()
->where('from_type', '=', $pages[0]->getMorphClass())
->whereIn('from_id', $pageIds)
->delete();
$this->dropReferencesFromEntities($entities);
foreach ($pages as $page) {
$models = $parser->extractLinkedModels($page->html);
foreach ($entities as $entity) {
$models = $parser->extractLinkedModels($entity->getAttribute($entity->htmlField));
foreach ($models as $model) {
$references[] = [
'from_id' => $page->id,
'from_type' => $page->getMorphClass(),
'from_id' => $entity->id,
'from_type' => $entity->getMorphClass(),
'to_id' => $model->id,
'to_type' => $model->getMorphClass(),
];
@@ -66,4 +68,29 @@ class ReferenceStore
Reference::query()->insert($referenceDataChunk);
}
}
/**
* Delete all the existing references originating from the given entities.
* @param Entity[] $entities
*/
protected function dropReferencesFromEntities(array $entities): void
{
$IdsByType = [];
foreach ($entities as $entity) {
$type = $entity->getMorphClass();
if (!isset($IdsByType[$type])) {
$IdsByType[$type] = [];
}
$IdsByType[$type][] = $entity->id;
}
foreach ($IdsByType as $type => $entityIds) {
Reference::query()
->where('from_type', '=', $type)
->whereIn('from_id', $entityIds)
->delete();
}
}
}