
【CakePHP2】HABTMモデルの練習
いくつか CakePHP でコンテンツを作成してきたのですが、HABTM(HasAndBelongsToMany)を利用したリレーションを作成したことがなかったので、どんなものなのか作成してみました。
HABTMで検索し、blogですかい さんで紹介されていたアニメと声優の関係を作成するというケースがなかなか分かりやすかったので、それに挑戦してみました。
CakePHPの「HABTM」作成
テーブル作成
今回は
- animes テーブル
- voiceactors テーブル
- animes_voiceactors テーブル
の3つのテーブルを用意します。
[]
↑ ER図もどき
肝は「animes_voiceactors」テーブル。
テーブルの命名規則でアルファベット順、どちらも複数形のテーブル名を付ける必要があります。
アルファベット順が逆になる「voiceactors_animes」テーブルでは「テーブルが無いよ!」というエラーが出ます。
ではチャチャッとテーブルをSQLで作成、データを放り込みます。
”atn_”はテスト用につけたプレフィックスです。
animes テーブル
[sql]
CREATE TABLE atn_animes
(
id
INT UNSIGNED AUTO_INCREMENT, --
created
DATETIME DEFAULT NULL, --
modified
DATETIME DEFAULT NULL, --
delete_flg
TINYINT NOT NULL default '0', -- 削除フラグ
publish_flg
TINYINT NOT NULL default '1', -- 公開フラグ
title
VARCHAR(255), -- タイトル
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO atn_animes
(id, created, modified, delete_flg, publish_flg, title)
VALUES
(null, NOW(), NOW(), 0, 1, 'うる星やつら');
INSERT INTO atn_animes
(id, created, modified, delete_flg, publish_flg, title)
VALUES
(null, NOW(), NOW(), 0, 1, 'シティハンター');
INSERT INTO atn_animes
(id, created, modified, delete_flg, publish_flg, title)
VALUES
(null, NOW(), NOW(), 0, 1, 'ハイスクール!奇面組');
INSERT INTO atn_animes
(id, created, modified, delete_flg, publish_flg, title)
VALUES
(null, NOW(), NOW(), 0, 1, 'ルパン三世 風魔一族の陰謀');
[/sql]
voiceactors テーブル
[sql]
CREATE TABLE atn_voiceactors
(
id
INT UNSIGNED AUTO_INCREMENT, --
created
DATETIME DEFAULT NULL, --
modified
DATETIME DEFAULT NULL, --
delete_flg
TINYINT NOT NULL default '0', -- 削除フラグ
publish_flg
TINYINT NOT NULL default '1', -- 公開フラグ
name
VARCHAR(255), -- 氏名
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO atn_voiceactors
(id, created, modified, delete_flg, publish_flg, name)
VALUES
(null, NOW(), NOW(), 0, 1, '古川 登志夫');
INSERT INTO atn_voiceactors
(id, created, modified, delete_flg, publish_flg, name)
VALUES
(null, NOW(), NOW(), 0, 1, '神谷 明');
INSERT INTO atn_voiceactors
(id, created, modified, delete_flg, publish_flg, name)
VALUES
(null, NOW(), NOW(), 0, 1, '千葉 繁');
[/sql]
animes_voiceactors テーブル
[sql]
-- HABTM
CREATE TABLE atn_animes_voiceactors
(
id
INT UNSIGNED AUTO_INCREMENT, --
created
DATETIME DEFAULT NULL, --
modified
DATETIME DEFAULT NULL, --
delete_flg
TINYINT NOT NULL default '0', -- 削除フラグ
publish_flg
TINYINT NOT NULL default '1', -- 公開フラグ
voiceactor_id
INT NOT NULL, --
anime_id
INT NOT NULL, --
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO atn_animes_voiceactors
(id, created, modified, delete_flg, publish_flg, voiceactor_id, anime_id)
VALUES
(null, NOW(), NOW(), 0, 1, 1, 1);
INSERT INTO atn_animes_voiceactors
(id, created, modified, delete_flg, publish_flg, voiceactor_id, anime_id)
VALUES
(null, NOW(), NOW(), 0, 1, 1, 4);
INSERT INTO atn_animes_voiceactors
(id, created, modified, delete_flg, publish_flg, voiceactor_id, anime_id)
VALUES
(null, NOW(), NOW(), 0, 1, 2, 1);
INSERT INTO atn_animes_voiceactors
(id, created, modified, delete_flg, publish_flg, voiceactor_id, anime_id)
VALUES
(null, NOW(), NOW(), 0, 1, 2, 2);
INSERT INTO atn_animes_voiceactors
(id, created, modified, delete_flg, publish_flg, voiceactor_id, anime_id)
VALUES
(null, NOW(), NOW(), 0, 1, 3, 1);
INSERT INTO atn_animes_voiceactors
(id, created, modified, delete_flg, publish_flg, voiceactor_id, anime_id)
VALUES
(null, NOW(), NOW(), 0, 1, 3, 3);
[/sql]
80年台の香りがプンプンですな(笑)
CakePHPのモデル作成
次にモデルの作成。
- Anime.php
- Voiceactor.php
- AnimesVoiceactor.php
上記3つを用意。
HABTM用のモデルは 前が複数形、後が単数形の単語になるというややこしい命名規則です。
Anime.php
[php]
array(
'className' => 'Voiceactor',
'joinTable' => 'animes_voiceactors', // 中間テーブルの名称(Model名ではなくテーブル名)
'foreignKey' => 'anime_id', // animes_voiceactorsの中のAnimeとのforeignKey
'associationForeignKey' => 'voiceactor_id', // アソシエーションしている中のforeignKey.ここではAnimesVoiceactorのvoiceactor_id
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
[/php]
Voiceactor.php
[php]
array(
'className' => 'Anime',
'joinTable' => 'animes_voiceactors', // 中間テーブルの名称(Model名ではなくテーブル名)
'foreignKey' => 'voiceactor_id', // animes_voiceactorsの中のVoiceactorとのforeignKey
'associationForeignKey' => 'anime_id', // アソシエーションしている中のforeignKey.ここではAnimesVoiceactorのanime_id
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
[/php]
AnimesVoiceactor.php
[php]
array(
'className' => 'Anime',
'foreignKey' => 'anime_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
),
'Voiceactor' => array(
'className' => 'Voiceactor',
'foreignKey' => 'voiceactor_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
),
);
}
[/php]
CakePHPの「HABTM」結果
Anime一覧
Anime一覧のデバッグ
[php]
// Anime一覧取得
$anime_data = $this->Anime->find( 'all',
array(
'conditions' => array('Anime.publish_flg' => 1),
'fields' => null,
'order' => array('Anime.id' => 'ASC'),
'recursive' => 1
)
);
[/php]
HABTM設定前
[default]
Array
(
[0] => Array
(
[Anime] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
)
)
[1] => Array
(
[Anime] => Array
(
[id] => 2
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => シティハンター
)
)
[2] => Array
(
[Anime] => Array
(
[id] => 3
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ハイスクール!奇面組
)
)
[3] => Array
(
[Anime] => Array
(
[id] => 4
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ルパン三世 風魔一族の陰謀
)
)
)
[/default]
AnimeモデルのHABTM設定後
[default]
Array
(
[0] => Array
(
[Anime] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
)
[Voiceactor] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 古川 登志夫
[AnimesVoiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 1
)
)
[1] => Array
(
[id] => 2
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 神谷 明
[AnimesVoiceactor] => Array
(
[id] => 3
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 2
[anime_id] => 1
)
)
[2] => Array
(
[id] => 3
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 千葉 繁
[AnimesVoiceactor] => Array
(
[id] => 5
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 3
[anime_id] => 1
)
)
)
)
[1] => Array
(
[Anime] => Array
(
[id] => 2
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => シティハンター
)
[Voiceactor] => Array
(
[0] => Array
(
[id] => 2
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 神谷 明
[AnimesVoiceactor] => Array
(
[id] => 4
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 2
[anime_id] => 2
)
)
)
)
[2] => Array
(
[Anime] => Array
(
[id] => 3
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ハイスクール!奇面組
)
[Voiceactor] => Array
(
[0] => Array
(
[id] => 3
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 千葉 繁
[AnimesVoiceactor] => Array
(
[id] => 6
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 3
[anime_id] => 3
)
)
)
)
[3] => Array
(
[Anime] => Array
(
[id] => 4
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ルパン三世 風魔一族の陰謀
)
[Voiceactor] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 古川 登志夫
[AnimesVoiceactor] => Array
(
[id] => 2
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 4
)
)
)
)
)
[/default]
Anime詳細
Anime詳細のデバッグ
[default]
Array
(
[Anime] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
)
[Voiceactor] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 古川 登志夫
[AnimesVoiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 1
)
)
[1] => Array
(
[id] => 2
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 神谷 明
[AnimesVoiceactor] => Array
(
[id] => 3
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 2
[anime_id] => 1
)
)
[2] => Array
(
[id] => 3
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 千葉 繁
[AnimesVoiceactor] => Array
(
[id] => 5
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 3
[anime_id] => 1
)
)
)
)
[/default]
Voiceactor一覧
Voiceactor一覧のデバッグ
[php]
// Voiceactor一覧取得
$voiceactor_data = $this->Voiceactor->find( 'all',
array(
'conditions' => array('Voiceactor.publish_flg' => 1),
'fields' => null,
'order' => array('Voiceactor.id' => 'ASC'),
'recursive' => 1
)
);
[/php]
HABTM設定前
[default]
Array
(
[0] => Array
(
[Voiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 古川 登志夫
)
)
[1] => Array
(
[Voiceactor] => Array
(
[id] => 2
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 神谷 明
)
)
[2] => Array
(
[Voiceactor] => Array
(
[id] => 3
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 千葉 繁
)
)
)
[/default]
VoiceactorモデルのHABTM設定後
[default]
Array
(
[0] => Array
(
[Voiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 古川 登志夫
)
[Anime] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
[AnimesVoiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 1
)
)
[1] => Array
(
[id] => 4
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ルパン三世 風魔一族の陰謀
[AnimesVoiceactor] => Array
(
[id] => 2
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 4
)
)
)
)
[1] => Array
(
[Voiceactor] => Array
(
[id] => 2
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 神谷 明
)
[Anime] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
[AnimesVoiceactor] => Array
(
[id] => 3
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 2
[anime_id] => 1
)
)
[1] => Array
(
[id] => 2
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => シティハンター
[AnimesVoiceactor] => Array
(
[id] => 4
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 2
[anime_id] => 2
)
)
)
)
[2] => Array
(
[Voiceactor] => Array
(
[id] => 3
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 千葉 繁
)
[Anime] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
[AnimesVoiceactor] => Array
(
[id] => 5
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 3
[anime_id] => 1
)
)
[1] => Array
(
[id] => 3
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ハイスクール!奇面組
[AnimesVoiceactor] => Array
(
[id] => 6
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 3
[anime_id] => 3
)
)
)
)
)
[/default]
Voiceactor詳細
Voiceactor詳細のデバッグ
[default]
Array
(
[Voiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:16:36
[modified] => 2015-03-12 17:16:36
[delete_flg] => 0
[publish_flg] => 1
[name] => 古川 登志夫
)
[Anime] => Array
(
[0] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => うる星やつら
[AnimesVoiceactor] => Array
(
[id] => 1
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 1
)
)
[1] => Array
(
[id] => 4
[created] => 2015-03-12 17:21:25
[modified] => 2015-03-12 17:21:25
[delete_flg] => 0
[publish_flg] => 1
[title] => ルパン三世 風魔一族の陰謀
[AnimesVoiceactor] => Array
(
[id] => 2
[created] => 2015-03-12 17:21:55
[modified] => 2015-03-12 17:21:55
[delete_flg] => 0
[publish_flg] => 1
[voiceactor_id] => 1
[anime_id] => 4
)
)
)
)
[/default]
各レコードの取得に際しての recursive 値は 1 ですが、HABTM設定後はいい具合にリレーションして取得していますね。
モデルさえきちんと設定できればあまり何も考えずに必要レコードを引っ張ってきてくれるので、なかなか便利な設定ではないでしょうか。
参考にしたサイト様
CakePHP2でHABTMなモデルを作った話 - blogですかい