Parameters in QueryBuilder (Doctrine/Symfony) - symfony

I have this code:
$sort = (is_null($sort)) ? 'i.name' : $sort;
$order = (!is_null($order) && $order == 'ASC') ? 'ASC' : 'DESC';
$queryBuilder
->select('s')
->from('Model:Item', 'i')
->where('i.isRemoved = false')
->orderBy(':sort', $order)
->setParameter('sort', $sort);
But I get this error:
[1/2] QueryException: SELECT i FROM Model:Item i WHERE i.isRemoved = false ORDER BY :sort DESC
I must be doing something wrong with the setParameter() call, but I can't figure out what?
Any ideas? Thanks

setParameter() is used to prevent SQL injection attacks, since many WHERE clauses rely on user input.
And this might be the cause of the problem here -- I'm not entirely sure that the ORDER BY clause supports parameter replacement. I may be wrong on that, but your error seems to indicate that either (1) it doesn't support it, or (2) you're getting bad input.
Where exactly are you getting your $sort from? If it's coming from some user input, then you might want to perform the sanitization logic here instead, and just set the resolved value in the orderBy() method.
Consider an example where id and name are actual fields. What happens when someone tries to pass in i.description? The entire SQL would fail. But not if you did something like this:
$sort = (in_array($sort, array('i.id', 'i.name')))
? $sort
: 'i.name';
// Now you've effectively sanitized the value
// i.e. It's *always* going to be either 'i.id' or 'i.name'
// So there's no reason to need something like setParameter()
$queryBuilder
->select('s')
->from('Model:Item', 'i')
->where('i.isRemoved = false')
->orderBy($sort);

Related

Call ElasticaBundle Finder and Index in a Service

On my project I use FOSElastica to filter many entities, then I use the same portion of code on many pages:
$query = ('' !== $request->get('q') && null !== $request->get('q')) ? $request->get('q') : null;
$teamId = ('' !== $request->get('team') && null !== $request->get('team')) ? $request->get('team') : $this->getUser()->getFavoriteTeam()->getId();
$page = (0 < (int) $request->get('p')) ? $request->get('p') : 1;
$repositoryManager = $this->get('fos_elastica.manager.orm');
$repository = $repositoryManager->getRepository('AppBundle:BiologicalOriginCategory');
$elasticQuery = $repository->searchByNameQuery($query, $page, $teamId, $this->getUser());
$categoryList = $this->get('fos_elastica.finder.app.biologicalorigincategory')->find($elasticQuery);
$nbResults = $this->get('fos_elastica.index.app.biologicalorigincategory')->count($elasticQuery);
$nbPages = ceil($nbResults / BiologicalOriginCategory::NUM_ITEMS);
In the previous code the only things that changes: class used to filter, sometimes by team, sometimes by project, sometimes no filtering.
I would want avoid replication code by doing an IndexFilter util, then, I just call the IndexFilter service, give him: the class I want filter, the queryString, and the class(es) used to filter it.
It works well for the 6 first lines, because I can call it directly in my service.
But I don't know how I can dynamically call ElasticaBundle Finder and Index:
$categoryList = $this->get('fos_elastica.finder.app.biologicalorigincategory')->find($elasticQuery);
$nbResults = $this->get('fos_elastica.index.app.biologicalorigincategory')->count($elasticQuery);
I'm forced to inject it in the Service, but there is more than 10 entities, I can't inject 20 different other services each time and juste use 2 of them...
Are there a way to retrieve Finder and Index with the class name ? (AppBundle\Entity\BiologicalOriginCategory) ? I do it for retrieve the repository $repository = $repositoryManager->getRepository($class); and it works.
Thanks a lot for your help.
EDIT:
I've maybe a solution to bypass the prolem, but not resolve it by using the fos_elastica.finder.app and fos_elastica.index.app.
Then when I do a query, Elasticsearch do in on the whole index, and in my RepositoryMethod, I add a \Elastica\Query\Type() to filter results by Type.
I think it's less efficient than do a request whithout QueryType bu on the specific Type. No ?

Symfony2 andWhere not working - just leave empty

I'm trying to build something based on options, and I use a QueryBuilder to make a request with JOIN and ORDER
Here's a simple example of code I could produce :
$query = $this->createQueryBuilder('s')
->leftJoin('s.ville', 'v')
->andWhere('s.name = :name')->setParameter('name', 'test')
->orderBy('s.id');
This lead to an exception... "Expected Literal, got 'ORDER'" 'cause in the final request, the WHERE clause is empty...
Any idea ?
Just do a where ? andWhere mean you have many where in your query :).
(Take care I think you want the name of the 'ville', because of your leftJoin).
$query = $this->createQueryBuilder('s')
->leftJoin('s.ville', 'v')
//v.name ?
->where('s.name = :name')->setParameter('name', 'test')
->orderBy('s.id');
I have the same problem using doctrine/orm < 2.4. In previous versions you had to use where() before an andWhere(). In Doctrine docs from version 2.5 they say:
// NOTE: ->andWhere() can be used directly, without any ->where() before
However, It's fixed in version 2.4.8 too, so you could use that version too.

How to set parameters in nested query in DQL

Hi,
I'd like to get number of created articles by a user, the query used to work until I added some parameters filtering the query by "fromDate" and "toDate" dates, here's my query :
// query String
$dql = 'SELECT u.idUser,
u.lastName,
u.email,
u.mobile,
(SELECT AVG(n.note)
FROM MyBundle:Note n
WHERE n.noteFor = u.idUser) AS note,
(SELECT COUNT(a)
FROM MyBundle:Article a
WHERE (a.createdBy = u.idUser) AND (a.createdAt BETWEEN :fromDate AND :toDate)) AS articles
FROM MyBundle:User u';
// create the actual query
$users= $em->createQuery($dql);
// set filter date parameter
$users->setParameter('fromDate', $fromDate.'00:00:00');
$users->setParameter('toDate', $toDate.'23:59:59');
I keep getting this error : Invalid parameter number: number of bound variables does not match number of tokens.
I tried searching in the doctrine documentation for how to set parameters in nested queries without finding anything. first I need to know if it's possible to do that then find where the error come from, Please Help !
Use setParameters() instead of setParameter()
$users->setParameters(array('fromDate'=> $fromDate.'00:00:00','toDate'=> $toDate.'23:59:59'))
So after performing some tests, I found out that the query worked well the way it was and the problem wasn't there.
I'm using KnpPaginatorBundle to paginate my queries, it seems that the problem was that it couldn't paginate complex queries like passing multiple parameters in the nested query. so I found a solution. So this is the old code :
// Pagination
$paginator = $this->get('knp_paginator');
$users = $paginator->paginate($users, 1, 10);
And this is the new code :
// Pagination
$paginator = $this->get('knp_paginator');
$users = $paginator->paginate($users, 1, 10, array('wrap-queries' => true) );
Thanks to Nisam and Matteo for their time, hope this helps someone

"This value is not valid" when using "query_builder" on "entity" buildForm on symfony2.1

Here is my problem.
I use the buildForm method on symfony 2.1 in order to build my form.
With the following code everything works just fine :
$builder->add('combat','entity',array(
class' => 'KarateCompetitionBundle:CompetitionCombat',
'empty_value' => 'Sélectionner un combat'));
But I want to filter and display only some Combat. That's why I have to use the query_builder option. When i do that i get the This value is not valid error message.
Here is the code :
$builder->add('combat','entity',array(
'class' => 'KarateCompetitionBundle:CompetitionCombat',
'empty_value' => 'Sélectionner un combat',
'query_builder' => function(CombatRepository $cr) {
return $cr->getAllWithoutBilanQueryBuilder();}));
I reduce at the minimum the code (i.e. no filtering on the getAllWithoutBilanQueryBuildermethod) in order to be able to find the problem.
public function getAllWithoutBilanQueryBuilder(){
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
return $queryBuilder->select('c')->from('KarateEntrainementBundle:CompetitionCombat', 'c');
}
I've compared both html code generate on each case and they are the same.
I put a var_dump($object) on the controller after binding the form with the request $form->bind($request) and it appears that when i use the query_builder option the combatis null while it is not null if i don't use it.
I can't manage to understand why ?
I found few post on the web with the same issue but none with an answer.
Is it possible that there is a symfony issue here or am I doing something wrong ?
I had the exact same problem and - in my case - traced it back to Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader.
When the form is validated, the entities are loaded by primary key in ORMQueryBuilderLoader::getEntitiesByIds() by adding an IN() clause to the query builder. In my case, this IN() clause was ineffective and all selectable entities were returned.
This, in turn, caused Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer::reverseTransform() to throw a TransformationFailedException, because the number of loaded entities and submitted choices were not the same.
I suppose there's other possible causes for this specific error. Here's what you could try:
Look at the generated query, run it manually and make sure it returns only the selected values
In Symfony\Component\Form\Form, try outputting the caught TransformationFailedException and see where it leads you.
If none of the above seem plausible, add some debug output to Symfony\Component\Form\Extension\Validator\Constraints\FormValidator and see whether you can narrow it down somewhat.
As an addition to #mike-b answer: for me failing QueryBuilder statement was ->having('...') query part:
...
'query_builder' => function(EntityRepository $er) use ($pn) {
return $er->createQueryBuilder('p')
->select('p')
->join('p.product', 'pr')
->where('p.name = ' . $pn->getId())
->andWhere("LENGTH(p.value_en) > 1")
->andWhere("p.value_en != ''")
/* when I remove this - validation passe flawlessly */
->having('COUNT(pr.id) > 1')
->groupBy('p.value_en)
->orderBy('p.value_en', 'ASC');
...
tested with Symfony version 2.3.6
I finally managed to make this worked :-)
So here was the fix.
On the getAllWithoutBilanQueryBuilder function, I replace
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
by
$queryBuilder = $this->createQueryBuilder('c');
I don't know what is exactly the difference and why this is now working.
But this is working.
Thanks you all for your help.

doctrine querybuilder limit and offset

i'm a symfony beginner and i want to make a blog with the framework. i use repository to get home articles with this method :
public function getHomeArticles($offset = null, $limit = null)
{
$qb = $this->createQueryBuilder('a')
->leftJoin('a.comments', 'c')
->addSelect('c')
->addOrderBy('a.created', 'DESC');
if (false === is_null($offset))
$qb->setFirstResult($offset);
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()
->getResult();
}
so in my database i have 10 articles. In my BlogController i use :
$blog = $em->getRepository('TestBlogBundle:Article')
->getHomeArticles(3,4);
With this i want 4 articles. But in return i also have one article.
What is the problem?
This is a know issue where setFirstResult() and setMaxResults() need to be use with care if your query contains a fetch-joined collection.
As stated about First and Max Result Items:
If your query contains a fetch-joined collection specifying the result
limit methods are not working as you would expect. Set Max Results
restricts the number of database result rows, however in the case of
fetch-joined collections one root entity might appear in many rows,
effectively hydrating less than the specified number of results.
Instead, you can:
Lazy load
use the Paginator (as stated by #Marco here)
Use Doctrine\Common\Collections\Collection::slice()

Resources