The Problem:
I have a cakephp 3.x query object with two nested associations, Organizations.Positions.Skills
, that is being set to a view variable, $Organizations
. I'm trying to sort the query's resulting top level array by a column in the first nested association. That is, I want to sort $Organizations
by a column in Positions
, specifically Positions.Ended
).
public function index()
{
$this->loadModel('Organizations');
$this->set('Organizations',
$this->Organizations->find('all')
->contain([ //eager loading
'Positions.Skills'
])
);
}
Model Info
Organizations has many Positions
Positions has many Skills
Research I've Done
order option
According to the cookbook find() has an order option: find()->order(['field']);
or find('all', ['order'=>'field DESC']);
However, this only applies to fields in the table find() is being called upon. In this case, Organizations. For example, this is how it's typically used.
//This works.
//Sorts Organizations by the name of the organization in DESC.
$this->loadModel('Organizations');
$this->set('Organizations',
$this->Organizations->find('all')
->contain([ //eager loading
'Positions.Skills'
])
->order(['organization DESC'])
);
but, trying to use it for nested associations doesn't work:
$this->set('Organizations',
this->Organizations->find(...)
->contain(...)
->order(['Organizations.Positions.ended DESC'])
);
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Organizations.Positions.ended' in 'order clause'
and altering it to refer to the field that'll be nested doesn't work either:
//also doesn't work.
$this->set('Organizations',
$this->Organizations->find(...)
->contain(...)
->order([Positions.ended DESC])
);
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Positions.ended' in 'order clause'
In both cases, the sql error is created when cakephp executes the PDO statement generated by the query.
sort option
Similarly, according to the cookbook, eager loading / associations has the 'sort' option: $this->loadModel('Organizations');
$this->set('Organizations',
$this->Organizations->find('all')
->contain([ //eager loading
'Positions.Skills',
'Positions' => [
'sort' => ['Positions.ended'=>'DESC']
]
])
);
But, this only sorts the nested association.Specifically, it sorts the associations that are nested. It does not sort the entire resulting set by the nested association (ergo, a multidimensional sort).
For example:
The Organization, Organization C (org id 1), has two positions:
- Position 5. Ended 2012
- Position 4. Ended 2014
And the Organization, Organization B (org id 2), has two positions:
- Position 3 Ended 2013
- Position 2 Ended 2015
The above code and data results in the following array being generated when the query is evaluated:
Organizations => [
0 => [
'organization' => 'Organization A',
positions => [
0 => [
'position' => 'Position 1',
'ended' => '2016'
]
]
],
1 => [
'organization' => 'Organization C',
'positions' => [
0 => [
'position' => 'Position 4',
'ended' => '2014'
],
1 => [
'position' => 'Position 5',
'ended' => '2012'
]
]
],
2 => [
'organization' => 'Organization B',
'positions' => [
0 => [
'position' => 'Position 2',
'ended' => '2015'
],
1 => [
'position' => 'Position 3',
'ended' => '2013'
]
]
]
]
other research
Likewise the following stackoverflow questions came up in my research:
http://stackoverflow.com/questions/26859700/cakephp-order-not-working
http://stackoverflow.com/questions/17670986/order-by-doesnt-work-with-custom-model-find-while-paginating
http://stackoverflow.com/questions/18958410/cakephp-paginate-and-sort-2nd-level-association
http://stackoverflow.com/questions/34705257/cakephp-paginate-and-sort-hasmany-association
Furthermore, I do know that PHP has its own sorting functions like sort()
and multisort()
; but, those can only be called once the query has been evaluated (by foreach). Alternatively, there's calling $organization->toArray() then using multisort; but, this would have to be done in the view, would break the MVC convention of separations of concerns (data and queries are manipulated by the controller and model, not the view!), and would be quite inefficient as it'll be called while the page is loading.
How then, do I sort a cakephp query by its nested associations?
Or, put more simply, how do I order/sort the query to produce the following array upon evaluation:
Organizations => [
0 => [
'organization' => 'Organization A',
'positions' => [
0 => [
'position' => 'Position 1',
'ended' => '2016'
]
]
],
0 => [
'organization' => 'Organization B',
'positions' => [
0 => [
'position' => 'Position 2',
'ended' => '2015'
],
1 => [
'position' => 'Position 3',
'ended' => '2013'
]
]
],
1 => [
'organization => 'Organization C',
'positions' => [
0 => [
'position' => 'Position 4',
'ended' => '2014'
],
1 => [
'position' => 'Position 5',
'ended' => '2012'
]
]
]
]
Background & Context:
I'm building a [portfolio website][7] for myself with cakephp 3.2 to showcase my web dev skills and assist in my quest for a dev career. For my resume page, I'm organizing the massive amount of data with nested accordions to mimic the resume style recruiters would expect to see on an actual resume. As a result, my view does the following:
- Looping through the top level view variable (Organizations)
- Rendering the organization details
- Looping through that organization's positions (still inside 1)
- render the position details
- loop through the position's relevant skills
- render each skill w/ the appropriate link to filter by that skill.
List item