Doctrine QueryBuilder Re-Use Parts - php

I want to count all fields that fits my conditions and get them page by page with doctrine query builder.
I'm generating the query depends my filter fields.
First section is counting the records so i can calculate the pages.
$qb = $em->createQueryBuilder();
$qb
->select('COUNT(m.id)')
->from('CSMediaBundle:MediaItem', 'm')
->where(
$qb->expr()->eq('m.media', $media->getId())
);
$filters = $request->request->get('filter');
if(!empty($filters['size'])) {
foreach($filters['size'] as $key => $value) {
if(!empty($value)) {
$qb->andWhere(
$qb->expr()->eq('m.'.$key, ':'.$key)
)->setParameter($key, $value);
}
}
}
if(!empty($filters['sliders'])) {
$qb
->leftJoin('CSSliderBundle:SliderItem', 's', 'ON', 'm.id = s.media_id')
->andWhere(
$qb->expr()->in('s.sliders', $filters['sliders'])
);
}
$media_count = $qb->getQuery()->getSingleScalarResult();
Second section is getting records by calculated page using same filters, just changing the select and final parts (getSingleScalarResult to getResult)
I wonder if is there any way to just change the select and the result parts so i would not use the filters again and again...

Yeah, that's what functions are for:
function filter($qb, $filters) {
if (!empty($filters['size'])) {
foreach($filters['size'] as $key => $value) {
if (!empty($value)) {
$qb->andWhere(
$qb->expr()->eq('m.'.$key, ':'.$key)
)->setParameter($key, $value);
}
}
}
if (!empty($filters['sliders'])) {
$qb
->leftJoin('CSSliderBundle:SliderItem', 's', 'ON', 'm.id = s.media_id')
->andWhere(
$qb->expr()->in('s.sliders', $filters['sliders'])
);
}
return $qb;
}
$filters = $request->request->get('filter');
// count
$qb = $em->createQueryBuilder();
$qb
->select('COUNT(m.id)')
->from('CSMediaBundle:MediaItem', 'm')
->where(
$qb->expr()->eq('m.media', $media->getId())
);
$media_count = filter($qb, $filters)->getQuery()->getSingleScalarResult();
// entities
$qb = $em->createQueryBuilder();
$qb
->select('m')
->from('CSMediaBundle:MediaItem', 'm')
->where(
$qb->expr()->eq('m.media', $media->getId())
);
$media_entities = filter($qb, $filters)->getQuery()->getResult();
Another way is to clone the query builder object:
$qb = $em->createQueryBuilder();
$qb->from('CSMediaBundle:MediaItem', 'm')
->where(
$qb->expr()->eq('m.media', $media->getId())
);
$filters = $request->request->get('filter');
if (!empty($filters['size'])) {
foreach($filters['size'] as $key => $value) {
if (!empty($value)) {
$qb->andWhere(
$qb->expr()->eq('m.'.$key, ':'.$key)
)->setParameter($key, $value);
}
}
}
if (!empty($filters['sliders'])) {
$qb
->leftJoin('CSSliderBundle:SliderItem', 's', 'ON', 'm.id = s.media_id')
->andWhere(
$qb->expr()->in('s.sliders', $filters['sliders'])
);
}
$qb2 = clone $qb;
$qb->select('COUNT(m.id)')
$media_count = $qb->getQuery()->getSingleScalarResult();
$qb2->select('m')
$media_entities = $qb2->getQuery()->getResult();

In Javascript-TypeORM you can clone the existing query builder as shown below.
let baseQuery=this.Repository.createQueryBuilder("user");
//Add conditions
if(user_type==="admin"){
baseQuery.where("user.user_type = : user_type",{user_type:"admin"})
}
//Clone query builder
let count_query= new SelectQueryBuilder(baseQuery);
const count= count_query.select('COUNT(*)', 'count').getRawOne();
let sum_query= new SelectQueryBuilder(baseQuery);
const sum= sum_query.select('SUM(user.amount)', 'amount').getRawOne();

Related

What does ':'(colon) mean in dql?

I'm working on a server that uses doctrine orm, and i came across the following function:
public function showForJustWhoCanSee() {
$subquery = $this->em->createQueryBuilder();
$subquery->select('userSignature')
->from('HospitalApi\Entity\EletronicDocumentSignature', 'signature')
->innerJoin('HospitalApi\Entity\User', 'userSignature', 'WITH', 'userSignature = signature.user')
//->where('signature.signed = 0')
->groupBy('signature._document')
->orderBy('signature.order', 'ASC');
$select = $this->em->createQueryBuilder();
$select->select('ed')
->from($this->getEntityPath(), 'ed')
->innerJoin("HospitalApi\Entity\User", "u", "with", "ed.user = u")
->leftJoin("HospitalApi\Entity\EletronicDocumentSignature", 'eds', 'WITH', 'eds._document = ed')
->leftJoin("eds.user", 'us', 'WITH', 'u = :user OR us = :user')
->where( $select->expr()->eq( 'us', $select->expr()->any( $subquery->getDQL() )) )
// ->andwhere('eds.signed = 0')
// ->andwhere( $select->expr()->in('ed.status', $this->_alowedStatusToSign) )
->orwhere('u = :user')
->setParameter('user', $this->getSession() )
->andWhere('ed.c_removed = 0');
return $select;
}
I would like to know what the colon on ':user' does at:
->leftJoin("eds.user", 'us', 'WITH', 'u = :user OR us = :user')
Thanks in advance.
The colon is used in parameter binding... you define a name: :user
And then you assign a value to that parameter in:
->setParameter('user', $this->getSession() )

Conditionally adding to Doctrine WHERE clause

How should one conditionally add constraints to the WHERE clause? For instance, if sex, maxAge, and minAge is provided, the WHERE clause should be WHERE sex=? AND maxAge<? AND minAge>?, but if maxAge is not provided, it should be WHERE sex=? AND minAge>? The logic below might work to determine whether where() or andWhere() is used, however, surely shouldn't be used.
<?php
// $qb instanceof QueryBuilder
$qb->select('u')
->from('User', 'u');
if(isset($param['sex'])) {
$qb->where('u.sex = ?1')->setParameter(1, $param['sex']);
}
if(isset($param['maxAge'])) {
if(isset($param['sex'])) {
$qb->andWhere('u.age < ?2')->setParameter(2, $param['maxAge']);
}
else {
$qb->where('u.age < ?2')->setParameter(2, $param['maxAge']);
}
}
if(isset($param['minAge'])) {
if(isset($param['sex'])|| isset($param['maxAge'])) {
$qb->andWhere('u.age > ?3')->setParameter(3, $param['minAge']);
}
else {
$qb->where('u.age > ?3')->setParameter(3, $param['minAge']);
}
}
There's no need for all those conditional checks. Just use andWhere everywhere.
<?php
// $qb instanceof QueryBuilder
$qb->select('u')
->from('User', 'u');
if(isset($param['sex'])) {
$qb->andWhere('u.sex = :sex')->setParameter('sex', $param['sex']);
}
if(isset($param['maxAge'])) {
$qb->andWhere('u.age < :maxAge')->setParameter('maxAge', $param['maxAge']);
}
if(isset($param['minAge'])) {
$qb->andWhere('u.age > :minAge')->setParameter('minAge', $param['minAge']);
}
I suppose you can create a single string for where:
$whereArr = $whereParams = [];
foreach (['sex' => 'u.sex = :sex', 'maxAge' => 'u.age < :maxAge', 'minAge' => 'u.age > :minAge'] as $key => $clause) {
if (isset($params[$key])) {
$whereArr[] = $clause;
$whereParams[':' . $key] = $params[$key];
}
}
if ($whereArr) {
$qb->where(implode(' and ', $whereArr))->setParameters($whereParams);
}
I'll probably go with ehymel's answer, but the following should work.
<?php
// $qb instanceof QueryBuilder
$qb->select('u')
->from('User', 'u');
$where = [];
if(isset($param['sex'])) {
$where[] = $qb->expr()->eq('sex', ':sex');
}
if(isset($param['maxAge'])) {
$where[] = $qb->expr()->lte('maxAge', ':maxAge');
}
if(isset($param['minAge'])) {
$where[] = $qb->expr()->gte('minAge', ':minAge');
}
if($where) {
$params=array_intersect_key($params, array_flip('sex','minAge','maxAge'));
$qb->add('where', $qb->expr()->andX(...$where))->setParameters($params);;
}

send a Nested array json from symfony2 to angular2

i have an sql table called "planning" whchi contain id_place , id_event , date_event. i wrote this code with symfony2 to send the dates of an event grouped by place.
$query2 = $em->createQueryBuilder();
$query2->select("p.libPlace , p.idPlace ")
->from('AppBundle:Place', 'p')
->leftJoin('AppBundle:Planning', 'pl', 'WITH', 'pl.idPlace = p.idPlace')
->where('pl.idCevent = :id')
->setParameter('id', $idEvent)
->groupBy('p.idPlace');
$q2 = $query2->getQuery();
$places = $q2->getResult();
$taille = sizeof($places);
$j=0; $a=array();
for ($i=0; $i<= $taille-1; $i++)
{
$query4 = $em->createQueryBuilder();
$query4->select(" z.dateEventPlace dateEventPlace")
->from('AppBundle:Planning', 'z')
->where('z.idCevent = :id')
->andWhere('z.idPlace = :idp')
->setParameter('id', $idEvent)
->setParameter('idp', $places[$i]['idPlace'])
->orderBy('dateEventPlace', 'ASC');
$q4 = $query4->getQuery();
$planning = $q4->getResult();
$p[$j]=$places[$i];
$j++;
$p[$j]=$planning;
$j++;
}return array('places' => $p);
i got this json response :
places":[
{"libPlace":"Le Colis\u00e9e","idPlace":1},
[{"dateEventPlace":"2017-03-19T08:09:00+0000"},{"dateEventPlace":"2017-03-19T14:19:00+0000"},{"dateEventPlace":"2017-03-24T14:08:00+0000"}],
{"libPlace":"ABC","idPlace":2},
[{"dateEventPlace":"2017-03-22T14:10:00+0000"},{"dateEventPlace":"2017-03-24T16:20:00+0000"}]
]
and i want to get something like this:
places":[
{
"libPlace":"Le Colis\u00e9e","idPlace":1,"times":[
{"dateEventPlace":"2017-03-19T08:09:00+0000"},{"dateEventPlace":"2017-03-19T14:19:00+0000"}]
},
{
"libPlace":"ABC","idPlace":2,"times":[
{"dateEventPlace":"2017-03-22T14:10:00+0000"}]
}
]
any solution pls??

How to improve query performance in symfony for multiple relationship?

Hi I am using symfony 2 for my application and using sg-datatable for displaying my data, now to feed the data table i used client side not server side and using this query:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('MyAppBundle:Artist')
->createQueryBuilder('c')
->select('c.id, c.name, c.sex, c.priority, c.bday, c.bmonth, c.byear')
->getQuery()
->getArrayResult();
$cacsData = $em->createQueryBuilder()
->select('a.id, r.name as role, c.title as work')
->from('MyAppBundle:CastAndCrew', 'q')
->innerJoin('q.role', 'r')
->innerJoin('q.artists', 'a')
->innerJoin('q.content', 'c')
->getQuery()
->getArrayResult();
$cacs = array();
foreach ($cacsData as $cacData) {
if (!array_key_exists($cacData['id'], $cacs)) {
$cacs[$cacData['id']] = array();
}
$cacs[$cacData['id']][] = $cacData;
}
foreach ($entities as &$artist) {
if (!array_key_exists($artist['id'], $cacs)) {
continue;
}
$roles = array();
$contents = array();
foreach ($cacs[$artist['id']] as $cac) {
$roles[] = $cac['role'];
$contents[] = $cac['work'];
}
$artist['role'] = implode('/', array_unique($roles));
$artist['works'] = implode('<br>', array_unique($contents));
}
$encoders = array(new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$datatable = $this->get("bbd_datatables.artist");
$datatable->setData($serializer->serialize($entities, "json"));
return $this->render('MyAppBundle:Artist:index.html.twig', array(
"datatable" => $datatable
));
}
everything is fine but the problem is query time, i have 20000+ data in my table and it's taking 250+ ms and because of the client side it paused few seconds then displays the data, can any one suggest me how i can improve the performance?

Multiple joins not working with createQueryBuilder

When I use createQuery it works:
$this->getEntityManager()
->createQuery(
'SELECT e FROM CPBundle:Event e, CPBundle:Player p, CPBundle:EventType et
WHERE e.player = p.id AND e.eventType = et.id';
)
->getResult();
but when I try the same query with createQueryBuilder it doesn't work:
public function findEventsByParams($params)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$query = $qb->select('e', 'p', 'et')
->from('CPBundle:Event', 'e')
->innerJoin('CPBundle:Player', 'p')
->innerJoin('CPBundle:EventType', 'et')
->where('e.player = p.id')
->andWhere('e.eventType = et.id');
if ($params['username']) {
$qb->andWhere('p.username = :username')
->setParameter('username', $params['username']);
}
//TODO: other params
return $query->getQuery()->getResult();
}
I'm getting this error message:
[Syntax Error] line 0, col 73: Error: Expected Literal, got 'JOIN'
See this answer: https://stackoverflow.com/questions/17989473/symfony2-doctrine-multiple-joins-returns-error
public function findEventsByParams($params)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$query = $qb->select('e')
->from('CPBundle:Event', 'e')
->innerJoin('e.player', 'p')
->innerJoin('e.eventType', 'et');
if ($params['username']) {
$qb->andWhere('p.username = :username')
->setParameter('username', $params['username']);
}
//TODO: other params
return $query->getQuery()->getResult();
}

Categories