2017-04-25 159 views
1

目标CakePHP的查询返回不同的结果,然后在SQL

我试图返回相关联的所有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。

$ q uery->指定者()输出:

"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->sql()在被创建下列SQL我的CakePHP应用程序查询生成器:

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匹配的ID被返回。

实际查询:

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中paramters生成并执行sql q ueries。它是一个相当大的文件,所以我使用调试器进行了检查,并记录了$ 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。谢谢!

+0

查询将返回来自'features'表的所有**记录,而不仅仅是匹配的记录。这个查询不太可能粘贴到MySQL中,只会返回匹配的记录。如果只需要匹配记录,则将左连接更改为内连接。 – Shadow

+0

该查询在连接中检查了id,它在MySQL中完全按预期工作。结果绝不符合CakePHP的结果。这种类型的查询适用于其他模型的CakePHP,因此我认为它可能与关系类型有关。 – jrquick

+0

不,它也会返回MySQL中的所有结果。请参阅下面提供的答案。 – Shadow

回答

2

不同于hasOnebelongsTo协会,正在使用连接在主查询除非显式地配置在一个单独的查询否则,hasManybelongsToMany相关联的数据总是被检索。

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

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

$query->contain([ 
    'FeaturesUserTypes' => function($q) use ($key, $value) { 
     return $q->where([$key => $value]); 
    } 
]); 

而且任何人谁读这一点,确保$key不保存用户的输入,otherwhise这将是一个SQL注入漏洞!

+0

这似乎是解决方案(现在运行自动化)。但是,我必须离开'leftJoinWith()',否则当我调用'toArray()'时,出现'FeaturesUserTypes.position'on不存在的错误(用在'order()')中。因此,最终该行从'$ query-> contains([$ function]);'转换为'$ query-> contains([$ join => $ function]);'其中'$ function = function($ q)使用($ key,$ value){ return $ q-> where([$ key => $ value]); });'。谢谢你! – jrquick

+0

@jrquick我看到......'order()'不在你的例子中,但我会在答案中添加一个提示。 – ndm

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是不合适的。