CakePHP

【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一覧

HABTM_anime_list

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詳細

HABTM_anime_detail_urusei

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一覧

HABTM_va_list

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詳細

HABTM_va_detail_furukawa

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ですかい

-CakePHP
-, ,