类似查询中关联模型的虚拟字段,但有时不构建为sql查询

时间:2014-01-30 13:01:25

标签: mysql cakephp cakephp-2.4

我有两种情况,即进行两次类似的查询,在一种情况下,相关模型的虚拟字段的代码不会生成到sql查询语句中,因此找不到。

在数据库中: *两个主表(hospitalshotels) *一个引用的表contacts *主表具有指向联系表的各种字段。对医院,例如一个director_contact_id和一个janitor_contact_id。对于酒店director_contact_idconcierge_contact_id

在CakePHP中 * Hospital模型有两个belongsTo关系DirectorContactJanitorContact * Hotel模型有两个belongsTo关系DirectorContactConciergeContact *联系人有一个虚拟字段full_name,类似于CONCAT(…)

在HospitalController中,每当我需要一个绑定到医院数据的联系人数据时,我就可以这样做:

$contain = array();
…
$contain['DirectorContact'] = array('fields' => array('id','full_name'));
…
$this->Hospital->find('all', array(
    …
    'contain' => $contain,
    …
));

生成的sql代码包含

CONCAT(…) AS DirectorContact__full_name

然而,在HotelController中同样不起作用。我也在那里:

$contain['DirectorContact'] = array('fields' => array('id','full_name'));

如果我

debug($this->Hotel->DirectorContact->virtualFields);

我得到了

array(
    'full_name' => 'CONCAT(…)'
)

但是当我运行动作时,我得到一个SQL错误,说明字段full_name未知。我可以看到,在生成的sql查询中缺少CONCAT(…) AS DirectorContact__full_name

在这两种情况下,联系表都被多次引用,至少有一个具有不同别名的关联。所以我不确定为什么CakePHP在一个案例中生成正确的查询而在另一个案例中没有。

当然,find语句比我在这里说的更复杂,包含更多,包含连接和字段。

问题1 :有谁知道什么可能触发CakePHP丢弃生成关联模型虚拟字段的代码?

我读到可容纳的行为有点微妙,在某些情况下最好使用连接。

因此,在虚拟字段不起作用的一种情况下,我使用了连接而不是包含。然而,虚拟字段没有生成,所以我明确地为两个字段做了这个:

$fields[] = 'DirectorContact.id';
$fields[] = 'CONCAT(…) AS `DirectorContact__name_or_company`';
$fields[] = 'ConciergeContact.id';
$fields[] = 'CONCAT(…) AS `ConciergeContact__name_or_company`';

如果我调试查询结果:

array(
    'Hotel' => array(
        'id' => '123',
    ),
    'DirectorContact' => array(
        'id' => '456',
        'name_or_company' => 'Some name'
    ),
    (int) 0 => array(
        'ConciergeContact__name_or_company' => 'Some other name',
    ),
    'ConciergeContact' => array(
        'id' => '789'
    ),
)

因此,对于第一个关联,自动化工作和CakePHP将虚拟字段DirectorContact__name_or_company的内容写入关联数组的DirectorContact部分,但另一个被放入“常规”部分键0引用的计算字段。

但更有趣的是:如果我将字段定义中模型引用的顺序交换为

$fields[] = 'ConciergeContact.id';
$fields[] = 'CONCAT(…) AS `ConciergeContact__name_or_company`';
$fields[] = 'DirectorContact.id';
$fields[] = 'CONCAT(…) AS `DirectorContact__name_or_company`';

结果是

array(
    'Hotel' => array(
        'id' => '123',
    ),
    'ConciergeContact' => array(
        'id' => '789'
    ),
    (int) 0 => array(
        'ConciergeContact__name_or_company' => 'Some other name',
        'DirectorContact__name_or_company' => 'Some name',
    ),
    'DirectorContact' => array(
        'id' => '456',
    ),
)

所以现在自动化不再起作用了,两个虚拟领域都被纳入了一般部分。

问题2 :有没有人知道这个的原因,以及如何让CakePHP的自动化适用于所有情况?

(使用版本2.4.3)

1 个答案:

答案 0 :(得分:1)

在深入研究CakePHP的代码之后,我找到了原因。

如果为查询定义fields选项,则不会创建virtualFields(即使您将它们添加到fields选项中)。

在单位DboSource.php中有函数read(Model $model, $queryData = array(), $recursive = null)

该功能代码的片段:

if (!empty($queryData['fields'])) {
    $bypass = true;
    …
} else {
    …
}
$_associations = $model->associations();
…
foreach ($_associations as $type) {
    foreach ($model->{$type} as $assoc => $assocData) {
        …
        if ($model->useDbConfig === $linkModel->useDbConfig) {
            if ($bypass) {
                $assocData['fields'] = false;
            }
            if ($this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null) === true) {
                …
            }
        }
    }
}

因此,当定义fields选项时,变量$bypass将设置为true。进一步向下,在构建所有关联模型的查询代码的情况下,当$bypass为真时,关联模型的字段定义设置为false。这显然也会删除虚拟字段。

然后,当您在查询的字段选项中包含该字段时,将无法找到它。

相当合乎逻辑......