I have a vanilla MySQL query as follows which works fine:
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`;
The background to this is that I have 2 tables, displays
and display_substances
. I want to render a table of every row in displays
and the corresponding values in display_substances
for a given substance ID (represented by ds.substance_id = 2
in the query above).
Not every row in displays
has a corresponding display_substances
value. If this is the case it should output the words "Not Listed". This effectively means that if there is no corresponding row in display_substances
- then there is no data for that display.id
- therefore the data is "Not Listed" in the database for that particular row. The query above does exactly this.
I want to be able to write my query using CakePHP's ORM syntax.
The structures for the tables are as follows.
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 | |
+--------------+-----------------------+------+-----+---------+----------------+
The Table classes exist with the appropriate relationships defined.
// src/Model/Table/DisplaysTable.php
$this->hasMany('DisplaySubstances', [
'foreignKey' => 'display_id'
]);
// src/Model/Table/DisplaysSubstancesTable.php
$this->belongsTo('Displays', [
'foreignKey' => 'display_id',
'joinType' => 'INNER'
]);
So far I have this:
$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();
This will get me the rows in displays
which have corresponding values in display_substances
for substance ID 2. This effectively is everything where we have data, but doesn't include any rows which are "Not Listed".
I don't know how to write the IFNULL...GROUP_CONCAT
part of my vanilla SQL query using Cake's ORM syntax.
I've read How to use group_contact in cakephp query? so can see that it's possible to use GROUP_CONCAT
in the ->select()
condition. But the linked example is a lot more simple because it doesn't use an IFNULL
condition and the corresponding action (returning "Not Listed").
Please can someone advise how to write this in Cake's ORM syntax, if this is possible?
CakePHP version is 3.5.18