182 lines
4.7 KiB
PHP
182 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Entity\Document;
|
|
use App\Entity\DocumentVersion;
|
|
use App\Entity\User;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use RuntimeException;
|
|
|
|
final readonly class DocumentService
|
|
{
|
|
public function __construct(
|
|
private EntityManagerInterface $em,
|
|
private TagRebuildJobService $tagRebuildJobService,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Creates a new document including version 1.
|
|
*/
|
|
public function createDocument(
|
|
string $title,
|
|
string $filePath,
|
|
User $user
|
|
): Document {
|
|
$document = new Document();
|
|
$document->setTitle(trim($title));
|
|
$document->setCreatedBy($user);
|
|
|
|
$version = new DocumentVersion();
|
|
$version->setVersionNumber(1);
|
|
$version->setFilePath($filePath);
|
|
$version->setChecksum($this->calculateChecksum($filePath));
|
|
$version->setCreatedBy($user);
|
|
$version->setActive(true);
|
|
|
|
$document->addVersion($version);
|
|
$document->setCurrentVersion($version);
|
|
|
|
$this->em->persist($document);
|
|
$this->em->persist($version);
|
|
$this->em->flush();
|
|
|
|
return $document;
|
|
}
|
|
|
|
/**
|
|
* Adds a new immutable version to an existing document.
|
|
*/
|
|
public function addVersion(
|
|
Document $document,
|
|
string $filePath,
|
|
User $user
|
|
): DocumentVersion {
|
|
$nextVersionNumber = $this->getNextVersionNumber($document);
|
|
|
|
$version = new DocumentVersion();
|
|
$version->setVersionNumber($nextVersionNumber);
|
|
$version->setFilePath($filePath);
|
|
$version->setChecksum($this->calculateChecksum($filePath));
|
|
$version->setCreatedBy($user);
|
|
$version->setActive(false);
|
|
|
|
$document->addVersion($version);
|
|
|
|
$this->em->persist($version);
|
|
$this->em->flush();
|
|
|
|
return $version;
|
|
}
|
|
|
|
/**
|
|
* Activates a document version and marks it for re-ingest.
|
|
*/
|
|
public function activateVersion(DocumentVersion $version): void
|
|
{
|
|
$document = $version->getDocument();
|
|
|
|
foreach ($document->getVersions() as $existingVersion) {
|
|
$existingVersion->setActive(false);
|
|
}
|
|
|
|
$version->setActive(true);
|
|
$document->setCurrentVersion($version);
|
|
$version->setIngestStatus(DocumentVersion::INGEST_PENDING);
|
|
|
|
$this->em->flush();
|
|
}
|
|
|
|
/**
|
|
* Archives a document.
|
|
*
|
|
* If the document had tag assignments, the tag index is rebuilt so the
|
|
* routing layer no longer works with an outdated active document set.
|
|
*/
|
|
public function archive(Document $document): void
|
|
{
|
|
if ($document->getStatus() === Document::STATUS_ARCHIVED) {
|
|
return;
|
|
}
|
|
|
|
$shouldRebuildTags = $this->hasTagAssignments($document);
|
|
|
|
$document->archive();
|
|
$this->em->flush();
|
|
|
|
if ($shouldRebuildTags) {
|
|
$this->triggerTagRebuildIfIdle();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a document.
|
|
*
|
|
* If the document had tag assignments, the tag index is rebuilt after the
|
|
* removal so stale document references disappear from tag-based routing.
|
|
*/
|
|
public function delete(Document $document): void
|
|
{
|
|
$shouldRebuildTags = $this->hasTagAssignments($document);
|
|
|
|
$this->em->remove($document);
|
|
$this->em->flush();
|
|
|
|
if ($shouldRebuildTags) {
|
|
$this->triggerTagRebuildIfIdle();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculates the SHA256 checksum for a file path.
|
|
*/
|
|
private function calculateChecksum(string $filePath): string
|
|
{
|
|
$filePath = trim($filePath);
|
|
|
|
if ($filePath === '') {
|
|
throw new RuntimeException('File path must not be empty.');
|
|
}
|
|
|
|
if (!is_file($filePath)) {
|
|
throw new RuntimeException('File not found for checksum.');
|
|
}
|
|
|
|
$checksum = hash_file('sha256', $filePath);
|
|
|
|
if ($checksum === false) {
|
|
throw new RuntimeException('Could not calculate file checksum.');
|
|
}
|
|
|
|
return $checksum;
|
|
}
|
|
|
|
/**
|
|
* Determines the next version number for a document.
|
|
*/
|
|
private function getNextVersionNumber(Document $document): int
|
|
{
|
|
$max = 0;
|
|
|
|
foreach ($document->getVersions() as $version) {
|
|
$max = max($max, $version->getVersionNumber());
|
|
}
|
|
|
|
return $max + 1;
|
|
}
|
|
|
|
private function hasTagAssignments(Document $document): bool
|
|
{
|
|
return $document->getDocumentTags()->count() > 0;
|
|
}
|
|
|
|
private function triggerTagRebuildIfIdle(): void
|
|
{
|
|
if (!$this->tagRebuildJobService->hasActiveJob()) {
|
|
$this->tagRebuildJobService->enqueueAndStartAsync();
|
|
}
|
|
}
|
|
} |