Получите оригинальные ассоциации после использования Containable поведения в CakePHP - PullRequest
0 голосов
/ 30 августа 2018

Фон: 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->belongsTo in 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 для управления порядком загрузки поведений, то есть public $actsAs = ['MyCustom', 'Containable'] , Это решение не является лучшим, потому что в других моделях могут быть другие поведения, которые зависят от Containable, поэтому порядок Containable должен быть явно установлен в каждой модели в приложении и надеяться, что я где-то не сломал приложение.

Подобный (связанный) вопрос был задан на SO здесь , но не имеет ответов.

Требуется более надежное решение, которое может удовлетворить потребности MyCustomBehavior без необходимости вносить изменения в остальную часть приложения и следить за непредвиденным поведением.

1 Ответ

0 голосов
/ 30 августа 2018

Попытка 1 (несовершенная, подверженная ошибкам) ​​:

Один из способов восстановить все исходные ассоциации - позвонить

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

Однако этот подход может не работать (ошибка SQL 1066 Not unique table/alias), чтобы работать правильно, если я использовал joins в моем вызове find (используя значение по умолчанию alias), чтобы явно присоединиться к уже связанной модели. Это связано с тем, что Containable также попытается объединить все эти таблицы, восстановленные с помощью вызова resetBindings(), в результате чего соединение будет выполнено дважды с одинаковым псевдонимом.

Попытка 2 (идеально # , никаких известных побочных эффектов ## ) :
Дальнейшее копание в поведении ядра 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'] (или hasMany, hasOne, hasAndBelongsToMany), чтобы включить любые модели, которые были связаны на лету. I мне это не нужно, поэтому я пропущу код для слияния.


# Идеально для моего собственного случая использования и настройки моего приложения.
## Никаких побочных эффектов не было обнаружено в моем тестировании, которое ограничено моим уровнем знаний / навыков.
Отказ от ответственности: мои работы в этом посте лицензированы под WTF Public License (WTFPL) . Итак, делайте все, что хотите, с материалом. Кроме того, я не несу ответственности за какие-либо финансовые, физические или психические потери в результате использования вышеуказанных материалов. Используйте на свой страх и риск и проведите собственное исследование ** **, прежде чем пытаться копировать / вставить. Не забудьте взглянуть на cc by-sa 3.0 , потому что SO говорит "Вклады пользователей, лицензируемые под cc by-sa 3.0, с указанием авторства". (проверьте нижний колонтитул на этой странице. Я знаю, вы никогда не замечали этого раньше!: p)

...