Skip to content

Instantly share code, notes, and snippets.

@luca-rath
Last active February 19, 2020 11:43
Show Gist options
  • Save luca-rath/d3fe7a6c4a182f21d11e30ce3496757d to your computer and use it in GitHub Desktop.
Save luca-rath/d3fe7a6c4a182f21d11e30ce3496757d to your computer and use it in GitHub Desktop.
Repository design
<?php
declare(strict_types=1);
namespace Intosite\Bundle\RecipeBundle\Recipe\Infrastructure\Doctrine;
use HandcraftedInTheAlps\Util\Model\Repository\ResourceRepository;
use Intosite\Bundle\RecipeBundle\Recipe\Domain\Model\BookInterface;
use Intosite\Bundle\RecipeBundle\Recipe\Domain\Repository\BookRepositoryInterface;
class BookRepository extends ResourceRepository implements BookRepositoryInterface
{
public function findOneBy(array $criteria, ?array $selectionStrategies = null, ?string $sortingStrategy = null, ?array $orderBy = null): ?BookInterface
{
$books = $this->findBy($criteria, $selectionStrategies, $sortingStrategy, $orderBy, 1, 0);
if (empty($books)) {
return null;
}
return $books[0];
}
public function findBy(array $criteria, ?array $selectionStrategy = null, ?string $sortingStrategy = null, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{
if (static::SORTING_STRATEGY_IDS === $sortingStrategy) {
if (!array_key_exists('id', $criteria) || is_array($criteria['id'])) {
throw new \InvalidArgumentException('If SORTING_STRATEGY_IDS is beeing used, $criteria must contain an array if ids.');
}
$ids = $criteria['id'];
$qb = $this->createQueryBuilder('b');
$qb
->select('b')
->where($qb->expr()->in('b.id', $ids))
->orderBy(sprintf('FIELD(b.id, %s)', implode(', ', $ids)));
$books = $qb->getQuery()->getArrayResult();
return $books;
}
/** @var BookInterface[] $books */
$books = $this->entityRepository->findBy($criteria, $orderBy, $limit, $offset);
return $books;
}
}
<?php
declare(strict_types=1);
namespace Intosite\Bundle\RecipeBundle\Recipe\Domain\Repository;
use HandcraftedInTheAlps\Util\Model\Repository\ResourceRepositoryInterface;
use Intosite\Bundle\RecipeBundle\Recipe\Domain\Model\BookInterface;
interface BookRepositoryInterface extends ResourceRepositoryInterface
{
public const SELECTION_STRATEGY_ALL = 'all';
public const SORTING_STRATEGY_IDS = 'ids';
/**
* @param array<string, mixed> $criteria
* @param array<string>|null $selectionStrategies
* @param array<string>|null $sortingStrategies
*/
public function findOneBy(array $criteria, ?array $selectionStrategies = null, ?string $sortingStrategies = null, ?array $orderBy = null): ?BookInterface;
/**
* @param array<string, mixed> $criteria
* @param array<string>|null $selectionStrategies
* @param array<string>|null $sortingStrategies
*
* @return array<BookInterface>
*/
public function findBy(array $criteria, ?array $selectionStrategies = null, ?string $sortingStrategy = null, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
}
<?php
declare(strict_types=1);
namespace HandcraftedInTheAlps\Util\Model\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
abstract class ResourceRepository implements ResourceRepositoryInterface
{
/**
* @var string
*/
protected $className;
/**
* @var EntityRepository
*/
protected $entityRepository;
/**
* @var EntityManagerInterface
*/
protected $entityManager;
public function __construct(
EntityManagerInterface $em,
ClassMetadata $class
) {
$this->entityRepository = new EntityRepository($em, $class);
$this->entityManager = $em;
$this->className = $this->entityRepository->getClassName();
}
public function create(): object
{
$book = new $this->className();
return $book;
}
public function add(object $book): void
{
$this->entityManager->persist($book);
}
public function remove(object $book): void
{
$this->entityManager->remove($book);
}
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
{
return $this->entityRepository->createQueryBuilder($alias, $indexBy);
}
}
<?php
declare(strict_types=1);
namespace HandcraftedInTheAlps\Util\Model\Repository;
use Doctrine\ORM\QueryBuilder;
interface ResourceRepositoryInterface
{
public function create(): object;
public function add(object $book): void;
public function remove(object $book): void;
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder;
}
@luca-rath
Copy link
Author

There is one drawback, the type definitions for create(), add() and remove() are just object instead of BookInterface.

@luca-rath
Copy link
Author

maybe change SELECTION_STRATEGY to RESOLVE_STRATEGY

@danrot
Copy link

danrot commented Feb 19, 2020

Wouldn't call them STRATEGY at all, because in my mind that would be an implementation of the Strategy Pattern, which it isn't. And I think the others agree when saying that returning object is far from ideal, since all type saftey is gone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment