Как написать запрос соединения для нескольких таблиц в CakePHP?

может ли кто-нибудь сказать мне, как получить объединенный результат из нескольких таблиц в cakePHP (используя mcc-архитектуру cakePHP). Например, у меня есть три таблицы для присоединения (tbl_topics, tbl_items, tbl_votes. Их взаимосвязь определяется следующим образом: в теме может быть много элементов, а элемент может иметь много голосов. Теперь я хочу получить список тем с подсчетом все голоса по всем пунктам для каждой темы. Запрос SQL для этого написан ниже:

SELECT Topic.*, count(Vote.id) voteCount 
FROM 
tbl_topics AS Topic 
LEFT OUTER JOIN tbl_items AS Item 
ON (Topic.id = Item.topic_id)
LEFT OUTER JOIN tbl_votes AS Vote
ON (Item.id = Vote.item_id); 

Моя проблема заключается в том, что я могу сделать это легко с помощью функции $this-><Model Name>->query, но для этого требуется, чтобы код sql записывался в контроллер, который я не хочу. Я пытаюсь найти другой способ сделать это (например, find()).

Ответы

Ответ 1

$markers = $this->Marker->find('all', array('joins' => array(
    array(
        'table' => 'markers_tags',
        'alias' => 'MarkersTag',
        'type' => 'inner',
        'foreignKey' => false,
        'conditions'=> array('MarkersTag.marker_id = Marker.id')
    ),
    array(
        'table' => 'tags',
        'alias' => 'Tag',
        'type' => 'inner',
        'foreignKey' => false,
        'conditions'=> array(
            'Tag.id = MarkersTag.tag_id',
            'Tag.tag' => explode(' ', $this->params['url']['q'])
        )
    )
))); 

как указано в статье nate abele: текст ссылки

Ответ 2

Я буду честным и скажу, что вы, вероятно, будете намного счастливее, если просто создадите функцию в своей модели, например getTopicVotes() и вызовите query(). Каждое другое решение, о котором я могу думать, сделает его более сложным и, следовательно, более уродливым.

Edit:

В зависимости от размера ваших данных и предположения, что вы правильно настроили свои отношения с образцом (тема hasMany Items hasMany Votes), вы можете сделать простую находку ( "все" ), содержащую все элементы и голоса, а затем сделайте что-нибудь вроде этого:

foreach ($this->data as &$topic)
{
    $votes = Set::extract('/Topic/Item/Vote', $topic);
    $topic['Topic']['vote_count'] = count($votes);
}

Здесь важны две вещи:

  • Если у вас много данных, вы, вероятно, должны забыть об этом подходе, он будет медленным, как черт.
  • Я написал это из своей памяти, и это может выглядеть не так в реальной жизни и/или может вообще не работать: -)

Ответ 3

Вы можете легко установить свойство "recursive" в запросе find().

$result = $this->Topic->find('all', array('recursive' => 2));

В качестве альтернативы вы можете использовать поведение Containable в своей модели. Затем вы можете использовать:

$this->Topic->contain(array(
    'Item',
    'Item.Vote',
));

$result = $this->Topic->find('all');

или

$result = $this->Topic->find('all', array(
    'contain' => array(
        'Item',
        'Item.Vote',
    ),
));

Ответ 4

Что вам нужно, это поддержка рекурсивных ассоциаций, что в настоящее время невозможно с запасом CakePHP.
Хотя это может быть достигнуто с помощью bindModel trickery
или экспериментальный РекурсивныйAssociationBehavior.

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