web-dev-qa-db-ja.com

CakePHPで複数のテーブルにまたがる結合クエリを作成するにはどうすればよいですか?

誰でも私に教えてもらえますか?結合された結果をCakePHPの複数のテーブルから取得する方法(cakePHP mvcアーキテクチャを使用)。たとえば、結合する3つのテーブル(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()など)。

15
Manish Sharma
$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の記事で言及されているように: link text

41
jmcneese

ここでは正直に言って、getTopicVotes()のような関数を作成してquery()を呼び出すだけで、モデルに関数を作成した方がずっと楽になると思います。私が考えることができる他のすべての解決策は、それをより複雑にし、したがって醜くするだけです。

編集:

データのサイズに応じて、モデル関係を適切に設定したと仮定すると(トピックhasManyアイテムhasMany投票)、allアイテムを含む簡単なfind( 'all')を実行できます投票してから、次のようにします。

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

ここでは2つのことが重要です。

  1. 大量のデータがある場合は、おそらくこのアプローチを忘れてください。地獄のように遅くなります。
  2. 私はこれを私の記憶から書いたので、実際にはこのように見えなかったり、まったく機能しない可能性があります:-)
2

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',
    ),
));
2
Sander Marechal

必要なのは、再帰的な関連付けのサポートです。これは、現在のCakePHPでは不可能です。
いくつかの bindModelトリック を使用して達成できますが
または実験的な RecursiveAssociationBehavior

これらのソリューションはどちらも、追加のコードを使用するか、アプリケーションの動作に依存する必要がありますが、純粋なSQLコードを記述したくない場合は、Cakeのページネーション、自動条件を使用できるというメリットがあります。モデルマジックなど。

1
duckyflip

この回答はすでに提出されていると思いますが、まだこれを求めている人のためにここに投稿しています。結合はfind()メソッドで行うことができ、以下のようになります。

$result = $this->ModelName1->find("all",array(
'fields' => array('ModelName1.field_name','Table2.field_names'), // retrieving fileds 
'joins' => array(  // join array
    array(
        'table' => 'table_name',
        'alias' => 'Table2',
        'type' => 'inner',
        'foreignKey' => false,
        'conditions'=> array('ModelName1.id = Table2.id') // joins conditions array
    ),
    array(
        'table' => 'table_name3',
        'alias' => 'Table3',
        'type' => 'inner',
        'foreignKey' => false,
        'conditions'=> array('Table3.id = Table2.id')
    )
))); 
0
Sanjun Dev