Доброго дня и всем мира.
Вообще - этот момент меня парит с самого появления entityQuery
. С тех пор так и не появилось внятных методов для инвертирования условия отбора сущностей с целью сделать выборку по ОТСУТСТВИЮ ЗАДАННОГО ЗНАЧЕНИЯ в полях МНОЖЕСТВЕННОГО ПОЛЯ (в моём случае 'checkboxes'
).
Допустим, выбрать из одиночного (single) поля чекбокса с наличием или отсутствием значения - не вопрос:
<?php
// Наличие.
$query->condition('field_is_paid', 1);
// Отсутствие.
$query->notExists('field_is_paid');
?>
Также, выбрать из multiple-поля типа 'checkboxes' с наличием значения - не особый вопрос:
<?php
// Наличие во множественном.
$query->condition(
$query->andConditionGroup()->condition('field_passed_statuses', 'paid')
);
?>
Однако, как поступить если нужно выбрать те сущности, у которых в множественном поле типа 'checkboxes'
не установлен заданный флаг/ключ? То есть, когда в случае из предыдущего примера нужно выбрать по полю 'field_passed_statuses'
, но только где НЕ УСТАНОВЛЕНО нужное значение (например, пусть так же: 'paid'
).
И вот тут начинаются танцы с бубном. Поскольку раз не установлен чекбокс - значит нет значения в рядах, а, значит - нет и результата. Кроме того, тут заведомо мешают "картине" другие значения из других дельт, которые могут присутствовать, а могут и отсутствовать. Т.е. поле может содержать значения других установленных ключей 'checkboxes'
, что уже будет означать, что оно не пустое и тогда под простые условия типа notExists()
или 'IS NOT NULL'
уже не попадёт.
К слову, я одно время выезжал именно по дельтам, но это способ работает, когда есть чёткое знание, что какая-то определённая часть из них обязательно заполнена, что позволяет нам точно адресоваться к нужному ключу по индексу дельты ключа. Например:
<?php
$query->condition(
$query->andConditionGroup()
->notExists('field_passed_statuses.3.value') // Not printed.
->notExists('field_passed_statuses.4.value') // Not sent.
->condition($query->andConditionGroup()->condition('field_passed_statuses', 'paid')) // Paid.
);
?>
Но это, разумеется, в некотором роде и мазохизм и костыли.
Затем одно время я делал фильтрующие exclude-массивы, предварительно отправляя entity-запрос на НАЛИЧИЕ значения заданного значения во множественном поле, а затем повторяя запрос и инвертируя условие отбора наподобие:
<?php
$query->condition('nid', $exclude_array, 'NOT IN');
?>
Это работало, но плодило много предварительных запросов и усложняло/запутывало код, что меня не устраивало.
В итоге я пришёл (отсюда: https://www.drupal.org/docs/8/api/database-api/dynamic-queries/conditions - см. "Using NOT IN With Multi Value field like roles > user entity") к более универсальной конструкции, при которой условие "НЕ СОДЕРЖИТ ЗНАЧЕНИЕ" применяется ко всем возможным дельтам поля. Однако, чтобы пройти по всем возможным дельтам поля в случае с множественными чекбоксами - нужно знать макс. возможное кол-во дельт для поля, т.е. - нужно предварительно получать все допустимые значения для поля (как они определены в настройках поля через админку). Выглядит это примерно так:
<?php
// Получаем доступные значения чекбоксов (кол-во ключей массива эквивалентно максимуму дельт).
$node_prototype = \Drupal::service('entity_type.manager')->getStorage('node')->create(['type' => 'order']);
$passed_statuses = $node_prototype->getFieldDefinition('field_passed_statuses')->getFieldStorageDefinition()->getSetting('allowed_values');
// Строим запрос.
$query = \Drupal::entityQuery('node');
$query->condition('type', 'order');
...
// Вот здесь то, о чём речь.
$andAllStatuses = $query->andConditionGroup();
$delta = 0;
foreach ($passed_statuses as $status => $title) {
// Последовательно добавляем условия для всех дельт
// вида "НЕ СОДЕРЖИТ ЗНАЧЕНИЕ или НЕ СУЩЕСТВУЕТ"
$orExcludeStatus = $query->orConditionGroup()
->condition('field_passed_statuses.' . $delta . '.value', 'received', 'NOT IN')
->condition('field_passed_statuses.' . $delta . '.value', NULL, 'IS NULL');
$andAllStatuses->condition($orExcludeStatus);
$delta ++;
}
$query->condition($andAllStatuses);
...
$orders = $query->execute();
?>
Это работает, но раздувает и делает менее понятным/разборчивым как запрос, так и код в целом. Да и не очень-то гибкое решение - в плане того, что нужно заведомо знать или получать (как в этом примере) массив всех возможных значений - только для того, чтобы узнать макс. кол-во допустимых дельт для поля.
Мне это тоже не очень нравится. Я много копал источников по этому вопросу, но внятного и более универсального решения, чем вышеприведённое, не нашёл.
Кто что может посоветовать в решении этого вопроса? Есть ли что-то более простое и удобное? В идеале хотелось бы видеть что-то короткое и удобочитаемое, наподобие:
<?php
// Как гипотетический пример отсутствия значения 'paid' во множественном поле чекбоксов.
$query->condition(
$query->andConditionGroup()->condition('field_passed_statuses', 'paid', 'NOT CONTAINS')
);
?>
Комментарии
Освежу тему, вновь актуально.
Не появились мысли/идеи ни у кого?
Сдается мне никто особо и не думал , тем более когда нихрена непонятна терминология, какие ещё к черту дельты, что это за дельты, у Дуная видимо или у Нила?
Но как видим никто не уточнял, видимо и не читал. Потому что телепатов нет
Исключительно из-за того, что вы не телепат, скромный ликбез.
А почему нельзя сразу сделать один запрос с NOT IN?
А ещё вот тут есть интересные примеры
Я тут не совсем понял, что подразумевается. NOT IN - вы здесь имеете в виду по значениям полей? По причине множественного поля. Мне же нужно условие "ОТСУТСТВУЕТ ОДНО УКАЗАННОЕ ЗНАЧЕНИЕ". В этом-то весь и цимес, так сказать. Здесь NOT IN не даст нужного эффекта, поскольку другие значения в этом поле будут тоже попадать под условия. Ну, к примеру имеем в рядах полей двух разных сущностей одного типа по две дельты 'key1' и 'key2':
'key1' => 'one',
'key2' => 'two',
]
и
'key1' => 'one',
'key2' => 'three',
]
И здесь
->condition('some_field', ['two'], 'NOT IN')
даст в результате оба ряда (0, 1), поскольку в первом ряду [0]['key1'] не содержит 'two' и во втором ряду [1]['key1'] и [1]['key2'] тоже не содержит 'two'. Что неправильно, поскольку в первой сущности [1]['key2'] равен именно 'two'.Или вы что-то другое подразумевали?
Да, спасибо. Я когда-то просматривал и эти примеры (просмотрел и сейчас), но не нашёл ничего подходящего под указанный случай. Проблема именно в отсутствии значения в множественных рядах. Не случайно на орге вытащили этот случай в отдельный пример: https://www.drupal.org/docs/8/api/database-api/dynamic-queries/condition... . Но они там именно создают условия под каждую дельту.
Да, страшное дело