CakePHP查询返回不同的结果,然后直接在sql

时间:2017-04-25 15:01:06

标签: php mysql cakephp

目标

我正在尝试返回所有features以及所有关联的(如果有的话)features_user_types,其中user_type_id =?。

例如,我有2 features。只要user_type_id = 2,我希望两者都与所有关联的features_user_types一起返回。如果没有匹配的feature_user_type,那么无论如何都应该返回feature

预期成果

示例输出: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": []
  }
]

实际结果

但是,目前它返回所有相关的features_user_types,尽管它们的id不等于2。

$ query-> toArray()输出:

"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
      }
     ]
  }
]

数据结构

表格结构:

features
-id

features_user_types
-id
-feature_id
-user_type_id
-position

user_types
-id

CakePHP关联定义:

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

我的cakephp应用程序中有一个查询构建器,它根据$query->sql()创建以下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

但是,如果我将其直接复制并粘贴到MySQL中,我会得到我期望的结果,只返回与features匹配的所有featurs_user_types

实际查询:

SELECT DISTINCT *
FROM features Features 
LEFT JOIN features_user_types FeaturesUserTypes 
  ON (FeaturesUserTypes.user_type_id = 2 
    AND Features.id = (FeaturesUserTypes.feature_id))

MySQL输出:

----------------------------------------------------------------------------
|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的:

我的AppController非常通用,但是可以从URL中获取参数来生成和执行sql查询。它是一个相当大的文件,所以我用调试器完成了它并记录了$ query被更改并填充变量的任何行,以使其更加明显。

$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();

对可能发生的事情有任何想法?我正在运行CakePHP 3和PHP 5.6.10。谢谢!

2 个答案:

答案 0 :(得分:3)

hasOnebelongsTo关联不同,它们在主查询中使用连接,除非另外明确配置,hasManybelongsToMany关联数据始终在单独检索查询。

请注意,sql()将始终只返回主查询,而不是用于预先加载关联的可能的其他查询!如果您需要了解其他查询,请查看DebugKits SQL日志面板。那么这意味着,您手动测试的查询不是ORM实际用于检索关联数据的查询。

您正在寻找的是传递包含的条件,即删除leftJoinWith()(除非您需要主查询中的关联数据以进行进一步的SQL级别操作),并将条件回调附加到{代替{1}}遏制:

FeaturesUserTypes

对于读取此内容的任何人,请确保$query->contain([ 'FeaturesUserTypes' => function($q) use ($key, $value) { return $q->where([$key => $value]); } ]); 不包含用户输入,否则这将是SQL注入漏洞!

另见

答案 1 :(得分:0)

ON子句中进行过滤通常 错误:

FROM a
JOIN b ON b.x = 2 AND a.z=b.z

将过滤移至WHERE

FROM a
JOIN b ON a.z=b.z
WHERE b.x = 2

另外,请不要使用LEFT,除非您想要“右”表中“缺失”行的空值。

我不知道您的数据,但似乎DISTINCT不合适。