Doctrine2 DQL Syntax error when ordering by count - doctrine-orm

This is Doctrine Repository function
public function mostReadArticleByUser($userId){
$total = $this->createQueryBuilder('ar')
->select('ar.articleId', 'COUNT(ar)')
->where('ar.authorId = :userId')
->groupBy('ar.articleId')
->orderBy('COUNT(ar)', 'DESC')
->setMaxResults(1)
->setParameter('userId', $userId)
->getQuery()
->getResult();
return $total;
}
which should be equivalent to this query
SELECT article_id, count(id)
FROM profile_article_reads
WHERE author_id = 2
GROUP BY article_id
Order by count(id) DESC
LIMIT 1;
When I execute this code I get error
Error: Expected end of string, got '('
QueryException: SELECT ar.articleId, COUNT(ar) FROM
SciProfileBundle\Entity\ProfileArticleReads ar WHERE ar.authorId =
:userId GROUP BY ar.articleId ORDER BY COUNT(ar) DESC

THe count funtion accept a field, so try with
COUNT(ar.id)
instead of:
COUNT(ar)
Probably for sorting is better using an alias, as example:
public function mostReadArticleByUser($userId){
$total = $this->createQueryBuilder('ar')
->select('ar.articleId', 'COUNT(ar.id) as total')
->where('ar.authorId = :userId')
->groupBy('ar.articleId')
->orderBy('total', 'DESC')
->setMaxResults(1)
->setParameter('userId', $userId)
->getQuery()
->getResult();
return $total;
}
Hope this help

Related

Symfony: Turning a SQL query into a correct Doctrine QueryBuilder Instance

I need a working query builder in symfony for the following SQL:
SELECT
(SELECT COUNT(ii.invoice_id) FROM invoice WHERE invoice_number = i.invoice_number) as c,
i.id as invoice_id, i.invoice_number, ii.logistician_id FROM invoice i
LEFT JOIN incoming_invoice ii ON i.id = ii.invoice_id
GROUP BY invoice_id
HAVING c < 2;
Im testing since yesterday but i do not get it working.
public function findAllWithoutIncomingInvoice(): array
{
return $this->createQueryBuilder('i')
->select('i, ii, COUNT(ii.invoice_id) FROM invoice WHERE invoice_number = i.invoice_number) as c')
->leftJoin('i.incomingInvoices', 'ii')
->groupBy('i.id')
->having('c < 2')
->setMaxResults(15)
->getQuery()
->getResult();
}
Error:
[Semantical Error] line 0, col 40 near 'invoice WHERE': Error: Class 'invoice' is not defined.
You should properly name your aliases, it make it easier to read back your code.
Also, make sure you use double quote to write your DQL, as Doctrine will require simple quote around text values
As for your answer, it should be something close to this :
public function findAllWithoutIncomingInvoice(): array {
$qb = $this->_em->createQueryBuilder();
$qb->select("invoice")
->from(Invoice::class, "invoice")
->join("invoice.incomingInvoice", "incomingInvoice")
->groupBy("invoice.id")
->having("COUNT(incomingInvoice.id) < 2");
return $qb->getQuery()->getResult();
}

Convert query to doctrine DQL

I have pretty big MySQL query for performance optimization I'm adding subqueries inside a join statement. With raw SQL all working fine. Here is the query:
SELECT
campaigns.id,
campaigns.name,
CONCAT(users.id, ' ', users.email) AS usersData,
CONCAT(campaigns.cpm, ' ', currencies.currency_code) AS cpm,
CONCAT(campaign_budgets.total_spend, ' ', currencies.currency_code) AS total_spend,
creatives.impressionsCount,
creatives.bidsCount,
creatives.winsAmount,
creatives.winsPercentage,
creatives.creativeIds
FROM campaigns
INNER JOIN users ON campaigns.user_id = users.id
INNER JOIN campaign_budgets ON campaigns.id = campaign_budgets.campaign_id
INNER JOIN currencies ON campaigns.currency_type_id = currencies.id
LEFT JOIN (
SELECT
GROUP_CONCAT(creatives.id) as creativeIds,
creatives.campaign_id,
creatives.user_id,
impressions.impressionsCount,
bids.bidsCount,
bids.winsAmount,
bids.winsPercentage
from creatives
LEFT JOIN (
SELECT
count(impressions.id) as impressionsCount,
impressions.user_id,
impressions.creative_id
from impressions
GROUP BY impressions.user_id
) as impressions ON creatives.user_id = impressions.user_id
LEFT JOIN (
SELECT
count(bids.id) as bidsCount,
SUM(CASE WHEN bids.status = 'won' THEN 1 ELSE 0 END) AS winsAmount,
SUM(CASE WHEN bids.status = 'won' THEN 1 ELSE 0 END) / COUNT(bids.id) * 100 AS winsPercentage,
bids.user_id,
bids.creative_id
from bids
GROUP BY bids.user_id
) as bids ON creatives.user_id = bids.user_id
GROUP BY creatives.campaign_id
) as creatives ON campaigns.id = creatives.campaign_id
GROUP BY campaigns.id
and I need convert it to Doctrine DQL somehow if is it possible. I've faced an issue when adding a subquery to join statement. Here is my code:
$columns = [
'campaign.id',
'campaign.name',
'CONCAT(owner.id,\' \', owner.email) as ownerEmail',
'CONCAT(campaign.cpm,\' \', currency.currencyCode) as cpm',
'CONCAT(budget.totalSpend,\' \', currency.currencyCode) as totalSpend',
'COUNT(imp.id) as impressionsCount',
'COUNT(bid.id) as totalBidsCount',
'SUM(case when bid.status = \'won\' then 1 else 0 end) as winsAmount',
'SUM(case when bid.status = \'won\' then 1 else 0 end)/COUNT(bid.id)*100 as winsPercentage',
];
$bids = $this->_em->getRepository(Bid::class)
->createQueryBuilder('bids')
->select([
'count(bids.id) as bidsCount',
'SUM(CASE WHEN bids.status = \'won\' THEN 1 ELSE 0 END) AS winsAmount',
'SUM(CASE WHEN bids.status = \'won\' THEN 1 ELSE 0 END) / COUNT(bids.id) * 100 AS winsPercentage',
'bids.userId',
'bids.creativeId'
])->getDQL();
$impressions = $this->_em->getRepository(Impression::class)
->createQueryBuilder('imp')
->select([
'count(imp.id) as impressionsCount',
'imp.userId',
'imp.creativeId'
])->getDQL();
$creative = $this->_em->getRepository(Creative::class) ->createQueryBuilder('cr')->select('cr.id')
->select([
'GROUP_CONCAT(cr.id) as creativeIds',
'cr.campaignId',
'cr.userId',
'impressions.impressionsCount',
'bids.bidsCount',
'bids.winsAmount',
'bids.winsPercentage'
])
->leftJoin(Impression::class, sprintf('(%s) as imp', $impressions), Expr\Join::WITH, 'imp.id = cr.userId')
->leftJoin(Bid::class, sprintf('(%s) as bid', $bids), Expr\Join::WITH, 'bids.id = cr.userId')
->getDQL();
$query = $this->createQueryBuilder('campaign')
->select($columns);
$query
->join('campaign.user', 'owner')
->join('campaign.campaignBudget', 'budget')
->join('campaign.currencyType', 'currency')
->leftJoin(Creative::class, sprintf('(%s) as creative', $creative), Expr\Join::WITH, 'campaign.id = cr.campaignId');
$query->groupBy('campaign.id');
$query->setMaxResults($limit);
$query->setFirstResult($offset);
return $query->getQuery()->useQueryCache(true)->getResult();
I'm getting the error [Syntax Error] line 0, col 626: Error: Expected Doctrine\ORM\Query\Lexer::T_IDENTIFIER, got '(', issue is in adding subquery to join statement.
I would appreciate any help!!
Okay I found a solution using DBAL instead of DQL using this as a reference

Doctrine query builder - nested queries

How can I convert this query into symfony 2 doctrine query builder?
SELECT
artist_id,
DATE,
balance,
TYPE
FROM TRANSACTION AS
t1
WHERE
DATE =(
SELECT
MAX(DATE)
FROM TRANSACTION
WHERE
artist_id = t1.artist_id AND
STATUS
IN(
'partial',
'pending',
'deducted',
'accepted'
) AND TYPE NOT LIKE 'payment'
)
GROUP BY
artist_id
ORDER BY
artist_id
I tried the following:
$qb = $this->getEntityManager()->createQueryBuilder()
->select('t.balance','a.id','t.date')
->from('TestMainBundle:Transaction','t')
->Join('t.artist','a')
->where("t.status in ('partial','pending','deducted','accepted')")
->andWhere("t.type NOT LIKE 'payment'")
->groupBy('a.id')
->orderBy('a.id');
return $qb->getQuery()->getResult();
But I am stuck with including the condition of max (date) as well. Any help on this is very much appreciated.
Your Doctrine query will look something like this,
$qb1 = $this->getDoctrine()->getManager()->createQueryBuilder();
$select = $qb1->select('MAX(date) AS max_data')
->from('YourBundle:Transaction', 's')
->where('s.artist_id = :ti_artist_id')
->andWhere('s.status IN (:statuses)')
->andWhere('s.type NOT LIKE :type')
->getQuery();
$qb2 = $this->getDoctrine()->getManager()->createQueryBuilder();
$result = $qb2->select('t.artist_id', 't.date', 't.balance', 't.type')
->from('YourBundle:Transaction', 't');
$result->where($qb2->expr()->eq('t.date', $select->getDQL()))
->setParameter('ti_artist_id', 't.id')
->setParameter('statuses', array('partial','pending','deducted','accepted'))
->setParameter('type', 'payment') //possibly '%payment%'
->orderBy('t.artist_id')
->getQuery()
->getResult();
Cheers!!!

How to use date() function / return hydrated objects?

In my Entity Repository for Doctrine2, I have the following:
$date = new DateTime('NOW');
$date = $date->format('Y-m-d');
if ($region) {
$region_sql = " AND WHERE region LIKE ?3 ";
} else {
$region_sql = "";
}
$sql = "SELECT *, count(month) as count FROM (SELECT *, date(date_from, 'start of month', '+1 month', '-1 day') as month FROM manifestations WHERE date_to >= :date_to " . $region_sql . ") GROUP BY month";
$stmt = $em->getConnection()->prepare($sql);
$stmt->bindValue(':date_to', $date);
if($region) {
$stmt->bindValue(3, sprintf('%%,%s,%%', $region));
}
$stmt->execute();
return $stmt->fetchAll();
But I need to change this so that it returns the objects hydrated instead of an array. I originally wanted to use DQL or queryBuilder but could not find a way to get the date() function to work.
With NativeQuery you can execute native SELECT SQL statements and map the results to Doctrine entities or any other result format supported by Doctrine.
What you want to do can be achieved using the ResultSetMappingBuilder.
ResultSetMappingBuilder is a convenience wrapper. It can generate the mappings for you based on Entities.
This is how I'd do it (I assume your query works, maybe you'll have to adjust it, as I will use a new alias):
Create the ResultSetMapping:
use Doctrine\ORM\Query\ResultSetMapping;// Don't forget this
$rsm = new ResultSetMappingBuilder($entityManager);// $entityManager points to your entity manager.
$rsm->addRootEntityFromClassMetadata('path/to/class/MyClass', 'a');// Notice the a, it's an alias that I'll later on use in the query.
$rsm->addScalarResult("count", "count");// column, alias
Prepare $region_sql part as you do in your code and add the a alias to whatever you want to map. a.* will be mapped to an object (notice the as a I use in the query):
$sql = "SELECT a.*, count(month) as count FROM (SELECT *, date(date_from, 'start of month', '+1 month', '-1 day') as month FROM manifestations WHERE date_to >= :date_to " . $region_sql . ") as a GROUP BY month";
Execute the query:
$query = $entityManager->createNativeQuery($sql, $rsm);
$query->setParameter('date_to', $date);
$result = $query->getResult();
This will give you an array of rows. Each of them will be a mixed array, $result[n][0] will contain the object and $result[n]["count"] the value of the count column of the query (name of the column is the same as the alias we set up in the $rsm) where n is the number of the row.

Doctrine use count() in NativeQuery with ResultSetMapping

i try to use NativeQuery of Doctrine. If i use my SQL ind Phpmyadmin i have the good result. If i use this, my var_dump return an empty array. I don't understand why.
$sql = 'SELECT COUNT(id) as nb FROM app_facture f WHERE SUBSTR(f.datecreate_facture, 1, 4) = 2014 AND SUBSTR(f.numero_facture,1,1) != "E"';
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Acme\MyBundle\Entity\Facture', 'f');
$rsm->addFieldResult('f', 'COUNT(id)', 'nb');
$query = $this->getEntityManager()->createNativeQuery($sql,$rsm);
$results = $query->getResult();
var_dump($results);//return empty array
Thanks
Try
$rsm->addFieldResult('f', 'nb', 'id');
this worked for me.