У меня есть объект запроса cakephp 3.x с двумя вложенными ассоциациями, Organizations.Positions.Skills
, который устанавливается в переменную вида, $Organizations
. Я пытаюсь сортировать полученный массив верхнего уровня запроса по столбцу в первой вложенной ассоциации. То есть, я хочу сортировать $Organizations
по столбцу в Positions
, в частности Positions.Ended
).
public function index() { $this->loadModel('Organizations'); $this->set('Organizations', $this->Organizations->find('all') ->contain([ //eager loading 'Positions.Skills' ]) ); }
-public function index() { $this->loadModel('Organizations'); $this->set('Organizations', $this->Organizations->find('all') ->contain([ //eager loading 'Positions.Skills' ]) ); }
Организации имеют много позиций
Позиции имеют много навыков
Согласно поваренной книге find () имеет опцию заказа: find()->order(['field']);
или find('all', ['order'=>'field DESC']);
Однако это относится только к полям в таблице find (). В этом случае организации. Например, так оно обычно используется.
//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']) );
-//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']) );
но, пытаясь использовать его для вложенных ассоциаций, не работает: $ this-> set («Организации», это-> Организации-> найти (…) -> содержать (…) -> order ([' Организации.Positions.ended DESC ']));
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Organizations.Positions.ended' in 'order clause'
и изменение его для ссылки на поле, которое будет вложенным, также не работает:
//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'
-//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'
В обоих случаях ошибка sql создается, когда cakephp выполняет инструкцию PDO, сгенерированную запросом.
Аналогичным образом, согласно поваренной книге , нетерпевая загрузка / ассоциации имеет опцию «сортировка»: $ this-> loadModel («Организации»);
$this->set('Organizations', $this->Organizations->find('all') ->contain([ //eager loading 'Positions.Skills', 'Positions' => [ 'sort' => ['Positions.ended'=>'DESC'] ] ]) );
-$this->set('Organizations', $this->Organizations->find('all') ->contain([ //eager loading 'Positions.Skills', 'Positions' => [ 'sort' => ['Positions.ended'=>'DESC'] ] ]) );
Но это только сортирует вложенную ассоциацию. В частности, она сортирует ассоциации, которые являются вложенными. Он не сортирует весь полученный набор вложенной ассоциацией (ergo, многомерный вид).
Например:
Организация, Florida Tech (org id 1), имеет две позиции:
И Организация, подходящая сегодня (org id 2), имеет две позиции:
Вышеприведенный код и данные приводят к созданию следующего массива при оценке запроса:
Organizations => [ 0 => [ 'organization => 'Plus Marketing Group', positions => [ 0 => [ 'position' => 'Energy Sales', 'ended => '2016' ] ] ], 1 => [ 'organization => 'Florida Tech', positions => [ 0 => [ 'position' => 'Research Assistant', 'ended => '2014' ], 1 => [ 'position' => 'Lab Technician', 'ended => '2012' ] ] ], 2 => [ 'organization' => 'Fit In Today', positions => [ 0 => [ 'position' => 'Full Stack Web Developer', 'ended => '2015' ], 1 => [ 'position' => 'market Data Analyst', 'ended => '2013' ] ] ] ]
Аналогичным образом в моих исследованиях появились следующие вопросы, связанные с stackoverflow:
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
Кроме того, я знаю, что PHP имеет свои собственные функции сортировки, такие как sort()
и multisort()
; но они могут быть вызваны только после того, как запрос был оценен (через foreach). Кроме того, вызывается $ organization-> toArray (), а затем используется multisort; но это должно было бы быть сделано в представлении, нарушило бы соглашение MVC о разграничении проблем (данные и запросы обрабатываются контроллером и моделью, а не представлением!), и были бы весьма неэффективными, поскольку они будут вызваны пока страница загружается.
Или, проще говоря, как мне упорядочить / отсортировать запрос для создания следующего массива при оценке:
Organizations => [ 0 => [ 'organization => 'Plus Marketing Group', positions => [ 0 => [ 'position' => 'Energy Sales', 'ended => '2016' ] ] ], 0 => [ 'organization' => 'Fit In Today', 'positions' => [ 0 => [ 'position' => 'Full Stack Web Developer', 'ended => '2015' ], 1 => [ 'position' => 'market Data Analyst', 'ended => '2013' ] ] ], 1 => [ 'organization => 'Florida Tech', 'positions' => [ 0 => [ 'position' => 'Research Assistant', 'ended => '2014' ], 1 => [ 'position' => 'Lab Technician', 'ended => '2012' ] ] ] ]
Я создаю [портфолио] [7] для себя с помощью cakephp 3.2, чтобы продемонстрировать свои навыки работы с веб-разработчиками и помочь в поисках квест-карьеры. Для моей страницы резюме я собираю огромное количество данных с вложенными аккордеонами, чтобы имитировать вербовку в стиле резюме, которую ожидали бы увидеть на фактическом резюме. В результате мой взгляд делает следующее:
Только hasOne
и belongsTo
ассоциациям, которые извлекаются через соединение по основному запросу. hasMany
ассоциации извлекаются в отдельных запросах, следовательно, ошибки при попытке ссылаться на поле Positions
.
То, что вы хотите, должно быть довольно легко решить, на уровне SQL, а также на уровне PHP.
На уровне SQL вы можете присоединиться к Positions
и упорядочить по вычисленному столбцу max(Positions.ended)
, например
$this->Organizations ->find('all') ->select(['max_ended' => $this->Organizations->query()->func()->max('Positions.ended')]) ->select($this->Organizations) ->contain([ 'Positions.Skills', 'Positions' => [ 'sort' => ['Positions.ended' => 'DESC'] ] ]) ->leftJoinWith('Positions') ->order([ 'max_ended' => 'DESC' ]) ->group('Organizations.id');
И это все, что должно дать вам результаты, которые вы хотите. Однако он может быть довольно медленным на огромных столах. Запрос будет выглядеть примерно так:
SELECT MAX(Positions.ended) AS max_ended, ... FROM organizations Organizations LEFT JOIN positions Positions ON Organizations.id = ( Positions.organization_id ) GROUP BY Organizations.id ORDER BY max_ended DESC
На уровне PHP это также довольно легко решить с помощью коллекций (обратите внимание, что запросы представляют собой коллекции – вроде), что-то вроде
$Organizations->map(function ($organization) { $organization['positions'] = collection($organization['positions'])->sortBy('ended')->toList(); return $organization; }); $sorted = $sorted->sortBy(function ($organization) { return isset($organization['positions'][0]['ended']) ? $organization['positions'][0]['ended'] : 0; });
Это также может быть реализовано в форматировании результатов, поэтому все происходит на уровне контроллера или модели, если вы настаиваете.
$query->formatResults(function ($results) { /* @var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */ $results = $results->map(function ($row) { $row['positions'] = collection($row['positions'])->sortBy('ended')->toList(); return $row; }); return $results->sortBy(function ($row) { return isset($row['positions'][0]['ended']) ? $row['positions'][0]['ended'] : 0; }); });
Это сработало для меня:
$this->Organizations->find('all') ->contain(['Positions' => ['sort' => ['Positions.ended' => 'DESC']]]) ->contain('Postions.Skills');
-$this->Organizations->find('all') ->contain(['Positions' => ['sort' => ['Positions.ended' => 'DESC']]]) ->contain('Postions.Skills');