I have an application in CakePHP 3.5.13. I have baked a legacy database which has not been written according to Cake's naming conventions.
One part of the application has a table of 255,693 rows called substances
. There are associated CAS numbers which have been put in a table called cas
and a mapping between these 2 tables called cas_substances
.
I am attempting to use CakePHP's ORM to write a query that searches for a given CAS.
I can't seem to get the query I want to execute written in the ORM, even though the MySQL equivalent for it is pretty simple. Let's say I was searching for all substance ID's which have a CAS that contained "1234" the query would look like this in MySQL:
SELECT DISTINCT( s.id ) FROM substances s
JOIN cas AS cas
ON ( (cas.value LIKE '%1234%') )
JOIN cas_substances AS cassub
ON (s.id = cassub.substance_id AND cassub.cas_id = cas.id)
Running this directly on the database (through Navicat) gives me 63 rows in 0.39 seconds - expected.
So in attempting to write this in Cake, I have configured my Table
classes as follows:
// src/Model/Table/CasTable.php
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('cas');
$this->setDisplayField('value');
$this->setPrimaryKey('id');
$this->belongsToMany('Substances', [
'foreignKey' => 'cas_id',
'targetForeignKey' => 'substance_id',
'joinTable' => 'cas_substances'
]);
}
// src/Model/Table/CasSubstancesTable.php
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('cas_substances');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->belongsTo('Cas', [
'foreignKey' => 'cas_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Substances', [
'foreignKey' => 'substance_id',
'joinType' => 'INNER'
]);
}
// src/Model/Table/SubstancesTable.php
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('substances');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsToMany('Cas', [
'foreignKey' => 'substance_id',
'targetForeignKey' => 'cas_id',
'joinTable' => 'cas_substances'
]);
// ...
}
Then in the Controller I am attempting to get the distinct (MySQL equivalent DISTINCT()
) substances.id
:
// Begin the query
$query = $Substances->find()->select(['id' => 'id'])->distinct();
Then modify the query to filter for my CAS:
$query = $query->contain('Cas', function ($q) {
return $q->where(['Cas.value' => '%'.$this->request->getData('cas_number').'%']);
});
When I attempt to output the results using debug($query->all())
it is giving me a PHP fatal error:
Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)
On closer inspection, it appears that my condition to filter the query based on CAS is not being applied. If I do debug($query->all()->count())
it is giving me 255,693 - the whole substances table without any filtering.
There are a few problems I have:
How do I write this query to filter the associated data? My work here is based on the Passing Conditions to Contain part of the documentation.
I'm concerned about how much data is being returned. If I run the MySQL equivalent of that query, it is giving me back just
substances.id
which is what I want. Cake is producing large objects - I know this is because of how the ORM works - but surely there are memory implications here? I need to write the results of my query to another table. How is using the ORM better (or easier) than just writing vanilla SQL then doingCREATE TABLE tmp_table AS . $sql_select_string
(where$sql_select_string
is theSELECT
statement given earlier)?
matching
method in your situation where - from what I understand - you want all substances that have al least one CAS number like %1234% – ariliaON
clause or where this appears in my code? I have$q->where
which I understood to be equivalent to aWHERE
SQL condition? If you could post an answer which shows the basics of doing a search on a related table, that would give me a great start. At the moment I can't even get the query to execute due to the memory issue, and possibly the fact it's not even considering my CAS filter condition. – Andy