Repository
An implementation of the repository pattern. This is an alternative to
Doctrine's standard EntityRepository
. Unlike Doctrine's, our repository
implements Collection
interface, so you can work with the repository like any
other implementation of Collection
.
Why?
Why not? A repository is essentially a collection of entities. It makes sense to implement it as just another collection, having the same behavior as any other collection.
Your CitizenRepository
will work practically the same way as
$country->getCitizens()
, except that the former contains all citizens, while
the latter contains citizens of a specific country.
Installation
composer require rekalogika/collections-orm
Creating a Repository
Repository interface:
use Rekalogika\Contracts\Collections\Repository;
/**
* @extends Repository<int,Citizen>
*/
interface CitizenRepository extends Repository
{
// you may wish to add custom methods here
}
Repository implementation:
use Rekalogika\Collections\ORM\AbstractRepository;
use Rekalogika\Collections\ORM\Configuration\RepositoryConfiguration;
/**
* @extends AbstractRepository<int,Citizen>
*/
class CitizenRepositoryImplementation extends AbstractRepository implements
CitizenRepository
{
public function __construct(ManagerRegistry $managerRegistry)
{
parent::__construct(
managerRegistry: $managerRegistry,
class: Citizen::class,
);
}
// you may wish to add custom methods here
}
Technically, it is not strictly required to create the interface for the repository. You can just create the implementation class. Creating the interface is a common practice in domain-driven design (DDD). The interface belongs to the domain layer, while the implementation belongs to the infrastructure layer. Other components of the application work with the interface, not the implementation directly.
The Minimal Flavor
If you want to use the minimal version of the repository, you can substitute:
Repository
withMinimalRepository
AbstractRepository
withAbstractMinimalRepository
RepositoryConfiguration
withMinimalRepositoryConfiguration
Convenience Methods
The base AbstractRepository
class provides convenience methods to be called by
the methods in the concrete repository implementation:
getEntityManager()
createQueryBuilder()
getDoctrineRepository()
createCriteriaRecollection()
createCriteriaPageable()
createQueryRecollection()
createQueryPageable()
Migrating from Doctrine's Repository
Persisting an entity
use Doctrine\ORM\EntityManagerInterface;
/**
* @var EntityRepository $repository This is an implementation of the repository
* mentioned in this document.
*/
/** @var EntityManagerInterface $entityManager */
$entity = new Entity();
- $entityManager->persist($entity);
+ $repository->add($entity);
$entityManager->flush();
Retrieving an entity
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
/**
* @var EntityRepository $repository This is an implementation of the repository
* mentioned in this document.
*/
/** @var EntityManagerInterface $entityManager */
/** @var EntityRepository<Entity> $doctrineRepository */
- $entity = $entityManager->find(Entity::class, $id);
- // or
- $entity = $doctrineRepository->find($id);
+ $entity = $repository->get($id);
+ // alternative that throws an exception if the entity is not found:
+ $entity = $repository->fetch($id);
Removing an entity
use Doctrine\ORM\EntityManagerInterface;
/**
* @var EntityRepository $repository This is an implementation of the repository
* mentioned in this document.
*/
/** @var EntityManagerInterface $entityManager */
/** @var Entity $entity */
- $entityManager->remove($entity);
+ $repository->removeElement($entity);
$entityManager->flush();
Iterating All Entities
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
/**
* @var EntityRepository $repository This is an implementation of the repository
* mentioned in this document.
*/
/** @var EntityManagerInterface $entityManager */
/** @var EntityRepository<Entity> $doctrineRepository */
- $entities = $doctrineRepository->findAll();
- foreach ($entities as $entity) {
- // do something
- }
+ // non-minimal flavor only:
+ foreach ($repository as $entity) {
+ // do something
+ }
+
+ // all flavors, iterating in batches, should never trigger out-of-memory
+ // situation:
+ foreach ($repository->withItemsPerPage(1000)->getPages() as $page) {
+ foreach ($page as $entity) {
+ // do something
+ }
+ $entityManager->clear();
+ }
Coexisting with Doctrine's Standard Repository
It should be technically possible to implement this repository in the same class as Doctrine's standard repository, but we don't have an implementation for that yet.
If you no longer use the default Doctrine's repository, you can safely remove
the 'repository' argument in your entity configuration, and remove Doctrine's
repository class to reduce confusion. The default Doctrine's repository is still
available by calling $entityManager->getRepository(Entity::class)
, it is just
you no longer have the option to add custom methods to it.
This implementation of repository sits above Doctrine's Entity Manager. The entity manager does not have the knowledge of the repository.