在CakePHP中使用可包含行为后,获取原始关联

时间:2018-08-30 08:17:38

标签: cakephp cakephp-2.x

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

问题:每当我在find()的{​​{1}}调用中包含相关模型时,由于{{1}行为将未包含的模型解除绑定。但是,如果我未在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->belongsTo中的MyCustomBehavior::beforeFind()

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

显而易见的解决方案:一种简单的解决方法是在MyModel->belongsTo中设置MyCustomBehavior::beforeFind()的行为,而不是$belongsTo = array( 'MyAnotherModel' => array( ... ), 'Creator' => array( ... ), 'Approver' => array( ... ), 'Status' => array( ... ), ); 来控制加载代码的顺序行为,即Containable。此解决方案不是最佳解决方案,因为在其他模型中可能还存在其他依赖于MyModel的行为,因此需要在应用程序的每个模型中明确设置AppModel的顺序,并希望我不会破坏该应用在某处。

有人在SO here上提出了类似(相关)的问题,但没有答案。

需要一种更强大的解决方案,该解决方案能够满足public $actsAs = ['MyCustom', 'Containable']的需求,而无需在应用程序的其余部分进行更改,也无需查找任何意外行为。

1 个答案:

答案 0 :(得分:0)

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

恢复所有原始关联的一种方法是致电

AlarmManager

但是,如果我在find调用中使用$MyModel->resetBindings($MyModel->alias); $belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc (使用默认1066 Not unique table/alias)显式加入一个已经存在的方法,则此方法可能无法正常工作(SQL错误joins)相关模型。这是因为alias还将尝试联接所有通过Containable调用还原的表,从而导致使用相同的别名执行两次联接。

尝试2个(完美的,没有已知的副作用 ##
进一步挖掘核心resetBindings()的行为和docs导致我反对Containable$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']hasManyhasOne)的结果以包含任何模型,可以肯定地进一步改进此解决方案。我不需要它,所以我将跳过合并代码。


完美 适用于我自己的用例和应用设置。
## 在我的测试中未发现副作用,这受我的专业知识/技能水平的限制。
免责声明:我在这篇文章中的工作获得了WTF Public License(WTFPL) 的许可。因此,用您想要的材料做些什么。此外,对于因使用上述材料而造成的任何形式的经济,身体或精神损失,我不承担任何责任。使用前需要您自担风险,并自行进行复制研究。不要忘了看cc by-sa 3.0,因为SO指出“根据cc by-sa 3.0许可的用户贡献需要归因。” (请检查此页脚上的页脚。我知道您从未在今天之前注意到它!:p)