Doctrine Query reverse join? - symfony

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

Related

How to combine queryBuilder and raw queries on join table from many to many relation?

I have got following query
SELECT news.*, count(*) AS common_tags
FROM news
JOIN news_news_tag ON news.id = news_news_tag.news_id
WHERE news_tag_id IN
(SELECT news_tag_id FROM news_news_tag WHERE news_id = 2 )
AND news.id != 2 GROUP BY news.id ORDER BY common_tags DESC
What I want to achieve is to get hydrated news objects ordered by number of common tags with provided news id. News and Tag are many to many relation with news_news_tag join table.
News entity have got much more other relations. This is why I don't want to create a native query by myself to handle all other relations.
I would like to convert above query to use it with query builder. I wasn't able to use a DQL because my where statement uses a join (junction) table and I also need to use a join on that table.
All in all I have got 2 problems:
How can I create DQL subquery to select something from many to many join table)? If I know that I could do something like: ->where($queryBuilder->expr()->in('u.id', $mySubQueryAsDQL))
How to add that join statement that I could use news_tag_id in where statement?
If it is not possible I think that I would need to create two bidirectional one-to-many and many-to-one relations instead of many-to-many and work on special joining entity.
I finally came up with following solution. I decided to split that query into two separate ones. In first I can use just simple raw query as I need results only to where statement. Second query can be build using query builder in a normal way.
You have to only get rid of an extra count column at the end.
However if someone knows the solution how to use my first raw query directly inside query builder as a raw subquery for where statement please share with me.
public function getRelatedNews($newsId, $limit = 6)
$connection = $this->getEntityManager()->getConnection();
$sql = 'SELECT news_tag_id FROM news_news_tag WHERE news_id = :newsId';
try {
$stmt = $connection->prepare($sql);
} catch (DBALException $e) {
return [];
}
$stmt->execute(['newsId' => $newsId]);
$newsTagId = $stmt->fetchAll();
if (empty($newsTagId)) {
return [];
}
$newsTagId = array_column($newsTagId, 'news_tag_id');
$query = $this->createQueryBuilder('n')
->addSelect('COUNT(n.id) as common_tags_count')
->innerJoin("n.tags", "t")
->andWhere('t.id IN(:tagsId)')->setParameter('tagsId', array_values($newsTagId))
->andWhere('n.id != :newsId')->setParameter('newsId', $newsId)
->orderBy('common_tags_count', 'DESC')
->setMaxResults($limit)
->groupBy('n.id')
;
$results = $query->getQuery()->getResult();
$news = [];
foreach ($results as $result) {
$news[] = $result[0];
}
return $news;
}

WHERE ... IN query with sub-query in Doctrine queryBuilder or equivalent

In my Symfony 4 project I have a User entity and a UserRepository.
I'm trying to implement the equivalent of this SQL query in the QueryBuilder(Doctrine 2) or even in DQL.
SELECT * FROM user WHERE account_manager_id IN (SELECT id FROM user WHERE account_manager_id = :managerAdminId AND roles LIKE '%ROLE_MANAGER%')
Or maybe use a different syntax.
I tried different things, but couldn't figure out how to write the WHERE ... IN with the sub-query.
This is all I could come up with, which I don't like because it fires multpiple queries for something I could do with a single one:
//App\Repository\UserRepository
public function getPublishersOfManagers($managerAdminId)
{
//SELECT * FROM user WHERE account_manager_id IN (SELECT id FROM user WHERE account_manager_id = :managerAdminId AND roles LIKE '%ROLE_MANAGER%')
$managerIds = $this->createQueryBuilder('u')
->select('u.id')
->where('u.roles LIKE :role')
->setParameter('role' , '%ROLE_MANAGER%')
->andWhere('u.accountManager = :managerAdminId')
->setParameter('managerAdminId' , $managerAdminId)
->getQuery()->getArrayResult();
$publishers = [];
foreach ($managerIds as $id) {
$publishers[] = $this->createQueryBuilder('u')
->select('u')
->where('u.roles LIKE :role')
->setParameter('role' , '%ROLE_PUBLISHER%')
->andWhere('u.accountManager = :managerAdminId')
->setParameter('managerAdminId' , $id)
->getQuery()->getResult();
}
return $publishers;
}
your query can be turned into something without a sub-query, but with a join instead, which should be equivalent (and should have the same runtime/complexity)
SELECT u
FROM user u
LEFT JOIN user am ON (am.id=u.accountManager)
WHERE am.roles LIKE '%ROLE_MANAGER%'
AND am.accountManager=:managerAdminId
AND u.roles LIKE '%ROLE_PUBLISHER%'
which can be translated into querybuilder accordingly (I have to assume, that you did not define your associations ... which I find disturbing, but you probably have your reasons):
return $this->createQueryBuilder('u')
->leftJoin('App\Entity\User', 'am', 'WITH', 'am.id=u.accountManager')
->andWhere('am.roles LIKE :role')
->setParameter('role', '%ROLE_MANAGER%')
->andWhere('am.accountManager = :managerAdminId')
->setParameter('managerAdminId', $managerAdminId)
->andWhere('u.roles LIKE :role2')
->setParameter('role2', '%ROLE_PUBLISHER%')
->getQuery()->getResult();
there is also the options of actually using sub-queries, but using sub-queries imho is always inconvenient - and ugly.
(you might have a look into writing just plain DQL queries, you might feel more at home ...?)
According to DQL query examples section within Doctrine's DQL documentation you need to either use EXISTS keyword within DQL query or use exists() method of Expr class.

CreateQueryBuilder on an association table that has no entity

I have an association table, that has no entity itself, inside an entity. I can do a raw or native query on it but I want to use createQueryBuilder on it. How can I?
Here is the raw query that I want to convert to createQueryBuilder:
$sql = 'SELECT t.* FROM tasks t LEFT JOIN question_tasks qt ON t.id = qt.task_id WHERE qt.question_id = :qtId';
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata(Task::class, 't');
$query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter('qtId', $questionId);
return $query->getResult();
Thank you.
It's kind of impossible use Doctrine ORM without entity. QueryBuilder just converts down to DQL. DQL make queries over your object model.
Says documentation
You need to think about DQL as a query language for your object model,
not for your relational schema.
Check docs here Doctrine Query Language

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.

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

Resources