$option = array();
$option['recursive'] = -1;
$option['joins'][] = array(
'type' => 'LEFT', //LEFT, INNER, OUTER
'table' => 'posts',
'alias' => 'Post', //下でPost.user_idと書くために
'conditions' => '`User`.`id`=`Post`.`user_id`',
);
$option['conditions'] = array('Post.isPrivate' => 1);
$this->User->find('all', $option)
User hasMany Postの場合 User->find()
だとPostはクエリーは別個に発行されます。そのためconditions
でPostのフィールドを使えません。
joins
を使う事で、上記のようにプライベートな投稿をしているユーザーを見つける事が出来ます。
上記までで素直に使えれば使いやすいけど joins
を使うとCakePHPのモデルの構造から大きく外れてしまいます。
User.id = 1が3つPostを持っていたら、LEFT JOINするとレコードは3つになります。User.id = 1+それぞれのPostをくっ付けたレコードです。CakePHPはどうやら、これをうまい事処理して、いつも通りのUser hasMany Post的な配列にはしてくれません。そのまま重複したレコードをUser->find()の結果として返してきます。
CakePHPは色々とドキュメントに書いてない機能やオプションが多いので、うまい事やってくれる方法、もしかしたら有るかもしれない。というか有ってほしい。
array(
(int) 0 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
)
),
(int) 1 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
)
),
(int) 2 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
)
)
)
$option = array();
$option['recursive'] = -1;
$option['joins'][] = array(
'type' => 'LEFT',
'table' => 'posts',
'alias' => 'Post',
'conditions' => '`User`.`id`=`Post`.`user_id`',
);
$option['conditions'] = array('User.id' => 1, 'Post.isPrivate' => 1);
$this->User->find('all', $option);
相性が悪いというか、上記の問題があるので、CakePHPのアソシエーションを使った取り方とは、カテゴリーが違うのかも。$option['recursive'] = -1
を設定しておかないと、重複した3つのUserレコード毎に3つのPostを持たせた配列を生成します。ただ、postsテーブルへのクエリーは1個にまとめてくれてはいます。
array(
(int) 0 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
),
(int) 1 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
),
(int) 2 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
)
)
$option = array();
// $option['recursive'] = -1; <- コメントアウト
$option['joins'][] = array(
'type' => 'LEFT',
'table' => 'posts',
'alias' => 'Post',
'conditions' => '`User`.`id`=`Post`.`user_id`',
);
$option['conditions'] = array('User.id' => 1, 'Post.isPrivate' => 1);
$this->User->find('all', $option);
CakePHPは、例えばUserが3人、それぞれ3レコードずつ持っているとしても、hasManyアソシエーションで取得する場合、usersテーブルへ1クエリー、その結果のUser.idをWHERE INに使って、postsテーブルに1クエリーの2クエリーでデータを取得しています。
$option['fields'] = array('*');
$option['fields'] = array('Post.*');
$option['fields'] = array('User.id', 'User.username', 'Post.title', 'Post.isPrivate');
fields
でちゃんと指定しないとJOINしたテーブルのデータがとれません。
array(
(int) 0 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
'id' => '1',
'isPrivate' => '1'
)
),
(int) 1 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
'id' => '2',
'isPrivate' => '1'
)
),
(int) 2 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
'id' => '3',
'isPrivate' => '1'
)
)
)
$option = array();
$option['recursive'] = -1;
$option['joins'][] = array(
'type' => 'LEFT',
'table' => 'posts',
'alias' => 'Post',
'conditions' => '`User`.`id`=`Post`.`user_id`',
);
$option['conditions'] = array('User.id' => 1, 'Post.isPrivate' => 1);
$option['fields'] = array('*'); //<- 追加
$this->User->find('all', $option);
上記2つが混ざった様な配列が出来上がり、データとして使いにくそうです。 ただ、クエリーは変わらず2クエリーだったりします。
array(
(int) 0 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
'id' => '1',
'isPrivate' => '1',
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
),
(int) 1 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
'id' => '2',
'isPrivate' => '1',
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
),
(int) 2 => array(
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'foo',
),
'Post' => array(
'id' => '3',
'isPrivate' => '1',
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
)
)
$option = array();
// $option['recursive'] = -1; <- コメントアウト
$option['joins'][] = array(
'type' => 'LEFT',
'table' => 'posts',
'alias' => 'Post',
'conditions' => '`User`.`id`=`Post`.`user_id`',
);
$option['conditions'] = array('User.id' => 1, 'Post.isPrivate' => 1);
$option['fields'] = array('*'); //<- 追加
$this->User->find('all', $option);