From 8a1cbac01692748c096d47d289051a0e6a96913b Mon Sep 17 00:00:00 2001 From: Rainrider Date: Tue, 22 Oct 2024 17:41:53 +0200 Subject: [PATCH] feat: split model and entity --- Dockerfile | 2 +- config/packages/doctrine.php | 4 +- src/BookStore/Domain/Model/Book.php | 17 +---- .../Doctrine/DoctrineBookRepository.php | 38 ++++++++-- .../Doctrine/Entity/BookEntity.php | 72 +++++++++++++++++++ .../Doctrine/DoctrineBookRepositoryTest.php | 8 ++- 6 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 src/BookStore/Infrastructure/Doctrine/Entity/BookEntity.php diff --git a/Dockerfile b/Dockerfile index b1ec9a3..c8565bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact ARG PHP_VERSION=8.2 -ARG CADDY_VERSION=2.7 +ARG CADDY_VERSION=2.8 # "php" stage FROM php:${PHP_VERSION}-fpm-alpine AS symfony_php diff --git a/config/packages/doctrine.php b/config/packages/doctrine.php index a11d32a..c3927c9 100644 --- a/config/packages/doctrine.php +++ b/config/packages/doctrine.php @@ -22,8 +22,8 @@ 'BookStore' => [ 'is_bundle' => false, 'type' => 'attribute', - 'dir' => '%kernel.project_dir%/src/BookStore/Domain', - 'prefix' => 'App\BookStore\Domain', + 'dir' => '%kernel.project_dir%/src/BookStore/Infrastructure', + 'prefix' => 'App\BookStore\Infrastructure', ], 'Shared' => [ 'is_bundle' => false, diff --git a/src/BookStore/Domain/Model/Book.php b/src/BookStore/Domain/Model/Book.php index dacebec..9d32bdb 100644 --- a/src/BookStore/Domain/Model/Book.php +++ b/src/BookStore/Domain/Model/Book.php @@ -11,31 +11,20 @@ use App\BookStore\Domain\ValueObject\BookName; use App\BookStore\Domain\ValueObject\Discount; use App\BookStore\Domain\ValueObject\Price; -use Doctrine\ORM\Mapping as ORM; -#[ORM\Entity] class Book { - #[ORM\Embedded(columnPrefix: false)] - private readonly BookId $id; + private BookId $id; public function __construct( - #[ORM\Embedded(columnPrefix: false)] private BookName $name, - - #[ORM\Embedded(columnPrefix: false)] private BookDescription $description, - - #[ORM\Embedded(columnPrefix: false)] private Author $author, - - #[ORM\Embedded(columnPrefix: false)] private BookContent $content, - - #[ORM\Embedded(columnPrefix: false)] private Price $price, + ?BookId $id = null, ) { - $this->id = new BookId(); + $this->id = $id ?? new BookId(); } public function update( diff --git a/src/BookStore/Infrastructure/Doctrine/DoctrineBookRepository.php b/src/BookStore/Infrastructure/Doctrine/DoctrineBookRepository.php index 2ff47b5..7eebb77 100644 --- a/src/BookStore/Infrastructure/Doctrine/DoctrineBookRepository.php +++ b/src/BookStore/Infrastructure/Doctrine/DoctrineBookRepository.php @@ -8,6 +8,7 @@ use App\BookStore\Domain\Repository\BookRepositoryInterface; use App\BookStore\Domain\ValueObject\Author; use App\BookStore\Domain\ValueObject\BookId; +use App\BookStore\Infrastructure\Doctrine\Entity\BookEntity; use App\Shared\Infrastructure\Doctrine\DoctrineRepository; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; @@ -17,7 +18,7 @@ */ final class DoctrineBookRepository extends DoctrineRepository implements BookRepositoryInterface { - private const ENTITY_CLASS = Book::class; + private const ENTITY_CLASS = BookEntity::class; private const ALIAS = 'book'; public function __construct(EntityManagerInterface $em) @@ -27,30 +28,55 @@ public function __construct(EntityManagerInterface $em) public function add(Book $book): void { - $this->em->persist($book); + /** @var ?BookEntity */ + $entity = $this->em->find(self::ENTITY_CLASS, $book->id()->value); + + if (null !== $entity) { + $entity->updateFromModel($book); + } else { + $entity = BookEntity::fromModel($book); + } + + $this->em->persist($entity); } public function remove(Book $book): void { - $this->em->remove($book); + /** @var ?BookEntity */ + $entity = $this->em->find(self::ENTITY_CLASS, $book->id()->value); + + if (null !== $entity) { + $this->em->remove($entity); + } } public function ofId(BookId $id): ?Book { - return $this->em->find(self::ENTITY_CLASS, $id->value); + /** @var ?BookEntity */ + $entity = $this->em->find(self::ENTITY_CLASS, $id->value); + + return null !== $entity ? $entity->toModel() : null; } public function withAuthor(Author $author): static { return $this->filter(static function (QueryBuilder $qb) use ($author): void { - $qb->where(sprintf('%s.author.value = :author', self::ALIAS))->setParameter('author', $author->value); + $qb->where(sprintf('%s.author = :author', self::ALIAS))->setParameter('author', $author->value); }); } public function withCheapestsFirst(): static { return $this->filter(static function (QueryBuilder $qb): void { - $qb->orderBy(sprintf('%s.price.amount', self::ALIAS), 'ASC'); + $qb->orderBy(sprintf('%s.price', self::ALIAS), 'ASC'); }); } + + public function getIterator(): \Iterator + { + foreach (parent::getIterator() as $entity) { + /** @var BookEntity $entity */ + yield $entity->toModel(); + } + } } diff --git a/src/BookStore/Infrastructure/Doctrine/Entity/BookEntity.php b/src/BookStore/Infrastructure/Doctrine/Entity/BookEntity.php new file mode 100644 index 0000000..7a8cacb --- /dev/null +++ b/src/BookStore/Infrastructure/Doctrine/Entity/BookEntity.php @@ -0,0 +1,72 @@ + true])] + private int $price, + ) { + } + + public static function fromModel(Book $model): self + { + return new self( + id: $model->id()->value, + name: $model->name()->value, + description: $model->description()->value, + author: $model->author()->value, + content: $model->content()->value, + price: $model->price()->amount, + ); + } + + public function toModel(): Book + { + $book = new Book( + id: new BookId($this->id), + name: new BookName($this->name), + description: new BookDescription($this->description), + author: new Author($this->author), + content: new BookContent($this->content), + price: new Price($this->price), + ); + + return $book; + } + + public function updateFromModel(Book $model): void + { + $this->name = $model->name()->value; + $this->description = $model->description()->value; + $this->author = $model->author()->value; + $this->content = $model->content()->value; + $this->price = $model->price()->amount; + } +} diff --git a/tests/BookStore/Integration/Doctrine/DoctrineBookRepositoryTest.php b/tests/BookStore/Integration/Doctrine/DoctrineBookRepositoryTest.php index 6cabd29..c92667b 100644 --- a/tests/BookStore/Integration/Doctrine/DoctrineBookRepositoryTest.php +++ b/tests/BookStore/Integration/Doctrine/DoctrineBookRepositoryTest.php @@ -4,6 +4,7 @@ namespace App\Tests\BookStore\Integration\Doctrine; +use App\BookStore\Domain\Model\Book; use App\BookStore\Domain\ValueObject\Author; use App\BookStore\Infrastructure\Doctrine\DoctrineBookRepository; use App\Shared\Infrastructure\Doctrine\DoctrinePaginator; @@ -154,7 +155,7 @@ public function testIteratorWithoutPagination(): void $i = 0; foreach ($repository as $book) { - static::assertSame($books[$i], $book); + static::assertEquals($books[$i]->id(), $book->id()); ++$i; } } @@ -170,6 +171,7 @@ public function testIteratorWithPagination(): void DummyBookFactory::createBook(), DummyBookFactory::createBook(), ]; + $bookIds = array_map(static fn (Book $b) => (string) $b->id(), $books); foreach ($books as $book) { $repository->add($book); @@ -180,7 +182,7 @@ public function testIteratorWithPagination(): void $i = 0; foreach ($repository as $book) { - static::assertContains($book, $books); + static::assertContains((string) $book->id(), $bookIds); ++$i; } @@ -190,7 +192,7 @@ public function testIteratorWithPagination(): void $i = 0; foreach ($repository as $book) { - static::assertContains($book, $books); + static::assertContains((string) $book->id(), $bookIds); ++$i; }