Joining Relationships On Entity SubClasses in Doctrine - php

I have two entities that share an abstract class creating class table inheritance. I can query for the entities by using the abstract class' repository and get all the entities that extend the abstract class as the result.
$qb = $this->createQueryBuilder('c')
->where('c.featured = true')
->orderBy('c.sticky', 'DESC')
->addOrderBy('c.weight', 'ASC')
->setFirstResult($offset)
->setMaxResults($limit);
// Returns 8 results, results in 34 queries
The sub classes contain ManyToMany relationships to other entities so if I query in this manner, those relationships result in additional queries since they are not being joined. How can you query for entities extending an abstract class and join their columns? I tried adding multiple from statements with left joins but the query returned fewer than the expected 8 results. That query builder looks something like this:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select(array(
'a', 'artist', 'country',
't', 'artwork', 'user'))
->from('AcmeArtworkBundle:Artwork', 'a')
->from('AcmeTourBundle:Tour', 't')
->leftJoin('a.artist', 'artist')
->leftJoin('a.country', 'country')
->leftJoin('t.artwork', 'artwork')
->leftJoin('t.user', 'user')
->where('a.featured = true')
->andWhere('t.featured = true')
->orderBy('a.sticky', 'DESC')
->addOrderBy('t.sticky', 'DESC')
->addOrderBy('a.weight', 'ASC')
->addOrderBy('t.weight', 'ASC')
->setFirstResult($offset)
->setMaxResults($limit);
// 5 results :-(

You can use a fetch join and Doctrine ORM's Paginator as described in the documentation:
$qb = $em->createQueryBuilder()
->select('c', 'd')
->from('MyInheritanceClass', 'c')
->join('c.d', 'd')
->where('c.featured = true')
->orderBy('c.sticky', 'DESC')
->addOrderBy('c.weight', 'ASC')
->setFirstResult($offset)
->setMaxResults($limit);
$paginator = new \Doctrine\ORM\Tools\Pagination($qb);
var_dump(count($paginator)); // 8 results, 2 queries

From the information provided by the link provided by #Ocramius, By default Doctrine loads entities using lazy loading. Associations are fetched only when they need to be. You can tell Doctrine to automatically fetch an association by setting the fetch mode to EAGER.
/**
* #var string $date
*
* #ORM\ManyToOne(targetEntity="Date", inversedBy="artwork", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="date", referencedColumnName="id")
*/
private $date;
It's important to note that you can actually cause more queries to be run by doing this if you aren't careful.

Related

Where clause to parents

I have for example two tables: Parent and Children. They have relations many-to-many between each other. Everything is working well, but I don't know how to use where clause in query builder for this.
My code:
$parent = $em->getRepository('AppBundle:Parent')->find(1);
$qb = $em->createQueryBuilder();
$qb->select('c, p')
->from('AppBundle:Children', 'c')
->leftJoin('c.parents', 'p')
->where('p.id = :parent')
->setParameter('parent', $parent)
;
$childrens = $qb->getQuery()->getResult();
This always return me null.
I know - I can use $parent->getChildrens(), but I would like use createQueryBuilder for AppBundle:Children.
How should the where clause look?

Doctrine 2 - Joined entity is still a proxy and generating two queries after request

$news = \DB::getEntityManager()->createQueryBuilder()
->select('n')
->from('Model\News', 'n')
->join('n.category', 'c')
->join('n.locales', 'l')
->where('l.language = :lang AND n.id = :id AND n.published = 1')
->setParameter('id', $news_id)
->setParameter('lang', $lang)
->getQuery()
->getResult();
$news->category->getName();
This code is generating TWO queries instead of ONE. Why?
After getting my $news entity, the category property is still a Doctrine proxy class (Application\Proxies\__CG__\Model\News\Category). Instance of \Model\News\Category will be loaded only after getting some of the category data (with, for example, $news->category->getName()) and it's generating additional SQL query.
How to make Doctrine to download \Model\News and related to it \Model\News\Category with only one query?
Below is a configuration for category field from \Model\News with annotations:
/**
* #ManyToOne(targetEntity="\Model\News\Category", inversedBy="news", fetch="EAGER")
*/
public $category;
As You can see... EAGER is not working...
Change your first line to ->select('n, c') so that Doctrine will hydrate both your News and Category entities.

DQL Allow Nullable Join?

In my entity 'Item', there is an ArrayCollection property known as "products" (of Entity 'Product') that can be empty
I need all product info (empty or not) in one DQL call for the array of 'Item' entities
$query = $this->getEntityManager()->createQueryBuilder();
$query->select(['v', 'p'])->from($this->getEntityName(), 'v')
->join('v.products', 'p')
->orderBy('v.datePublished', 'DESC')
->setMaxResults($limit);
The problem here, is that the only records that get returned are ones that have at least one product in them. I need to return Items that have empty products as well.
Any thoughts?
Ok, so.. this is all that needed to happen:
$query = $this->getEntityManager()->createQueryBuilder();
$query->select(['v', 'p'])->from($this->getEntityName(), 'v')
->join('v.products', 'p')
->orderBy('v.datePublished', 'DESC')
->setMaxResults($limit);
becomes
$query = $this->getEntityManager()->createQueryBuilder();
$query->select(['v', 'p'])->from($this->getEntityName(), 'v')
->leftJoin('v.products', 'p')
->orderBy('v.datePublished', 'DESC')
->setMaxResults($limit);
I'm excited to get back to my life now.

Doctrine2 fetching rows that have manyToMany association by QueryBuilder

everyone.
I have 2 entities City and POI. Mapping looks like this:
class City {
/**
* #ORM\ManyToMany(targetEntity="POI", mappedBy="cities")
* #ORM\OrderBy({"position" = "ASC"})
*/
protected $pois;
and
class POI {
/**
* #ORM\ManyToMany(targetEntity="City", inversedBy="pois")
* #ORM\JoinTable(name="poi_cities")
*/
protected $cities;
I would like to fetch all POIs that have at least 1 association with some City using QueryBuilder. I should probably use exists() function but I don't quiet know how.
You'd have to Left join them and check if cities is null.
$qb->select('p', 'c')
->from('AcmeDemoBundle:POI', 'p')
->leftJoin('p.cities', 'c')
->where('c IS NOT NULL');
I haven't tested it, but I hope it gives you the general direction. You can read more about the QueryBuilder from here.
Docrine2 was changed in 2013, so the other solution displays error Error: Cannot add having condition on a non result variable. Now we cannot use joined alias just as a condition variable. We should use any of its properties like c.id
So you should fix the code to
$qb->select('p', 'c')
->from('AcmeDemoBundle:POI', 'p')
->leftJoin('p.cities', 'c')
->where('c.id IS NOT NULL');
$results = $qb->getQuery()->execute();
If you want to select entities that does not have any cities, use IS NULL.
$qb->leftJoin('p.cities', 'city')
->where('city.id IS NULL')
->getQuery()
->execute();
Description of a problem and link to the commit that responsible for that - http://www.doctrine-project.org/jira/browse/DDC-2780

Doctrine 2 - Find all entities in a repository except one with a particular id value

I'm using Doctrine 2.
I want to get all Entities of an entity class except for the one with id = 0.
I could use QueryBuilder like this:
// $em is EntityManager
$em->createQueryBuilder()
->select('c')
->from('Category', 'c')
->where('c.id <> 0')
->getQuery()
->getResult();
But looking at this code, it feels like too much for such a simple task.
I'm curious to know if there is any other simpler way in Doctrine for doing this.
Nop, that's how you should do it. You could omit query builder and pass entire DQL query:
$em->createQuery("SELECT c FROM Category c WHERE c.id != 0")->getResult();
It's not the better practice to pass the ID number, you might pass the Category object like this :
public function findAllExcept($category) {
$qb = $this->createQueryBuilder('Category');
$qb->add('select', 'c')
->add('from', 'Category c')
->add('where', 'c != :category')
->setParameter('category', $category);
return $qb->getQuery()->getResult();
}
For comparisons and conditions I recommend use Doctrine as well. It will save your time in case of switch between databases, for instance MySQL to PostgreSQL:
$this->createQueryBuilder()
->select('c')
->from('Category', 'c')
->where($qb->expr()->neq('c.id', '?1'))
->setParameter('1', $category)
->getQuery()
->getResult()

Categories