QueryBuilder OneToMany filter entity without relations - symfony

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');

Related

Drupal entityQuery multiple node types and join

Looking to join multiple nodes via a query and join. Let's say one node has a matching ID of another field in another node. I would like to join them into an array so that I can output other fields with them indexed together on the same ID. Example: node1.nid and node2.field.target_id
How do I do this?
$nids = \Drupal::entityQuery('node')->accessCheck(FALSE)
->join('node2', 'n2', 'n.nid = n2.field.target_id');
->condition('type', 'node1', 'node2')
->sort('title')->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
$response = array();
This is definitely doable and it looks like you're almost there, but it looks like you're combining the Database API with the Entity API, specifically trying to run Database joins on EntityQuery fetches.
Now, if you want to continue the Database query route, which may make it a little easier to join the values of multiple entities and output them, this is what I would recommend:
The first thing that I notice is that you're attempting to chain the join. Unfortunately, according to the Database API Docs
Joins cannot be chained, so they have to be called separately (see Chaining).
I have done similar to this with a join between two custom content types. Here is the code I used:
$connection = Database::getConnection();
if ($connection->schema()->tableExists('companies') && $connection->schema()->tableExists('users')) {
$query = $connection->select('companies', 'c');
$query->join('users', 'u', 'c.sourceid1 = u.sourceid1');
$results = $query->fields('u', ['destid1', 'sourceid1'])
->fields('c', ['destid1'])
->execute()
->fetchAll();
return $results;
}
Now this assumes that you've brought in Drupal\Core\Database\Database, but let's walk through the process.
$connection = Database::getConnection();
This is fetching the database connection for your site
if ($connection->schema()->tableExists('companies') && $connection->schema()->tableExists('users')) {
This is a check I always do to make sure the tables I'm working with exist.
$query = $connection->select('companies', 'c');
One of your tables needs to be the "base" table. In this case, I chose companies.
$query->join('users', 'u', 'c.sourceid1 = u.sourceid1');
This line is where the join actually happens. Notice that it's not chained, but it's own command. We're joining these tables, users and companies, on this sourceid1.
$results = $query->fields('u', ['destid1', 'sourceid1'])
->fields('c', ['destid1'])
->execute()
->fetchAll();
This is where I'm pulling whichever fields I want from both entities. In this case I want destid1 and sourceid1 from my user table and destid1 from my company table.
The ->execute() call actually runs the query, and ->fetchAll() returns an array of all matching results. This will get you to being able to JSONify all the things.
However, if you want to stick with the entityQuery route, that's also viable. My personal preference is to use a regular query for more complex things and an entityQuery if I just need to get a value from a single field or return a single entity.
For an entityQuery, well, that's a bit different. I wouldn't really recommend using an entityQuery for a join here. Instead, using entityTypeManager if you know or can get the matching value from both tables.
For example, let's assume that field_content_type_a_id on content type A matches field_related_type_a_id on content type B.
You could load one, let's go with type A for now:
// Get the current NID somehow... that's up to you
$entity_a = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
// Get the value of the field you want to match to the other content type
$matching_value = $entity_a->get('field_content_type_a_id')->getValue();
// Get the entities with matching type A values
$entity_b_array = \Drupal::entityTypeManager()->getStorage('node')
->loadByProperties(['field_related_type_a_id' => $matching_value]);
// Do something with $entity_b_array.

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.

Doctrine duplicating query results?

I have the following code
$this->em = $this->container->get('doctrine.orm.entity_manager');
$qb = $this->em->getRepository('CoreBundle:ServiceProvider')->createQueryBuilder('c');
$qb->select('count(venue.id) as vencount');
$qb->from('CoreBundle:ServiceProvider','venue');
$count = $qb->getQuery()->getOneOrNullResult()['vencount'];
which is write its returning a number of venues but the problem is that this number is mistaken because in the ServiceProvider table i have only 5 records but this query is returning 25. I tried to add a new record so they are 6 and yes the result was 36.
So I added group by the id and it fixed the issue anyone can tell me why is this happening ?
It's because when you create a query from a repository Doctrine assumes its a select and inject the select and from clausule's for you.
This is the sql you get from using the getRepository method:
SELECT count(i0_.id) AS sclr_0 FROM Entity i1_, Entity i0_
(Note that the entity is twice in the FROM).
Using just:
$qb = $this->em->createQueryBuilder();
$qb->select('count(venue.id) as vencount');
$qb->from('CoreBundle:ServiceProvider','venue');
You get:
SELECT count(i0_.id) AS sclr_0 FROM Entity i0_
Which is probably what you are looking for.
Another alternative is to get it from the repository but clear the sql parts with:
$qb = $this->em->getRepository('CoreBundle:ServiceProvider')->createQueryBuilder('c')->resetDQLParts();
But this way you lost the very purpose of using the repository in the first place.

Doctrine2 left join on multiple levels making multiple requests

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();

Symfony2 QueryBuilder join ON and WITH difference

I'm new with Symfony2 and I built successfully my first join through QueryBuilder and Doctrine 2.
Probably this is a stupid question but both on-line and in the Symfony2's methods I was unable to find anything for understanding the difference between the join clauses "WITH" and "ON".
For example this is my join code:
->leftJoin('EcommerceProductBundle:ProductData', 'pdata', 'WITH', 'prod.id = IDENTITY(pdata.product)')
It works good but if I put ON instead of WITH I get the following error:
[Syntax Error] line 0, col 200: Error: Expected
Doctrine\ORM\Query\Lexer::T_WITH, got 'ON'
Why? I've seen among the objects that there are both the T_ON and T_WITH like join clauses, but which is their usage difference? What is their uses like?
#florian gave you the correct answer but let me try to explain it on example:
In sql, joins are done like this:
SELECT * FROM category
LEFT JOIN product ON product.category_id = category.id
(or something like this)
Now in Doctrine, you don't need to use ON clause because doctrine knows that from relations annotations in your entities. So above example would be:
// CategoryRepository.php
public function getCategoriesAndJoinProducts()
{
return $this->createQueryBuilder("o")
->leftJoin("o.products", "p")->addSelect("p")
->getQuery()->getResult() ;
}
Both would fetch all categories and join products associated with them.
Now comes the WITH clause. If you want to join only products with price bigger than 50, you would do this in SQL:
SELECT * FROM category
LEFT JOIN product ON product.category_id = category.id AND product.price>50
In Doctrine:
// CategoryRepository.php
public function getCategoriesAndJoinProductsWithPriceBiggerThan($price)
{
return $this->createQueryBuilder("o")
->leftJoin("o.products", "p", "WITH", "p.price>:price")
->setParameter("price", price)->addSelect("p")
->getQuery()->getResult() ;
}
So, in reality you should never, ever use ON if you are using Doctrine. If you have a need for something like that, you can be almost sure that you screwed something else.
In theory, ON permits you to give the full join criterias, while WITH permits to add additional criterias to the default ones (IMHO).
But, what DQL permits is to avoid giving the JOIN criterias:
You just have to say: $qb->leftJoin('prod.pdata', 'pdata');
And doctrine2 will handle the join correctly.
Here is a related question about that: Can I use "ON" keyword in DQL or do I need to use Native Query?

Resources