Kant CMS: Grundgerüst, visueller Editor, Mediathek
Symfony 7.4 / Twig / Doctrine DBAL auf herkömmlichem PHP-Webhosting. Versionierte Blockdokumente (v1-v3) mit unveränderlichen Revisionen, getrennten Entwürfen/Veröffentlichungen und optimistischer Konflikterkennung. Visueller Editor mit Strukturbaum, Live-Vorschau, responsiven Gerätewerten, Abschnittsvorlagen, Bildmediathek, Galerien und Effekten. Öffentliche Ausgabe ohne Editor-JavaScript. Geprüft: 41 PHP-Tests / 155 Assertions, 10 JavaScript-Modelltests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
82b07820bc
87 files changed
+14463
No files matched your search
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Content;
|
||||
|
||||
use App\Domain\StaleWrite;
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
final class PageRepository
|
||||
{
|
||||
public function __construct(private Connection $db, private DocumentValidator $validator)
|
||||
{
|
||||
}
|
||||
|
||||
public function saveDraft(string $pageId, array $document, int $expectedVersion): string
|
||||
{
|
||||
$this->validator->validate($document);
|
||||
|
||||
return $this->db->transactional(function () use ($pageId, $document, $expectedVersion): string {
|
||||
$id = bin2hex(random_bytes(16));
|
||||
$changed = $this->db->executeStatement(
|
||||
'UPDATE page SET version = version + 1, draft_revision_id = ? WHERE id = ? AND version = ?',
|
||||
[$id, $pageId, $expectedVersion],
|
||||
);
|
||||
if (1 !== $changed) {
|
||||
throw new StaleWrite('The page changed. Reload before saving.');
|
||||
}
|
||||
$this->db->insert('revision', [
|
||||
'id' => $id, 'page_id' => $pageId,
|
||||
'document' => json_encode($document, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE),
|
||||
'created_at' => gmdate('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
return $id;
|
||||
});
|
||||
}
|
||||
|
||||
public function publish(string $pageId, string $revisionId, int $expectedVersion): void
|
||||
{
|
||||
$this->db->transactional(function () use ($pageId, $revisionId, $expectedVersion): void {
|
||||
$raw = $this->db->fetchOne('SELECT document FROM revision WHERE id = ? AND page_id = ?', [$revisionId, $pageId]);
|
||||
if (false === $raw) {
|
||||
throw new \InvalidArgumentException('Revision does not belong to this page.');
|
||||
}
|
||||
$this->validator->validate(json_decode($raw, true, 512, JSON_THROW_ON_ERROR));
|
||||
$changed = $this->db->executeStatement(
|
||||
'UPDATE page SET published_revision_id = ?, version = version + 1 WHERE id = ? AND version = ?',
|
||||
[$revisionId, $pageId, $expectedVersion],
|
||||
);
|
||||
if (1 !== $changed) {
|
||||
throw new StaleWrite('The page changed. Reload before publishing.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function findPublished(string $slug, string $locale = 'de'): ?array
|
||||
{
|
||||
$page = $this->db->fetchAssociative(
|
||||
'SELECT p.id, p.slug, p.locale, r.document, r.id AS revision_id, w.name AS website_name FROM page p JOIN revision r ON r.id = p.published_revision_id AND r.page_id = p.id JOIN website w ON w.id = p.website_id WHERE p.slug = ? AND p.locale = ? AND p.website_id = ?',
|
||||
[$slug, $locale, 'default'],
|
||||
);
|
||||
if (false === $page) {
|
||||
return null;
|
||||
}
|
||||
$page['document'] = json_decode($page['document'], true, 512, JSON_THROW_ON_ERROR);
|
||||
$this->validator->validate($page['document']);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
public function listForEditor(): array
|
||||
{
|
||||
return $this->db->fetchAllAssociative('SELECT p.id, p.slug, p.locale, p.version, p.published_revision_id, p.draft_revision_id, r.document FROM page p LEFT JOIN revision r ON r.id = p.draft_revision_id AND r.page_id = p.id WHERE p.website_id = ? ORDER BY p.slug', ['default']);
|
||||
}
|
||||
|
||||
public function findForEditor(string $id): ?array
|
||||
{
|
||||
$row = $this->db->fetchAssociative('SELECT p.*, r.document FROM page p JOIN revision r ON r.id = p.draft_revision_id AND r.page_id = p.id WHERE p.id = ? AND p.website_id = ?', [$id, 'default']);
|
||||
if (false === $row) {
|
||||
return null;
|
||||
}
|
||||
$row['version'] = (int) $row['version'];
|
||||
$row['document'] = json_decode($row['document'], true, 512, JSON_THROW_ON_ERROR);
|
||||
$this->validator->validate($row['document']);
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user