Skip to content

Commit

Permalink
feat: split model and entity
Browse files Browse the repository at this point in the history
  • Loading branch information
Rainrider committed Oct 23, 2024
1 parent 7693a1d commit 8a1cbac
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions config/packages/doctrine.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
17 changes: 3 additions & 14 deletions src/BookStore/Domain/Model/Book.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
38 changes: 32 additions & 6 deletions src/BookStore/Infrastructure/Doctrine/DoctrineBookRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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();
}
}
}
72 changes: 72 additions & 0 deletions src/BookStore/Infrastructure/Doctrine/Entity/BookEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace App\BookStore\Infrastructure\Doctrine\Entity;

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Domain\ValueObject\BookContent;
use App\BookStore\Domain\ValueObject\BookDescription;
use App\BookStore\Domain\ValueObject\BookId;
use App\BookStore\Domain\ValueObject\BookName;
use App\BookStore\Domain\ValueObject\Price;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\AbstractUid;

#[ORM\Entity]
#[ORM\Table(name: 'book')]
class BookEntity
{
public function __construct(
#[ORM\Id]
#[ORM\Column(type: 'uuid')]
private readonly AbstractUid $id,
#[ORM\Column(length: 255)]
private string $name,
#[ORM\Column(length: 1023)]
private string $description,
#[ORM\Column(length: 255)]
private string $author,
#[ORM\Column(length: 65535)]
private string $content,
#[ORM\Column(options: ['unsigned' => 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down

0 comments on commit 8a1cbac

Please sign in to comment.