在CakePHP中使用Containable行为后获取原始关联

问题描述 投票:0回答:1

背景:CakePHP 2.6.3。一个非常稳定的应用。创建新行为(MyCustomBehavior)以输出一些额外信息。我有一个模型MyModel充当Containable(在AppModel中定义),然后MyCustom(在MyModel中定义)。 MyCustomBehavior的编写方式需要与我的应用程序中的模型与其他模型的关联一起使用。

问题:每当我在find()MyModel调用中包含相关模型时,我无法获得MyModel关联的完整列表,因为Containable行为解除了未包含的模型的绑定。但是,如果我没有在我的contain选项中设置find()或设置'contain' => false,一切都按预期工作。

样本MyModel->belongsTo

public $belongsTo = array(
    'MyAnotherModel' => array(
        'className' => 'MyAnotherModel',
        'foreignKey' => 'my_another_model_id',
        'conditions' => '',
        'fields' => '',
        'order' => ''
    ),
    'Creator' => array(
        'className' => 'User',
        'foreignKey' => 'user_id',
        'conditions' => '',
        'fields' => '',
        'order' => ''
    ),
    'Approver' => array(
        'className' => 'User',
        'foreignKey' => 'approver_id',
        'conditions' => '',
        'fields' => '',
        'order' => ''
    ),
    'Status' => array(
        'className' => 'Status',
        'foreignKey' => 'status_id',
        'conditions' => '',
        'fields' => '',
        'order' => ''
    ),
);

样本find()

$this->MyModel->find('all', array(
    'fields' => array(...),
    'conditions' => array(...),
    'contain' => array('Approver', 'Status')
));

MyModel->belongsToMyCustomBehavior::beforeFind()的结果

$belongsTo = array(
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);

预计在MyModel->belongsToMyCustomBehavior::beforeFind()

$belongsTo = array(
    'MyAnotherModel' => array(
        ...
    ),
    'Creator' => array(
        ...
    ),
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);

明显的解决方案:解决问题的一种愚蠢方法是简单地在Containable而不是MyModel中设置AppModel行为来控制加载行为的顺序,即public $actsAs = ['MyCustom', 'Containable']。这个解决方案不是最好的,因为在其他模型中可能还有其他行为依赖于Containable,所以Containable的顺序需要在app中明确地设置在每个模型中,并希望我没有在某个地方破坏应用程序。

关于SO here的一个类似的(相关的)问题,但没有答案。

需要一个更强大的解决方案,可以满足MyCustomBehavior的需求,而无需在应用程序的其余部分进行更改,并寻找任何意外的行为。

cakephp cakephp-2.x
1个回答
0
投票

尝试1(不完美,容易出错):

恢复所有原始关联的一种方法是调用

$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo;    // will now have original belongsTo assoc

但是,如果我在查找调用中使用1066 Not unique table/alias(使用默认的joins)显式连接到已经关联的模型,这种方法可能会失败(SQL错误alias)正常工作。这是因为Containable还将尝试加入由resetBindings()调用恢复的所有这些表,导致使用相同的别名执行两次连接。

尝试2(完美#,没有已知的副作用##): 进一步挖掘核心Containable行为和docs导致我反对$MyModel->__backOriginalAssociation$MyModel->__backAssociation(奇怪的是,ContainableBehavior从未使用$__backContainableAssociation作为变量名称暗示)这是由这种行为创建和使用来执行resetBindings()。因此,我的最终解决方案是简单地检查我的模态是否启用了Containable(在我的情况下是冗余的,因为它附加在AppModel中并且从未在整个应用程序中禁用或分离)并检查对象是否在模型上设置。

// somewhere in MyCustomBehavior
private function __getOriginalAssociations(Model $Model, $type = 'belongsTo') {
    if(isset($Model->__backAssociation['belongsTo']) && !empty($Model->__backAssociation['belongsTo'])) {   // do an additional test for $Model->Behaviors->enabled('Containable') if you need
        return $Model->__backAssociation[$type];
    }

    return $Model->$type;
}

public function beforeFind(Model $Model, $query) {
    // somewhere in MyCustomBehavior::beforeFind()
    ...
    $belongsToAssoc = $this->__getOriginalAssociations($MyModel, 'belongsTo');    // will now have original belongsTo assoc
    ...
return $query;
}

$__backAssociation暂时拥有模型关联以允许动态(非)绑定。通过合并$Model->belongsTo$Model->__backAssociation['belongsTo'](或hasManyhasOnehasAndBelongsToMany)的结果,可以进一步改进这种解决方案,以包括任何动态绑定的模型。我不需要它,所以我将跳过代码进行合并。


#非常适合我自己的用例和我的应用程序设置。 ##我的测试中没有发现因我的专业知识/技能水平而受到限制的副作用。 免责声明:我在这篇文章中的工作是根据WTF Public License(WTFPL)许可的。所以,用材料做你想要的f ** k。此外,对于因使用上述材料而导致的任何财务,身体或精神损失,我概不负责。在尝试复制/粘贴之前,请自担风险并进行自己的研究。不要忘记看看cc by-sa 3.0,因为SO说“用户贡献在cc by-sa 3.0下获得许可,需要归属。” (查看此页面上的页脚。我知道你今天之前从未注意到它!:p)

© www.soinside.com 2019 - 2024. All rights reserved.