Ранее подход, который я опишу, использовался для nodequeue, сейчас с заменой плагина фунционалом entityqueue, тоже самое можно применить и для него.
Проблема:
Создано представление (view), где источник данных — элементы datasource Search API. Т.е. вы добавили какие поля для индексации, и используете их во view.
Теперь требуется, например, сделать сортировку элементов, используя entityqueue. Вы создали entity_subqueue, но она не появится у вас в секции Advanced -> Relationships вашей view, т.к. вы не добавили поля этой сущности в ваш datasource.
Можно попробовать перейти в настройки источника данных — /admin/config/search/search-api, и там в секции fields попробовать добавить поля для entityqueue.
Но ничего подходящего и решающего вашу задачу там нет, поэтому приходится искать другое решение.
Решение (используем суррогатное поле):
Мы можем добавить скрытое от редактирования поле с весом элемента прямо в сущность (например, node). При изменении нужной entityqueue мы будем обновлять веса в сущностях.
Останется добавить это суррогатное поле в datasource, и тогда можно будет использовать его во view для сортировки.
Отслеживать изменения удобно через hook_update.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?php use Drupal\Core\Entity\EntityInterface; use Drupal\node\Entity\Node; /** * Implements entity_update hook. */ function MODULE_entity_update(EntityInterface $entity) { if ($entity->getEntityTypeId() == 'entity_subqueue' && $entity->bundle() == 'ENTITYQUEUE_MACHINENAME') { // передаём список нод из очереди (entityqueue) _MODULE_entityqueue_update($entity->items->getValue()); } } /** * Updates weights. */ function _MODULE_entityqueue_update($items) { // соберем node id в массив $node_ids = []; foreach ($items as $i => $item) { if (isset($item['target_id'])) { $node_ids[$item['target_id']] = $i; } } // чтобы упростить сортировку // выберем какое то большое число для всех // нод, которых нет в списке (entityqueue) // Это же значение нужно задать как значение по // умолчанию для вашего весового поля. $default_value = 100000; // выберем полный список нод // у меня он отфильтрован по типу product $all_node_ids = \Drupal::entityQuery('node') ->condition('status', 1) ->condition('type', 'product') ->accessCheck(TRUE) ->execute(); // проверим и обновим ноды $all_nodes = Node::loadMultiple($all_node_ids); foreach ($all_nodes as $node) { $weight = $default_value; if (isset($node_ids[$node->id()])) { $weight = $node_ids[$node->id()]; } $old_value = $node->field_item_sorting->value; if ($old_value != $weight) { // обновляем значения весов в нодах // у меня поля называется field_item_sorting $node->set('field_item_sorting', $weight); $node->save(); } } } |
Значения полей установятся после первого изменения в entityqueue. entity_update hook отслеживает все точки изменения (я встречал решения, когда цепляют form_alter на форму списка элементов самой queue).
Возможные проблемы реализации
Иногда это не работает из-за не правильной типизации. Мы создали поле field_item_sorting как integer, но при индексации Search API может (даже не смотря на выбранный тип поля) передавать его как строковый.
Тогда view будет выдавать довольно странные результаты. Давайте попробуем это пофиксить.
Во-первых, пересоздадим поле field_item_sorting как строковое, я назвал его field_item_sorting_abc. Далее, его нужно добавить опять в наш datasource Search API. И третий шаг — настроить сортировку, используя новое поле, во view
Функция обновления весов немного изменится.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
/** * Updates weights. */ function _MODULE_entityqueue_update($items) { // соберем node id в массив $node_ids = []; foreach ($items as $i => $item) { if (isset($item['target_id'])) { $node_ids[$item['target_id']] = $i; } } // дефолт значение стало строковым, // но мы всё равно будем хранить там числа $default_value = '00100000'; // выберем полный список нод $all_node_ids = \Drupal::entityQuery('node') ->condition('status', 1) ->condition('type', 'product') ->accessCheck(TRUE) ->execute(); // проверим и обновим ноды $all_nodes = Node::loadMultiple($all_node_ids); foreach ($all_nodes as $node) { $weight = $default_value; if (isset($node_ids[$node->id()])) { // дополним вес нулями // для того чтобы результат сравнения строк был // корректен $weight = str_pad( $node_ids[$node->id()], 8, '0', STR_PAD_LEFT ); } $old_value = $node->field_item_sorting_abc->value; if ($old_value != $weight) { // обновляем значения весов в нодах // у меня поля называется field_item_sorting $node->set('field_item_sorting_abc', $weight); $node->save(); } } } |