From 57d806e8e9295b83aeefb1b9b57980cd57093929 Mon Sep 17 00:00:00 2001 From: Howriq Date: Mon, 17 Nov 2025 12:30:51 +0200 Subject: [PATCH 1/2] book tutorial code v1 Signed-off-by: Howriq --- bin/clear-config-cache.php | 0 bin/composer-post-install-script.php | 0 bin/doctrine | 17 ++++ bin/doctrine-migrations | 8 ++ composer.json | 11 ++- config/cli-config.php | 18 +++++ config/config.php | 1 + data/cache/.gitignore | 2 - src/App/src/ConfigProvider.php | 78 +++++++++++++++++++ src/App/src/Entity/AbstractEntity.php | 27 +++++++ src/App/src/Entity/TimestampsTrait.php | 53 +++++++++++++ src/Book/src/ConfigProvider.php | 61 +++++++++++++++ src/Book/src/Entity/Book.php | 50 ++++++++++++ .../src/Factory/CreateBookHandlerFactory.php | 24 ++++++ .../src/Factory/DeleteBookHandlerFactory.php | 25 ++++++ .../src/Factory/ListBookHandlerFactory.php | 25 ++++++ .../src/Factory/UpdateBookHandlerFactory.php | 25 ++++++ src/Book/src/Handler/CreateBookHandler.php | 32 ++++++++ src/Book/src/Handler/DeleteBookHandler.php | 47 +++++++++++ src/Book/src/Handler/ListBooksHandler.php | 35 +++++++++ src/Book/src/Handler/UpdateBookHandler.php | 48 ++++++++++++ src/Book/src/RoutesDelegator.php | 27 +++++++ src/Migrations/Version20251116200547.php | 31 ++++++++ 23 files changed, 640 insertions(+), 5 deletions(-) mode change 100644 => 100755 bin/clear-config-cache.php mode change 100644 => 100755 bin/composer-post-install-script.php create mode 100644 bin/doctrine create mode 100644 bin/doctrine-migrations create mode 100644 config/cli-config.php mode change 100644 => 100755 config/config.php delete mode 100755 data/cache/.gitignore create mode 100644 src/App/src/Entity/AbstractEntity.php create mode 100644 src/App/src/Entity/TimestampsTrait.php create mode 100644 src/Book/src/ConfigProvider.php create mode 100644 src/Book/src/Entity/Book.php create mode 100644 src/Book/src/Factory/CreateBookHandlerFactory.php create mode 100644 src/Book/src/Factory/DeleteBookHandlerFactory.php create mode 100644 src/Book/src/Factory/ListBookHandlerFactory.php create mode 100644 src/Book/src/Factory/UpdateBookHandlerFactory.php create mode 100644 src/Book/src/Handler/CreateBookHandler.php create mode 100644 src/Book/src/Handler/DeleteBookHandler.php create mode 100644 src/Book/src/Handler/ListBooksHandler.php create mode 100644 src/Book/src/Handler/UpdateBookHandler.php create mode 100644 src/Book/src/RoutesDelegator.php create mode 100644 src/Migrations/Version20251116200547.php diff --git a/bin/clear-config-cache.php b/bin/clear-config-cache.php old mode 100644 new mode 100755 diff --git a/bin/composer-post-install-script.php b/bin/composer-post-install-script.php old mode 100644 new mode 100755 diff --git a/bin/doctrine b/bin/doctrine new file mode 100644 index 0000000..8480ff9 --- /dev/null +++ b/bin/doctrine @@ -0,0 +1,17 @@ +#!/usr/bin/env php +get(EntityManager::class); +$entityManager->getEventManager(); + +ConsoleRunner::run(new SingleManagerProvider($entityManager)); diff --git a/bin/doctrine-migrations b/bin/doctrine-migrations new file mode 100644 index 0000000..d40cc55 --- /dev/null +++ b/bin/doctrine-migrations @@ -0,0 +1,8 @@ +#!/usr/bin/env php +get(EntityManager::class); +$entityManager->getEventManager(); + +return DependencyFactory::fromEntityManager( + new ConfigurationArray($container->get('config')['doctrine']['migrations']), + new ExistingEntityManager($entityManager) +); \ No newline at end of file diff --git a/config/config.php b/config/config.php old mode 100644 new mode 100755 index 1c58178..aba220f --- a/config/config.php +++ b/config/config.php @@ -30,6 +30,7 @@ // Default App module config \Light\App\ConfigProvider::class, + \Light\Book\ConfigProvider::class, \Light\Page\ConfigProvider::class, // Load application config in a pre-defined order in such a way that local settings diff --git a/data/cache/.gitignore b/data/cache/.gitignore deleted file mode 100755 index d6b7ef3..0000000 --- a/data/cache/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/src/App/src/ConfigProvider.php b/src/App/src/ConfigProvider.php index 82b5fcc..0025ce1 100644 --- a/src/App/src/ConfigProvider.php +++ b/src/App/src/ConfigProvider.php @@ -4,9 +4,22 @@ namespace Light\App; +use Core\App\DBAL\Types\SuccessFailureEnumType; +use Core\App\DBAL\Types\YesNoEnumType; +use Core\App\Factory\EntityListenerResolverFactory; +use Core\App\Resolver\EntityListenerResolver; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; +use Dot\Cache\Adapter\ArrayAdapter; +use Dot\Cache\Adapter\FilesystemAdapter; use Light\App\Factory\GetIndexViewHandlerFactory; use Light\App\Handler\GetIndexViewHandler; use Mezzio\Application; +use Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType; +use Ramsey\Uuid\Doctrine\UuidBinaryType; +use Ramsey\Uuid\Doctrine\UuidType; +use Roave\PsrContainerDoctrine\EntityManagerFactory; class ConfigProvider { @@ -20,6 +33,7 @@ public function __invoke(): array { return [ 'dependencies' => $this->getDependencies(), + 'doctrine' => $this->getDoctrineConfig(), 'templates' => $this->getTemplates(), ]; } @@ -39,8 +53,13 @@ public function getDependencies(): array ], ], 'factories' => [ + 'doctrine.entity_manager.orm_default' => EntityManagerFactory::class, GetIndexViewHandler::class => GetIndexViewHandlerFactory::class, ], + 'aliases' => [ + EntityManager::class => 'doctrine.entity_manager.orm_default', + EntityManagerInterface::class => 'doctrine.entity_manager.orm_default', + ] ]; } @@ -65,4 +84,63 @@ public function getTemplates(): array ], ]; } + + private function getDoctrineConfig(): array + { + return [ + 'cache' => [ + 'array' => [ + 'class' => ArrayAdapter::class, + ], + 'filesystem' => [ + 'class' => FilesystemAdapter::class, + 'directory' => getcwd() . '/data/cache', + 'namespace' => 'doctrine', + ], + ], + 'configuration' => [ + 'orm_default' => [ + 'result_cache' => 'filesystem', + 'metadata_cache' => 'filesystem', + 'query_cache' => 'filesystem', + 'hydration_cache' => 'array', + 'typed_field_mapper' => null, + 'second_level_cache' => [ + 'enabled' => true, + 'default_lifetime' => 3600, + 'default_lock_lifetime' => 60, + 'file_lock_region_directory' => '', + 'regions' => [], + ], + ], + ], + 'connection' => [ + 'orm_default' => [ + 'doctrine_mapping_types' => [ + UuidBinaryType::NAME => 'binary', + UuidBinaryOrderedTimeType::NAME => 'binary', + ], + ], + ], + 'driver' => [ + // The default metadata driver aggregates all other drivers into a single one. + // Override `orm_default` only if you know what you're doing. + 'orm_default' => [ + 'class' => MappingDriverChain::class, + ], + ], + 'migrations' => [ + 'migrations_paths' => [ + 'Migrations' => 'src/Migrations', + ], + 'all_or_nothing' => true, + 'check_database_platform' => true, + ], + 'types' => [ + UuidType::NAME => UuidType::class, + UuidBinaryType::NAME => UuidBinaryType::class, + UuidBinaryOrderedTimeType::NAME => UuidBinaryOrderedTimeType::class, + ], + ]; + } } diff --git a/src/App/src/Entity/AbstractEntity.php b/src/App/src/Entity/AbstractEntity.php new file mode 100644 index 0000000..7e04bb1 --- /dev/null +++ b/src/App/src/Entity/AbstractEntity.php @@ -0,0 +1,27 @@ +uuid = Uuid::uuid7(); + } + + public function getUuid(): UuidInterface + { + return $this->uuid; + } +} diff --git a/src/App/src/Entity/TimestampsTrait.php b/src/App/src/Entity/TimestampsTrait.php new file mode 100644 index 0000000..d004f47 --- /dev/null +++ b/src/App/src/Entity/TimestampsTrait.php @@ -0,0 +1,53 @@ +created; + } + + public function getCreatedFormatted(string $dateFormat = 'Y-m-d H:i:s'): string + { + return $this->created->format($dateFormat); + } + + public function getUpdated(): ?DateTimeImmutable + { + return $this->updated; + } + + public function getUpdatedFormatted(string $dateFormat = 'Y-m-d H:i:s'): ?string + { + if ($this->updated instanceof DateTimeImmutable) { + return $this->updated->format($dateFormat); + } + + return null; + } + + #[ORM\PrePersist] + public function created(): void + { + $this->created = new DateTimeImmutable(); + } + + #[ORM\PreUpdate] + public function touch(): void + { + $this->updated = new DateTimeImmutable(); + } +} diff --git a/src/Book/src/ConfigProvider.php b/src/Book/src/ConfigProvider.php new file mode 100644 index 0000000..9b6aeff --- /dev/null +++ b/src/Book/src/ConfigProvider.php @@ -0,0 +1,61 @@ + $this->getDependencies(), + 'doctrine' => $this->getDoctrineConfig(), + ]; + } + + private function getDependencies(): array + { + return [ + 'delegators' => [ + Application::class => [ + RoutesDelegator::class, + ], + ], + 'factories' => [ + CreateBookHandler::class => CreateBookHandlerFactory::class, + ListBooksHandler::class => ListBookHandlerFactory::class, + UpdateBookHandler::class => UpdateBookHandlerFactory::class, + DeleteBookHandler::class => DeleteBookHandlerFactory::class, + ] + ]; + } + + private function getDoctrineConfig(): array + { + return [ + 'driver' => [ + 'orm_default' => [ + 'drivers' => [ + 'Light\Book\Entity' => 'BookEntities', + ], + ], + 'BookEntities' => [ + 'class' => AttributeDriver::class, + 'cache' => 'array', + 'paths' => [__DIR__ . '/Entity'], + ], + ], + ]; + } +} \ No newline at end of file diff --git a/src/Book/src/Entity/Book.php b/src/Book/src/Entity/Book.php new file mode 100644 index 0000000..43ce4b0 --- /dev/null +++ b/src/Book/src/Entity/Book.php @@ -0,0 +1,50 @@ +created(); + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function getAuthor(): ?string + { + return $this->author; + } + + public function setAuthor(string $author): void + { + $this->author = $author; + } +} diff --git a/src/Book/src/Factory/CreateBookHandlerFactory.php b/src/Book/src/Factory/CreateBookHandlerFactory.php new file mode 100644 index 0000000..43d8475 --- /dev/null +++ b/src/Book/src/Factory/CreateBookHandlerFactory.php @@ -0,0 +1,24 @@ +get(EntityManagerInterface::class); + assert($template instanceof EntityManagerInterface); + + return new CreateBookHandler($template); + } +} \ No newline at end of file diff --git a/src/Book/src/Factory/DeleteBookHandlerFactory.php b/src/Book/src/Factory/DeleteBookHandlerFactory.php new file mode 100644 index 0000000..07b66c7 --- /dev/null +++ b/src/Book/src/Factory/DeleteBookHandlerFactory.php @@ -0,0 +1,25 @@ +get(EntityManagerInterface::class); + assert($template instanceof EntityManagerInterface); + + return new DeleteBookHandler($template); + } +} \ No newline at end of file diff --git a/src/Book/src/Factory/ListBookHandlerFactory.php b/src/Book/src/Factory/ListBookHandlerFactory.php new file mode 100644 index 0000000..6523299 --- /dev/null +++ b/src/Book/src/Factory/ListBookHandlerFactory.php @@ -0,0 +1,25 @@ +get(EntityManagerInterface::class); + assert($template instanceof EntityManagerInterface); + + return new ListBooksHandler($template); + } +} \ No newline at end of file diff --git a/src/Book/src/Factory/UpdateBookHandlerFactory.php b/src/Book/src/Factory/UpdateBookHandlerFactory.php new file mode 100644 index 0000000..829c7b0 --- /dev/null +++ b/src/Book/src/Factory/UpdateBookHandlerFactory.php @@ -0,0 +1,25 @@ +get(EntityManagerInterface::class); + assert($template instanceof EntityManagerInterface); + + return new UpdateBookHandler($template); + } +} \ No newline at end of file diff --git a/src/Book/src/Handler/CreateBookHandler.php b/src/Book/src/Handler/CreateBookHandler.php new file mode 100644 index 0000000..f063f1e --- /dev/null +++ b/src/Book/src/Handler/CreateBookHandler.php @@ -0,0 +1,32 @@ +setAuthor("PHP developer"); + $newBook->setTitle("Doctrine is very cool"); + + $this->entityManager->persist($newBook); + $this->entityManager->flush(); + + return new JsonResponse( + "Book created!", + ); + } +} \ No newline at end of file diff --git a/src/Book/src/Handler/DeleteBookHandler.php b/src/Book/src/Handler/DeleteBookHandler.php new file mode 100644 index 0000000..e42f53f --- /dev/null +++ b/src/Book/src/Handler/DeleteBookHandler.php @@ -0,0 +1,47 @@ +getAttribute('uuid'); + + if (!$uuid) { + return new JsonResponse([ + 'error' => 'UUID is required' + ], 400); + } + + // Find the book by UUID + $book = $this->entityManager + ->getRepository(Book::class) + ->find($uuid); + + if (!$book) { + return new JsonResponse([ + 'error' => 'Book not found' + ], 404); + } + + $this->entityManager->remove($book); + $this->entityManager->flush(); + + return new JsonResponse([ + 'message' => "Book $uuid deleted successfully" + ]); + } +} \ No newline at end of file diff --git a/src/Book/src/Handler/ListBooksHandler.php b/src/Book/src/Handler/ListBooksHandler.php new file mode 100644 index 0000000..e889c35 --- /dev/null +++ b/src/Book/src/Handler/ListBooksHandler.php @@ -0,0 +1,35 @@ +entityManager + ->getRepository(Book::class) + ->findAll(); + + $data = array_map(function (Book $book) { + return [ + 'id' => $book->getUuid()->toString(), + 'title' => $book->getTitle(), + 'author' => $book->getAuthor(), + ]; + }, $books); + + return new JsonResponse($data); + } +} \ No newline at end of file diff --git a/src/Book/src/Handler/UpdateBookHandler.php b/src/Book/src/Handler/UpdateBookHandler.php new file mode 100644 index 0000000..a221b13 --- /dev/null +++ b/src/Book/src/Handler/UpdateBookHandler.php @@ -0,0 +1,48 @@ +getAttribute('uuid'); + + if (!$uuid) { + return new JsonResponse([ + 'error' => 'UUID is required' + ], 400); + } + + $book = $this->entityManager + ->getRepository(Book::class) + ->find($uuid); + + if (!$book) { + return new JsonResponse([ + 'error' => 'Book not found' + ], 404); + } + + $book->setTitle("Updated doctrine is even cooler"); + + $this->entityManager->persist($book); + $this->entityManager->flush(); + + return new JsonResponse([ + 'message' => "Book $uuid updated successfully" + ]); + } +} \ No newline at end of file diff --git a/src/Book/src/RoutesDelegator.php b/src/Book/src/RoutesDelegator.php new file mode 100644 index 0000000..708b5b4 --- /dev/null +++ b/src/Book/src/RoutesDelegator.php @@ -0,0 +1,27 @@ +get('/books/create', [CreateBookHandler::class] , 'books::create'); + $app->get('/books/list', [ListBooksHandler::class] , 'books::list'); + + $app->get('/books/update/{uuid}', [UpdateBookHandler::class] , 'books::update'); + $app->get('/books/delete/{uuid}', [DeleteBookHandler::class] , 'books::delete'); + + return $app; + } +} \ No newline at end of file diff --git a/src/Migrations/Version20251116200547.php b/src/Migrations/Version20251116200547.php new file mode 100644 index 0000000..d039877 --- /dev/null +++ b/src/Migrations/Version20251116200547.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE books (title VARCHAR(500) DEFAULT NULL, author VARCHAR(500) DEFAULT NULL, uuid BINARY(16) NOT NULL, created DATETIME NOT NULL, updated DATETIME DEFAULT NULL, PRIMARY KEY (uuid)) DEFAULT CHARACTER SET utf8mb4'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE books'); + } +} From 6c69d4308465657a06c6f18a95fb871033d403ad Mon Sep 17 00:00:00 2001 From: Howriq Date: Mon, 17 Nov 2025 12:37:42 +0200 Subject: [PATCH 2/2] book tutorial code v1 Signed-off-by: Howriq --- config/cli-config.php | 2 +- src/App/src/ConfigProvider.php | 24 +++++++++---------- src/Book/src/ConfigProvider.php | 9 +++---- .../src/Factory/CreateBookHandlerFactory.php | 6 ++++- .../src/Factory/DeleteBookHandlerFactory.php | 7 ++++-- .../src/Factory/ListBookHandlerFactory.php | 7 ++++-- .../src/Factory/UpdateBookHandlerFactory.php | 7 ++++-- src/Book/src/Handler/CreateBookHandler.php | 4 +++- src/Book/src/Handler/DeleteBookHandler.php | 14 ++++++----- src/Book/src/Handler/ListBooksHandler.php | 6 ++++- src/Book/src/Handler/UpdateBookHandler.php | 14 ++++++----- src/Book/src/RoutesDelegator.php | 14 +++++++---- 12 files changed, 70 insertions(+), 44 deletions(-) diff --git a/config/cli-config.php b/config/cli-config.php index 0464101..0b723df 100644 --- a/config/cli-config.php +++ b/config/cli-config.php @@ -15,4 +15,4 @@ return DependencyFactory::fromEntityManager( new ConfigurationArray($container->get('config')['doctrine']['migrations']), new ExistingEntityManager($entityManager) -); \ No newline at end of file +); diff --git a/src/App/src/ConfigProvider.php b/src/App/src/ConfigProvider.php index 0025ce1..a4d8158 100644 --- a/src/App/src/ConfigProvider.php +++ b/src/App/src/ConfigProvider.php @@ -4,10 +4,6 @@ namespace Light\App; -use Core\App\DBAL\Types\SuccessFailureEnumType; -use Core\App\DBAL\Types\YesNoEnumType; -use Core\App\Factory\EntityListenerResolverFactory; -use Core\App\Resolver\EntityListenerResolver; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; @@ -21,6 +17,8 @@ use Ramsey\Uuid\Doctrine\UuidType; use Roave\PsrContainerDoctrine\EntityManagerFactory; +use function getcwd; + class ConfigProvider { /** @@ -54,12 +52,12 @@ public function getDependencies(): array ], 'factories' => [ 'doctrine.entity_manager.orm_default' => EntityManagerFactory::class, - GetIndexViewHandler::class => GetIndexViewHandlerFactory::class, + GetIndexViewHandler::class => GetIndexViewHandlerFactory::class, ], - 'aliases' => [ + 'aliases' => [ EntityManager::class => 'doctrine.entity_manager.orm_default', EntityManagerInterface::class => 'doctrine.entity_manager.orm_default', - ] + ], ]; } @@ -100,12 +98,12 @@ private function getDoctrineConfig(): array ], 'configuration' => [ 'orm_default' => [ - 'result_cache' => 'filesystem', - 'metadata_cache' => 'filesystem', - 'query_cache' => 'filesystem', - 'hydration_cache' => 'array', - 'typed_field_mapper' => null, - 'second_level_cache' => [ + 'result_cache' => 'filesystem', + 'metadata_cache' => 'filesystem', + 'query_cache' => 'filesystem', + 'hydration_cache' => 'array', + 'typed_field_mapper' => null, + 'second_level_cache' => [ 'enabled' => true, 'default_lifetime' => 3600, 'default_lock_lifetime' => 60, diff --git a/src/Book/src/ConfigProvider.php b/src/Book/src/ConfigProvider.php index 9b6aeff..5c200ed 100644 --- a/src/Book/src/ConfigProvider.php +++ b/src/Book/src/ConfigProvider.php @@ -1,5 +1,7 @@ ListBookHandlerFactory::class, UpdateBookHandler::class => UpdateBookHandlerFactory::class, DeleteBookHandler::class => DeleteBookHandlerFactory::class, - ] + ], ]; } @@ -45,7 +46,7 @@ private function getDoctrineConfig(): array { return [ 'driver' => [ - 'orm_default' => [ + 'orm_default' => [ 'drivers' => [ 'Light\Book\Entity' => 'BookEntities', ], @@ -58,4 +59,4 @@ private function getDoctrineConfig(): array ], ]; } -} \ No newline at end of file +} diff --git a/src/Book/src/Factory/CreateBookHandlerFactory.php b/src/Book/src/Factory/CreateBookHandlerFactory.php index 43d8475..ea0476e 100644 --- a/src/Book/src/Factory/CreateBookHandlerFactory.php +++ b/src/Book/src/Factory/CreateBookHandlerFactory.php @@ -1,5 +1,7 @@ getAttribute('uuid'); - if (!$uuid) { + if (! $uuid) { return new JsonResponse([ - 'error' => 'UUID is required' + 'error' => 'UUID is required', ], 400); } @@ -31,9 +33,9 @@ public function handle(ServerRequestInterface $request): ResponseInterface ->getRepository(Book::class) ->find($uuid); - if (!$book) { + if (! $book) { return new JsonResponse([ - 'error' => 'Book not found' + 'error' => 'Book not found', ], 404); } @@ -41,7 +43,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $this->entityManager->flush(); return new JsonResponse([ - 'message' => "Book $uuid deleted successfully" + 'message' => "Book $uuid deleted successfully", ]); } -} \ No newline at end of file +} diff --git a/src/Book/src/Handler/ListBooksHandler.php b/src/Book/src/Handler/ListBooksHandler.php index e889c35..fa22929 100644 --- a/src/Book/src/Handler/ListBooksHandler.php +++ b/src/Book/src/Handler/ListBooksHandler.php @@ -1,5 +1,7 @@ getAttribute('uuid'); - if (!$uuid) { + if (! $uuid) { return new JsonResponse([ - 'error' => 'UUID is required' + 'error' => 'UUID is required', ], 400); } @@ -30,9 +32,9 @@ public function handle(ServerRequestInterface $request): ResponseInterface ->getRepository(Book::class) ->find($uuid); - if (!$book) { + if (! $book) { return new JsonResponse([ - 'error' => 'Book not found' + 'error' => 'Book not found', ], 404); } @@ -42,7 +44,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $this->entityManager->flush(); return new JsonResponse([ - 'message' => "Book $uuid updated successfully" + 'message' => "Book $uuid updated successfully", ]); } -} \ No newline at end of file +} diff --git a/src/Book/src/RoutesDelegator.php b/src/Book/src/RoutesDelegator.php index 708b5b4..472dbf2 100644 --- a/src/Book/src/RoutesDelegator.php +++ b/src/Book/src/RoutesDelegator.php @@ -1,5 +1,7 @@ get('/books/create', [CreateBookHandler::class] , 'books::create'); - $app->get('/books/list', [ListBooksHandler::class] , 'books::list'); + $app->get('/books/create', [CreateBookHandler::class], 'books::create'); + $app->get('/books/list', [ListBooksHandler::class], 'books::list'); - $app->get('/books/update/{uuid}', [UpdateBookHandler::class] , 'books::update'); - $app->get('/books/delete/{uuid}', [DeleteBookHandler::class] , 'books::delete'); + $app->get('/books/update/{uuid}', [UpdateBookHandler::class], 'books::update'); + $app->get('/books/delete/{uuid}', [DeleteBookHandler::class], 'books::delete'); return $app; } -} \ No newline at end of file +}