Использование IFNULL ... GROUP_CONCAT () в Cake PHP 3 - PullRequest
2 голосов
/ 13 марта 2020

У меня есть запрос ванили MySQL, который работает следующим образом:

SELECT d.*, IFNULL(
     (SELECT GROUP_CONCAT(value) FROM display_substances `ds` 
         WHERE `ds`.`display_id` = `d`.`id`
         AND ds.substance_id = 2 
         GROUP BY `ds`.`display_id`
     ), 'Not Listed'
) `substances` FROM displays `d`;

Основанием для этого является то, что у меня есть 2 таблицы, displays и display_substances. Я хочу отобразить таблицу каждой строки в displays и соответствующие значения в display_substances для данного идентификатора вещества (представленного ds.substance_id = 2 в запросе выше).

Не каждая строка в displays имеет соответствующее значение display_substances. Если это так, он должен вывести слова «Нет в списке». Это фактически означает, что если в display_substances нет соответствующей строки - тогда нет данных для этого display.id - поэтому в базе данных для этой конкретной строки данные "Не указаны". Вышеупомянутый запрос делает именно это.

Я хочу иметь возможность написать свой запрос, используя синтаксис Cake PHP ORM.

Структуры для таблиц следующие.

mysql> DESCRIBE displays;
+----------+----------------------+------+-----+---------+----------------+
| Field    | Type                 | Null | Key | Default | Extra          |
+----------+----------------------+------+-----+---------+----------------+
| id       | smallint(5) unsigned | NO   | PRI | NULL    | auto_increment |
| label    | varchar(255)         | NO   |     | NULL    |                |
+----------+----------------------+------+-----+---------+----------------+

mysql> DESCRIBE display_substances;
+--------------+-----------------------+------+-----+---------+----------------+
| Field        | Type                  | Null | Key | Default | Extra          |
+--------------+-----------------------+------+-----+---------+----------------+
| id           | mediumint(8) unsigned | NO   | PRI | NULL    | auto_increment |
| display_id   | smallint(5) unsigned  | NO   | MUL | NULL    |                |
| substance_id | mediumint(8) unsigned | NO   | MUL | NULL    |                |
| value        | text                  | NO   |     | NULL    |                |
+--------------+-----------------------+------+-----+---------+----------------+

Классы Table существуют с определенными соответствующими отношениями.

// src/Model/Table/DisplaysTable.php
$this->hasMany('DisplaySubstances', [
    'foreignKey' => 'display_id'
]);

// src/Model/Table/DisplaysSubstancesTable.php
$this->belongsTo('Displays', [
   'foreignKey' => 'display_id',
   'joinType' => 'INNER'
]);

Пока у меня есть это:

    $substance_id = 2;

    $data = 
        $DisplaySubstances->find()
        ->contain(['Displays'])
        ->where(['DisplaySubstances.substance_id' => $substance_id)
        ->select(['Displays.id', 'Displays.label', 'Displays.anchor', 'DisplaySubstances.value'])
        ->enableHydration(false)->toArray();

Это даст мне строки в displays которые имеют соответствующие значения в display_substances для идентификатора вещества 2. Это фактически все, где у нас есть данные, но не включает в себя строки, которые «не перечислены».

Я не знаю, как писать IFNULL...GROUP_CONCAT часть моего ванильного запроса SQL, использующего синтаксис Cake ORM.

Я прочитал Как использовать group_contact в запросе cake php? , чтобы убедиться, что это возможно использовать GROUP_CONCAT в состоянии ->select(). Но связанный пример намного проще, потому что он не использует условие IFNULL и соответствующее действие (возвращающее «Not Listed»).

Пожалуйста, кто-то может посоветовать, как написать это в синтаксисе Cake ORM , если это возможно?

Торт PHP версия 3.5.18

1 Ответ

2 голосов
/ 13 марта 2020

IFNULL и GROUP_CONCAT являются функциями SQL, поэтому вы должны использовать конструктор функций, который можно использовать для создания любого вызова функции, который вы хотите, его обработчик вызовов magi c создаст обобщенный c вызов функции, если нет конкретной реализации для вызванного метода, ie $functionsBuilder->IFNULL() и $functionsBuilder->GROUP_CONCAT() будут просто работать.

Конструктор функций также принимает выражения, так что вы можете передать другой объект запроса вашему IFNULL() вызов, ie подзапрос в вашем примере SQL.

$subquery = $DisplaySubstances->find()
    ->select(function (\Cake\ORM\Query $query) {
        return [
            $query->func()->GROUP_CONCAT(['value' => 'identifier'])
        ];
    })
    ->where(function (\Cake\Database\Expression\QueryExpression $exp) use ($substance_id) {
        return [
            $exp->equalFields('DisplaySubstances.display_id', 'Displays.id'),
            'DisplaySubstances.substance_id' => $substance_id
        ];
    })
    ->group('DisplaySubstances.display_id');

$query = $Displays->find()
    ->select(function (\Cake\ORM\Query $query) use ($subquery) {
        // Before CakePHP 3.8.3 queries need to be wrapped in an additional expression
        // in order for the query builder to generate them wrapped in parentheses in SQL
        $subquery = $query->newExpr($subquery);

        return [
            'substances' => $query->func()->IFNULL([$subquery, 'Not Listed'])
        ];
    })
    ->select($Displays);

См. Также

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...