CakePHP: использование многоуровневого контейнерного поведения - PullRequest
6 голосов
/ 22 ноября 2011

Я довольно долго пытался использовать Containable Behavior в CakePHP, но не могу заставить его работать так, как ожидал.

Мое приложение отличается, но для упрощения я приведу этот пример. Допустим, у меня есть форум с темами и действиями, и действия могут быть оценены. Общие отношения будут:

Форум: hasMany [Тема]
Тема: ownTo [Форум], hasMany [Активность]
Активность: ownTo [Тема], hasMany [Рейтинг]
Рейтинг: принадлежит [Активность]

Чего я хочу добиться, так это с помощью метода поиска получить все оценки, выполненные на определенном форуме. То, что я должен сделать, должно быть следующим:

 $this->Rating->find('count', array(
    'contain' => array(
        'Activity' => array(
            'Thread'
        )
    ),
    'conditions' => array(
        'Thread.forum_id' => 1
    )
));

Но результат запроса:

SELECT COUNT(*) AS `count` FROM `ratings` AS `Rating` LEFT JOIN `activities` AS `Activity` ON (`Rating`.`activity_id` = `Activity`.`id`) WHERE `Thread`.`forum_id` = 1;

Я выполнил это с помощью опции 'joins', но она более сложная, и мне приходится использовать это своего рода действие во многих ситуациях.

Все файлы, связанные с примером, можно найти здесь: http://dl.dropbox.com/u/3285746/StackOverflow-ContainableBehavior.rar

Спасибо

Обновление 23/11/2011

После изучения структуры и благодаря ответам Moz Morris и api55 я нашел источник проблемы.

Основная проблема заключалась в том, что, как я понял CakePHP, я думал, что он каждый раз запрашивает использование соединений. То, что он этого не делает, реальная операция, которую он будет выполнять для получения результата, который я искал, будет выглядеть примерно так:

SELECT * FROM Rating JOIN Activity...
SELECT * FROM Activity JOIN Thread...
SELECT * FROM Activity JOIN Thread...
...

Это означает, что он будет выполнять запрос для получения всех действий, а затем для каждого действия выполнять запрос для получения потоков ... Мой подход был неудачным не из-за неправильного использования контейнерного поведения, а потому Параметр условий был применен ко всем запросам, и на первом из них произошел сбой из-за отсутствия таблицы потоков. После выяснения этого, есть два возможных решения:

  • Как и сказал api55, используя условия внутри массива «Содержать», он будет применять их только к запросам, использующим таблицу Thread. Но при этом проблема остается, потому что у нас слишком много запросов.

  • Как сказал Моз Моррис, связывание модели потоков с рейтингом также будет работать, и она будет выполнять один запрос, чего мы и хотим. Проблема в том, что я вижу это как патч, который пропускает отношения между моделями и не следует философии CakePHP.

Я отметил правильное решение api55, поскольку оно решает конкретную проблему, с которой я столкнулся, но оба дают решение проблемы.

Ответы [ 2 ]

5 голосов
/ 22 ноября 2011

Прежде всего, вы поместили переменную actAs в appModel? без этого это поведение вообще не будет работать (я вижу, что оно не работает правильно, так как не объединяется с таблицей потоков)

Я бы сделал это сверху, я имею в виду на форуме, поэтому вы выбираете свой форум (я не уверен, что вы хотите форум или ветку) и получаете весь его рейтинг, если нет никакого рейтинга, вы в конечном итоге получите ключ рейтинга пустым .

как то так

AppModel

public $actsAs = array('Containable');

регулятор рейтинга

 $this->Rating->Activity->Thread->Forum->find('count', array(
    'contain' => array(
        'Thread' => array(
             'Activity' => array(
                 'Rating' => array (
                       'fields' => array ( 'Rating.*' )
                  )
              )
        )
    ),
    'conditions' => array(
        'Forum.id' => 1
    )
));

Тогда, если вам нужно только значение в таблице рейтинга, просто используйте Set: extract, чтобы получить массив этого значения.

Поскольку вы это сделали, ЭТО ДОЛЖНО работать, но я рекомендую не использовать forum_id там, а в условиях, содержащихся внутри, как это

'contain' => array(
    'Activity' => array(
        'Thread' => array(
              'conditions' => array('Thread.forum_id' => 1)
         )
    )
),

Кроме того, никогда не забывайте переменную actAs в модели, использующей поведение (или в модели приложения)

2 голосов
/ 22 ноября 2011

Несмотря на то, что мне нравится решение api55, я думаю, что результаты немного запутанны - зависит от того, что вы намереваетесь делать с предполагаемыми данными.

Я предполагаю, что когда вы сказали, что используете метод 'joins', выя говорил об использовании этого метода:

$this->Rating->bindModel(array(
  'belongsTo' => array(
    'Thread' => array(
      'foreignKey' => false,
      'conditions' => 'Thread.id = Activity.thread_id',
    ),
    'Forum' => array(
      'foreignKey' => false,
      'conditions' => 'Forum.id = Thread.forum_id'
    )
  )
));

$ratings = $this->Rating->find('all', array(
  'conditions' => array(
    'Forum.id' => 1 // insert forum id here
  )
));

Мне это кажется немного чище, и вам не нужно беспокоиться об использовании сдерживаемого поведения в вашей AppModel.Стоит задуматься.

...