I have a Doctrine2 listener & filter that acts as a means of filtering out any unapproved/draft entities, which works fine on the entity that it's applied to, however, I don't see how to get it to work for its relations.
Lets say that entity is called Category, I then have Products related to that Category, when I do a findBy()
for Products, I need the query to check that the Category they relate to is approved.
select * from products p
left join category c on p.category_id = c.id
where p.id = 5
and c.approved = true
The bits in bold are what need to be injected by my filter or equivalent.
How can I go about implementing this?
So far I have a subquery injected as part of the where in the filter, but this seems hellish, and I'm thinking there must be a better way:
class ApprovableFilter extends SQLFilter
{
protected $listener;
protected $entityManager;
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
$config = $this->getListener()->getConfiguration($this->getEntityManager(), $targetEntity->name);
/* this bit works fine for the category */
if (isset($config['approvable']) && $config['approvable']) {
$column = $targetEntity->columnNames[$config['fieldName']];
return $targetTableAlias.'.'.$column.' = true';
}
/* this bit works for products.. but seems like a pretty poor solution */
if (isset($targetEntity->associationMappings['category'])) {
$config = $this->getListener()->getConfiguration(
$this->getEntityManager(),
$targetEntity->associationMappings['category']['targetEntity']
);
return '(
select d.id from dealership d
where d.id = '.$targetTableAlias.'.dealership_id
and d.'.$config['fieldName'].' = true
) is not null';
}
}
Your solution can be made a little more general by using annotations. I've written a gist about it at: https://gist.github.com/technetium/0c62164400a411e9ffc3713260448b25
The best i can think of, is to retrieve your Products via the Category object.
That way you only need to filter for the category.approved field.
For example:
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
$config = $this->getListener()->getConfiguration($this->getEntityManager(), $targetEntity->name);
/* this bit works fine for the category */
if (isset($config['approvable']) && $config['approvable']) {
$column = $targetEntity->columnNames[$config['fieldName']];
return $targetTableAlias.'.'.$column.' = true';
}
}
Then your Category entity should have a products collection (assuming you have a Bidirectional relation).
use Doctrine\Common\Collections\ArrayCollection;
class Category {
/**
* @var ArrayCollection $products
* @ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection;
}
public function getProducts()
{
return $this->products;
}
}
That way you can first retrieve your Category
$category = $this->get('doctrine')->getRepository('SomeBundle:Category')->find(5);
if( $category ) {
//Here you now the category is approved
$products = $category->getProducts();
}
hope this helps.
To answer @lracicot question and to give an example for unidirectional relation: I would create a ProjectRepository method for example:
...
findByCategoryApproved( $product_id, $approved = true )
{
$query =
'SELECT p
FROM AcmeBundle:Product
LEFT JOIN p.categories c
WHERE p.id = :id AND c.approved = :approved';
return $this
->getEntityManager()
->createQuery( $query )
->setParameter( 'id', $product_id )
->setParameter( 'approved', $approved )
->getOneOrNullResult();
}
...
$product = $doctrine
->getRepository('AcmeBundle:Product')
->findByCategoryApproved(5);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With