アソシエーション | 結合モデルのデータ表示と更新 | 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