Сортировка результатов запроса по вложенной ассоциации в cakephp 3

Проблема:

У меня есть объект запроса 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), имеет две позиции:

  1. Лаборант. Завершено в 2012 году
  2. Научный сотрудник. Закончено 2014 год

И Организация, подходящая сегодня (org id 2), имеет две позиции:

  1. Аналитик рыночных данных завершил 2013 год
  2. Полные веб-разработчики стека завершены в 2015 году

Вышеприведенный код и данные приводят к созданию следующего массива при оценке запроса:

 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 о разграничении проблем (данные и запросы обрабатываются контроллером и моделью, а не представлением!), и были бы весьма неэффективными, поскольку они будут вызваны пока страница загружается.

Как тогда, я сортирую запрос cakephp своими вложенными ассоциациями?

Или, проще говоря, как мне упорядочить / отсортировать запрос для создания следующего массива при оценке:

 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, чтобы продемонстрировать свои навыки работы с веб-разработчиками и помочь в поисках квест-карьеры. Для моей страницы резюме я собираю огромное количество данных с вложенными аккордеонами, чтобы имитировать вербовку в стиле резюме, которую ожидали бы увидеть на фактическом резюме. В результате мой взгляд делает следующее:

  1. Цитирование через переменную представления верхнего уровня (Организации)
    1. Оказание подробностей организации
    2. Пересечение позиций этой организации (все еще внутри 1)
    3. отобразить детали позиции
    4. преодолевать соответствующие навыки позиции
      1. визуализируйте каждое умение w / соответствующую ссылку для фильтрации с помощью этого навыка.

Только hasOne и belongsTo ассоциациям, которые извлекаются через соединение по основному запросу. hasMany ассоциации извлекаются в отдельных запросах, следовательно, ошибки при попытке ссылаться на поле Positions .

То, что вы хотите, должно быть довольно легко решить, на уровне SQL, а также на уровне PHP.

Уровень SQL

На уровне 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

На уровне 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; }); }); 

Видеть

  • Cookbook> Доступ к базе данных и ORM> Query Builder> Использование SQL-функций
  • Cookbook> Доступ к базе данных и ORM> Query Builder> Использование leftJoinWith
  • Cookbook> Доступ к базе данных и ORM> Query Builder> Запросы – это объекты коллекции
  • Cookbook> Доступ к базе данных и ORM> Query Builder> Добавление расчетных полей
  • Поваренная книга> Коллекции> Сортировка

Это сработало для меня:

 $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');