Doctrine2 left join on multiple levels making multiple requests - symfony

I have 4 entities that are related in hierarchical levels: Company, Department and Employee. Company and Department are related with a ManyToOne bidirectional relation. Department and Employee are related through another entity with 2 OneToMany bidirectional relations because I needed additional parameters for the relation. So basically the final schema is this :
Company <-> Department <-> DepartmentEmployee <-> Employee
I'm trying to select one department from the company of the current user and to get all the employees of this department. I'm using a custom repository to build my query with the query builder like this:
// DepartmentRepository.php
public function getOneWithEmployees($slug, $company)
{
$qb = $this->createQueryBuilder('d')
->where('d.slug = :slug')
->andWhere('c.slug = :company')
->setParameters(array('slug' => $slug, 'company' => $company))
->leftJoin('d.company', 'c')
->addSelect('c')
->leftJoin('d.departmentEmployee', 'r')
->addSelect('r')
->leftJoin('r.employee', 'e')
->addSelect('e');
return $qb->getQuery()->getOneOrNullResult();
}
The point being to reduce the number of queries made, but when I execute this query, I still get 32 queries made to the database (I have 15 employees in the department).
When I remove the part
->leftJoin('r.employee', 'e')
->addSelect('e')
I get only one query executed like expected.
How can I do a left join on a left join without triggering multiples queries?

My Employee entity is the inverse side of 2 OneToOne relations: User and Invitation. When I explicitly include these relations in the query with left join, no extra queries are made, but if I leave them out then Doctrine automatically makes queries to fetch them. Looking in the Doctrine FAQ I found this:
4.7.1. Why is an extra SQL query executed every time I fetch an entity with a one-to-one relation?
If Doctrine detects that you are fetching an inverse side one-to-one association it has to execute an additional query to load this object, because it cannot know if there is no such object (setting null) or if it should set a proxy and which id this proxy has.
To solve this problem currently a query has to be executed to find out this information.
Link
So the only solution to avoid extra queries is to build my query like this:
$qb = $this->createQueryBuilder('d')
->where('d.slug = :slug')
->andWhere('c.slug = :company')
->setParameters(array('slug' => $slug, 'company' => $company))
->leftJoin('d.company', 'c')
->addSelect('c')
->leftJoin('d.departmentEmployee', 'r')
->addSelect('r')
->leftJoin('r.employee', 'e')
->addSelect('e')
->leftJoin('e.user', 'u')
->addSelect('u')
->leftJoin('e.invitation', 'i')
->addSelect('i');
return $qb->getQuery()->getOneOrNullResult();

Related

Doctrine Query reverse join?

I have these relations.
I need to retrieve all contents but order by star.content_Idcontent first.
It works with easy SQL:
SELECT content.idcontent
FROM content
LEFT JOIN star ON content.idcontent = star.content_Idcontent
ORDER BY star.content_Idcontent DESC,content.idcontent
But I don't know how to do it with Doctrine because content is not the owner of the relation.
Must I create a bidirectional relation or is there a way to make it work?
You can create a query in ContentRepository like:
$qb = $this
->createQueryBuilder('content')
->join(
'App\Entity\Star',
'star',
\Doctrine\ORM\Query\Expr\LeftJoin::WITH,
'content.idcontent = star.content_Idcontent '
)
->orderBy('star.content_Idcontent', 'DESC')
;
$qb->getResult();
Assuming that your Entity Star is located in App\Entity\Star

Doctrine query with a condition on number of relationships

I have two entities: Location and Program. They have a many-to-many relationship. A program has the attribute $status, which can be 'published' or 'unpublished'.
Now I wish to run a query (with the doctrine query builder) to return all of my Location entities which have a relationship with at least 10 Program entities with the $status being 'published'.
My code so far
public function findLocationsOfPublishedPrograms()
{
$qb = $this->createQueryBuilder('l')
->innerJoin('l.programs', 'p')
->andWhere('p.status = \'published\'')
->getQuery()->getResult();
return $qb;
}
This query retuns all Location entities that have at least one published program. But is there any way I could set a condition to only return Location entities which have at least 10 published programs?
You could innerJoin WITH p.status = published. Then addWhere with a count of p.* >= 10
Examples of innerJoin with conditions are in the docs:
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html#working-with-querybuilder

Doctrine2 QueryBuilder select entity and count of associated entities

I'm having a huge problem with ORM QueryBuilder. What I need to do is:
I need to fetch order with count of its products and plenty of associated entities (associated with order), but I assume they're not relevant here. I also need to order result by that count.
Could anyone give me an example of how this can be achieved? I would like to avoid "inline" DQLs if possible.
You can get data via Doctrine Query Builder.
You are supposed to left join products from Order and then group by order id. You can have COUNT(product.id) in your select statement and use the alias in order by clause to make your orders sorted. Below is a small code snippet from Repository.
/**
* #return \Doctrine\ORM\Query
*/
public function getHotelAndRoomType()
{
$qb = $this->createQueryBuilder('order')
->select('partial order.{id, orderId} as order, count(product.id) as total_products_in_order')
->leftJoin('AppBundle:Product', 'product', 'WITH', 'product.order = order.id')
->groupBy('order.id')
->orderBy('total_products_in_order', 'DESC')
;
return $qb->getQuery()->execute();
}
Note : Code not tested.

One of listeners must count and slice given target

I'm getting the above error when performing a search in a Symfony2 CRM I've been working on. According to Google searches it seems this is an issue relating to the KNP Paginator bundle, but I cannot seem to find a solid solution.
In this instance, I am using data from an OpenCart database, and I need to be able to search by Postcode and Company name, both of which exist in the address table which is joined via a mapped value within the Customer entity, defaultaddress.
To this end, I've had to write a custom query in a function called findCustomerByPostcode like so:
$this->getEntityManager()
->createQuery('SELECT c FROM AppBundle:Oc49Customer c JOIN AppBundle:Oc49Address a WITH c.defaultaddress = a.id WHERE a.postcode LIKE :postcode')
->setParameter('postcode','%'.$postcode.'%')
->getResult();
However, when I perform a search on postcode, I get the following error in the browser:
One of listeners must count and slice given target
which refers to the Paginator.php file within the KNP bundle. I have updated to the most recent version, 2.5 yet I cannot seem to shake this error, and to me it does not even make sense.
Any help is much appreciated, as I cannot think of another way of search via a value within a joined table.
Which returns results from the postcode search, and then in the Controller:
$customers = $customer_repository->findCustomerByPostcode($filter_value);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$customers,
$request->query->get('page', 1),
20
);
You can use the queryBuilder with the left join
public function findCustomerByPostcode($postcode) {
$query = $this->entityManager->createQueryBuilder();
$query
->select('c', 'a')
->from('AppBundle:Customer', 'c')
->leftJoin('c.address', 'a')
->setParameter('postcode', $postcode)
return $query->getQuery()->getResult();
}

QueryBuilder OneToMany filter entity without relations

I have a model
House #OneToMany(People)
People #ManyToOne(House)
i need a QueryBuilder for filter all Houses without People
Current not working code
$houseRepository
->createQueryBuilder('h')
->join('h.people', 'p')
->where('p is NULL');
always return me nothing, i have 3 house in the database only one have people
You need to use left join for such queries. something like:
$houseRepository
->createQueryBuilder('h')
->leftJoin('h.people', 'p')
->where('p is NULL');

Resources