本文介绍了在CakePHP中使用Containable行为后获取原始关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Background: CakePHP 2.6.3. A pretty stable app. New behavior (MyCustomBehavior) created to output some extra info.I have a Model MyModel acting as Containable (defined in AppModel) and then MyCustom (defined in MyModel). MyCustomBehavior is written in a way that it needs to work with the Model's associations with other Models in my app.

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

Problem: Whenever I contain related models in my find() call of MyModel, I cannot get a complete list of MyModel associations because Containable behavior unbinds the models that are not contained. However, if I don't set contain in my find() options or set 'contain' => false everything works as expected.

示例 MyModel->属于

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->的结果属于 MyCustomBehavior :: beforeFind()

Result of MyModel->belongsTo in MyCustomBehavior::beforeFind()

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

期望 MyModel->属于 MyCustomBehavior :: beforeFind()

Expected MyModel->belongsTo in MyCustomBehavior::beforeFind()

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

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

Obvious solution: One dumb way to solve the problem is to simply set Containable behavior in MyModel instead of AppModel to control the order of loading the behaviors, i.e., public $actsAs = ['MyCustom', 'Containable']. This solution is not the best because there may be other behaviors in other models that depend on Containable, so the order of Containable needs to set in each model in app explicitly and hope that I didn't break the app somewhere.

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

A similar(related) question was asked on SO here but has no answers.

需要一个更强大的解决方案,可以解决 MyCustomBehavior 的需求,而无需在应用程序的其余部分进行更改

Need a more robust solution that can address the needs of MyCustomBehavior without having to make changes in rest of the app and looking out for any unexpected behavior.

推荐答案

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

Attempt 1 (Imperfect, error prone):

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

One way to recover all the original associations is to call

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

但是,这种方法可能会失败(SQL错误 1066不是唯一的表/别名)如果我在查找调用中使用了 joins (使用默认的 alias )以显式加入已关联的模型。这是因为 Containable 还将尝试联接所有通过 resetBindings()调用还原的表,从而导致两次执行联接

However, this approach it may fail (SQL error 1066 Not unique table/alias) to work correctly if I had used joins in my find call (using default alias) to explicitly join to an already associated model. This is because Containable will also attempt to join all these tables restored by resetBindings() call resulting in join being performed twice with same alias.

尝试2 (perfect ,没有已知的副作用, no known side effects):
Further digging through the core Containable behavior and docs led me to object $MyModel->__backOriginalAssociation and $MyModel->__backAssociation (weird enough that ContainableBehavior never used $__backContainableAssociation as the variable name suggests) that was created and used by this behavior to perform resetBindings(). So, my final solution was to simply check if Containable is enabled on my modal (redundant in my case because it is attached in AppModel and is never disabled or detached throughout the app) and check if the object is set on the model.

// 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' ] (或 hasMany hasOne hasAndBelongsToMany )包含所有在运行中绑定的模型。我不需要它,因此我将跳过合并代码。

$__backAssociation holds model associations temporarily to allow for dynamic (un)binding. This solution can definitely be further improved by merging results of $Model->belongsTo and $Model->__backAssociation['belongsTo'] (or hasMany, hasOne, hasAndBelongsToMany) to include any models that were bound on the fly. I don't need it, so I will skip the code for merging.

完美,适合我自己的用例和应用设置。

在我的测试中未发现副作用,受我的专业知识/技能水平的限制。

免责声明:我的工作本帖子中的内容已获得 的许可。因此,用您想要的材料做些什么。此外,对于因使用上述材料而造成的任何形式的经济,身体或精神损失,我不承担任何责任。使用前需要您自担风险,并自行进行复制研究。不要忘了看,因为根据CC by-sa 3.0许可的用户贡献,需要提供出处。 (请检查此页脚上的页脚。我知道您从未在今天之前注意到它!:p)

Perfect for my own use case and my app setup.
No side effects were found in my testing that is limited by my level of expertise/skill.
Disclaimer: My work in this post is licensed under WTF Public License(WTFPL). So, do what the f**k you want with the material. Additionally, I claim no responsibility for any financial, physical or mental loss of any kind whatsoever due to the use of above material. Use at your own risk and do your own f**king research before attempting a copy/paste. Don't forget to take a look at cc by-sa 3.0 because SO says "user contributions licensed under cc by-sa 3.0 with attribution required." (check the footer on this page. I know you never noticed it before today! :p)

这篇关于在CakePHP中使用Containable行为后获取原始关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-05 13:43
查看更多