Doctrine 2 : Select specific columns from Entity and its Join entities - symfony

I have this scenario where I have to select specific columns of Entity and join entities.
This statement works and fetches all columns of all Entities.
$this->createQueryBuilder('e')
->select('e','bs','t')
->leftJoin('e.bits', 'bs')
->leftJoin('bs.tests', 't')
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY)
Question 1
I want something like this
Try 1
$this->createQueryBuilder('e')
->select('e.title','bs.name','bs.content','t.id','t.date')
->leftJoin('e.bits', 'bs')
->leftJoin('bs.tests', 't')
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY)
But this throws errors like
[Semantical Error] line 0, col -1 near 'SELECT e.title,': Error: Cannot select entity through identification variables without choosing at least one root entity alias.
Try 2
$this->createQueryBuilder()
->select('e.title','bs.name','bs.content','t.id','t.date')
->from($this->_entityName, "e")
->leftJoin('e.bits', 'bs')
->leftJoin('bs.tests', 't')
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY)
This throws error
Warning: Missing argument 1 for Doctrine\ORM\EntityRepository::createQueryBuilder(),
Try 3
This works but only if I select from just 1 Entity
$this->createQueryBuilder('e')
->select('e.title')
->leftJoin('e.bits', 'bs')
->leftJoin('bs.tests', 't')
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY)
Question 2
Is there a smart way to remove specific columns from select query instead of writing all columns. I have around 30 columns in an Entity and its really messy to write 27 of them which I want in a query
Thanks

Try to use partial like this
$this->createQueryBuilder()
->select(['partial e.{title}','partial bs.{name, content}','partial t.{id, date}'])
->from($this->_entityName, "e")
->leftJoin('e.bits', 'bs')
->leftJoin('bs.tests', 't')
->getQuery()
->getArrayResult

Related

Doctrine limit to a number ignoring duplicate

I'm using Symfony 3.4 and I have a request in my user repository.
I want the top 5 users based on score. So I have created my request with an order by on score column and a limit to 5 result.
$query = $this->createQueryBuilder('u')
->orderBy('u.score', 'DESC')
->setMaxResults(5)
->getQuery()
->getResult();
But I want the top 5 with taking the user with same score. For exmaple with:
User : Score
Jack : 100
Mick : 50
Joe : 10
Daniel : 25
Fred : 75
James : 100
Billy : 2
I want to return 6 result (because two user have the same score). What I want
Jack
James
Fred
Mick
Daniel
Joe
And If I have an other user with the same score than Mick, it should return 7 result.
The number of result return the top 5 best score but with all the user with this score.
How can I edit my query to do it ?
You should use a subquery selecting all the user that have the top5 score, as example:
$subQuery = $this->createQueryBuilder('u1')
->select('DISTINCT u1.score')
->orderBy('u1.score', 'DESC')
->setMaxResults(5)
->GetDQL();
$query = $this->createQueryBuilder('u2');
$query->where(
$query->expr()->in(
'u2.score', $subquery))
->orderBy('u2.score', 'DESC')
->getQuery()
->getResult();
Hope this help
I have found a solution with two request (feel free to say if you have better solution)
This is my code working but in my question I simplify the text, in reality my score has store on a user_session table, so it has more join and more condition but it can help.
First I get all the top 5 score with a distinct to group the same score.
$now = new \DateTime();
$leaderScore = $this->createQueryBuilder('u')
->select('us.score')
->join('u.sessionUsers', 'us')
->join('us.session', 's')
->where(':now BETWEEN s.start_date AND s.end_date')
->setParameter('now', $now)
->setMaxResults(self::NUMBER_LEADERS_DISPLAY)
->distinct()
->orderBy('us.score', 'DESC')
->getQuery()
->getResult();
I save the worst score of the top 5:
$minScore = min($leaderScore);
And I make another request to get all users with a higher or egal score than $minScore
$query = $this->createQueryBuilder('u')
->join('u.sessionUsers', 'us')
->join('us.session', 's')
->where(':now BETWEEN s.start_date AND s.end_date')
->andWhere('u.roles LIKE :role')
->andWhere('us.score >= :minScore')
->orderBy('us.score', 'DESC')
->setParameter('now', $now)
->setParameter('role', '%PLAYER%')
->setParameter('minScore', $minScore);
Hope this help :)

Two foreign keys reference to same table in symfony2 querybuilder

Table1(identity1, a1,b1)
Table2(identity, foreign(identity1),foreign(identity1))
Now when I use this query
$query = $qb->select('a1', ' b1')
->from('table2', 'm1')
->join('ApiMapBundle:tabl1, 'u1', 'WITH', $qb->expr()->orX('m1. foreign(a1) =u1.identity', 'm1. foreign(b1) = u1.identity '))
->andWhere('m1.identity=:tt')
->setParameter('tt', $cn)
->getQuery()
->getResult();
Now the problem with this query is that sometimes it gives id let's tt:5 so it gives me a value like this
Array(
0 => Array(id => 1, b1=> 8000225),
1 => Array(id => 9, b1 => 8000234)) given).
Basically in table the values structure is like this
Table2(Identity=5,foreign1=9,foreign2=1)
Any idea that how can I exactly get the given structure? Because in some cases it is fine they give me proper foreign 1 and foreign 2 but in other cases it make it alternative. Any idea?

How can I order by match result in doctrine querybuilder?

I have a custom dql match function and would like to select the match result as 'score' before ordering by that score. How can I adjust the following query to achieve this?
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('i', 'bi')
->from('AdminBundle:Items', 'i')
->where('i.instock=1')
->leftJoin('AdminBundle:MyTable','bi',\Doctrine\ORM\Query\Expr\Join::WITH, 'i.id = bi.productId')
->andWhere('bi.productId IS NULL')
->andWhere('MATCH (i.brand, i.store, i.title, i.description, i.keywords) AGAINST (:search BOOLEAN) > 0')
->andWhere('i.instock = 1')
->setParameter('search', $searchString)
->setMaxResults( $limit )
;
$results = $qb->getQuery()->getArrayResult();
I do not know if I understand your question, do you want to add to your select what you have in your where (match...) and order by it?
You can add not-mapped selects like this:
$qb->addSelect('MY_FUNCTION(my.params) AS HIDDEN mySelectAlias')
In your case, sth like:
$qb->addSelect('(MATCH (i.brand, i.store, i.title, i.description, i.keywords) AGAINST (:search BOOLEAN)) AS HIDDEN mySelectAlias')
And in your order by:
$qb->orderBy('mySelectAlias', 'desc');
Complete example:
https://coderwall.com/p/o5snag

How to fetch row count of related entities?

I have two entities, Class and Student. One Class can have multiple Students (oneToMany):
# YAML notation for Entity 'Class'
...
oneToMany:
students:
targetEntity: MyBundle\Entity\Student
mappedBy: class
For fetching all Classes, I'm writing my own query like this:
SELECT c
FROM MyBundle:Class c
WHERE c.whatever = :parameter
ORDER BY c.id DESC
Now I'm trying to fetch a list of Classes, ordered (DESC) by the count of related Students. So that the result would look like:
Class.id Class.count(Student)
-------- --------------------
3 109
1 81
4 58
2 21
How would I go there? I tried something somewhat like this:
SELECT
c,
COUNT(c.students) AS students
FROM MyBundle:Class c
WHERE c.whatever = :param
GROUP BY c.id
ORDER BY students DESC
(Note: I implement DoctrineExtensions' Date function)
But I'm getting an error:
[Semantical Error] line 0, col 26 near 'students)': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected.
Try to use this in your Class repository:
public function getAllClassesOrderedByNumberOfStudents()
{
$qb = $this->createQueryBuilder('class');
$buffer = $qb
->select('class, COUNT(students) AS students_per_class')
->innerJoin('class.students', 'students')
->groupBy('class.id')
->orderBy('students_per_class', 'DESC');
$q = $buffer->getQuery();
return $q->getResult();
}

symfony2 doctrine select IFNULL

Ok i have this code:
SELECT
IFNULL(s2.id,s1.id) AS effectiveID,
IFNULL(s2.status, s1.status) AS effectiveStatus,
IFNULL(s2.user_id, s1.user_id) as effectiveUser,
IFNULL(s2.likes_count, s1.likes_count) as effectiveLikesCount
FROM statuses AS s1
LEFT JOIN statuses AS s2 ON s2.id = s1.shared_from_id
WHERE s1.user_id = 4310
ORDER BY effectiveID DESC
LIMIT 15
And i need to rewrite it to querybuilder. Something like that?
$fields = array('IFNULL(s2.id,s1.id) AS effectiveID','IFNULL(s2.status, s1.status) AS effectiveStatus', 'IFNULL(s2.user_id, s1.user_id) as effectiveUser','IFNULL(s2.likes_count, s1.likes_count) as effectiveLikesCount');
$qb=$this->_em->createQueryBuilder()
->select($fields)
->from('WallBundle:Status','s1')
->addSelect('u')
->where('s1.user = :user')
->andWhere('s1.admin_status = false')
->andWhere('s1.typ_statusu != :group')
->setParameter('user', $user)
->setParameter('group', 'group')
->leftJoin('WallBundle:Status','s2', 'WITH', 's2.id=s1.shared_from_id')
->innerJoin('s1.user', 'u')
->orderBy('s1.time', 'DESC')
->setMaxResults(15);
var_dump($query=$qb->getQuery()->getResult());die();
This error is
[Syntax Error] line 0, col 7: Error: Expected known function, got 'IFNULL'
Use COALESCE instead of IFNULL like this
$fields = array('COALESCE(s2.id,s1.id) AS effectiveID','COALESCE(s2.status, s1.status) AS effectiveStatus', 'COALESCE(s2.user_id, s1.user_id) as effectiveUser','COALESCE(s2.likes_count, s1.likes_count) as effectiveLikesCount');
COALESCE return the first value not null in the list, so if A is null and B not null, then COALESCE(A,B) will return B.
There is a Doctrine extension that adds this among others.
This is the DQL file from IFNULL.
https://github.com/beberlei/DoctrineExtensions/blob/master/src/Query/Mysql/IfNull.php
This chapter explains how you use them.
http://symfony.com/doc/2.0/cookbook/doctrine/custom_dql_functions.html

Resources