CakePHP1.2

モデルのアソシエーションについて。
以下のような3つのテーブルがあるとき

1.所有物一覧情報テーブル(
id
user_id
item_id
quantity(数量)


2.アイテムテーブル(
id
item_name
item_size
maker_id


3.メーカーテーブル(
id
maker_name


user_idから所有物一覧情報を探しにいってmaker_nameまで取得できるようにアソシエーションを設定したいのですが可能でしょうか。
belongsToを親子関係にする方法はわかるのですが、孫要素まで設定する方法がわかりません。

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2008/10/15 13:50:00
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:t_shiono No.1

回答回数256ベストアンサー獲得回数22

ポイント200pt

Modelのrecursiveの指定では駄目なのでしょうか?

User, Item, Makerのモデルがあり、

User hasAndBelongsTo Item

Item hasAndBelongsTo User

Item belongsTo Maker

Maker hasMany Item

というアソシエーションを与えておけば、

コントローラ内で、

$this->User->findAll(array('id' => ユーザID), null, null, null, null, 2);

とすれば、メーカー名まで取得できます。

id:dedara

recursiveというものを知りませんでした。

うまくいきそうなので試してみます

ありがとうございました


【追記】

わからない点がいろいろ出てきたのでコメント欄に書きました

2008/10/15 03:25:08
  • id:t_shiono
    回答の方で出したのが正攻法だと思いますが、データの形式なんかがこれでは嫌だということであれば、該当するモデルのafterFind()オーバーライドすることになると思います。
  • id:dedara
    試そうと思ったのですが提示していただいたアソシエーションの設定についてよくわかりませんでした。
    ユーザー情報を持つUserモデルとItemモデルをhasAndBelongsToManyで関連付けないといけないということでしょうか。

    最初に試してみたのは所有物一覧テーブルを参照するOwnモデルからItemモデルにbelongsToで関連付け、
    次にItemモデルからMakerモデルにbelongsToで関連付けてやってみましたがうまくいきませんでした。
    関連付けが足りないのかと考えItemモデルからOwnモデルに、MakerモデルからItemモデルにそれぞれhasManyで関連付けましたが、これもうまくいきません。
    このようにbelongsToで繋げていくだけではダメなのでしょうか。

    hasAndBelongsToManyを使う場合ですが、ユーザー情報を持つUserモデルからjointableとして「所有物一覧情報テーブル」を設定し、foreignKeyにuser_id、assosiationfreignKeyにitem_idを設定するという感じでしょうか。
    またこれもUserモデルからItemモデル、ItemモデルからUserモデル、それぞれ両方からの関連付けが必要なのでしょうか。

    長々とすみません。
  • id:t_shiono
    beloingsToであっても問題はないと思います。
    具体的に何ができなかったのでしょうか?

    さきほど確認したもので、強引に関連テーブルのモデルItemsUserを作成して、

    $itemsUsers = $this->ItemsUser->findAll(array('user_id' => ユーザID), null, null, null, null, 2);

    とすることで、メーカー名まで取得できますよ。
    例えばですが、これで取得できた所有物モデルのリストの先頭のもののメーカー名であれば、

    $itemsUsers[0]['Item']['Maker']['name']

    のような形で取得できます。


    hasAndBeloingsToManyで提示したのは、最終的にはそんな感じになると想像したためです。
    所有物一覧情報テーブルというのは、ユーザテーブル(あればですが)とアイテムテーブルとの関連テーブルですよね?
    関連付けについては、片方向でも構わないと思います。ようはやりたいことができればよいので、先ほど上げたものは、一通り列挙したまでです。

  • id:dedara
    具体的には以下のような状況です

    ●Ownモデル
    //var $useTable = "owns (所有物一覧情報テーブル)"
    var $recursive = 2;
    var $belongsTo = array("Item"=>
      array("className" => "Item",
        "conditions" => "",
        "order" => "",
        "foreignKey" => "item_id")
        );

    ここまでで$this->Own->findAll()でアイテム情報まで取れます。
    メーカー名も取りたいので

    ●Itemモデル
    //var $useTable = "items (アイテムテーブル)"
    var $belongsTo = array("Maker"=>
      array("className" => "Maker",
        "conditions" => "",
        "order" => "",
        "foreignKey" => "maker_id")
        );

    としてありますが、
    $this->Own->findAll( $cond , $fields , null , null , null, 2);
    こんな風に取ろうとすると
    Unknown column 'Maker.maker_name' in 'field list'
    と怒られ
    発行されてるクエリーを見ると
    SELECT `Item`.`id`, `Item`.`item_name`, `Own`.`quantity`, `Item`.`maker_id`, `Maker`.`maker_name`, `Item`.`item_size` FROM `owns` AS `Own` LEFT JOIN `items` AS `Item` ON (`Own`.`item_id` = `Item`.`id`) WHERE `Own`.`user_id` = 6
    アイテムテーブルがLeftJoinされてるだけ、、という感じです
  • id:t_shiono
    提示してもらったモデル自体は問題ないと思いますが、次のことを確認してみてください。

    ・app/tmp/cache/model内にあるキャッシュを削除する
    ・$fieldsで指定している部分をnullにするとどうなりますか?

    あとは、Makerモデルも念のため見せていただけますか?

    手元でうまく動作しているモデルは次のようになっています。

    ・ItemsUser
    var $belongsTo = array('Item' => array('className' => 'Item',
    'foreignKey' => 'item_id',
    'conditions' => '',
    'fields' => '',
    'order' => ''));


    ・Item
    var $belongsTo = array('Maker' => array('className' => 'Maker',
    'foreignKey' => 'maker_id',
    'conditions' => '',
    'fields' => '',
    'order' => ''));

    ・Maker
    var $hasMany = array('Item' => array('className' => 'Item',
    'foreignKey' => 'maker_id',
    'dependent' => false,
    'conditions' => '',
    'fields' => '',
    'order' => '',
    'limit' => '',
    'offset' => '',
    'exclusive' => '',
    'finderQuery' => '',
    'counterQuery' => ''));
  • id:dedara
    $fieldsをnullにしたらうまくいきました。
    Makerモデルには何も設定していません。

    $fields = array("Item.id","Item.item_name","Own.quantity","Item.maker_id","Maker.maker_name","Item.item_size");

    設定の仕方がおかしかったのでしょうか、この辺りはまた調べてみます
  • id:t_shiono
    動作するようになってよかったですね。

    このfieldsに指定するとうまくいかない件ですが、現状の階層構造を持ったデータの取得は一気にJOINして行なわないために問題となります。今後は改善されていくと期待していますが、現在のcakePHPの実装にはパフォーマンス的にまだチューニング可能な余地が残っています。

    今回のSELECTも

    SELECT
    *
    FROM
    users_items
    LEFT JOIN items ON users_items.item_id = items.id
    LEFT JOIN makers ON items.maker_id = makers.id
    WHERE
    users_items.user_id = AAAA;

    というようなクエリを発行してくれればよいのでですが、残念ながら現状では、まずデフォルトの階層数である1までしかJoinしてクエリを発行しません。つまり、

    SELECT
    *
    FROM
    users_items
    LEFT JOIN items ON users_items.item_id = items.id
    WHERE
    users_items.user_id = AAAA;

    というクエリをまず発行します。
    その上で、取得できたitemsの項目に対して、

    SELECT * FROM makers WHERE id = XXXX;
    SELECT * FROM makers WHERE id = YYYY;
    SELECT * FROM makers WHERE id = ZZZZ;

    というリクエストを発行します。

    そのため、$fieldsに2階層目以降のモデルのカラムが含まれているとエラーになってしまうのです。

  • id:dedara
    >動作するようになってよかったですね。
    はい。大変助かりました。

    なるほど。そうなんです、最終的には一度にJOINしてくれるものと考えていたので、そこから間違っていたようです。2階層目以降の$fieldsの制御は少し工夫が必要そうです。詳しい補足までありがとうございました

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません