Implementando el Patrón Specification en PHP con Doctrine Criteria
El Patrón Specification es un patrón de diseño que encapsula la lógica de negocio necesaria para validar si un objeto satisface un criterio particular. Combinado con el poder de Doctrine Criteria, podemos crear consultas dinámicas y altamente mantenibles para nuestras entidades. Este enfoque separa la lógica de selección de datos del acceso a datos, mejorando la flexibilidad y la reutilización del código.
<?php
use Doctrine\ORM\Query\Expr\Comparison;
use Doctrine\ORM\QueryBuilder;
interface SpecificationInterface
{
public function modifyQuery(QueryBuilder $qb, string $alias): void;
}
class AndSpecification implements SpecificationInterface
{
private SpecificationInterface $left;
private SpecificationInterface $right;
public function __construct(SpecificationInterface $left, SpecificationInterface $right)
{
$this->left = $left;
$this->right = $right;
}
public function modifyQuery(QueryBuilder $qb, string $alias): void
{
$this->left->modifyQuery($qb, $alias);
$this->right->modifyQuery($qb, $alias);
}
}
class IsActiveSpecification implements SpecificationInterface
{
public function modifyQuery(QueryBuilder $qb, string $alias): void
{
$qb->andWhere($alias . '.isActive = :isActive')
->setParameter('isActive', true);
}
}
class ProductNameContainsSpecification implements SpecificationInterface
{
private string $searchTerm;
public function __construct(string $searchTerm)
{
$this->searchTerm = $searchTerm;
}
public function modifyQuery(QueryBuilder $qb, string $alias): void
{
$qb->andWhere($qb->expr()->like($alias . '.name', ':searchTerm'))
->setParameter('searchTerm', '%' . $this->searchTerm . '%');
}
}
En el ejemplo anterior, definimos una interfaz SpecificationInterface
con un método modifyQuery
que permite modificar un QueryBuilder
de Doctrine. Implementamos especificaciones concretas como IsActiveSpecification
para filtrar entidades activas y ProductNameContainsSpecification
para buscar productos por nombre. También incluimos una especificación AndSpecification
para combinar especificaciones. La clave está en usar el QueryBuilder
de Doctrine para construir las consultas dinámicamente.
<?php
use Doctrine\ORM\EntityManagerInterface;
class ProductRepository
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function findBySpecification(SpecificationInterface $specification): array
{
$qb = $this->entityManager->createQueryBuilder();
$qb->select('p')
->from('App\Entity\Product', 'p');
$specification->modifyQuery($qb, 'p');
return $qb->getQuery()->getResult();
}
}
// Uso:
// $isActiveSpec = new IsActiveSpecification();
// $nameContainsSpec = new ProductNameContainsSpecification('Producto');
// $combinedSpec = new AndSpecification($isActiveSpec, $nameContainsSpec);
// $products = $productRepository->findBySpecification($combinedSpec);
Finalmente, en el repositorio, el método findBySpecification
recibe una instancia de SpecificationInterface
, crea un QueryBuilder
, aplica la especificación y ejecuta la consulta. El código de uso muestra cómo combinar especificaciones para crear consultas complejas de forma concisa y mantenible. Este enfoque promueve la separación de preocupaciones, facilitando las pruebas unitarias y la evolución del sistema.
No hay comentarios:
Publicar un comentario