optimize tag and rebuilding
This commit is contained in:
134
src/Tag/TagService.php
Normal file
134
src/Tag/TagService.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tag;
|
||||
|
||||
use App\Entity\Tag;
|
||||
use App\Entity\Document;
|
||||
use App\Entity\DocumentTag;
|
||||
use App\Service\TagRebuildJobService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class TagService
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $em,
|
||||
private TagRebuildJobService $jobs,
|
||||
) {}
|
||||
|
||||
// =========================================================
|
||||
// TAG CREATE
|
||||
// =========================================================
|
||||
|
||||
public function create(string $slug, string $label, ?string $description = null): Tag
|
||||
{
|
||||
$slug = trim($slug);
|
||||
$label = trim($label);
|
||||
|
||||
if ($label === '' || $slug === '') {
|
||||
throw new \InvalidArgumentException('Label und Slug sind Pflichtfelder.');
|
||||
}
|
||||
|
||||
if ($this->slugExists($slug)) {
|
||||
throw new \RuntimeException('Slug existiert bereits.');
|
||||
}
|
||||
|
||||
$tag = new Tag($slug, $label, $description);
|
||||
|
||||
$this->em->persist($tag);
|
||||
$this->em->flush();
|
||||
|
||||
$this->triggerRebuildIfIdle();
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// TAG DELETE
|
||||
// =========================================================
|
||||
|
||||
public function deleteById(string $tagId): void
|
||||
{
|
||||
$tag = $this->em->getRepository(Tag::class)->find($tagId);
|
||||
|
||||
if (!$tag instanceof Tag) {
|
||||
throw new \RuntimeException('Tag nicht gefunden.');
|
||||
}
|
||||
|
||||
$this->delete($tag);
|
||||
}
|
||||
|
||||
public function delete(Tag $tag): void
|
||||
{
|
||||
$this->em->remove($tag);
|
||||
$this->em->flush();
|
||||
|
||||
$this->triggerRebuildIfIdle();
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// DOCUMENT TAG SYNC
|
||||
// =========================================================
|
||||
|
||||
/**
|
||||
* Synchronisiert alle Tags eines Dokuments.
|
||||
* Löst einen Rebuild aus, da document_ids Teil des NDJSON sind.
|
||||
*/
|
||||
public function syncDocumentTags(Document $document, array $newTagIds): void
|
||||
{
|
||||
$newTagIds = array_unique($newTagIds);
|
||||
|
||||
$currentRelations = $this->em
|
||||
->getRepository(DocumentTag::class)
|
||||
->findBy(['document' => $document]);
|
||||
|
||||
$currentTagIds = array_map(
|
||||
fn(DocumentTag $dt) => (string) $dt->getTag()->getId(),
|
||||
$currentRelations
|
||||
);
|
||||
|
||||
$toAdd = array_diff($newTagIds, $currentTagIds);
|
||||
$toRemove = array_diff($currentTagIds, $newTagIds);
|
||||
|
||||
foreach ($toAdd as $tagId) {
|
||||
$tag = $this->em->getRepository(Tag::class)->find($tagId);
|
||||
if ($tag instanceof Tag) {
|
||||
$this->em->persist(new DocumentTag($document, $tag));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($currentRelations as $relation) {
|
||||
if (in_array((string) $relation->getTag()->getId(), $toRemove, true)) {
|
||||
$this->em->remove($relation);
|
||||
}
|
||||
}
|
||||
|
||||
if ($toAdd || $toRemove) {
|
||||
$this->em->flush();
|
||||
$this->triggerRebuildIfIdle();
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// INTERNAL HELPERS
|
||||
// =========================================================
|
||||
|
||||
private function slugExists(string $slug): bool
|
||||
{
|
||||
return (int) $this->em->createQueryBuilder()
|
||||
->select('COUNT(t.id)')
|
||||
->from(Tag::class, 't')
|
||||
->where('t.slug = :slug')
|
||||
->setParameter('slug', $slug)
|
||||
->getQuery()
|
||||
->getSingleScalarResult() > 0;
|
||||
}
|
||||
|
||||
private function triggerRebuildIfIdle(): void
|
||||
{
|
||||
if (!$this->jobs->hasActiveJob()) {
|
||||
$this->jobs->enqueueAndStartAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user