Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine 2.3 Criteria. Accessing a related Object

I am trying to set up a Criteria according to the Doctrine Docs.

Unfortunately they don't tell you how to access attributes of an related Object. Let me give you an example.

I have an ArrayCollection of Products. Every Product has a Category. I want to filter the ArrayCollection for a Category Name. Now I am trying to set up a Criteria as follows:

$criteria = Criteria::create()
  ->where(Criteria::expr()->eq("category.name", "SomeCategoryName"));

Now I get the following Exception:

An exception has been thrown during the rendering of a template ("Unrecognized field: category.name")

How can I access a related Object?

like image 205
Robin Avatar asked Aug 09 '13 16:08

Robin


2 Answers

I looked into the source code Criteria::expr()->eq("name", --- second value ---). Second value expects an instance of Doctrine\Common\Collections\Expr\Value. So it's not possible to put another Expr or criteria in there. Only the Expr And and Or take another Expr. I'm pretty sure you are suppose to solve this with other functions like filter() or get an iterator with getIterator(). This is how it can be done with the filter() method.

$filteredProducts = 
    $products->filter(function($key, $element) use ($categoryName) {
        return $element->getCategory()->getName() === categoryName;
    });

If you can an Iterator for each next relation you can nest foreach loops and filter inside those.

like image 87
Flip Avatar answered Nov 14 '22 08:11

Flip


That probably belongs in a repository method, rather than a filter method. If you're wanting to get a pre-filtered list of Products in a collection on a parent object (like an Order or something), you can filter the child collection in the query builder. However, you have to deal with the possibly confusing side-effect of not having fully hydrated objects.

This should give you a list of Order objects, which only having Product children matching a category name.

class OrderRepository extends EntityRepository {
  public function findOrderWithProductCategory($category)
  {
    $builder = $this->createQueryBuilder('o')
      ->select('o, p')
      ->leftJoin('o.products', 'p')
      ->join('p.category', 'c', 'WITH', 'c.name = :category')
      ->setParameter('category', $category);
  }
}

If you don't know what kind of categories you're interested until later, then you're probably better using @Flip's solution anyway, and pre-hydrating all the categories. Using partial hydration and standard ArrayCollection::filter() closures, performs pretty well in most cases.

That said, it would be quite nice as a feature. I suspect the Doctrine guys would be reluctant because the current Criteria implementation is very light-weight and they probably want to keep it that way.

like image 4
Ryan Avatar answered Nov 14 '22 08:11

Ryan