How to set parameters in nested query in DQL - symfony

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

Related

How to combine queryBuilder and raw queries on join table from many to many relation?

I have got following query
SELECT news.*, count(*) AS common_tags
FROM news
JOIN news_news_tag ON news.id = news_news_tag.news_id
WHERE news_tag_id IN
(SELECT news_tag_id FROM news_news_tag WHERE news_id = 2 )
AND news.id != 2 GROUP BY news.id ORDER BY common_tags DESC
What I want to achieve is to get hydrated news objects ordered by number of common tags with provided news id. News and Tag are many to many relation with news_news_tag join table.
News entity have got much more other relations. This is why I don't want to create a native query by myself to handle all other relations.
I would like to convert above query to use it with query builder. I wasn't able to use a DQL because my where statement uses a join (junction) table and I also need to use a join on that table.
All in all I have got 2 problems:
How can I create DQL subquery to select something from many to many join table)? If I know that I could do something like: ->where($queryBuilder->expr()->in('u.id', $mySubQueryAsDQL))
How to add that join statement that I could use news_tag_id in where statement?
If it is not possible I think that I would need to create two bidirectional one-to-many and many-to-one relations instead of many-to-many and work on special joining entity.
I finally came up with following solution. I decided to split that query into two separate ones. In first I can use just simple raw query as I need results only to where statement. Second query can be build using query builder in a normal way.
You have to only get rid of an extra count column at the end.
However if someone knows the solution how to use my first raw query directly inside query builder as a raw subquery for where statement please share with me.
public function getRelatedNews($newsId, $limit = 6)
$connection = $this->getEntityManager()->getConnection();
$sql = 'SELECT news_tag_id FROM news_news_tag WHERE news_id = :newsId';
try {
$stmt = $connection->prepare($sql);
} catch (DBALException $e) {
return [];
}
$stmt->execute(['newsId' => $newsId]);
$newsTagId = $stmt->fetchAll();
if (empty($newsTagId)) {
return [];
}
$newsTagId = array_column($newsTagId, 'news_tag_id');
$query = $this->createQueryBuilder('n')
->addSelect('COUNT(n.id) as common_tags_count')
->innerJoin("n.tags", "t")
->andWhere('t.id IN(:tagsId)')->setParameter('tagsId', array_values($newsTagId))
->andWhere('n.id != :newsId')->setParameter('newsId', $newsId)
->orderBy('common_tags_count', 'DESC')
->setMaxResults($limit)
->groupBy('n.id')
;
$results = $query->getQuery()->getResult();
$news = [];
foreach ($results as $result) {
$news[] = $result[0];
}
return $news;
}

One of listeners must count and slice given target

I'm getting the above error when performing a search in a Symfony2 CRM I've been working on. According to Google searches it seems this is an issue relating to the KNP Paginator bundle, but I cannot seem to find a solid solution.
In this instance, I am using data from an OpenCart database, and I need to be able to search by Postcode and Company name, both of which exist in the address table which is joined via a mapped value within the Customer entity, defaultaddress.
To this end, I've had to write a custom query in a function called findCustomerByPostcode like so:
$this->getEntityManager()
->createQuery('SELECT c FROM AppBundle:Oc49Customer c JOIN AppBundle:Oc49Address a WITH c.defaultaddress = a.id WHERE a.postcode LIKE :postcode')
->setParameter('postcode','%'.$postcode.'%')
->getResult();
However, when I perform a search on postcode, I get the following error in the browser:
One of listeners must count and slice given target
which refers to the Paginator.php file within the KNP bundle. I have updated to the most recent version, 2.5 yet I cannot seem to shake this error, and to me it does not even make sense.
Any help is much appreciated, as I cannot think of another way of search via a value within a joined table.
Which returns results from the postcode search, and then in the Controller:
$customers = $customer_repository->findCustomerByPostcode($filter_value);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$customers,
$request->query->get('page', 1),
20
);
You can use the queryBuilder with the left join
public function findCustomerByPostcode($postcode) {
$query = $this->entityManager->createQueryBuilder();
$query
->select('c', 'a')
->from('AppBundle:Customer', 'c')
->leftJoin('c.address', 'a')
->setParameter('postcode', $postcode)
return $query->getQuery()->getResult();
}

Multiple orderby within Criteria

I currently use the Criteria to filter a collection of objects. But when I want to achieve with 2 orderBy fields, only the first is considered. I do not understand.
$events = new Collections\ArrayCollection($results);
$dateFrom = new \DateTime($date);
$dateTo = new \DateTime(date('Y-m-d H:i:s', strtotime($date . ' + 1 day')));
$criteria = Criteria::create()
->where(Criteria::expr()->eq('activity', $activity));
$criteria->orderBy(array(
"time" => "ASC",
"title" => "ASC"
));
How can I make it work with two orderby fields and not only the first ?
Thank you in advance for any answers !
Your code is correct, assuming that $criteria is then applied correctly to the ArrayCollection.
Judging by the names of your fields you're trying to order by, it's possible that title doesn't affect the order because time doesn't repeat among the elements in the collection.
If this isn't the case, please provide more information on the results you're getting and I will update my answer.
Update (in response to additional data provided that has since been deleted):
You are sorting the collection correctly. The problem is that you're then feeding the ArrayCollection into the Paginator, which apparently cannot sort by more than one field.
There's an open issue in Knp's tracker about this: https://github.com/KnpLabs/KnpPaginatorBundle/issues/109.

Doctrine Translatable how to only fetch records with a translation

I'm using Symfony2 + Doctrine + Translatable from DoctrineExtensions.
I have an entity article which has a couple of translatable fields like Title and Content. What is happening now is that I've created a bunch of articles in Chinese language, but have not added a translation for English. This is giving me some problems when I try to display a top 8 most recent articles on my homepage... When I view my homepage in English, it will show a bunch of title-less articles there (the Chinese articles that dont have a translation in English).
I found a solution for this in the Translatable extension by using ORM query Hint:
"\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER".
This lets you directly query the translated fields of your entity which are actually stored in ext_translations and not in the entity's table.
As a result the code I use to fetch the articles for the homepage looks like this:
public function getNewestArticles($limit = 1){
$dql =
"SELECT a FROM NewsBundle:Article a
WHERE a.published = 1
AND a.title != ''
ORDER BY a.created_at DESC";
$query = $this->_em->createQuery($dql);
$query->setMaxResults($limit);
$query->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
$result = $query->getResult();
return $result;
}
This actually works, but the generated query is so inefficient that it slows down the page a lot... I'm talking 1800 ms for that query. I've also tried to just put the fields I need in the select statement, which improves the performance, but still not enough.
Any ideas?
you can try to optimize the query manually. use locale as the parameter in your repository
public function getNewestArticles($locale, $limit = 1){
//your own superfast query
}
in the controller then set the locale with
$locale = $request->getLocale();
$em->getNewestArticles($locale, $limit);
additional question: have you set indexes on your tables?

Doctrine Querybuilder issues in symfony2. Usage questions

Using doctrine for the first time in a project, and I'm having a few issues with the query builder.
First up, in a controller I used the following:
$conn = $this->get('database_connection');
$users = $conn->fetchAll('SELECT * FROM Users');
This worked fine and returns an array of users from my DB.
I then tried to use the query builder to fetch the forenames of all users. By looking at examples i found the following:
$conn = $this->get('database_connection');
$qb = $conn->createQueryBuilder();
$qb->select("forename")
->from("Users", "u")
->where("u.id = :user_id")
->setParameter('user_id', 1);
$query = $qb->getQuery();
$results = $query->getResults();
I get told that the gDoctrine\DBAL\Query\QueryBuilder::getQuery() method is not defined, which I found odd as almost all the examples I found use it.
I did a search and found Doctrine documentation but I'm now confused how to use it at all.
Would someone be kind enough to give me an example of how to use the above to retrieve the forename for the User with id 1. I'm sure that once I have a simple example that work I will be fine from there.
Thanks!
Now Resolved:
After looking at the Documentation (and with help from others), I found the general layout for the queryBuilder is as follows:
$conn = $this->get('database_connection');
$qb = $conn->createQueryBuilder();
$stmt = $qb->select("forename")
->from("Users", "u")
->where("u.id = :user_id")
->setParameter('user_id', 1)
->execute();
$userNames = $stmt->fetchAll();
The general idea being that the execute method returns a Doctrine\DBAL\Driver\Statement, with the parameters set as specified. From this statement you can call one of the various method stated here to get the results from the DB.
Hope this helps someone else who is having problems!
I think you might be confused slightly by the documents on doctrine and using it with symfony. From the error message you are getting a DBAL\Query\QueryBuilder. There is not a method getQuery for that object. You should be able to use the execute method to get the results you want.
Now with that being said the way I would do it is through either a repository class for the Users entity, or I would do
$em = $this->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
then do what you were doing before...

Resources