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

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.

Related

Doctrine ORM many-to-many find all by taglist

I'v got simple m2m relation (book -> book_mark <- mark). I want to find item(book) by 1-2-3... x-count of tags(marks). Example: Book1 got these tags: [Mark1, Mark2, Mark3], Book2 got these tags: [Mark1, Mark3, Mark4]. Search list is [Mark1, Mark2]. I want to find only items, which have ALL tags from Search list, i.e. only Book1 in this example.
I have tried many ways and spend much time google it, but didn't find the answer.
Closest that I have is this:
return $this->createQueryBuilder('b')
->select('b, m')
->leftJoin('b.marks_list', 'm')
->andWhere(':marks_list MEMBER OF b.marks_list')
->setParameter('marks_list', $marksList)
->getQuery()->getArrayResult();
But it's looking for books which have at least 1of the parameters, not all of them together
Next, I'v decided that I'm absolutely wrong and start thinking this way:
public function findAllByMarksList(array $marksList)
{
$qb = $this->createQueryBuilder('b')
->select('b, m')
->leftJoin('b.marks_list', 'm');
for ($i = 0; $i<count($marksList); $i++){
$qb->andWhere('m.id in (:mark'.$i.')')
->setParameter('mark'.$i, $marksList[$i]);
}
return $qb->getQuery()->getArrayResult();
}
But here I faced another problem: This code is checking only 1 mark and then always returns an empty set if the number of parameters is more than 1.
Best regards.
So, updated answer... It works (I have relation many to many between reviews and brands) it's the same situation, but for example if you have
Book 1 - mark1, mark2, mark3
Book 2 - mark1, mark2, mark3, mark4
with this code you will also find the book2, because all of it marks are in this list.
If you need to find book which haves only these 3 tags, you additionally need to add checking for count. (Count of tags = count of tagList)
public function test()
{
// just selecting for list for test
$brandsList = $this->_em->createQueryBuilder()
->select('b')
->from('ReviewsAdminBundle:Brands', 'b')
->where('b.id in (:brandIds)')
->setParameter('brandIds', [6,4])
->getQuery()
->getResult();
dump($brandsList);
// query part
$qb = $this->createQueryBuilder('r')
->select('r')
->leftJoin('r.brand', 'brands')
->where('r.published = 1');
foreach ($brandsList as $oneBrand) {
/** #var Brands $oneBrand */
$identifier = $oneBrand->getId();
$qb->andWhere(':brand'.$identifier.' MEMBER OF r.brand')
->setParameter('brand'.$identifier, $identifier);
}
dump($qb->getQuery()->getResult());
die;
}
ps. Additionally you can check doctrine2 queryBuilder must return only result matching with array values (ids): 0/Null and/or One and/or Many id(s) have to return One result (close to our situation)
And, I think there's not better way of accomplishing this. Either you use multiple andWheres to compare id OR use MEMBER OF

Symfony 3 one-to-many, get parent with all children if one child satisfy the codition

I have these two tables
I want to get all products(with all the children) that have at least one child with log_id = 13. Let's say I have the following rows in eorder_product_config table:
The function that retrieves the products looks like this:
public function getProducts($logId){
$q = $this
->createQueryBuilder('p')
->select('p', 'pc')
->where('pc.logisticStatus = :logId')
->setParameter('logId', $logId)
->getQuery();
return $q->getResult();
}
This will get the product(id = 18) with only 2 children(id = 46,48) in the productConfigs collection and I want have all 5 children if there is at least one that has log_id = 13.
I've found a workaround using subqueries:
public function getProducts($logId){
// search for configs that have log_id = 13
$subQuery = $this->createQueryBuilder('pp')
->select('DISTINCT pp.id')
->leftJoin('pp.productConfigs', 'ppc')
->leftJoin('ppc.logisticStatus', 'pls')
->where('ppc.logisticStatus = :logId');
//->andWhere('ppc.id = p.id'); // used for EXIST query method
// main query
$q = $this->createQueryBuilder('p');
$q->select('p', 'pc');
$q->leftJoin('p.productConfigs', 'pc')
// inject subquery, check to see if current product is in
// the subquery result
$q->where($q->expr()->in('p.id', $subQuery->getDQL()));
//$q->where($q->expr()->exists($subQuery->getDQL()))
$q->setParameter('logId', $logId);
return $q->getQuery()->getResult();
}
***I've seen that using the EXIST query does't work as it should that's why I choose the IN query. But in the raw sql query they both return same results.

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.

Doctrine ODM Query IS NULL

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

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