I want to use symfony's query builder and add a where to the last item in an array collection
$query = $em->getRepository('RlBookingsBundle:Booking')->createQueryBuilder('b')
->select('b, v, c, ca, q')
->leftJoin('b.vehicle', 'v')
->leftJoin('b.customer', 'c')
->leftJoin('c.address', 'ca')
->leftJoin('b.quote', 'q')
->leftJoin('b.history', 'h') //This is an array collection
->orderBy('b.edited', 'DESC')
;
I want to use only the latest value from history as it is a log but only the most recent entry is valid
->where('h.status IN (:status)')
->setParameter('status', [7]);
Will return all results with h.status = 7 but I would like it to only query the most recent result. Is there anyway to do this?
I tried a groupby on the history field but this seems to groupby with data from the first entry, even if I add an orderby to it.
If the results you get are already ok, but you only want the first, you could just use
...
->setMaxResults(1)
...
If you want to order by history ID desc, you may want to add another orderBy clause before the existing one
...
->orderBy('h.id', 'DESC')
->orderBy('b.edited', 'DESC')
...
If it's more complex than that, I strongly suggest you perform a separate query to get the desired record(s) from history, and THEN use it as a filter, instead of the leftJoin.
Related
I have Post entity. Post entity have one Category, many Tags and many Comments.
How to get 'most commented posts' ordered by most commented?
When I am doing this:
$query = $this->createQueryBuilder('post')
->addSelect('COUNT(comments.id) AS HIDDEN comments_num')
->innerJoin('post.comments', 'comments')
->groupBy('post.id')
->orderBy('comments_num', 'DESC')
->addOrderBy('post.created', 'DESC')
->getQuery()
;
it is working but Doctrine makes so many queries, but the problem is the more comments the more queries *times something... It cant be that way. On the page with this query results I have 71 queries and the number of them grows with commented posts number.
When I am doing the same but adding left joining and selecting category, tags and comments there... queries number is low and independent from comments number but when I pass that query to Paginator it gets kind of empty pages after few pages with results...
$query = $this->createQueryBuilder('post')
->addSelect('category, tags, comments')
->addSelect('COUNT(c) AS HIDDEN comments_num')
->innerJoin('post.comments', 'c')
->innerJoin('post.comments', 'comments')
->leftJoin('post.category', 'category')
->leftJoin('post.tags', 'tags')
->groupBy('post.id')
->addGroupBy('tags.id')
->addGroupBy('comments.id')
->orderBy('comments_num', 'DESC')
->addOrderBy('post.created', 'DESC')
->getQuery()
;
query will be passed to Paginator so it must be build in DQL or using QueryBuilder.
edit:
I think there is a bug in Paginator, because... When I dump that second query results I am getting good results in array collection of 12. But when I pass that query to Paginator and then call Paginator.count() I get results: 55 :D when there are 44 posts in test database :)
In The first moment Write your query just with SQL, after do you can execute pure SQL in doctrine.
After you try convert your SQL for DQL
Let's assume an Event table, I want to get the first latest event which will be held after current_date and I ordered it by event_start_date.
But it is querying the first orderBy and take then running condition over it.
EVENT::where(Event::START_DATE, '>', 'CURDATE()')->orderBy(Event::START_DATE)->take($nThEvent)->get()->toArray();
Does this work for you
EVENT::where(Event::START_DATE, '>', 'CURDATE()')->latest('START_DATE')->first();
You should use whereRaw() if you have to call sql functions, e.g.:
EVENT::whereRaw(Event::START_DATE.' > CURDATE()')
->latest(Event::START_DATE)
->take($nThEvent)
->get()->toArray();
BTW you can always debug your query with toSql(), it returns a string with the generated SQL:
EVENT::whereRaw(Event::START_DATE.' > CURDATE()')
->latest(Event::START_DATE)
->take($nThEvent)
->toSql();
Please note that latest(Event::START_DATE) is equal to write orderBy(Event::START_DATE, 'desc').
Update: if you want to get only one record you should use first() not get() that returns instead a collection, e.g.:
EVENT::whereRaw(Event::START_DATE.' > CURDATE()')
->latest(Event::START_DATE)
->first()
I have following code in my Repository
// ProductBundle/Repository/ProductRepository.php
$qb->where($qb->expr()->eq('afp.id', 15));
$qb->andWhere($qb->expr()->eq('afp.id', 14));
return $qb
->select('a', 'afp')
->leftJoin('a.productFields', 'afp')
->getQuery()
->getResult();
But I always get null return, but I want to get products which have both productFields (so orWhere is not good).
You want to use MEMBER OF instead of comparing id. Otherwise, you're looking for a record that has two different id values, which of course isn't possible.
This will do what you want:
$qb->where($qb->expr()->isMemberOf(15, 'a.productFields'));
$qb->andWhere($qb->expr()->isMemberOf(14, 'a.productFields'));
Try something like this (Symfony 5):
$qb->andWhere(':c MEMBER OF a.productFields');
$qb->setParameter('c', 15);
I need to order data by two columns (when the rows have different values for column number 1, order by it; otherwise, order by column number 2)
I'm using a QueryBuilder to create the query.
If I call the orderBy method a second time, it replaces any previously specified orderings.
I can pass two columns as the first parameter:
->orderBy('r.firstColumn, r.secondColumn', 'DESC');
But I cannot pass two ordering directions for the second parameter, so when I execute this query the first column is ordered in an ascending direction and the second one, descending. I would like to use descending for both of them.
Is there a way to do this using QueryBuilder? Do I need to use DQL?
You have to add the order direction right after the column name:
$qb->orderBy('column1 ASC, column2 DESC');
As you have noted, multiple calls to orderBy do not stack, but you can make multiple calls to addOrderBy:
$qb->addOrderBy('column1', 'ASC')
->addOrderBy('column2', 'DESC');
In Doctrine 2.x you can't pass multiple order by using doctrine 'orderBy' or 'addOrderBy' as above examples. Because, it automatically adds the 'ASC' at the end of the last column name when you left the second parameter blank, such as in the 'orderBy' function.
For an example ->orderBy('a.fist_name ASC, a.last_name ASC') will output SQL something like this 'ORDER BY first_name ASC, last_name ASC ASC'. So this is SQL syntax error. Simply because default of the orderBy or addOrderBy is 'ASC'.
To add multiple order by's you need to use 'add' function. And it will be like this.
->add('orderBy','first_name ASC, last_name ASC'). This will give you the correctly formatted SQL.
More info on add() function. https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html#low-level-api
Hope this helps. Cheers!
you can use ->addOrderBy($sort, $order)
Add:Doctrine Querybuilder btw. often uses "special" modifications of the normal methods, see select-addSelect, where-andWhere-orWhere, groupBy-addgroupBy...
You can use orderBy() followed by an addOrderBy() - nesting several orderBy()'s is not possible, but nesting several addOrderBy()'s also works after the initial orderBy().
Example:
$this->createQueryBuilder('entity')
->orderBy('entity.addDate', 'DESC')
->addOrderBy('entity.id', 'DESC')
The orderBy method requires either two strings or an Expr\OrderBy object. If you want to add multiple order declarations, the correct thing is to use addOrderBy method, or instantiate an OrderBy object and populate it accordingly:
# Inside a Repository method:
$myResults = $this->createQueryBuilder('a')
->addOrderBy('a.column1', 'ASC')
->addOrderBy('a.column2', 'ASC')
->addOrderBy('a.column3', 'DESC')
;
# Or, using a OrderBy object:
$orderBy = new OrderBy('a.column1', 'ASC');
$orderBy->add('a.column2', 'ASC');
$orderBy->add('a.column3', 'DESC');
$myResults = $this->createQueryBuilder('a')
->orderBy($orderBy)
;
The comment for orderBy source code notes: Keys are field and values are the order, being either ASC or DESC.. So you can do orderBy->(['field' => Criteria::ASC]).
I have written a query with codeigniters database library, it looks like this,
public function getCandidateCredits($candidate_id)
{
$this->db->select('*')
->from('credits')
->join('candidate_has_credits', 'candidate_has_credits.credits_credit_id = credits.credit_id', 'left')
->where('credits.candidates_candidate_id', (int)$candidate_id)
->order_by('candidate_has_credits.date_created', 'DESC')
->order_by('credits.credit_position', 'DESC');
$query = $this->db->get();
return $query->result_array();
}
What this query is meant to do is, get all a members credits, and the order them firstly by date (newest first), then by credit position (highest number first).
I am having a couple of problems though, the credits should be ordered by date, but only if there is no credit position, if there is a credit position (0 - 999) then that should take precendence where ordering the credits, if there is a date and a credit position then the credits should be ordered by credit position and then date.
Is this possible? I feel like I am miles away from where I need, the results I return seem to be return no obvious ordering. Not sure if it makes a difference but the date_created field is a DATETIME.
You are correctly using a left join, but have put the order by clauses in the wrong order, so first of all, flip them:
->order_by('credits.credit_position', 'DESC')
->order_by('candidate_has_credits.date_created', 'DESC')
This should do it, except that now those candidate_has_credit rows that do not have corresponding credits records (so the credit_position is null) will be in the end, and I assume you want those on top.
There is a small trick to push null values to top when using DESC sorting providing you know the maximum value available in that field:
->order_by("ifnull('credits.credit_position',1000) desc,candidate_has_credits.date_created desc")
Note that I am using the form of order_by method which contains only one parameter, that one should not escape the function and is marginally faster.
Check the ifnull documentation to see how it works.
Here's a shortened version:
public function getCandidateCredits($candidate_id)
{
return $this->db->from('credits')
->join('candidate_has_credits', 'candidate_has_credits.credits_credit_id = credits.credit_id', 'left')
->where('credits.candidates_candidate_id', (int)$candidate_id)
->order_by('credits.credit_position', 'DESC')
->order_by('candidate_has_credits.date_created', 'DESC')
->get()->result_array();
}