Doctrine ODM Query IS NULL - symfony

Is it possible to use IS NULL in Doctrine ODM Query? Only thing i found is fieldIsset(), which generates IS NOT NULL, but i cannot negate it. I've tired
$queryBuilder
->andWhere()
->not()
->fieldIsset('fieldSelector');
but, resulting query is (NOT document.[fieldSelector] IS NOT NULL). Did anyone ran into similar problem?

try like this:
$queryBuilder
->where('fieldSelector IS NULL')
;
Cheers
EDIT:
Sorry, did not see you use ODM...
So, maybe by this way:
$queryBuilder
->field('fieldSelector')->equals(null)
;

If you want to more conditions you can use javascript in where function
$queryBuilder
->where("function() { return (typeof(this.fieldSelector) =='undefined'); }")
this is reference
link

try like this:
$queryBuilder
->eq($queryBuilder->ifNull('$field', null), null)
;
To count it:
$queryBuilder
->project()
->field('count')
->cond(
$queryBuilder->eq($queryBuilder->ifNull('$field', null), null),
0,
1
)
->group()
->field('id')
->expression(null)
->field('count')
->expression($queryBuilder->sum('$count'))

Related

Symfony 3 Too many parameters

I'm new to Symfony, and I got an error while running a query :
public function getFilteredArticles($page, $nbPerPage, $data) {
$query = $this->createQueryBuilder('a')
->leftJoin('a.images', 'i')
->addSelect('i')
->leftJoin('a.type_stockage', 't')
->addSelect('t')
->leftJoin('a.famille', 'f')
->addSelect('f');
if ($data['famille'] != '') {
$query->where('f.id = :famille')
->setParameter('famille', $data['famille']);
}
if ($data['rds'] == false) {
$query->where('a.stock_actuel > 0');
}
if ($data['recherche'] != '' && $data['recherche'] != null) {
$query->where('a.ref_article LIKE :recherche')
->setParameter('recherche', '%' . $data['recherche'] . '%');
}
$query->leftJoin('a.sousfamille', 's')
->orderBy('a.ref_article', 'ASC')
->getQuery();
$query->setFirstResult(($page - 1) * $nbPerPage)
->setMaxResults($nbPerPage);
return new Paginator($query, true);
}
This query have conditionnals parameters as you can see, that returns the list of articles I need for a table. But when I run this query to fill my table, I got the error :
An exception has been thrown during the rendering of a template ("Too
many parameters: the query defines 0 parameters and you bound 1").
I don't know why he is expecting 0 parameters. I tried using setParameters instead, but the result is the same.
Does anyone has an idea?
You should use andWhere() methods instead of where().
where() method removes all previous where, but setParameter() does not. That's why he found more parameters than where clauses.
I personally never use where if the condition has no sense to be the first condition, to avoid this kinds of errors.
if ($data['famille'] != '') {
$query->andWhere('f.id = :famille')
->setParameter('famille', $data['famille']);
}
if ($data['rds'] == false) {
$query->andWhere('a.stock_actuel > 0');
}
if ($data['recherche'] != '' && $data['recherche'] != null) {
$query->andWhere('a.ref_article LIKE :recherche')
->setParameter('recherche', '%' . $data['recherche'] . '%');
}
where() php doc
Specifies one or more restrictions to the query result.
Replaces any previously specified restrictions, if any.
andWhere() php doc
Adds one or more restrictions to the query results, forming a logical
conjunction with any previously specified restrictions.
My error, in Symfony 4, using Doctrine 2.6 was
Too many parameters: the query defines 0 parameters and you bound 2
The problem was that I wasn't defining the parameters in andWhere method as
$this->createQueryBuilder('q')
...
->andWhere('q.propertyDate IS NOT NULL') //this also couldn't find anywhere
->andWhere('q.parameterName = :parameterName')
->setParameters(['q.parameterName' => $parameterName, ...2nd parameter])
As I couldn't find any answer to my problem, but was similar to this one, I thought to maybe help someone who is struggling like I was.
also in symfony 5 and 6 you should use andWhere() methods instead of where().
where() method removes all previous where, but setParameter() does not. That's why he found more parameters than where clauses.

How to save row order which is pointed in "IN()" expression?

I have a array of entity IDs - my task is fetch that entities in the order which is pointed in the array. I have found that combination of "IN()" and "FIND_IN_SET" can solve that task.
I built a query in repository class with the help of QueryBuilder:
$qb = $this->createQueryBuilder('v');
$qb
->select('v')
->addSelect("FIND_IN_SET('v.id', '$vehiclesStr')")
->andWhere('v.id IN(:vehicles)')
->setParameter('vehicles', $vehiclesArr)
;
return $qb->getQuery()->getResult();
As you see, I use "FIND_IN_SET" function from beberlei/DoctrineExtensions. It was registered accordingly to that issue.
Suppose $vehiclesStr = '219,197,213,198'; and respectively
$vehiclesArr = [219,197,213,198];
The problem - order is not saving. I receive following result, where vehicles are ordered by ASC e.g. 197, 198, 213..:
UPDATE / built SQL by Doctrine:
SELECT
v0_.vehicle_id AS vehicle_id_0,
v0_.number AS number_1,
v0_.cargo_movers AS cargo_movers_2,
v0_.vat AS vat_3,
v0_.bargain AS bargain_4,
v0_.cargo_search_radius AS cargo_search_radius_5,
v0_.adr AS adr_6,
v0_.tir AS tir_7,
v0_.passing_cargo AS passing_cargo_8,
v0_.description AS description_9,
v0_.created_at AS created_at_10,
v0_.updated_at AS updated_at_11,
v0_.transport_service_id AS transport_service_id_12,
v0_.vehicle_photo_id AS vehicle_photo_id_13,
v0_.vehicle_driver_id AS vehicle_driver_id_14,
v0_.vehicle_type_id AS vehicle_type_id_15,
v0_.vehicle_body_size_id AS vehicle_body_size_id_16,
v0_.vehicle_full_size_id AS vehicle_full_size_id_17,
v0_.vehicle_show_to_cargo_sender_id AS vehicle_show_to_cargo_sender_id_18,
v0_.vehicle_body_equipment_id AS vehicle_body_equipment_id_19,
v0_.vehicle_loading_type_id AS vehicle_loading_type_id_20,
v0_.vehicle_price_around_town_id AS vehicle_price_around_town_id_21,
v0_.vehicle_price_out_of_town_id AS vehicle_price_out_of_town_id_22
FROM
vehicle v0_
WHERE
v0_.vehicle_id IN (?)
ORDER BY
FIND_IN_SET('v.id', '219,188') ASC
Parameters: [[219, 188]]
You're not using ORDER BY clause at all. Putting additional field in SELECT clause won't change order.WHy would it?
Try to use orderBy instead of addSelect.
$qb = $this->createQueryBuilder('v');
$qb
->select('v')
->andWhere('v.id IN(:vehicles)')
->setParameter('vehicles', $vehiclesArr)
->orderBy("FIND_IN_SET(v.id, '$vehiclesStr')", 'ASC');
;
return $qb->getQuery()->getResult();
Assuming $vehiclesStr has correct value of course.

Specific SQL query to Doctrine translation

I need to transcript this kind of request in doctrine under symfon:
SELECT node.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;
I try this, but it doesn't work:
$nodesDQL = $this->createQueryBuilder('childs')
->select('childs')
->from('AppBundle:NestedCategory', 'parent')
->join('AppBundle:NestedCategory', 'childs')
->where(new BetweenExpression('childs.lft', 'parent.lft', 'parent.right'))
->andWhere('parent = :parent')
->setParameter('parent', $node);
I can't join like this, any idea welcomes !
I think about subrequest but how in doctrine ?
Regards.
PS: I'm frenchy with english difficulties.
I don't think you need to join parent if each child has parent id already, it can just be a criteria.
E.g.
$qb = $this->createQueryBuilder('children');
$qb->select('children')
->from('AppBundle:NestedCategory', 'children')
->where($qb->expr()->between('children.lft', ':parentLft', ':parentRgt'))
->andWhere('children.parent = :parent')
->setParameter('parent', $node)
->setParameter('parentLft', $node->getLft()) // assuming you can get lft/rgt from parent
->setParameter('parentRgt', $node->getRgt())
->getQuery()
->getResult()
;
This would get you all the children of the parent node where child node left is between parent right/left.

How to add dynamically select aliases inside a query

Context: Given the fact that the following query :
$queryBuilder = $this->createQueryBuilder("cv")
->leftJoin('cv.user', 'u')
->where('cv.game = :game')
->setParameter('game', $game);
Will trigger 1+X distinct queries (one to get all the CV, then if u.user is used in the template, will trigger X other queries to fetch users).
If I want to optimize and to reduce those multiple unoptimized queries to 1 single query, i'll do so :
$queryBuilder = $this->createQueryBuilder("cv")
->select('cv, u')
->leftJoin('cv.user', 'u')
->where('cv.game = :game')
->setParameter('game', $game);
This Way, i'll be able to save X queries.
Now, my problem is in my repository, I have conditional joins and I want to chain the select aliases at different places in my code.
Like (simplified example) :
$queryBuilder = $this->createQueryBuilder("cv")
->select('cv, u')
->leftJoin('cv.user', 'u')
->where('cv.game = :game')
->setParameter('game', $game);
if ($myCondition === true) {
$queryBuilder->add('select', 'l');
$queryBuilder->join('cv.level', 'l');
}
But it seems that the add->('select') does not stack like an addWhere().
Are there any other solutions than using a custom solution like this :
$queryBuilder = $this->createQueryBuilder("cv")
->leftJoin('cv.user', 'u')
->where('cv.game = :game')
->setParameter('game', $game);
$aliases = array('cv', 'u');
if ($myCondition === true) {
$aliases[] = 'l';
$queryBuilder->add('select', 'l');
$queryBuilder->join('cv.level', 'l');
}
$queryBuilder->select(implode(',', $aliases);
Thanks.
// Replace
$queryBuilder->add('select', 'l');
// With
$queryBuilder->addSelect('l');
And a bit of unsolicited advice. I know how much "un optimized queries" bother most people, including myself. However, consider doing some bench marks on large data sets. It's surprising how fast lazy loading is. Very little difference even with thousands of queries.

Call to undefined method Slice in Doctrine

I have this function,
public function getWall(){
$q = $this->createQueryBuilder('f');
$q->leftJoin("f.profilo", 'p');
$q->leftJoin("p.utente", 'u');
$q->where('(f.foto_eliminata IS NULL OR f.foto_eliminata != 1)');
$q->andWhere('p.fase_registrazione = :fase');
$q->andWhere('u.locked = :false');
$q->slice(0, 20);
$q->setParameter(':fase', 100);
$q->setParameter('false', false);
$q->orderBy('f.created_at', 'desc');
$dql = $q->getQuery();
$results = $dql->execute();
return $results;
}
but I get this error,
Call to undefined method Doctrine\ORM\QueryBuilder::slice()
Ok, so, u get this error, cause QueryBuilder has no such method. But Collection has. If u want to use slice, a possible variant is:
use Doctrine\Common\Collections;
public function getWall(){
$result = $this->createQueryBuilder('f')
->leftJoin("f.profilo", 'p')
->leftJoin("p.utente", 'u')
->where('(f.foto_eliminata IS NULL OR f.foto_eliminata != 1)')
->andWhere('p.fase_registrazione = :fase')
->andWhere('u.locked = :false')
->setParameter('fase', 100)
->setParameter('false', false)
->orderBy('f.created_at', 'desc')
->getQuery()
->getResult();
// $result typed as array
return new Collections\ArrayCollection($result))->slice(0,20); // convert array to collection, then slice
}
By the way, it is not a good idea to 'limit' result of the query in a such way.
U can use setMaxResults(20), and not to select all objects at all.
About lazy collections (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html): after selecting result objects, u can take some object from result collection: $r = $result[0] after that:
$portfilos = $r->getPortfolio(); // returns for example Collection with some objects;
// its Lazy, without SQL query!
$portfolios->slice(0, 20); // queries first 20 potfolios
To use slice is a rather good idea, if u have lots of objects in some relation.
p.s. sry, mb I didn't recognized your problem, but tried :)
EDITED
fixed errors in code.

Resources