Добрый день, ни как не могу допереть с выборкой и сравнением.
Есть объект Этап, у него может быть неограниченное количество работ, у каждой работы есть дата начала и дата окончания работы. Необходимо Этапу присваивать значения начала самой "ранней работы" и значения окончания самой "поздней" работы.
Т.е. делаю выборку из БД:
<?php $query = \Drupal::entityQuery('node');
$query->condition('status', 1);
$query->condition('field_project_id', $prid);
$or = $query->orConditionGroup();
$or->condition('type', 'project_stage');
$or->condition('type', 'project_job');
$query->condition($or);
$query->sort('nid' , 'ASC');
$entity_ids = $query->execute();
if ($entity_ids) {
$nodes = \Drupal\node\Entity\Node::loadMultiple($entity_ids);
foreach ($nodes as $node) {
$id = $node->id();
$title = $node->getTitle();
$type = $node->getType();
if ($type == 'project_job') {
$parentId = $node->get('field_project_stage_id')->getValue()[0]['value'];
if (!$node->get('field_job_date_start')->isEmpty()) {
$start = $node->get('field_job_date_start')->date->getTimestamp()*1000;
}
if !$node->get('field_job_date_end')->isEmpty()) {
$end = $node->get('field_job_date_end')->date->getTimestamp()*1000;
} else {
$end = $start + 23*60*60*1000;
}
//Получил дату начала и конца для определенной работы
$time[$parentId] = array('start'=>$start, 'end'=>$end);//добавил в общий массив с ключом ID родителя
}
}
}?>
А дальше не знаю как в этом массиве $time, сравнить все даты (числа timestamp) с одинаковыми ключами и потом еще и добавить эту информацию к родителю. Может не в ту сторону копаю.
Комментарии
Имхо, подобные "полотенца" обычно никто не рассматривает всерьёз, поскольку это предполагает детальное вникание в какие-то личные "велосипеды" автора.
Мне, например, с ходу показалось странным, что запрашиваются два разных типа сущностей, хотя обработка происходит только по одному из них (
'project_job'
).Кроме того, есть подозрение (поскольку весь код не виден и вообще назначение полей непонятно), что
$parentId
не будет являться уникальным ключом массива, стало быть элемент массива$time[$parentId] = array('start'=>$start, 'end'=>$end);
может тупо перезаписываться с новыми значениями в случае совпадения'field_project_stage_id'
. И тогда в этом случае:сравнивать будет просто нечего и не с чем.
В общем случае можно порекомендовать обычную сортировку массива чтобы выбрать большее/меньшее значения.
<?php
//На коленке написано и только чтобы понять в какую сторону смотреть
foreach ($nodes as $node) {
$time[$start][] = array('start'=>$start, 'end'=>$end, 'id'=>$id);
}
//Сортируем массив по возрастианию$time_start = ksort($time);
//Сортируем массив по убыванию
$time_end = asort($time);
$start_parent = current($time_start)['start'];
$end = end($time_end)['end'];
?>
Всё одним SQL-запросом делается, типа
FROM project_job
GROUP BY field_project_stage_id;
Я, естественно упростил физическую структуру данных, но идея должна быть ясна.
Эту же выборку без труда можно накликать мышкой во views и просто считать нужные результаты на лету, ничего лишнего не сохраняя.
Тссс! Спрячьте быстрее, пока не набежали разъярённые приверженцы Drupal-way.
Даже интересно было бы послушать набежавших
Неужели друпал-вей - это, презрев возможности СУБД (для которых кстати Друпал представляет шикарную обертку и на уровне API, и на уровне админки Views), вытащить всю базу в пхп-массив и потом ковыряться в нем?
Да хотя бы отсюда: https://drupal.ru/comment/717949#comment-717949
Меня опять неправильно поняли
Я вовсе не предлагаю лезть в БД напрямую - я просто описал в самой простой и наглядной форме правильный запрос, который можно (и нужно) реализовать либо мышкой во вьюс (если нет реальной необходимости хранить рассчитанные данные в Этапе) либо, если всё-таки их зачем-то нужно хранить, через API. Мой запрос возвращает готовые рассчитанные данные, и с массивом результатов не нужно проводить вообще никаких манипуляций - просто бери готовые значения и перекладывай куда надо.
Тут уже не первый раз задают вопрос как на PHP решить задачу, с которой явно лучше справится СУБД.
"Напрямую" и я не имел в виду. Т.е. кастомные запросы через Database Service (который есть API) тоже могут быть признаны аморальными - поскольку уже не EntityQuery
То есть, это всё как раз-таки понятно (в том числе и про задачи, с которыми лучше справляются SQL-запросы), но как, например, будет реализовано вышеуказанное решение через Drupal Database Connection? Хотя бы просто, чтобы показать ТС, как решается вопрос средствами API Drupal 8.
$result = $query
->condition('type', 'project_job')
->groupBy('field_project_stage_id')
->aggregate('field_job_date_start', 'MIN')
->aggregate('field_job_date_end', 'MAX')
->execute();
вернёт массив с количеством строк, равным количеству этапов, где для каждого этапа будут лежать готовые даты его начала и конца. Остаётся только положить готовые значения в поля Этапов.
Спасибо за наводку! Однозначно возьму на вооружение.
Спасибо всем за ответы, да код представлен не полностью, выборка данных выполняется для дальнейшей обработки в стороннем скрипте, поэтому мне нужны поля из обеих сущностей. Подсказали вот такое решение:
<?phpfunction extractMaxIntervalsFromChildren(array $elements) {
return array_reduce($elements, function($res, $element) {
if (!isset($element['parent_id'])) {
return $res;
}
$parentId = $element['parent_id'];
$parent = $res[$parentId] ?? ['id' => $parentId, 'min' => null, 'max' => null];
$parent['min'] = min($parent['min'], $element['start']) ?: $element['start'];
$parent['max'] = max($parent['max'], $element['end']) ?: $element['end'];
$res[$parentId] = $parent;
return $res;
}, []);
}?>