GOAL
I am attempting to return all features and all associated (if any) features_user_types where the user_type_id = ?.
So for example, I have 2 features. I want both to be returned along with all associated features_user_types as long as the user_type_id = 2. If there is no matching feature_user_type then it should return feature anyway.
EXPECTED RESULTS
Example Output: WHERE user_type_id = 2
"features": [
{
"id": 1,
"features_user_types": [
{
"id": 79,
"feature_id": 1,
"user_type_id": 2,
"position": 3
}
]
},
{
"id": 2,
"features_user_types": []
}
]
ACTUAL RESULTS
However, currently it is returning all associated features_user_types despite their id not equaling 2.
$query->toArray() Output:
"features": [
{
"id": 1,
"features_user_types": [
{
"id": 79,
"feature_id": 1,
"user_type_id": 2,
"position": 3
}
]
},
{
"id": 2,
"features_user_types": [
{
"id": 72,
"feature_id": 2,
"user_type_id": 1,
"position": 9
}
]
}
]
DATA STRUCTURE
Table Structure:
features
-id
features_user_types
-id
-feature_id
-user_type_id
-position
user_types
-id
CakePHP Association Definitions:
FeaturesTable:
$this->belongsToMany('UserTypes', [
'foreignKey' => 'feature_id',
'targetForeignKey' => 'user_type_id',
'joinTable' => 'features_user_types'
]);
$this->hasMany('FeaturesUserTypes', [
'foreignKey' => 'feature_id'
]);
UserTypesTable:
$this->belongsToMany('Features', [
'foreignKey' => 'user_type_id',
'targetForeignKey' => 'feature_id',
'joinTable' => 'features_user_types'
]);
$this->hasMany('FeaturesUserTypes', [
'className' => 'FeaturesUserTypes',
'foreignKey' => 'user_type_id'
]);
FeaturesUserTypesTable:
$this->belongsTo('Features', [
'foreignKey' => 'feature_id',
'joinType' => 'INNER'
]);
$this->belongsTo('UserTypes', [
'foreignKey' => 'user_type_id',
'joinType' => 'INNER'
]);
QUERY OBJECT
I have a query builder in my cakephp app that is creating the following sql according to the $query->sql():
SELECT DISTINCT
Features.id AS `Features__id`,
Features.created AS `Features__created`,
Features.modified AS `Features__modified`,
Features.name AS `Features__name`,
Features.description AS `Features__description`
FROM features Features
LEFT JOIN features_user_types FeaturesUserTypes
ON (FeaturesUserTypes.user_type_id = 2
AND Features.id = (FeaturesUserTypes.feature_id))
MySQL
However, if I copy and paste this directly into MySQL I get the results that I expect, all features with only featurs_user_types matching the id are returned.
Actual Query:
SELECT DISTINCT *
FROM features Features
LEFT JOIN features_user_types FeaturesUserTypes
ON (FeaturesUserTypes.user_type_id = 2
AND Features.id = (FeaturesUserTypes.feature_id))
MySQL Output:
----------------------------------------------------------------------------
|ID (feature id)|ID (feature_user_type_id)|feature_id|user_type_id|position|
| 1 | 79 | 1 | 2 | 3 |
| 2 | NULL | NULL | NULL | NULL |
----------------------------------------------------------------------------
CODE
AppController:
My AppController is very generic but built to take it paramters from URLs to generate and execute sql queries. It is a rather large file so instead I went through it with a debugger and recorded any lines that $query was altered and filled in the variables to make it more obvious.
$key = 'FeaturesUserTypes.user_type_id';
$value = 2;
$model = $this->loadModel();
$query = $model->find('all', ['fields' => $this->getFields()]);
$query->contain(['FeaturesUserTypes']);
$query->leftJoinWith('FeaturesUserTypes', function($q) use ($key, $value) {
return $q->where([$key => $value]);
});
$query->distinct();
$results = $query->toArray();
Any idea on what could be happening? I am running CakePHP 3 and PHP 5.6.10. Thanks!
featurestable, not just the matching ones. It is unlikely that this query pasted into MySQL would return the matching records only. If you wanted the matching records only, then change left join to inner join.