Я пытаюсь написать запрос, используя CakePHP 3.7 ORM, где нужно добавить столбец в набор результатов. Я знаю, что в MySQL такие вещи возможны: MySQL: динамически добавлять столбцы к результатам запроса
До сих пор я реализовал 2 пользовательских средств поиска . Первый выглядит следующим образом:
// src/Model/Table/SubstancesTable.php
public function findDistinctSubstancesByOrganisation(Query $query, array $options)
{
$o_id = $options['o_id'];
$query = $this
->find()
->select('id')
->distinct('id')
->contain('TblOrganisationSubstances')
->where([
'TblOrganisationSubstances.o_id' => $o_id,
'TblOrganisationSubstances.app_id IS NOT' => null
])
->orderAsc('Substances.app_id')
->enableHydration(false);
return $query;
}
Второй пользовательский искатель:
// src/Model/Table/RevisionSubstancesTable.php
public function findProductNotifications(Query $query, array $options)
{
$date_start = $options['date_start'];
$date_end = $options['date_end'];
$query = $this
->find()
->where([
'RevisionSubstances.date >= ' => $date_start,
'RevisionSubstances.date <= ' => $date_end
])
->contain('Substances')
->enableHydration(false);
return $query;
}
Я использую искатели внутри контроллера, чтобы проверить его:
$Substances = TableRegistry::getTableLocator()->get('Substances');
$RevisionSubstances = TableRegistry::getTableLocator()->get('RevisionSubstances');
$dates = // method to get an array which has keys 'date_start' and 'date_end' used later.
$org_substances = $Substances->find('distinctSubstancesByOrganisation', ['o_id' => 123);
if (!$org_substances->isEmpty()) {
$data = $RevisionSubstances
->find('productNotifications', [
'date_start' => $dates['date_start'],
'date_end' => $dates['date_end']
])
->where([
'RevisionSubstances.substance_id IN' => $org_substances
])
->orderDesc('RevisionSubstances.date');
debug($data->toArray());
}
Логика заключается в том, что я использую первый пользовательский искатель для создания объекта запроса, который содержит уникальные (DISTINCT
в SQL) id
поля из таблицы substances
, основанные на конкретной компании (обозначается какполе o_id
). Затем они передаются во второй пользовательский искатель путем реализации where(['RevisionSubstances.substance_id IN' ...
.
Это работает и дает мне все правильные данные. Пример вывода из оператора debug()
выглядит следующим образом:
(int) 0 => [
'id' => (int) 281369,
'substance_id' => (int) 1,
'date' => object(Cake\I18n\FrozenDate) {
'time' => '2019-09-02T00:00:00+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'comment' => 'foo',
'substance' => [
'id' => (int) 1,
'app_id' => 'ID000001',
'name' => 'bar',
'date' => object(Cake\I18n\FrozenDate) {
'time' => '2019-07-19T00:00:00+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
}
]
],
Проблема, с которой я столкнулся, заключается в следующем: Каждый из возвращаемых результатов содержит app_id
поле (['substance']['app_id']
в массиве выше). Что мне нужно сделать, это выполнить подсчет (COUNT()
в MySQL) для другой таблицы на основе этого, а затем добавить это к результирующему набору.
Я не уверен, как это сделать для парыпричины. Во-первых, я понимаю, что пользовательские искатели возвращают объекты запроса , но запрос не выполняется в этот момент . Поскольку я не выполнил запрос - до вызова $data->toArray()
- я не уверен, как бы я ссылался на app_id
таким образом, чтобы на него можно было ссылаться на строку?
Эквивалентный SQL, который быдайте мне требуемые результаты:
SELECT COUNT (myalias.app_id) FROM (
SELECT
DISTINCT (tbl_item.i_id),
tbl_item.i_name,
tbl_item.i_code,
tbl_organisation_substances.o_id,
tbl_organisation_substances.o_sub_id,
tbl_organisation_substances.app_id,
tbl_organisation_substances.os_name
FROM
tbl_organisation_substances
JOIN tbl_item_substances
ON tbl_organisation_substances.o_sub_id = tbl_item_substances.o_sub_id
JOIN tbl_item
ON tbl_item.i_id = tbl_item_substances.i_id
WHERE
tbl_item.o_id = 1
AND
tbl_item.date_valid_to IS NULL
AND
tbl_organisation_substances.app_id IS NOT NULL
ORDER BY
tbl_organisation_substances.app_id ASC
) AS myalias
WHERE myalias.app_id = 'ID000001'
Это COUNT()
, где app_id
равно ID000001
.
Так что в массиве, который я дал ранее, мне нужнодобавить что-то в массив для хранения этого, например,
'substance' => [
// ...
],
'count_app_ids' => 5
(при условии, что запросом было возвращено 5 строк).
У меня есть классы таблиц для всех упомянутых таблицв приведенном выше запросе.
Итак, мой вопрос: как вы пишете это, используя ORM, и добавляете результат обратно в набор результатов перед выполнением запроса?
Возможно ли это вообще? Единственное другое решение, которое я могу придумать, - записать данные (из того запроса, который у меня работает) во временную таблицу, а затем выполнить последовательные запросы, которые UPDATE
с числом, основанным на app_id
. Но я действительно не заинтересован в этом решении, потому что это может привести к огромным проблемам с производительностью. Кроме того, я хотел бы иметь возможность разбивать свой запрос на страницы, поэтому в идеале нужно, чтобы все было ограничено одним оператором SQL, даже если это выполняется несколькими поисковыми системами.
Я отметил это с помощью MySQL, а также CakePHP, потому чтоя даже не уверен, что это достижимо с точки зрения MySQL, хотя он и смотрит на связанный пост SO, как это можно сделать? Это создает дополнительную сложность необходимости писать эквивалентный запрос с использованием ORM Cake.