Storyline: per-chapter DM notes
Each chapter page gets a collapsible "Chapter notes" card at the top — a free-text log the DM fills during play (the story the party tells after the Crooked Goose, names given to NPCs, threads for next time). Saved per chapter slug with a "saved <timestamp>" marker, same shape as the Enemy/NPC game-notes field. New: ChapterNote entity (slug-keyed, unique) + repository findOrCreate(), ChapterNoteType form, chapter_note table migration (ENGINE = InnoDB pinned). The storyline chapter route now also accepts POST to save. Chapters stay hand-written Twig; only the mutable notes live in the DB. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Form\ChapterNoteType;
|
||||
use App\Repository\ChapterNoteRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
@@ -46,13 +50,27 @@ class StorylineController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/storyline/{slug}', name: 'app_storyline_chapter', requirements: ['slug' => '[a-z0-9-]+'], methods: ['GET'])]
|
||||
public function chapter(string $slug): Response
|
||||
#[Route('/storyline/{slug}', name: 'app_storyline_chapter', requirements: ['slug' => '[a-z0-9-]+'], methods: ['GET', 'POST'])]
|
||||
public function chapter(string $slug, Request $request, ChapterNoteRepository $notes, EntityManagerInterface $em): Response
|
||||
{
|
||||
if (!isset(self::CHAPTERS[$slug])) {
|
||||
throw $this->createNotFoundException('No such storyline chapter.');
|
||||
}
|
||||
|
||||
$note = $notes->findOrCreate($slug);
|
||||
$form = $this->createForm(ChapterNoteType::class, $note);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if (null === $note->getId()) {
|
||||
$em->persist($note);
|
||||
}
|
||||
$em->flush();
|
||||
$this->addFlash('success', 'Chapter notes saved.');
|
||||
|
||||
return $this->redirectToRoute('app_storyline_chapter', ['slug' => $slug]);
|
||||
}
|
||||
|
||||
$slugs = array_keys(self::CHAPTERS);
|
||||
$pos = array_search($slug, $slugs, true);
|
||||
|
||||
@@ -60,6 +78,8 @@ class StorylineController extends AbstractController
|
||||
'chapters' => self::CHAPTERS,
|
||||
'slug' => $slug,
|
||||
'chapter' => self::CHAPTERS[$slug],
|
||||
'note' => $note,
|
||||
'form' => $form,
|
||||
'prev' => $pos > 0 ? $slugs[$pos - 1] : null,
|
||||
'next' => $pos < \count($slugs) - 1 ? $slugs[$pos + 1] : null,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user