How to use wildcards in createQueryBuilder? - symfony

In my repository class i use:
public function getItemsByTag($tag)
{
$qb = $this->createQueryBuilder('c')
->select('c')
->where('c.tags LIKE %bipolar%')
->addOrderBy('c.id');
return $qb->getQuery()
->getResult();
}
But unfortunately this doesn't work.. Anybody knows how this can work? Or do I have to build a custom query without the QueryBuilder?
Thanks!

Searching based on a single parameter:
I think it should go:
public function getItemsByTag($tag)
{
$qb = $this->createQueryBuilder('c')
->select('c')
->where('c.tags LIKE :tag')
->addOrderBy('c.id')
->setParameter('tag', $tag);
return $qb->getQuery()->getResult();
}
But I think that it is discouraged to do a LIKE as part of a where using the query builder so you should do:
$qb = $this->createQueryBuilder('c');
$qb->select('c')
->where($qb->expr()->like('c.tags', '?1'))
->addOrderBy('c.id')
->setParameter(1, $tag);
return $qb->getQuery()->getResult();
Check out the docs for more information, there is an example of a like expression in the section entitled Helper Methods
I should also point out that I used a different convention in each example for passing a parameter into a query, the first used a named parameter :tag which is set by setParameter('tag', $value) the second is just a numbered parameter ?1, you could have just as easily have used a named parameter in the second example if you wished to as well.
Searching with an array of parameters:
You also asked about doing an array of likes. Here it is with an OR expression but if you wanted to search for all tags you could change it to an AND.
In order to make a "LIKE array" you just have to build up the expression on its own.
$qb = $this->createQueryBuilder('c');
$orExpr = $qb->expr()->orX();
for ($i = 0; $i < count($tags); $i++) {
$orExpr->add($qb->expr->like('c.tags', "?$i"));
// You may have to set params later in a loop after $orExpr has been
// added to the queryBuilder.
$qb->setParameter($i, $tags[$i]);
}
$qb->select('c')->where($orExpr)->addOrderBy('c.id');
return $qb->getQuery()->getResult();

If you don't want to substitute your query with variables but use a static string you have to put the string in apostrophes.
You have to use apostrophes instead of quotes! Otherwise the Doctrine2 Lexer will throw an Exception.
So in your case Mike you can use:
'c.tags LIKE \'%bipolar%\''
or
"c.tags like '%bipolar%'"

I don't know much about Symfony, but based on what I know about PHP and MySQL, I imagine you mean 'c.tags LIKE "%bipolar%"'. You likely need quotation marks around %bipolar%.

simply:
public function getItemsByTag($tag)
{
$qb = $this->createQueryBuilder('c')
->select('c')
->where( $qb->expr()->like('c.tags', ':tags') )
->addOrderBy('c.id');
$qb->setParameter('tags', '%' . $tag . '%' );
return $qb->getQuery()->getResult();
}

Related

can't get result object in doctrine

I got DQL like this:
$sql =$qb->select('login,id')
->from('Cusomter','c')
->where('c.login = :login')
->setParameter('login',$login);
$rs = $sql->getQuery()->getResult();
I want the result would return as object with 2 properties: Login,id.
Is there anyway i can do that? I tried CustomHydrator but it won't work out.
this is my CustomHydrator:
namespace Hydrator;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
class CustomHydrator extends AbstractHydrator
{
protected function hydrateAllData()
{
return $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}
Then i added $em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'Hydrator\CustomHydrator');
and run :$rs = $sql->getQuery()->getResult('CustomHydrator');
There are a few ways to go about this. If this is a query that you need to reuse, then Doctrine custom hydrators is perhaps the best approach. If not, you could simply cast the result to an object.
If you need a single result:
$sql =$qb->select('c.login, c.id')
->from('Cusomter','c')
->where('c.login = :login')
->setParameter('login', $login);
$rs = (object) $sql->getQuery()->getSingleResult();
If you need to return an array of objects (i.e. multiple result items), then you could just cast each result:
$sql =$qb->select('c.login, c.id')
->from('Cusomter','c')
->where('c.login = :login')
->setParameter('login', $login);
$rs_new = array_map(function ($value) {
return (object) $value;
}, $sql->getQuery()->getResult());
Better way to use Entity as result http://symfony.com/doc/current/doctrine.html#creating-an-entity-class
As I understand, you need just one result, so use next code inside repository:
$rs = $this->findOneBy(['login' => $login]);

'Invalid parameter number: number of bound variables does not match number of tokens' Symfony

I'm working on a symfony project entity with query builder. When I try to run this function I get this issue.
Invalid parameter number: number of bound variables does not match number of tokens
public function json_filterAllproductsAction() {
$search = "";
$category = 1;
//Combine tables and create the query with querybuilder
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p')
->from('EagleAdminBundle:Products', 'p')
->orderBy('p.id', 'DESC');
if ($category != 0) {
$qb->where($qb->expr()->in('p.category', '?1'))
->setParameter(1, $category);
}
$qb->where('p.productTitle LIKE :title')
->setParameter('title', "$search%");
//convert to json using "JMSSerializerBundle"
$serializer = $this->container->get('serializer');
$jsonproducts = $serializer->serialize($qb->getQuery()->getResult(), 'json');
return new Response($jsonproducts);
}
I think error is in
$qb->where($qb->expr()->in('p.category', '?1'))
->setParameter(1, $category);
It would be great help someone can help me.
You have two issues here. The first is that your last where clause overwrites the first one. This can be fixed by using andWhere. The second is that your mixing named parameters (:title) with positional parameters (?1). Mixing is a no no. And you don't really need the expr object. Try:
$qb->select('product')
->from('EagleAdminBundle:Products', 'product')
->orderBy('product.id', 'DESC');
if ($category) {
$qb->andWhere('product.category IN (:category)');
$qb->setParameter('category', $category);
}
$qb->andWhere('product.productTitle LIKE :title');
$qb->setParameter('title', "$search%");

doctrine query onetonmany / notIn with objects / symfony forms querybuilder

I am using symfony 2 and doctrine to prefilter a form field type 'entity' with the help of a querybuilder.
My querybuilder should return all products which the user has not already added to a list.
All relations are bidirectionnal.
I have products linked to userIngredients (oneToMany) each linked to one user (manyToOne)
I have come with this so far but it's not working, I get products not added by other users.
return $this
->createQueryBuilder('p')
->leftJoin('p.userIngredients', 'i')
->where('i.user <> ?1')
->setParameter(1,$user);
1; Any clue on how to correct this ?
Alternatively, I could select the products I don't want and then reselect those who don't match but using an expression and NotIn seems to only work for strings
$products = $this
->createQueryBuilder('p')
->leftJoin('p.userIngredients', 'i')
->where('i.user = ?1')
->setParameter(1,$user)
->getQuery()
->getResult();
return $this
->createQueryBuilder('p')
->where($this->createQueryBuilder('p')->expr()->notIn('p', $products));
2; how could we correct this to make it work with objects ?
3; alternatively : is there a way to pass not a querybuilder but an array of results to symfony form builders ?
I got thinks thanks to Javad:
(slight modification, I'm using an array result, not dql):
$qb = $this->_em->createQueryBuilder();
$ids = $qb
->select('p.id')
->from('AppBundle:MarketPlace\Product','p','p.id')
->leftJoin('p.userIngredients', 'i')
->where('i.user = ?1')
->setParameter(1,$user)
->getQuery()
->getResult();
//I don't know why I couldn't directly get an array of ids otherwise... if you know how to do better directly from the query, I'm interested (getScalarResult does not make it)
$ids=array_keys($ids);
$result = $this
->createQueryBuilder('p')
->where($this->createQueryBuilder('p')->expr()->notIn('p.id', $ids));
return $result;

Using wildcards with Doctrine's createQuery method

Can anyone enlighten me as to why this query isn't working please? I tried alternating between single and double quotes as well.
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery('SELECT t FROM AcmeBlogBundle:BlogTag t WHERE t.title LIKE \'%:title%\'')
->setParameter('title', $keyword);
Doctrine simply returns Invalid parameter number: number of bound variables does not match number of tokens.
Also, is it better to perform such a query using the createQuery method or createQueryBuilder?
PDO treats both the keyword and the % wildcards as a single token. You cannot add the wildcards next to the placeholder. You must append them to the string when you bind the params.
Also see this comment on the php docs.
Therefore you will need to do the following:
$qb = $em->createQueryBuilder();
$qb
->select('tag')
->from('AcmeBlogBundle:BlogTag', 'tag')
->where($qb->expr()->like('tag.title', ':title'))
->setParameter('title', '%' . $keyword . '%')
;
or
$query = $em->createQuery('SELECT t FROM AcmeBlogBundle:BlogTag t WHERE t.title LIKE :title');
$query->setParameter('title', '%' . $keyword . '%');
I prefer to use the query builder because it is nicer for structuring and making your statement more maintainable

Count Rows in Doctrine QueryBuilder

I'm using Doctrine's QueryBuilder to build a query, and I want to get the total count of results from the query.
$repository = $em->getRepository('FooBundle:Foo');
$qb = $repository->createQueryBuilder('n')
->where('n.bar = :bar')
->setParameter('bar', $bar);
$query = $qb->getQuery();
//this doesn't work
$totalrows = $query->getResult()->count();
I just want to run a count on this query to get the total rows, but not return the actual results. (After this count query, I'm going to further modify the query with maxResults for pagination.)
Something like:
$qb = $entityManager->createQueryBuilder();
$qb->select('count(account.id)');
$qb->from('ZaysoCoreBundle:Account','account');
$count = $qb->getQuery()->getSingleScalarResult();
Some folks feel that expressions are somehow better than just using straight DQL. One even went so far as to edit a four year old answer. I rolled his edit back. Go figure.
Here is another way to format the query:
return $repository->createQueryBuilder('u')
->select('count(u.id)')
->getQuery()
->getSingleScalarResult();
It's better to move all logic of working with database to repositores.
So in controller you write
/* you can also inject "FooRepository $repository" using autowire */
$repository = $this->getDoctrine()->getRepository(Foo::class);
$count = $repository->count();
And in Repository/FooRepository.php
public function count()
{
$qb = $repository->createQueryBuilder('t');
return $qb
->select('count(t.id)')
->getQuery()
->getSingleScalarResult();
}
It's better to move $qb = ... to separate row in case you want to make complex expressions like
public function count()
{
$qb = $repository->createQueryBuilder('t');
return $qb
->select('count(t.id)')
->where($qb->expr()->isNotNull('t.fieldName'))
->andWhere($qb->expr()->orX(
$qb->expr()->in('t.fieldName2', 0),
$qb->expr()->isNull('t.fieldName2')
))
->getQuery()
->getSingleScalarResult();
}
Also think about caching your query result - http://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers
public function count()
{
$qb = $repository->createQueryBuilder('t');
return $qb
->select('count(t.id)')
->getQuery()
->useQueryCache(true)
->useResultCache(true, 3600)
->getSingleScalarResult();
}
In some simple cases using EXTRA_LAZY entity relations is good
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
If you need to count a more complex query, with groupBy, having etc... You can borrow from Doctrine\ORM\Tools\Pagination\Paginator:
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);
Since Doctrine 2.6 it is possible to use count() method directly from EntityRepository. For details see the link.
https://github.com/doctrine/doctrine2/blob/77e3e5c96c1beec7b28443c5b59145eeadbc0baf/lib/Doctrine/ORM/EntityRepository.php#L161
Example working with grouping, union and stuff.
Problem:
$qb = $em->createQueryBuilder()
->select('m.id', 'rm.id')
->from('Model', 'm')
->join('m.relatedModels', 'rm')
->groupBy('m.id');
For this to work possible solution is to use custom hydrator and this weird thing
called 'CUSTOM OUTPUT WALKER HINT':
class CountHydrator extends AbstractHydrator
{
const NAME = 'count_hydrator';
const FIELD = 'count';
/**
* {#inheritDoc}
*/
protected function hydrateAllData()
{
return (int)$this->_stmt->fetchColumn(0);
}
}
class CountSqlWalker extends SqlWalker
{
/**
* {#inheritDoc}
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
return sprintf("SELECT COUNT(*) AS %s FROM (%s) AS t", CountHydrator::FIELD, parent::walkSelectStatement($AST));
}
}
$doctrineConfig->addCustomHydrationMode(CountHydrator::NAME, CountHydrator::class);
// $qb from example above
$countQuery = clone $qb->getQuery();
// Doctrine bug ? Doesn't make a deep copy... (as of "doctrine/orm": "2.4.6")
$countQuery->setParameters($this->getQuery()->getParameters());
// set custom 'hint' stuff
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountSqlWalker::class);
$count = $countQuery->getResult(CountHydrator::NAME);
For people who are using only Doctrine DBAL and not the Doctrine ORM, they will not be able to access the getQuery() method because it doesn't exists. They need to do something like the following.
$qb = new QueryBuilder($conn);
$count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);
To count items after some number of items (offset), $qb->setFirstResults() cannot be applied in this case, as it works not as a condition of query, but as an offset of query result for a range of items selected (i. e. setFirstResult cannot be used togather with COUNT at all). So to count items, which are left I simply did the following:
//in repository class:
$count = $qb->select('count(p.id)')
->from('Products', 'p')
->getQuery()
->getSingleScalarResult();
return $count;
//in controller class:
$count = $this->em->getRepository('RepositoryBundle')->...
return $count-$offset;
Anybody knows more clean way to do it?
Adding the following method to your repository should allow you to call $repo->getCourseCount() from your Controller.
/**
* #return array
*/
public function getCourseCount()
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('count(course.id)')
->from('CRMPicco\Component\Course\Model\Course', 'course')
;
$query = $qb->getQuery();
return $query->getSingleScalarResult();
}
You can also get the number of data by using the count function.
$query = $this->dm->createQueryBuilder('AppBundle:Items')
->field('isDeleted')->equals(false)
->getQuery()->count();

Resources