アソシエーション | 結合モデルのデータ表示と更新 | hasMany
アソシエーションは複数のモデルを1つのモデルにまとめる技術。テーブル結合のJOINと似ているが少し異なる。
一つのモデルに複数のテーブル情報を定義しておけば、あたかも一つのテーブルのようにデータを取得や更新ができる。
ただし、バリデーションは各テーブルに該当するモデルごとに定義したほうが良い。
またDB更新も複雑なテーブル連結だと上手く行かないようである。下記の更新処理もテーブルごとに分けて更新している。
コントローラ:/Controller/TestHasManyController
class TestHasManyController extends AppController { public $name = 'TestHasMany'; public $uses = array ( 'TestHasManyA', 'TestHasManyB', 'TestHasManyC' ); public function index() { if (! empty ( $this->request->data )) { $dataSource = $this->TestHasManyA->getDataSource (); $dataSource->begin (); $this->TestHasManyA->saveAll ( $this->request->data ['TestHasManyA'], array ( 'atomic' => false ) ); $errors = $this->TestHasManyA->validationErrors; if (empty ( $errors )) { $this->TestHasManyB->saveAll ( $this->request->data ['TestHasManyB'], array ( 'atomic' => false ) ); $errors = $this->TestHasManyB->validationErrors; } if (empty ( $errors )) { $this->TestHasManyC->saveAll ( $this->request->data ['TestHasManyC'], array ( 'atomic' => false ) ); $errors = $this->TestHasManyC->validationErrors; } if (empty ( $errors )) { $dataSource->commit (); } else { $dataSource->rollback (); } } else { } $data = $this->TestHasManyA->find ( 'all' ); $this->set ( array ( 'data' => $data ) ); } }
モデル:/Model/TestHasManyA
App::uses('Model', 'Model'); class TestHasManyA extends Model { public $name = 'TestHasManyA'; //アソシエーション(モデル連結)hasManyは1:多の関係を表す。 public $hasMany = array( 'TestHasManyB' => array( 'className' => 'TestHasManyB', 'foreignKey' => 'test_has_many_a_id', 'dependent' => true //連動削除 ), 'TestHasManyC' => array( 'className' => 'TestHasManyC', 'foreignKey' => 'test_has_many_a_id', 'dependent' => true //連動削除 ), ); //バリデーション情報 public $validate = array( 'name' => array( 'maxLength'=>array( 'rule' => array('maxLength', 5), 'message' => '一般名は5文字以内です。' ) ), ); }
モデル:/Model/TestHasManyB
App::uses('Model', 'Model'); class TestHasManyB extends Model { public $name = 'TestHasManyB'; public $validate = array( 'kind' => array( 'maxLength'=>array( 'rule' => array('maxLength', 8), 'message' => '種類は8文字以内です。' ) ), ); }
モデル:/Model/TestHasManyC
App::uses('Model', 'Model'); class TestHasManyC extends Model { public $name = 'TestHasManyC'; public $validate = array( 'note' => array( 'maxLength'=>array( 'rule' => array('maxLength', 16), 'message' => 'ノートは16文字以内です。' ) ), ); }
ビュー:/View/TestHasMany/index.ctp
<h1>結合モデルのデータ表示と更新 | hasMany</h1> <table border="1"> <thead> <tr><th>A_ID</th><th>一般名</th><th>テーブルBデータ</th><th>テーブルCデータ</th></tr> </thead> <tbory> <?php echo $this->Form->create('DataX', array('url' => '#')); foreach($data as $i=> $ary){ echo "<tr>"; $entA=$ary['TestHasManyA']; $id=$entA['id']; echo "<td>"; echo $entA['id']; echo $this->Form->input("TestHasManyA.{$id}.id", array('type' => 'hidden','value' => $id,'label' => false,)); echo "</td>\n"; echo "<td>"; echo $this->Form->input("TestHasManyA.{$id}.name", array('type' => 'text','value' => $entA['name'],'label' => false,)); echo "</td>"; //Bデータを表示 $dataB=$ary['TestHasManyB']; echo "<td>\n<ul>"; foreach($dataB as $i_b=> $entB){ $id=$entB['id']; echo "<li>"; echo $this->Form->input("TestHasManyB.{$id}.kind", array('type' => 'text','value' => $entB['kind'],'label' => false,)); echo $this->Form->input("TestHasManyB.{$id}.id", array('type' => 'hidden','value' => $entB['id'],'label' => false,)); echo $this->Form->input("TestHasManyB.{$id}.test_has_many_a_id", array('type' => 'hidden','value' => $entB['test_has_many_a_id'],'label' => false,)); echo "</li>\n"; } echo "</ul></td>\n"; //Cデータを表示 $dataC=$ary['TestHasManyC']; echo "<td>\n<ul>"; foreach($dataC as $i_c=>$entC){ $id=$entC['id']; echo "<li>"; echo $this->Form->input("TestHasManyC.{$id}.note", array('type' => 'text','value' => $entC['note'],'label' => false,)); echo $this->Form->input("TestHasManyC.{$id}.id", array('type' => 'hidden','value' => $entC['id'],'label' => false,)); echo $this->Form->input("TestHasManyC.{$id}.test_has_many_a_id", array('type' => 'hidden','value' => $entC['test_has_many_a_id'],'label' => false,)); echo "</li>\n"; } echo "</ul></td>\n"; echo "</tr>\n"; } ?> </tbory> </table> <?php echo $this->Form->submit('DB更新'); echo $this->Form->end(); ?>
DBテーブル
id | name |
---|---|
1 | ネコX7 |
2 | カエル |
id | test_has_many_a_id | kind |
---|---|---|
1 | 1 | ペルシャネコ |
2 | 1 | スフィンクス |
id | test_has_many_a_id | note |
---|---|---|
1 | 1 | いろは |
2 | 2 | にほへと |
3 | 2 | ちりぬのを |
アソシエーションの考え方 | hasOne,belongsTo,hasMany,hasAndBelongsToMany
関係 | 対 | 英語 | 和訳 | 説明 | 外部キー | |
---|---|---|---|---|---|---|
hasOne | 1 対 1 | A has one Bの | A(当モデル)は1つのB(外部モデル)を有する。 | Aは1つ、Bも1つ。Aは兄、Bは弟という感じ。Bが兄であるAに配慮しているイメージ。 | BにAへの外部キーあり | |
belongsTo | 多 対 1 | A belongs to X | A(当モデル)はXに属します | 1つのXに複数のAが結びつく。Aから見てXは親のようなものである。 | A(当モデル)にXへの外部キーあり | |
hasMany | 1 対 多 | A has many C | Aは多くのCを有してます | Aは1つ、Cは複数である。Aから見てCは子のようなもの。 | CにAへの外部キーあり | |
hasAndBelongsToMany | 多 対 多 | A has and belongs to many D | Aは多くのDに属します | AとDを連結した第3のテーブルあり。AとDは基本他人同士だが、会社(第3のテーブル)によって関係を結び付けられているイメージ。 | Aは外部キーなし、Dも外部キーなし |
補足資料
A hasOne B
A
id | a_name |
---|---|
2 | 那覇市 |
3 | 国頭村 |
B
那覇市は1つのカニを有してます。id | a_id | b_name |
---|---|---|
10 | 2 | カニ |
国頭村は有してません。
A LEFT JOIN B
B belongsTo A
A
id | b_id | a_name |
---|---|---|
2 | 10 | 那覇市 |
3 | 10 | 国頭村 |
B
カニは那覇市と国頭村に属するid | b_name |
---|---|
10 | カニ |
11 | ハゼ |
ハゼはどこにも属さない
A LEFT JOIN B
A hasMany B
A
id | a_name |
---|---|
2 | 那覇市 |
3 | 国頭村 |
B
那覇市はカニとハゼを持っているid | a_id | b_name |
---|---|---|
10 | 2 | カニ |
11 | 2 | ハゼ |
12 | 3 | イカ |
13 | 3 | エイ |
国頭村はイカとエイを持っている
※ 重複で有することはできない。つまりカニは那覇市と国頭村の両方で持たせることはできない。多 対 多という関係になるため。
B LEFT JOIN A
A hasAndBelongsToMany B
A
id | a_name |
---|---|
2 | 那覇市 |
3 | 国頭村 |
B
id | b_name |
---|---|
10 | カニ |
11 | ハゼ |
12 | イカ |
13 | エイ |
AB
那覇市はカニとハゼを持っているa_id | b_id |
---|---|
2 | 10 |
2 | 11 |
3 | 10 |
3 | 12 |
国頭村はカニとイカを持っている
カニは那覇市と国頭村に属する
ハゼは那覇に属する
イカは国頭村に属する
エイはどこにも属さない
AB LEFT JOIN A
AB LEFT JOIN B