How can I modify this doctrine query builder? - symfony

The profiler shows this query:
SELECT
count(DISTINCT c0_.id) AS sclr0
FROM
customers c0_
LEFT JOIN customers_addresses c1_ ON c0_.id = c1_.customer_id
LEFT JOIN customers_phones c2_ ON c0_.id = c2_.customer_id
WHERE
(
c0_.is_lead = ?
AND c0_.firstname LIKE ?
)
OR c0_.lastname LIKE ?
OR c0_.email LIKE ?
OR c0_.company LIKE ?
OR c1_.street_address1 LIKE ?
OR c1_.street_address2 LIKE ?
OR c1_.city LIKE ?
OR c1_.state = ?
OR c1_.zipcode = ?
OR c2_.phone LIKE ?
And clearly is not what I want, I want it to be like this:
SELECT
count(DISTINCT c0_.id) AS sclr0
FROM
customers c0_
LEFT JOIN customers_addresses c1_ ON c0_.id = c1_.customer_id
LEFT JOIN customers_phones c2_ ON c0_.id = c2_.customer_id
WHERE
(
c0_.is_lead = ?
)
AND
(c0_.firstname LIKE ?
OR c0_.lastname LIKE ?
OR c0_.email LIKE ?
OR c0_.company LIKE ?
OR c1_.street_address1 LIKE ?
OR c1_.street_address2 LIKE ?
OR c1_.city LIKE ?
OR c1_.state = ?
OR c1_.zipcode = ?
OR c2_.phone LIKE ?
)
My query builder looks like this:
public function search($keywords = null, $lead=0){
if ($keywords === null) return $this->findAllOrderedByName($lead);
$qb = $this->createQueryBuilder ('c')
->addSelect('a')
->addSelect('p')
->leftJoin('c.addresses', 'a')
->leftJoin('c.phones', 'p');
$qb->where('c.isLead = :lead');
$qb->andWhere('c.firstname like :firstname');
$qb->orWhere('c.lastname like :lastname');
$qb->orWhere('c.email like :email');
$qb->orWhere('c.company like :company');
$qb->orWhere('a.streetAddress1 like :streetAddress1');
$qb->orWhere('a.streetAddress2 like :streetAddress2');
$qb->orWhere('a.city like :city');
$qb->orWhere('a.state = :state');
$qb->orWhere('a.zipcode = :zipcode');
$qb->orWhere('p.phone like :phone');
$qb->setParameter('lead', $lead);
$qb->setParameter('firstname', '%' . $keywords . '%');
$qb->setParameter('lastname', '%' . $keywords . '%');
$qb->setParameter('email', '%' . $keywords . '%');
$qb->setParameter('company', '%' . $keywords . '%');
$qb->setParameter('streetAddress1', '%' . $keywords . '%');
$qb->setParameter('streetAddress2', '%' . $keywords . '%');
$qb->setParameter('city', '%' . $keywords . '%');
$qb->setParameter('state', $keywords);
$qb->setParameter('zipcode', $keywords);
$qb->setParameter('phone', '%' . $keywords . '%');
$qb->orderBy('c.lastname', 'ASC');
return $qb;
}
The way I currently have my query builder returns results even when the search keywords do not match any records because of the joins, I don't know how I can modify the query builder method, any ideas?

You can write it as
$qb->andWhere('(c.firstname like :firstname
OR c.lastname like :lastname
OR c.email like :email
OR c.company like :company
OR a.streetAddress1 like :streetAddress1
OR a.streetAddress2 like :streetAddress2
OR a.city like :city
OR a.state = :state
OR a.zipcode = :zipcode
OR p.phone like :phone)');

Have you tried to used addAnd(<condition>) instead of andWhere() ?

I think you could make use of the orX and then rewrite it as..
public function search($keywords = null, $lead=0){
if ($keywords === null) return $this->findAllOrderedByName($lead);
$qb = $this->createQueryBuilder ('c')
->addSelect('a')
->addSelect('p')
->leftJoin('c.addresses', 'a')
->leftJoin('c.phones', 'p');
$qb->where(
$qb->expr()->eq('c.isLead', ':lead')
)->andWhere(
$qb->expr()
->orX($qb->expr()->like('c.firstname', ':keywords'))
->add($qb->expr()->like('c.lastname', ':keywords'))
->add($qb->expr()->like('c.email', ':keywords'))
->add($qb->expr()->like('c.company', ':keywords'))
->add($qb->expr()->like('a.streetAddress1', ':keywords'))
->add($qb->expr()->like('a.streetAddress2', ':keywords'))
->add($qb->expr()->like('a.city', ':keywords'))
->add($qb->expr()->eq('a.state', ':keywords'))
->add($qb->expr()->eq('a.zipcode', ':keywords'))
->add($qb->expr()->like('p.phone', 'keywords'))
);
$qb->setParameter('lead', $lead);
$qb->setParameter('keywords', '%' . $keywords . '%');
$qb->orderBy('c.lastname', 'ASC');
return $qb;
}

Related

Query builder - adding methods depending on if statement

I have a problem with query builder notation. I would like to add additional methods depending on if statement. I'm writhing like below but it is not working
$repository->createQueryBuilder('k')
->where('k.autor LIKE :autor OR k.tytul LIKE :tytul ');
if($tag!==''){
$repository->createQueryBuilder('k')->andWhere('k.cena=:jezyk');
$repository->createQueryBuilder('k') ->setParameter('jezyk',$tag);
}
$repository->createQueryBuilder('k')->setParameter('autor', '%' . $s . '%');
$repository->createQueryBuilder('k')->setParameter('tytul', '%' . $s . '%');
query=$repository->createQueryBuilder('k')->getQuery();
$searched_books = $query->getResult();
I suppose that I made mistakeswith objects. Can somebody h.lp me? Thanks in advance
the problem is that the createQueryBuilder method will create a new query builder at each call. You should try something like that:
$qb = $repository->createQueryBuilder('k');
$qb->where('k.autor LIKE :autor OR k.tytul LIKE :tytul');
if ($tag !== '') {
$qb->andWhere('k.cena = :jezyk');
$qb->setParameter('jezyk', $tag);
}
$qb->setParameter('autor', '%' . $s . '%');
$qb->setParameter('tytul', '%' . $s . '%');
$searched_books = $qb->getQuery()->getResult();

Query with join not work

i am doing a query in my controller like this:
$aviso = $em->getRepository("FabricacionBundle:Aviso")->findBy(array("fecha" => $fecha));
$dql = "SELECT a FROM PedidosBundle:Articulo a WHERE a.aviso = :aviso";
if(isset($_GET['filterField']) && isset($_GET['filterValue'])){
$dql = "SELECT a FROM PedidosBundle:Articulo a JOIN ProductosBundle:Producto p WHERE a.aviso = :aviso";
$dql .= " AND " . $_GET['filterField'] . " LIKE '%" . $_GET['filterValue'] . "%'";
}
$query = $em->createQuery($dql)
->setParameter("aviso", $aviso[0]->getId());
//dump($query);die();
$paginator = $this->get('knp_paginator');
$articulos = $paginator->paginate(
$query,
$request->query->get('page', 1),
25
);
When i dont use the filter, this work, but when i use the filter i get the next error:
Cannot count query which selects two FROM components, cannot make distinction
Where is the problem? Thanks!
What SQL engine are you running ?
Note, that it's usually a very bad practice to insert anything directly from your $_GET variable into your SQL query, as this can lead to SQL injection.
Imagine, that someone sends \'; DROP TABLE something; -- in your $_GET['filterField'] - everything is gone.
From the top of my head, there is a lacking JOINing condition.

Create a Query using QueryBuilder with two Many-to-Many relation in Symfony2

Good evening everybody!
I have got a problem with the QueryBuilder in Symfony2.
A USER can be part of some GROUPS. Each GROUP can access to CATEGORIES. Here is a little database diagram.
Now, I would like to retrieve Categories for a User with the QueryBuilder in order to put the result in a form thanks to my CategoryRepository. Here is the corresponding SQL Statement (in the example I put 1 for the user_id but it is provided by $user->getId() in the CategoryRepository.
SELECT theCategoryObject
FROM category C, category_group CG, group G, user_group UG, user U
WHERE FU.id = 1
AND U.user_id = UG.user_id
AND UG.group_id = G.group_id
AND CG.group_id = G.group_id
AND CG.category_id = C.category_id
I tried to use EXISTS and MEMBER OF but it does not filter anything.
public function findByUserQueryBuilder(User $user)
{
$qb = $this->createQueryBuilder('c');
$qb->where('c.isSelectionable = true');
foreach ($user->getGroups() as $group)
{
$qb->andWhere('EXISTS (SELECT ' . $group->getId() . ' FROM ISIMA\Bundle\UserBundle\Entity\Group g' . $group->getId() . ' WHERE g' . $group->getId() . ' MEMBER OF c.groups)');
}
return $qb;
}
Thanks for your future answers !
Finally, I found the solution ! I forgot to define the group Id and place an Or condition in case of the User is part of several groups.
public function findByUserQueryBuilder(User $user)
{
$qb = $this->createQueryBuilder('c');
$qb->where('c.isSelectionable = true')
$i = 0;
foreach ($user->getGroups() as $group)
{
$query = 'EXISTS (SELECT 1 FROM ISIMA\Bundle\UserBundle\Entity\Group g' . $group->getId() . ' WHERE g' . $group->getId() . '.id = ' . $group->getId() . ' AND g' . $group->getId() . ' MEMBER OF c.groups)';
if($i == 0)
{
$qb->andWhere($query);
$i = 1;
}
else
{
$qb->orWhere($query);
}
}
return $qb;
}

how to search array words with Doctrine DQL using bucle

i'm trying to search words stored in array in this way:
$dql_cat =' SELECT c
FROM FrontendBundle:Categoria c
WHERE';
foreach ($palabras as $palabra){
if ($palabra === reset($palabras)){
$dql_cat .= ' c.nombre LIKE %'.$palabra.'%';
}
else{
$dql_cat .= ' OR c.nombre LIKE %'.$palabra.'%';
}
}
$dql_cat .= ' ORDER BY c.nombre';
$query = $em->createQuery($dql_cat);
$resultados['categorias'] = $query->getResult();
But i get an exception with the query.
Query result:
SELECT c
FROM FrontendBundle:Categoria c
WHERE c.nombre LIKE %carpinteria% OR c.nombre LIKE %aluminio% ORDER BY c.nombre
Query exception:
QueryException: [Syntax Error] line 0, col 98: Error: Expected StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression, got '%'
I think that is more proper to use queryBuilder to avoid mistakes but i don't know how to use it with a parameters array.
I need a solution with or without queryBuilder.
Thanks.
queryBuilder would be as follows:
$qb = $em->createQueryBuilder();
$qb->select('c')
->from('FrontendBundle:Categoria', 'c');
foreach ($palabras as $palabra){
if ($palabra === reset($palabras)){
$qb->where($qb->expr()->like('c.nombre', $qb->expr()->literal('%' . $palabra . '%')));
}
$qb->orWhere($qb->expr()->like('c.nombre', $qb->expr()->literal('%' . $palabra . '%')));
}
$query = $qb->orderBy('c.nombre', 'ASC')
->getQuery();
$resultados['categorias'] = $query->getResult();
To begin with %$palabra% should be quoted, right?:
foreach ($palabras as $palabra){
if ($palabra === reset($palabras)){
$dql_cat .= " c.nombre LIKE '%".$palabra."%'";
}
else{
$dql_cat .= " OR c.nombre LIKE '%".$palabra."%'";
}
}

Doctrine Query Builder LOWER not working

Somehow doctrine is not allowing me to compare two lowerstring values: that of a variable and the first name of a user.
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('d')
->from('MyBundle:User', 'd')
->where('LOWER(d.firstName) LIKE :fName')
->setParameter('fName', strtolower('%'.$fName.'%'));
$result = $qb->getQuery()->execute();
only when $fName has an uppercase string (i.e. 'Rob'), it will return results like 'Robert' and 'Robby'. But what I want is that even when $fName is spelled lowercase ('rob'), these results should come up. It seems that the d.firstNames are not lowered. Why is this the case?
I found the solution:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('u')
->from('MyBundle:User', 'u')
->where($qb->expr()->andX(
$qb->expr()->like('LOWER(u.firstName)', '?1'),
$qb->expr()->like('LOWER(u.lastName)', '?2')
))
->setParameter('1', '%' . strtolower($firstName) . '%')
->setParameter('2', '%' . strtolower($lastName) . '%');
$result = $qb->getQuery()->execute();
Still curious why the first attempt was not working.
you can use :
mb_strtolower()
http://php.net/manual/en/function.mb-strtolower.php

Resources