CakePHP2 のアソシエーション!モデルを連携させて複数テーブルからデータを取得する勉強します!

CakePHP のバリデーションの基本を勉強します。 | oki2a24 の次の投稿となります。

以前ひとつのテーブルからのデータ取得を行いました。それも、モデルなしで。今回はモデルをしっかり使って、そしてテーブルは複数を連携させて、という例となります。2つのテーブルを使用しますが、連携のさせ方を網羅させて、しらみつぶしに動きを調べようと思います。

今回は、hasAndBelongsToMany を除いた hasOne、belongTo、hasMany の3パターンを取り扱います。

ポイント

  • モデルに連携したい相手のモデルを書く。書き方は、hasOne、belongTo、hasMany、hasAndBelongsToMany の4種類ある。
  • hasOne 1:1。相手テーブルに自テーブルの id 値を設定します。例、users テーブルの1つの行は、profiles テーブルの1つの行を持っている。
  • belongTo n:1。自テーブルに相手テーブルの id 値を設定します。例、users テーブルの1行は、recipes テーブルの多くの行を持つことができる。
  • hasMany 1:n。相手テーブルに自テーブルの id 値を設定します。例、recipes テーブルの多くの行は、users テーブルの1つの行に所属している。
  • belongTo と hasMany はどちらも1つに対して複数の対応ですが、1:n ( hasMany )の場合は1つの行に対して複数の行が紐づいた状態でデータを取得できます。一方、n:1 ( belongTo ) の場合は1つの行に対して1つの行が紐づいた状態でデータを取得できます。
  • モデルの連携は適当では駄目です。エラーとなります。または、変なデータが取れると思います。

前提

  • CakePHP 2.1.3 導入済み。
  • ドキュメントルート/cake/sample/(ここにCakePHP が入ってます。app ディレクトリとか、.travis.yml ファイルとか。)

連携のパターンとテーブル

personals テーブルと、boards テーブルの2つを例にとります。boards テーブルに、personal_id 列を設け、これで personals テーブルと連携します。3つの連携方法と組み合わせて、全部で次の6パターンを試しました。

結果も出てしまっていますけれども、パターンは次のとおりです。

No 関係 Personal モデル Board モデル Boards コントローラ index アクション 正誤
1 Personal hasOne Board public $hasOne = ‘Board’; $this->Personal->find(‘all’);
2 Personal belongsTo Board public $belongsTo = ‘Board’; $this->Personal->find(‘all’);
3 Personal hasMany Board public $hasMany = ‘Board’; $this->Personal->find(‘all’);
4 Board hasOne Personal public $hasOne = ‘Personal’; $this->Board->find(‘all’);
5 Board belongsTo Personal public $belongsTo = ‘Personal’; $this->Board->find(‘all’);
6 Board hasMany Personal $this->Board->find(‘all’);

テーブルは次の二つです。行データのSQL も載せておきます。

-- DB、ユーザ、の作成。パスワードの設定
GRANT ALL PRIVILEGES ON sampledb.* TO sampleuser@localhost IDENTIFIED BY 'samplepassword';
FLUSH PRIVILEGES;
CREATE DATABASE sampledb CHARACTER SET utf8;

--
-- テーブルの構造 `boards`
--

CREATE TABLE IF NOT EXISTS `boards` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `personal_id` INT NOT NULL,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;

--
-- テーブルのデータをダンプしています `boards`
--

INSERT INTO `boards` (`id`, `personal_id`, `title`, `content`) VALUES
(1, 1, 'はっけよーい', 'のこった!★のこった!★'),
(2, 1, '位置について、よーい', 'ドン!ドン!ドン!ドドンガドン! ドン!ドン!ドン!ドドンガドン! '),
(3, 1, 'こんにちは!', 'こんにちは!こんにちは!'),
(4, 1, 'おはよう!', 'おはようっ!ぐっとモーニング!'),
(5, 1, 'はっけはっけ', 'のこった!のこった!のこった!のこった!'),
(6, 2, 'かかかかkっか', 'カッカッカッカッカッ!'),
(7, 2, '佐藤佐藤!!', '佐藤佐藤佐藤佐藤'),
(8, 2, 'あまいよ', 'あっま~い!甘い、砂糖だよ♪'),
(9, 3, 'しましま', 'しまうましまうまサファリアフリカ');

--
-- テーブルの構造 `personals`
--

CREATE TABLE IF NOT EXISTS `personals` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  `comment` varchar(255) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

--
-- テーブルのデータをダンプしています `personals`
--

INSERT INTO `personals` (`id`, `username`, `password`, `comment`, `created`, `modified`) VALUES
(1, 'yamada', 'yamada', '山田', NULL, NULL),
(2, 'sato', 'sato', '佐藤', NULL, NULL),
(3, 'kato', 'kato', '加藤', NULL, NULL);

ソース

cake/sample/app/Model/Personal.php

<?php
class Personal extends AppModel {
	public $name = 'Personal';
	// public $hasOne = 'Board';
	// public $belongsTo = 'Board';
	// public $hasMany = 'Board';
}
?>

4、5、6行目のコメントパターンによって外して試します。
cake/sample/app/Model/Board.php

<?php
class Board extends AppModel {
	public $name = 'Board';
	// public $hasOne = 'Personal';
	// public $belongsTo = 'Personal';
	// public $hasMany = 'Personal';
}
?>

こちらも、4、5、6行目のコメントパターンによって外して試します。
cake/sample/app/Controller/BoardsController.php

<!--?php class BoardsController extends AppController { 	// The name of this controller. Controller names are plural, named after the model they manipulate. 	public $name = 'Boards'; 	// An array containing the class names of models this controller uses. 	public $uses = array('Board', 'Personal'); 	/** 	 * 初期表示 	 */ 	public function index() { 		// Personal を取得。 		// $data = $this--->Personal-&gt;find('all');
		// Board を取得。
		// $data = $this-&gt;Board-&gt;find('all');
		// ビューで使えるように DB データをセット
		$this-&gt;set('data', $data);
	}
}
?&gt;

public $uses でこのコントローラーで使用するモデルを指定します。2つのテーブルの連携を試すので、2つのモデルを指定しています。

14、16行目で、どちらのモデルから find するかをパターンによって切り替えます。

cake/sample/app/View/Boards/index.ctp

<h1>複数テーブル連携サンプル</h1>
<pre>
<?php
print_r($data);
?>
</pre>

結果 1.Personal hasOne Board

以下、http://localhost/cake/sample/boards/ にアクセスしたときの結果となります。

その前に、テーブルの連携図を掲載します。

localhost - localhost - sampledb - phpMyAdmin 3.4.5_1.jpg

結果の要点です。

Array
(
    [0] => Array
        (
            [Personal] => Array
                (
                    [id] => 1
                    [username] => yamada
                    [password] => yamada
                    [comment] => 山田
                    [created] => 
                    [modified] => 
                )

            [Board] => Array
                (
                    [id] => 1
                    [personal_id] => 1
                    [title] => はっけよーい
                    [content] => のこった!★のこった!★
                )

        )
・・・
)

発行された SQL です。

SELECT `Personal`.`id`, `Personal`.`username`, `Personal`.`password`, `Personal`.`comment`, `Personal`.`created`, `Personal`.`modified`, `Board`.`id`, `Board`.`personal_id`, `Board`.`title`, `Board`.`content` FROM `sampledb`.`personals` AS `Personal` LEFT JOIN `sampledb`.`boards` AS `Board` ON (`Board`.`personal_id` = `Personal`.`id`) WHERE 1 = 1

結果 2.Personal belongsTo Board

localhost - localhost - sampledb - phpMyAdmin 3.4.5_2.jpg

結果の要点です。

Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Personal.board_id' in 'on clause'

発行された SQL です。

SELECT `Personal`.`id`, `Personal`.`username`, `Personal`.`password`, `Personal`.`comment`, `Personal`.`created`, `Personal`.`modified`, `Board`.`id`, `Board`.`personal_id`, `Board`.`title`, `Board`.`content` FROM `sampledb`.`personals` AS `Personal` LEFT JOIN `sampledb`.`boards` AS `Board` ON (`Personal`.`board_id` = `Board`.`id`) WHERE 1 = 1

結果 3.Personal hasMany Board

localhost - localhost - sampledb - phpMyAdmin 3.4.5_3.jpg

結果の要点です。

Array
(
    [0] => Array
        (
            [Personal] => Array
                (
                    [id] => 1
                    [username] => yamada
                    [password] => yamada
                    [comment] => 山田
                    [created] => 
                    [modified] => 
                )

            [Board] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [personal_id] => 1
                            [title] => はっけよーい
                            [content] => のこった!★のこった!★
                        )

                    [1] => Array
                        (
                            [id] => 2
                            [personal_id] => 1
                            [title] => 位置について、よーい
                            [content] => ドン!ドン!ドン!ドドンガドン! ドン!ドン!ドン!ドドンガドン! 
                        )

                    [2] => Array
                        (
                            [id] => 3
                            [personal_id] => 1
                            [title] => こんにちは!
                            [content] => こんにちは!こんにちは!
                        )

                    [3] => Array
                        (
                            [id] => 4
                            [personal_id] => 1
                            [title] => おはよう!
                            [content] => おはようっ!ぐっとモーニング!
                        )

                    [4] => Array
                        (
                            [id] => 5
                            [personal_id] => 1
                            [title] => はっけはっけ
                            [content] => のこった!のこった!のこった!のこった!
                        )

                )

        )
・・・
)

発行された SQL です。

1    SELECT `Personal`.`id`, `Personal`.`username`, `Personal`.`password`, `Personal`.`comment`, `Personal`.`created`, `Personal`.`modified` FROM `sampledb`.`personals` AS `Personal` WHERE 1 = 1
2    SELECT `Board`.`id`, `Board`.`personal_id`, `Board`.`title`, `Board`.`content` FROM `sampledb`.`boards` AS `Board` WHERE `Board`.`personal_id` IN (1, 2, 3)

結果 4.Board hasOne Personal

localhost - localhost - sampledb - phpMyAdmin 3.4.5_4.jpg

結果の要点です。

Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Personal.board_id' in 'on clause'

発行された SQL です。

SELECT `Board`.`id`, `Board`.`personal_id`, `Board`.`title`, `Board`.`content`, `Personal`.`id`, `Personal`.`username`, `Personal`.`password`, `Personal`.`comment`, `Personal`.`created`, `Personal`.`modified` FROM `sampledb`.`boards` AS `Board` LEFT JOIN `sampledb`.`personals` AS `Personal` ON (`Personal`.`board_id` = `Board`.`id`) WHERE 1 = 1

結果 5.Board belongsTo Personal

localhost - localhost - sampledb - phpMyAdmin 3.4.0_55.jpg

結果の要点です。

Array
(
    [0] => Array
        (
            [Board] => Array
                (
                    [id] => 1
                    [personal_id] => 1
                    [title] => はっけよーい
                    [content] => のこった!★のこった!★
                )

            [Personal] => Array
                (
                    [id] => 1
                    [username] => yamada
                    [password] => yamada
                    [comment] => 山田
                    [created] => 
                    [modified] => 
                )

        )
・・・
)

発行された SQL です。

SELECT `Board`.`id`, `Board`.`personal_id`, `Board`.`title`, `Board`.`content`, `Personal`.`id`, `Personal`.`username`, `Personal`.`password`, `Personal`.`comment`, `Personal`.`created`, `Personal`.`modified` FROM `sampledb`.`boards` AS `Board` LEFT JOIN `sampledb`.`personals` AS `Personal` ON (`Board`.`personal_id` = `Personal`.`id`) WHERE 1 = 1

結果 6.Board hasMany Personal

localhost - localhost - sampledb - phpMyAdmin 3.4.5_6.jpg

結果の要点です。

Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Personal.board_id' in 'field list'

発行された SQL です。

SELECT `Personal`.`id`, `Personal`.`username`, `Personal`.`password`, `Personal`.`comment`, `Personal`.`created`, `Personal`.`modified`, `Personal`.`board_id` FROM `sampledb`.`personals` AS `Personal` WHERE `Personal`.`board_id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9)

おわりに

結果の画面をキャプチャしたものも載せておきます。すごく長い投稿となってしまいました!

1.ersonal hasOne Board

1 Personal hasOne Board.jpg

2.ersonal belongsTo Board

2 Personal belongsTo Board.jpg

3.ersonal hasMany Board

3 Personal hasMany Board.jpg

4.oard hasOne Personal

4 Board hasOne Personal.jpg

5.oard belongsTo Personal

5 Board belongsTo Personal.jpg

6.oard hasMany Personal

6 Board hasMany Personal.jpg 

ディスカッションに参加

4件のコメント

  1. 大変参考になります。

    原作本のフォローが中途半端で ちゃんと動作しないから
    途方にくれていました。

コメントを残す

コメントを残す