Как написать запрос соединения для нескольких таблиц в 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, автоматические условия, магия модели и т.д.
Ответ 5
Вы должны изучить HaBTM (имеет и принадлежит многим)
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html