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

在CakePHP中使用Containable行为后获取原始关联,cakephp,cakephp-2.x,Cakephp,Cakephp 2.x,背景:CakePHP 2.6.3。一个相当稳定的应用程序。创建新行为(MyCustomBehavior)以输出一些额外信息。 我有一个模型MyModel充当Containable(在AppModel中定义),然后是MyCustom(在MyModel中定义)MyCustomBehavior的编写方式需要处理模型与我的应用程序中其他模型的关联 问题:每当我在MyModel的find()调用中包含相关模型时,我都无法获得MyModel关联的完整列表,因为Containable行为会解除未包含的模型的绑

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

问题:每当我在
MyModel
find()调用中包含相关模型时,我都无法获得
MyModel
关联的完整列表,因为
Containable
行为会解除未包含的模型的绑定。但是,如果我没有在我的
find()
选项中设置
contain
,或设置
'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' => ''
    ),
);
$this->MyModel->find('all', array(
    'fields' => array(...),
    'conditions' => array(...),
    'contain' => array('Approver', 'Status')
));
$belongsTo = array(
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
$belongsTo = array(
    'MyAnotherModel' => array(
        ...
    ),
    'Creator' => array(
        ...
    ),
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
示例
find()

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' => ''
    ),
);
$this->MyModel->find('all', array(
    'fields' => array(...),
    'conditions' => array(...),
    'contain' => array('Approver', 'Status')
));
$belongsTo = array(
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
$belongsTo = array(
    'MyAnotherModel' => array(
        ...
    ),
    'Creator' => array(
        ...
    ),
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
MyCustomBehavior::beforeFind()中
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' => ''
    ),
);
$this->MyModel->find('all', array(
    'fields' => array(...),
    'conditions' => array(...),
    'contain' => array('Approver', 'Status')
));
$belongsTo = array(
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
$belongsTo = array(
    'MyAnotherModel' => array(
        ...
    ),
    'Creator' => array(
        ...
    ),
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
MyCustomBehavior::beforeFind()中,预期
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' => ''
    ),
);
$this->MyModel->find('all', array(
    'fields' => array(...),
    'conditions' => array(...),
    'contain' => array('Approver', 'Status')
));
$belongsTo = array(
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
$belongsTo = array(
    'MyAnotherModel' => array(
        ...
    ),
    'Creator' => array(
        ...
    ),
    'Approver' => array(
        ...
    ),
    'Status' => array(
        ...
    ),
);
显而易见的解决方案:解决问题的一个愚蠢方法是在
MyModel
中简单地设置
Containable
行为,而不是
AppModel
来控制加载行为的顺序,即
public$actsAs=['MyCustom',Containable']
。这个解决方案不是最好的,因为在依赖于
Containable
的其他模型中可能存在其他行为,所以
Containable
的顺序需要在应用程序中的每个模型中明确设置,希望我没有在某个地方破坏应用程序

有人问了一个类似的(相关的)问题,但没有答案

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

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

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

$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo;    // will now have original belongsTo assoc
但是,如果我在find调用中使用了
连接
(使用默认的
别名
)显式连接到已经关联的模型,这种方法可能会失败(SQL错误
1066非唯一表/别名
)。这是因为
Containable
还将尝试联接由
resetBindings()
调用还原的所有这些表,从而使用相同的别名执行两次联接

尝试2(完美,无已知副作用):
进一步挖掘核心
Containable
行为,我发现object
$MyModel->\uuuu backOriginalAssociation
$MyModel->\uu backAssociation
(奇怪的是
ContainableBehavior
从未使用变量名所示的
$\uu backOriginalAssociation
)此行为创建并使用它来执行
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;
}
$\uuuu backAssociation
暂时保留模型关联以允许动态(取消)绑定通过将
$Model->belongsTo
$Model->\uu backAssociation['belongsTo']
(或
hasMany
hasOne
hasandblongstomy
)的结果合并以包含任何动态绑定的模型,此解决方案肯定会得到进一步改进。
我不需要它,因此,我将跳过合并的代码


#非常适合我自己的用例和我的应用程序设置。
###在我的测试中没有发现受我的专业知识/技能水平限制的副作用。

免责声明:我在这篇文章中的作品是根据授权的。所以,对材料做你想做的事。此外,我对因使用上述材料而造成的任何经济、身体或精神损失不承担任何责任。在尝试复制/粘贴之前,请自行承担使用风险并进行深入研究。别忘了看一看,因为上面写着“根据sa 3.0抄送授权的用户贡献,需要署名。”(查看此页的页脚。我知道你今天之前从未注意到过它!:p)

好吧,我想有人会发布一个比我的答案更好的答案,或者至少告诉我一个我认为完美的解决方案可能存在的问题。因为在这篇帖子上没有太多的活动,我将我的解决方案标记为已被接受,直到有人能够提供一个具有良好推理能力的更好的解决方案。如果值得的话,我不介意改变我接受的解决方案。