2017-04-25 57 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是不合適的。