How to sort a collection in Magento? - php

I'm trying to sort a collection by attribute_id. I thought it would be easy, but I think I'm not using it correctly:
$attributes = Mage::getResourceModel('eav/entity_attribute_collection')
->setOrder('attribute_id');
echo $attributes->getSelect();
Result:
SELECT `main_table`.* FROM `eav_attribute` AS `main_table`
Why isn't there any order by?

You're actually doing it the right way. However, since Magento uses EAV, it needs to apply tricks to help performance.
One of these tricks is the timing used to build the eventual SQL string. Usually it's lazily loaded at the last minute and it's not until you actually indicate you want to access a collection's data, that you can see the full SQL used to produce the collection. For example running your code, but prompting magento to actually construct and load the collection, produces the expected output.
$attributes = Mage::getResourceModel('eav/entity_attribute_collection')
->setOrder('attribute_id');
$attributes->count(); // forces the collection to load
echo $attributes->getSelect()->assemble();
This results in the SQL:
SELECT `main_table`.* FROM `eav_attribute` AS `main_table` ORDER BY attribute_id DESC
So you were on the right path, just Magento was doing its level best to confuse you. It's very good at that.

Use this instead of $attributes->getSelect();:
$attributes->getSelect()->order('main_table.attribute_id ASC');
Don't ask why.

Related

What is the dafault ordering in Laravel Eloquent Model::all() function?

In the Laravel Eloquent Model::all() function, what is the default ordering for the returned query? I ask this because I'm pretty sure it's in ascending order by primary key which defaults do 'id' when you make the model through
php artisan make:model Model -m
However, when I call it like this:
return $users = User::all();
I get the following results in the browser:
The results seem to be in no particular order by any of the attributes.
I am fully aware I can order them by id by doing
return $users = User::orderBy('id', 'asc')->get();
But just a few days ago they were being ordered automatically. What gives?
The default sort order for laravel is simply nothing. It does not apply a default "ORDER BY" clause, which means that it follows PostgreSQL rules for unordered results. From some similar answers here and here:
Do not depend on order when ORDER BY is missing.
Always specify ORDER BY if you want a particular order -- in some situations the engine can eliminate the ORDER BY because of how it
does some other step.
GROUP BY forces ORDER BY. (This is a violation of the standard. It can be avoided by using ORDER BY NULL.)
SELECT * FROM tbl -- this will do a "table scan". If the table has
never had any DELETEs/REPLACEs/UPDATEs, the records will happen to be
in the insertion order, hence what you observed.
If you had done the same statement with an InnoDB table, they would
have been delivered in PRIMARY KEY order, not INSERT order. Again,
this is an artifact of the underlying implementation, not something to
depend on.
With a great agreement with FrankerZ's answer, I would like to add, whenever you wonder what query laravel is structuring under the ORM, DB listener will be quickest option to use :
<?php
\DB::listen(function($sql) {
dump($sql);
});
W.r.t. this question it would give select * from users when you do User::all().
Note: Check listen function as per the laravel version you are using, the parameter count are different in older and newer versions.
Reference : Documentation Link

Laravel: Order by specific ID

I've been looking like crazy but I can't seem to find a way of achieving something similar to this with Laravel's Eloquent:
select id,name
from friends
order by id=5 desc
Example taken from the following link: mysql SQL: specific item to be first and then to sort the rest of the items
I was hoping a simple Group::orderBy('id', $id, 'DESC')->get() would work, but no such luck.
I've also looked into using DB instead but the orderBy method for that class takes in exactly the same arguments and doesn't have an option for specific IDs. Are there any alternatives?
Thank you very much for all the help!
not sure , but may be you could try using orderByRaw(), like,
$id = 5;
Group::orderByRaw(DB::raw("FIELD(id, $id)"))
->get();

Paginator with "$fetchJoinCollection = true" won't respect "ORDER BY" in doctrine DQL?

Having a strange issue. We are using MariaDB 5.5 and doctrine/orm 2.3.3, and trying to use the Doctrine Paginator with DQL.
http://docs.doctrine-project.org/en/latest/tutorials/pagination.html
The DQL has an ORDER BY clause [see below for an illustration example]. However, the result is not sorted at all for a given page size. And, if we increase the page size to cover the entire result set, the sorting becomes correct.
$dql = "SELECT a, b FROM EntityA a JOIN a.propertyB b ORDER BY a.createdOn DESC";
$query = $this->em->createQuery($dql)
->setMaxResults($pageSize)
->setFirstResult($offset);
$paginator = new Paginator($query, $fetchJoinCollection=true);
....
I dumped the sql and manually ran it. The sql also gave the correct sorting. So something is causing the sorting issue inside Doctrine's Paginator class.
When I set $fetchJoinCollection=false and passed it to the Paginator constructor, the sorting became correct for any given $pageSize!
Read Doctrine source code [Doctrine/ORM/Tools/Pagination/Paginator.php]. With $fetchJoinCollection=true, doctrine uses a WhereInWalker to get the final result, which doesn't respect the ORDER By clause in the DQL, because the IN() clause doesn't generate the result in the same order as the ids inside the IN() clause.
A sorting solution for the IN() clause can be found in Ordering by the order of values in a SQL IN() clause. But I can't find Doctrine using that.
Anyone with Doctrine internal knowledge would shed some light?! Thanks!
Found out that people have taken care of this issue already.
http://www.doctrine-project.org/jira/browse/DDC-2593

Doctrine 2.1: Getting and assigning COUNT(t.id) from a subquery?

I have two entities in Doctrine 2.1: Category and Site each category has many sites and each site has a parent category.
I would like to make a single update query (in DQL) which will update a field called count of the Category entity with the number of related sites.
So in SQL I would do something like this:
UPDATE categories c SET c.count = (SELECT COUNT(s.id) FROM sites s WHERE s.category_id = c.id);
This would work beautifuly, in DQL it might something like this:
UPDATE PackageNameBundle:Category c SET c.count = (SELECT COUNT(s.id) FROM PackageNameBundle:Site s WHERE s.category = c)
Such attempt raises [Syntax Error] line 0, col 61: Error: Expected Literal, got 'SELECT'.
Subqueries DO work in DQL, but the problem here (as far as I see it) is that Doctrine cannot assign the returned value from the subquery, to the c.count. This is understandable since I might fetch more than 1 field in the subquery and even more than one row. It magicaly works in MySQL since it sees one row, one field and for convenience returns a single integer value. Doctrine on the other hand has to be object oriented and has to work with different engines where such convertions might not be supported.
Finally, my question is:
What is the best way to do this in Doctrine, should I go with Native SQL or it can be done with DQL and how?
Thanks in advance!
EDIT: I just found this quote in the DQL Docs:
References to related entities are only possible in the WHERE clause and using sub-selects.
So, I guess assigning anything but a scalar value is impossible?
The main question remains though..
You can use native sql queries in Doctrine also, for that kind of specific queries. DQL is powerful in its own way, but it's also limited due to performance constraints. Using native sql queries and mapping the results will achieve the same thing, and there is no disadvantage in doing that.
The documentation explains it in detail.

Behavior Difference between find('count') and find('all') cakePHP

I am having an odd problem in CakePHP where
$this->something->find('count');
works perfectly, yet
$this->something->find('all');
returns nothing (not even an empty array, any errors, or anything).
edit: turns out I am getting an sql error: "SQL Error: 1054: Unknown column" - for a column that does indeed exist. (users.display_name in the sql query below):
SELECT item.id, item.name, item.description, item.user_id, users.display_name FROM item LEFT JOIN users ON (item.user_id = users.id);
I also tried using findAllBy as well as paginate (paginate is actually what I am trying to do - although from what I've gathered, paginate and find('all') are pretty similar in functionality).
The odd thing is that find('all') works everywhere else - it's just in this specific controller that it is acting odd. I am not getting any errors, simply an empty result.
I'm thinking that I may be overlooking something quite simple, but any help is appreciated. Thanks!
So, as per our discussion, the problem you're having is with the virtual fields. Have a look at the documentation, more specifically at the virtual fields and model aliases and limitation of virtualFields sections.
From your description above, it looks like you have a join specified your virtual field which would be causing the error you're seeing because it'll add the JOIN before the FROM. If you insist on using the virtual field, I'd suggest you rewrite it to use a subquery. Make sure your subquery only returns 1 column.
Example: (http://web-development-blog.co.uk/2011/03/08/cakephp-virtual-field-count-another-modeltable/)
public $virtualFields = array(
'count' => 'SELECT COUNT(*) FROM stacks Stack'
);
Alternatively, you can use the Model::beforeFind to bind the necessary models (if necessary) and change the query parameters.
If you can't figure it out, please post your model and I'll help you.
The specific problem you're having with the difference in behaviour is that find('count') will run a basic COUNT(*) query on your database to determine the number of rows.
find('all'), however, runs a different query, and if the SQL you've provided is what it's trying to use, it's invalid:
SELECT item.id, item.name, item.description, item.user_id, users.display_name LEFT JOIN users ON (item.user_id = users.id);
There's no FROM declaration (SELECT from what, exactly?), and if you've not customised your Item or User or App models, (setting $useTable = false maybe?) you're dealing with an unusual error.
One thing to look out for, if these models work fine in other controllers, is any alteration to the properties of each model in each controller. They won't act differently on a per-controller basis unless you have code in each controller that tells it to.

Categories