How to make a Doctrine subquery? - symfony

I have two mysql tables and corresponding entities:
Question
--------
id
text
level
Asked
--------
* #ORM\ManyToOne(targetEntity="QuestionEntity")
* #ORM\JoinColumn(name="question", referencedColumnName="id")
question
user
I want to query for all questions that has a specific level and the question haven't been asked from a given user. How can I do this with an entity manager query builder?
With MySQL syntax the query would be:
SELECT
*
FROM
Question
WHERE
Question.level = 1
AND Question.id NOT IN( SELECT Asked.question FROM Asked WHERE Asked.user = 23)
I tried multiple things like Tomasz Madeyski commented, but I still got 500 Internal Server Error. This is my code: (I tested the subquery and it was fine when it is stand alone)
$em = $this->getDoctrine()->getManager();
$fbUser = $em->getRepository(FbUserEntity::class)->findOneBy(['fbId' => $session->get('fb_user_id')]);
$qb = $em->createQueryBuilder();
$qb2 = $qb;
$questions = $qb->select('q')
->from('MyBundle:QuestionEntity', 'q')
->where('q.level = :level')
->setParameter('level', $level)
->andWhere($qb->expr()->notIn(
'q.id',
$qb2->select('a.question')
->from('LMyBundle:AskedEntity', 'a')
->where('a.user = :userid')
->setParameter('userid', $fbUser->getId())
->getDQL()
))
->getQuery()
->getResult();

Try something like this:
$qb = $em->createQueryBuilder();
$subQuery = $qb->select('a.question')
->from('YourBundle:Asked', 'a')
->andWhere('a.user = 23')
$query = $qb->select('q')
->from('YourBundle:Question', 'q')
->andWhere('q.level = 1')
->andWhere($qb->expr()->notIn('q.id', $subQuery->getDQL())
->getQuery()
->getResult()
The key here is $qb->expr()->notIn() part

Investigating the logs I figured out that instead of
$qb = $em->createQueryBuilder();
$qb2 = $qb;
I should create a new queryBuilder for qb2:
$qb = $em->createQueryBuilder();
$qb2 = $em->createQueryBuilder();
But this still gives me an error. After some experimentingI figured out that if the subquery is executed, it gives something like this format:
array (size=2)
0 =>
array (size=1)
'question' => int 1
1 =>
array (size=1)
'question' => int 2
So I run through the subquery result like this:
$alreadyAsked = [];
foreach($asked as $q){
$alreadyAsked[] = $q['question'];
}
And pass the $alreadyAsked array to the main query like this, then it works:
->andWhere($qb->expr()->notIn('q.id', $alreadyAsked))

Related

DQL How to return all records when parameter is empty

I am building a Symfony app where a user can make some search through a search form with many fields. Now my DQL query looks like this:
$users = $repository->createQueryBuilder('u')
->addSelect('u')
->from('AppBundle:User', 'b')
->where('u.Number = :Number **OR u.Number = :blank'**)
->andWhere('u.Code = :Code')
->setParameter('Number', $Number)
->setParameter('blank', $blank)
->setParameter('Code', $code)
->getQuery()
->getResult();
The problem is that I want my request to return ALL records related to the criteria when a user leaves some fields blank. Right now no records are returned because the system thinks I want entries with particular "blank" criteria. I would appreciate any ideas. Thank you
You can use Expr class in DQL to further structure query around 'blank' field submitted by user. Simple if(!empty($field)) will do the trick
$query = $repository->createQueryBuilder('u')
->addSelect('u')
->from('AppBundle:User', 'b')
->where('u.Number = :Number')
->andWhere('u.Code = :Code')
->setParameter('Number', $Number)
->setParameter('Code', $code);
if(!empty($blank)){
$query->andWhere($query->expr()->orX(
$query->expr()->eq('u.Number', ':blank')
));
$query->setParameter('blank', $blank);
}
$users = $query->getQuery()->getResult();
If I understand correctly, you actually have 2 parameters in this case, which are: Number and Code. And if one of them is "blank", then you have the blank variable set ?
If this is the case, I think you should write your request like this:
$query = $repository->createQueryBuilder('u')
->addSelect('u')
->from('AppBundle:User', 'b')
if ($Number != "")
$query->where('u.Number = :Number')
->setParameter('Number', $Number);
if ($code != "") {
if ($Number != "") $query->andWhere('u.Code = :Code');
else $query->where('u.Code = :Code');
$query->setParameter('Code', $code);
}
$users = $query->getQuery()->getResult();

Doctrine Query Builder Bug?

I have run into an issue in Doctrine. I have built the following query with queryBuilder
$qb = $query = $this->repoLibrary->createQueryBuilder('l');
$query = $qb
->innerJoin('l.productVariant', 'v')
->innerJoin('v.product', 'p')
->innerJoin('p.taxons', 't', 'WITH', 't.id IN (:array)')
->where('l.user = :user')
->groupBy('l.id HAVING count(DISTINCT t.id) >= :count')
->setParameter('user', $user)
->setParameter('array', $s)
->setParameter('count', count($taxons))
->getQuery();
Here is the query that is logged prior to execution:
SELECT s0_.id AS id0, s0_.consumed_at AS consumed_at1, s0_.created_at AS created_at2, s0_.updated_at AS updated_at3, s0_.user_id AS user_id4, s0_.variant_id AS variant_id5
FROM src_library s0_
INNER JOIN src_variant s1_ ON s0_.variant_id = s1_.id
INNER JOIN src_product s2_ ON s1_.product_id = s2_.id
INNER JOIN src_taxon_product s4_
ON s2_.id = s4_.product_id
INNER JOIN src_taxon s3_ ON s3_.id = s4_.taxon_id
AND (s3_.id IN (1,4))
WHERE s0_.user_id = 1
GROUP BY s0_.id HAVING count(DISTINCT s3_.id) = ? ["1,4",1,2]
When I execute this query (after inserting the parameters seen above) directly in MySQL it works perfectly, returning the 2 results that I'm looking for.
However, when it is executed by Doctrine it returns an empty array.
Any Ideas??
After scouring the interwebs I found the following answer. The problem is with the 'IN' clause. As articulated here:
https://groups.google.com/forum/#!topic/doctrine-dev/-_cINyk2dvs
My problem was being caused by the fact that I was building the 'IN' array as a string.
$s = "1,4"
instead of
$s = array(1,4);
This made all the difference in the world, and also made me feel like a n00b.
Your code looks fine and should work. I can't see your whole code but I'm guessing that it is returning an empty array because you have not actually executed the prepared sql statement yet. You should call the "getResult()" to do this.
Try this:
$qb = $query = $this->repoLibrary->createQueryBuilder('l');
$query = $qb
->select('l, v.id, p.id')
->innerJoin('l.productVariant', 'v')
->innerJoin('v.product', 'p')
->innerJoin('p.taxons', 't', 'WITH', 't.id IN (:array)')
->where('l.user = :user')
->groupBy('l.id HAVING count(DISTINCT t.id) >= :count')
->setParameter('user', $user)
->setParameter('array', $s)
->setParameter('count', count($taxons))
->getQuery()
->getResult();

delete several rows where attribute = x

I need to delete all rows where the image_id = x applies
like this
DELETE FROM `ImageVoters` WHERE image_id =1
How do I do this using DQL?
Since im trying to delete several rows at once the remove() function won't work
EntityManager#remove() expects parameter 1 to be an entity object, array given.
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery('SELECT u FROM GabrielUploadBundle:ImageVoters u WHERE u.image_id = 1');
$db_imageactions = $query->getResult();
$em->remove($db_imageactions);
$em->flush();
This is the code that works
$qb = $em->createQueryBuilder();
$qb->delete('GabrielUploadBundle:ImageVoters', 'v')
->where($qb->expr()->eq('v.image_id', ':imageId'))
->setParameter('imageId', $current_imageId);
$qb->getQuery()->execute();
You could create an ORM queryBuilder, to create a clean/safe query object by using internal orm methods
$qb->delete('My\Image\Namespace\ImageVoters', 'ivoter')
->where($qb->expr()->eq('ivoter.image_id', ':imageId')
->setParameter('imageId', 1);
$qb->getQuery()->execute();
You can also execute any raw SQL like this:
$sql = "DELETE FROM ImageVoters WHERE image_id =".$image_id;
$q = $em->getConnection()->exec($sql);

symfony2 doctrine query negation of where

i wanna query for all of my categories like this:
$othercategories = $this->getDoctrine()->getRepository('Bundle:Category')->findBy(
array('language' => $language, 'active' => 1),
array('sorting' => 'ASC')
);
what i wanna do is to add another parameter to my query, i want all categories EXCEPT one with a specific id. so like:
WHERE id NOT IN ( 2 )
or
WHERE id <> 2
how can i achieve that?
You can use DQL queries like this
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery( 'SELECT c FROM Bundle:Category c WHERE c.language = :language AND c.active = 1 AND c.id NOT IN ( 2 ) ORDER BY c.language ASC' )
->setParameter('language', $language);
$category= $query->getResult();
Sorry I couldn't test this because I am using my phone to answer this question and I don't know your entity variables. Let me know what changes you did to make it work, it will help others.
For more info check http://symfony.com/doc/master/book/doctrine.html
You can add these queries in repository and reuse them. Refer the Cook book on http://symfony.com/doc/master/cookbook/index.html
Hope this helped.
You can use this syntax if you prefer
$repository = $this->getDoctrine()->getRepository('Bundle:Category');
$queryBuilder = $repository->createQueryBuilder();
$notInCategoryIds = array(2); // Category ids that will be excluded
$queryBuilder->select('c')
->from('Bundle:Category', 'c')
->where('c.language = :language')->setParameter('language', $language)
->andWhere('c.active = :active')->setParameter('active', 1)
->andWhere($queryBuilder->expr()->notIn('c.id', $notInCategoryIds)
->orderBy('c.sorting', 'ASC');
$results = $queryBuilder->getQuery()->getResult();
It's probably going to be more useful for other developers that prefers this syntax

'where not in' query with doctrine query builder

Im trying to reproduce this query:
SELECT * FROM `request_lines`
where request_id not in(
select requestLine_id from `asset_request_lines` where asset_id = 1
)
in doctrine query builder,
I am stuck on the where request_id not in(select
I currently have:
$linked = $em->createQueryBuilder()
->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where()
->getQuery()
->getResult();
You need to use query builder expressions, and this means you need access to the query builder object. Also, the code is easier to write if you generate the subselect list ahead of time:
$qb = $em->createQueryBuilder();
$nots = $qb->select('arl')
->from('$MineMyBundle:MineAssetRequestLine', 'arl')
->where($qb->expr()->eq('arl.asset_id',1))
->getQuery()
->getResult();
$linked = $qb->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where($qb->expr()->notIn('rl.request_id', $nots))
->getQuery()
->getResult();
It is possible to do this in one Doctrine query:
$qb = $this->_em->createQueryBuilder();
$sub = $qb;
$sub = $qb->select('arl')
->from('$MineMyBundle:MineAssetRequestLine', 'arl')
->where($qb->expr()->eq('arl.asset_id',1));
$linked = $qb->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where($qb->expr()->notIn('rl.request_id', $sub->getDQL()))
->getQuery()
->getResult();
Check the reference in this answer here
Using Symfony 5, this solution might help those, who are trying to set parameters on a subquery, the notIn() 2nd argument accepts an array or you could pass a DQL instead and that's what we are doing here and keep in mind that the parameters should be added to the main query as below.
$main = $this->em->createQueryBuilder();
$sub = $main;
$sub = $sub->select('arl')
->from('$MineMyBundle:MineAssetRequestLine', 'arl')
->where($sub->expr()->eq('arl.asset_id',':id'));
$linked = $main->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where($main->expr()->notIn('rl.request_id', $sub->getDQL()))
->setParameter('id', 1)
->getQuery()
->getResult();

Resources