Is this DQL right - dql

$query = $this->em->createQueryBuilder();
$query->select('(case when (e.startDate>=CURRENT_DATE()) then count(es.event) END) upComingEvent);
$query->from('Entities\ES','es')
->leftjoin('Entities\E', 'e', 'with', 'e.id=es.event');
$query->addGroupBy('es.userId');
$query->addOrderBy('upComingEvent' , 'desc');
I am trying to find upcoming event of any user and write this DQL which throws exception
Uncaught exception 'Doctrine\ORM\Query\QueryException with message "printing above dql"

I believe you need to correct your case clause and move case statement inside count() method to count only rows where provided criteria is met.
$query->select('count(case when e.startDate>=CURRENT_DATE() then 1 end) upComingEvent);
Formulating complicated Doctrine2 DQL query
Or with conditional sum()
$query->select('sum(case when e.startDate>=CURRENT_DATE() then 1 else 0 end) upComingEvent);

Related

Subquery inside the 'FROM' statement in doctrine?

I would like to write this SQL query
select id FROM
(SELECT article.id AS id, MATCH(titre, intro, contenu) AGAINST ('query') as score FROM article ORDER BY score DESC) t1
where score>0
in doctrine with a querybuilder subquery.
I'am using a doctrine extension to interpret the MATCH AGAINST.
I cannot find the doctrine/querybuilder syntax to create the subquery inside the 'FROM'
So I do :
$this->createQueryBuilder('a')
->andWhere('MATCH(a.titre, a.intro, a.contenu) AGAINST (:q boolean) >0')
->orderBy('MATCH(a.titre, a.intro, a.contenu) AGAINST (:q boolean)', 'DESC')
->setParameter('q', $query)
This syntax is working but i would like to do it efficiently without repeating the 'MATCH(a.titre, a.intro, a.contenu) AGAINST (:q boolean) twice.
So I need to write the subquery version.
I think there are at least 2 approaches here and neither requires a subquery:
1) You can use HAVING instead of WHERE and reference the selected column name (+ hide that column from results with HIDDEN keyword). Though this might be less performant as it would do condition matching and sorting on the already selected data, so it is not available for query optimizer to optimize it.
$this->createQueryBuilder('a')
->select('a.id, HIDDEN MATCH(a.titre, a.intro, a.contenu) AGAINST (:q boolean) AS score')
->andHaving('score > 0')
->orderBy('score', 'DESC')
->setParameter('q', $query);
2) If repetition is the only issue you are having, just define the repeated part as a variable and use that instead:
$score = 'MATCH(a.titre, a.intro, a.contenu) AGAINST (:q boolean)'
$this->createQueryBuilder('a')
->select('a.id')
->andWhere("{$score} > 0")
->orderBy($score, 'DESC')
->setParameter('q', $query)
As for repetition in the query causing performance penalty - as far as I remember you don't need to worry about it - MySQL should handle that for you by reusing the same value (not recalculating it). In other words it would not be doing the matching twice just because it is present in your query twice - as long as the expression is identical, optimizer will take of that. (Although now I can't find the exact place in the docs where it dais that)

Symfony3.4 / Doctrine : subquery in FROM Clause : Error: Class 'SELECT' is not defined

I'm working on a Symfony 3.4 project and I'm trying to translate an sql query to DQL query but I get an Issue.
Mysql Query:
select sum(montant_paye)
from
(select montant_paye
from vente
where client_id = 1
and montant_paye > 0
order by date ASC
limit 2)
as T;
DQL Query (Error):
return $this->getEntityManager()
->createQuery('
SELECT SUM(montantPaye) as Total
FROM
SELECT v.montantPaye
FROM AppBundle:Vente v
where v.montantPaye > 0
AND v.client = '.$clientId.'
ORDER BY v.date ASC
limit 2
')
->getResult();
Error :
[Semantical Error] line 0, col 71 near 'SELECT v.montantPaye
': Error: Class 'SELECT' is not defined.
Is any one have a solution for a correct DQL query ?
Quoting from Christophe stoef Coevoet (Symfony Core Developer):
DQL is about querying objects. Supporting subselects in the FROM clause means that the DQL parser is not able to build the result set mapping anymore (as the fields returned by the subquery may not match the object anymore).
This is why it cannot be supported (supporting it only for the case you run the query without the hydration is a no-go IMO as it would mean that the query parsing needs to be dependant of the execution mode).
In your case, the best solution is probably to run a SQL query instead
(as you are getting a scalar, you don't need the ORM hydration anyway)
Details here.
add this function to your VenteRepository:
public function sumMontantPaye($clientId)
{
return $this->createQueryBuilder("v")
->select("sum(v.montantPaye) as sum")
->where("v.client = :id")
->andWhere("v.montantPaye > 0")
->setParameter("id", $clientId)
->setMaxResults(2)
->getQuery()->getSingleResult();
}
you can access the sum using $result["sum"] assuming $result is the variable assigned to this function in the controller

Join a subquery with Doctrine DQL

Using Doctrine in Symfony2, I need to recover each items and for each of them, the latest timestamp of their report.
So I would like to execute a query using DQL, which would be like this in SQL:
SELECT * from `item` i
LEFT JOIN `kit` k ON k.`id` = i.`kit_id`
LEFT JOIN
(SELECT e.`item_id`, MAX(e.`dateCreation`)
FROM `entete_rapport` e
GROUP BY e.`item_id`) latest ON latest.`item_id` = i.`id`
I am not able to have the same with DQL. I guess I have to separate the subquery et the main one, with something like this:
$subSelect->select('e AS ItemId, MAX(e.dateCreation) AS latest')
->from('CATUParkBundle:EnteteRapport', 'e')
->groupBy('e.item');
$qb->select('i')
->from('CATUParkBundle:Item', 'i')
->leftJoin('i.kit', 'k')
->leftJoin('CATUParkBundle:EnteteRapport f', sprintf('(%s)', $subSelect->getDQL()), 'latest', 'f.id = latest.ItemId');
I am not able to make this query work, I really need you guys.
Thank you in advance, you're awesome!
Seems like subqueries in joins do not work in dql. (I get error message: [Semantical Error] line 0, col 52 near '(SELECT e': Error: Class '(' is not defined.)
You could run $em->getConnection()->prepare($yourRawSql)->execute()->fetchAll(), but this returns a raw result array (no objects). See raw sql queries on knp-lab
Or do it in dql with HAVING instead of a subquery join:
$qb->select('i', 'i')
->addSelect('MAX(e.dateCreation)', 'date')
->from('CATUParkBundle:Item', 'i')
->leftJoin('i.kit', 'k')
->leftJoin('i.rapportOrWhatEver', 'f')
->groupBy('e.id');

Symfony and Doctrine: order by time difference

I am trying to build a query that retrieves all the most recent and upcoming activities from database.
The entity activity has a field named date of type DateTime. So in my repository I was thinking of building something like this:
$query = $repository
->createQueryBuilder('a');
$query->orderBy( 'DATEDIFF( a.date, NOW())' , 'ASC');
$query->setMaxResults( 6 );
return $query;
Unfortunately I get the following error:
[Syntax Error] line 0, col 59: Error: Expected end of string, got '('
The Dql that is generated by my query:
SELECT a FROM MyBundle\Entity\Activity a ORDER BY DATEDIFF( a.date, NOW()) ASC
I also tried installing beberlei/DoctrineExtensions, but either it is not working or I was unable to configure it correctly.
Anyone has any suggestion?
Thanks in advance
date_diff si already implemented as Doctrine DQL statement as described here
for use as ordering statement I suggest you to use the HIDDEN select keyword as explained in this article
So your DQL is like this:
SELECT
a,
DATE_DIFF( a.date, CURRENT_TIMESTAMP() ) AS HIDDEN score
FROM MyBundle:Entity a
ORDER BY score
And add the max result on the query. Let me know if you need help to adapt as query builder statement
Hope this help
Why don't you just use
$query = $repository
->createQueryBuilder('a');
$query->orderBy( 'DATEDIFF( a.date, CURRENT_TIMESTAMP())' , 'ASC');
$query->setMaxResults( 6 );
return $query;
?

Symfony createQueryBuilder with many to many

In my system candidate and profession has many to many relationship. I need to implement following query in symfony.
SELECT c. *
FROM candidate AS c
LEFT JOIN candidate_profession AS cp ON cp.candidate_id=c.id
WHERE cp.profession_id = 2
So i wrote following code.
$matched = $em->getRepository('AppBundle:Candidate')
->createQueryBuilder('c')
->where('c.professions = :profession')
->setParameter('profession', $job->getProfession())
->getQuery()
->getResult();
$job->getProfession() is return profession object. But it show following error.
[Semantical Error] line 0, col 51 near 'professions =': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected.
How i implement that query?
I think your query should look like this:
$em->getRepository('AppBundle:Candidate')
->createQueryBuilder('c')
->leftJoin('c.professions', 'p')
->where('p.id = :profession')
->setParameter('profession', $job->getProfession())
->getQuery()
->getResult();
More about join clauses: http://doctrine-dbal.readthedocs.org/en/latest/reference/query-builder.html#join-clauses
First of all, I don't know where you have placed that snippet of code but I strongly advice you to migrate it into a Repository if you don't have already done (but looking at code I'm not sure you have)
Second, you need to pass an ID as ->getProfession() (as you already noticed) will return the whole object and you don't need it
So your query should be like this
$matched = $em->getRepository('AppBundle:Candidate')
->createQueryBuilder('c')
->where('c.professions = :profession')
->setParameter('profession', $job->getProfession()->getId())
->getQuery()
->getResult();
Please pay attention
You didn't specify the cardinality of relationship between job and profession: if is a something-to-Many you can't simply use ->getId() as returned object is an ArrayCollection, so, in that case, you need to do a loop to extract all id(s) and then use something like "IN" clause

Resources