Last active
September 26, 2023 00:59
-
-
Save chalasr/77be8eee5e3ecd3c06ec to your computer and use it in GitHub Desktop.
Implements a custom EntityRepository using Factory in Symfony2.6+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Util\Doctrine\Repository; | |
use App\Util\Doctrine\Entity\AbstractEntity; | |
use Doctrine\Common\Persistence\ObjectRepository; | |
use Doctrine\ORM\EntityRepository; | |
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | |
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; | |
/** | |
* Enhanced EntityRepository that pre-handle exceptions. | |
* | |
* @author Robin Chalas <rchalas@sutunam.com> | |
*/ | |
class EnhancedRepository extends EntityRepository implements ObjectRepository | |
{ | |
const NOT_FOUND_MESSAGE = 'The resource cannot be found'; | |
const ALREADY_EXISTS_MESSAGE = 'A resource already exists'; | |
/** | |
* Creates a new resource. | |
* | |
* @param array $properties | |
* | |
* @return object | |
*/ | |
public function create(array $properties) | |
{ | |
$entity = new $this->_entityName(); | |
foreach ($properties as $property => $value) { | |
$setter = 'set'.ucfirst($property); | |
$entity->$setter($value); | |
} | |
$this->_em->persist($entity); | |
try { | |
$this->_em->flush(); | |
} catch (Exception $e) { | |
throw new UnprocessableEntityHttpException($e->getMessage()); | |
} | |
return $entity; | |
} | |
/** | |
* Updates an existing resource. | |
* | |
* @param AbstractEntity $entity | |
* @param array $properties | |
* | |
* @return object | |
*/ | |
public function update(AbstractEntity $entity, array $properties) | |
{ | |
foreach ($properties as $property => $value) { | |
$setter = 'set'.ucfirst($property); | |
$entity->$setter($value); | |
} | |
try { | |
$this->_em->flush(); | |
} catch (Exception $e) { | |
throw new UnprocessableEntityHttpException($e->getMessage()); | |
} | |
return $entity; | |
} | |
/** | |
* Deletes a given resource. | |
* | |
* @param AbstractEntity $entity | |
* | |
* @return void | |
*/ | |
public function delete(AbstractEntity $entity) | |
{ | |
$this->_em->remove($entity); | |
try { | |
$this->_em->flush(); | |
} catch (Exception $e) { | |
throw new UnprocessableEntityHttpException($e->getMessage()); | |
} | |
} | |
/** | |
* Finds a resource by identifier. | |
* | |
* @param int $id The resource identifier | |
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants or NULL | |
* @param int|null $lockVersion The lock version. | |
* | |
* @return object | |
* | |
* @throws NotFoundHttpException | |
*/ | |
public function findOrFail($id, $lockMode = null, $lockVersion = null) | |
{ | |
$entity = $this->find($id); | |
if (null === $entity) { | |
throw new NotFoundHttpException( | |
sprintf('%s (\'id\' = %d)', self::NOT_FOUND_MESSAGE, $id) | |
); | |
} | |
return $entity; | |
} | |
/** | |
* Find resource by criteria or fail. | |
* | |
* @param array $criteria | |
* @param array|null $orderBy | |
* @param int|null $limit | |
* @param int|null $offset | |
* | |
* @return object | |
* | |
* @throws NotFoundHttpException | |
*/ | |
public function findByOrFail(array $criteria, array $orderBy = null, $limit = null, $offset = null) | |
{ | |
$entities = $this->findBy($criteria, $orderBy); | |
if (count($entities) == 0) { | |
throw new NotFoundHttpException( | |
sprintf('%s (%s)', self::NOT_FOUND_MESSAGE, $this->implodeCriteria($criteria)) | |
); | |
} | |
return $entities; | |
} | |
/** | |
* Find resource by criteria or create a new. | |
* | |
* @param array $criteria | |
* @param array|null $orderBy | |
* | |
* @return object | |
* | |
* @throws NotFoundHttpException | |
*/ | |
public function findOneByOrFail(array $criteria, array $orderBy = null) | |
{ | |
$entity = $this->findOneBy($criteria, $orderBy); | |
if (null === $entity) { | |
throw new NotFoundHttpException( | |
sprintf('%s (%s)', self::NOT_FOUND_MESSAGE, $this->implodeCriteria($criteria)) | |
); | |
} | |
return $entity; | |
} | |
/** | |
* Check for existing resource by criteria and fail. | |
* | |
* @param array $criteria | |
* @param array|null $orderBy | |
* | |
* @return object | |
* | |
* @throws UnprocessableEntityHttpException | |
*/ | |
public function findOneByAndFail(array $criteria, array $orderBy = null) | |
{ | |
$entity = $this->findOneBy($criteria, $orderBy); | |
if (null !== $entity) { | |
throw new UnprocessableEntityHttpException( | |
sprintf('%s (%s)', self::ALREADY_EXISTS_MESSAGE, $this->implodeCriteria($criteria)) | |
); | |
} | |
return $entity; | |
} | |
/** | |
* Find resource by criteria or create a new. | |
* | |
* @param array $criteria | |
* @param array|null $orderBy | |
* | |
* @return object | |
*/ | |
public function findOneByOrCreate(array $criteria, array $orderBy = null) | |
{ | |
$entity = $this->findOneBy($criteria, $orderBy); | |
if (null === $entity) { | |
return $this->create($criteria); | |
} | |
return $entity; | |
} | |
/** | |
* Get criteria as string. | |
* | |
* @param array $criteria The query criteria | |
* | |
* @return string | |
*/ | |
private function implodeCriteria(array $criteria) | |
{ | |
if (true === empty($criteria)) { | |
return 'no fields'; | |
} | |
$keys = implode(', ', array_keys($criteria)); | |
$values = implode(', ', array_values($criteria)); | |
return sprintf("%s' = '%s'", $keys, $values); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\AcmeBundle; | |
use App\Util\DependencyInjection\Compiler\CompilerPass; | |
use Symfony\Component\DependencyInjection\ContainerBuilder; | |
use Symfony\Component\HttpKernel\Bundle\Bundle; | |
class AppSportBundle extends Bundle | |
{ | |
public function build(ContainerBuilder $container) | |
{ | |
parent::build($container); | |
$container->addCompilerPass(new CompilerPass()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Util\DependencyInjection\Compiler; | |
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | |
use Symfony\Component\DependencyInjection\ContainerBuilder; | |
use Symfony\Component\DependencyInjection\Definition; | |
use Symfony\Component\DependencyInjection\Reference; | |
/** | |
* Repository factory CompilerPass. | |
* | |
* @author Robin Chalas <rchalas@sutunam.com> | |
*/ | |
class CompilerPass implements CompilerPassInterface | |
{ | |
/** | |
* Process compilation of containers. | |
* | |
* @param ContainerBuilder $container | |
*/ | |
public function process(ContainerBuilder $container) | |
{ | |
$factory = $container->findDefinition('app.doctrine.repository.factory'); | |
$repositories = []; | |
foreach ($container->findTaggedServiceIds('app.repository') as $id => $params) { | |
foreach ($params as $param) { | |
$repositories[$param['class']] = $id; | |
$repository = $container->findDefinition($id); | |
$repository->replaceArgument(0, new Reference('doctrine.orm.default_entity_manager')); | |
$definition = new Definition(); | |
$definition->setClass('Doctrine\ORM\Mapping\ClassMetadata'); | |
$definition->setFactoryService('doctrine.orm.default_entity_manager'); | |
$definition->setFactoryMethod('getClassMetadata'); | |
$definition->setArguments([$param['class']]); | |
$repository->replaceArgument(1, $definition); | |
} | |
} | |
$factory->replaceArgument(0, $repositories); | |
$container->findDefinition('doctrine.orm.configuration')->addMethodCall('setRepositoryFactory', [$factory]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Util\Doctrine\Repository; | |
use Doctrine\ORM\EntityManagerInterface; | |
use Doctrine\ORM\Repository\RepositoryFactory; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
/** | |
* Factory that set a default EntityRepository for specific entities. | |
* | |
* @author Robin Chalas <rchalas@sutunam.com> | |
*/ | |
class EnhancedRepositoryFactory implements RepositoryFactory | |
{ | |
private $repositories; | |
private $container; | |
private $default; | |
/** | |
* Constructor. | |
* | |
* @param array $repositories Registered repositories | |
* @param ContainerInterface $container | |
* @param RepositoryFactory $default | |
*/ | |
public function __construct(array $repositories, ContainerInterface $container, RepositoryFactory $default) | |
{ | |
$this->ids = $repositories; | |
$this->container = $container; | |
$this->default = $default; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getRepository(EntityManagerInterface $entityManager, $entityName) | |
{ | |
if (isset($this->ids[$entityName])) { | |
return $this->container->get($this->ids[$entityName]); | |
} | |
$metadata = $entityManager->getClassMetadata($entityName); | |
$entityNamespace = $metadata->getName(); | |
if (is_subclass_of($entityNamespace, '\App\Util\Doctrine\Entity\EntityInterface', true)) { | |
return new EnhancedRepository($entityManager, $metadata); | |
} | |
return $this->default->getRepository($entityManager, $entityName); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
app.doctrine.repository.factory: | |
class: App\Util\Doctrine\Repository\EnhancedRepositoryFactory | |
arguments: | |
- [] | |
- '@service_container' | |
- '@app.doctrine.repository.factory.default' | |
app.doctrine.repository.factory.default: | |
class: Doctrine\ORM\Repository\DefaultRepositoryFactory |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment