命名規則
DB
- テーブル名は小文字で複数形です。
- 複数の単語がある場合は、アンダースコアでつなぐ
- カラム名は小文字で単数形
- 複数の単語がある場合は、アンダースコアでつなぐ
- 外部キーカラムは
AAAAs(複数形)_id
- 外部キーカラムを定義した場合、外部テーブルAAAAsが必要
- DBのテーブル自体にリレーションの定義は必要?→なくてもいい。というよりはなくても動く。でもあったほうがいいと思う。
①マスターテーブルパターン
Lessons belongs to Category
特にテーブル定義でリレーションは設定しないでやってみます。
-- テーブル定義
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED
);
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
-- データを入れる
insert into categories(name) values ('理系');
insert into categories(name) values ('文系');
insert into lessons(name, categories_id) values ('Math1', 1);
insert into lessons(name, categories_id) values ('English1', 2);
insert into lessons(name, categories_id) values ('Science1', 2);
insert into lessons(name, categories_id) values ('Japanese', 2);
cakephpプロジェクトを作成
$ php composer.phar create-project --prefer-dist cakephp/app:^3.8 test1
モデル作成
$bin/cake bake all lessons
確認用のnginxのconfとapp_local.phpをいじって確認できるようにする。(この辺は各々の環境で。)
xxx.xxx.xxx.xxx/lessons
にアクセス
特にリレーション組んでいないのに、勝手にCategoriesのテーブルと紐付けて、nameを引っ張ってきてくれている。
②マスターテーブルパターン(マスターテーブルのカラム名を変更)
①と同じ条件で、categoriesテーブルのカラム名だけ変更する。
-- テーブル定義
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED
);
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50)
);
-- データを入れる
insert into categories(title) values ('理系');
insert into categories(title) values ('文系');
insert into lessons(name, categories_id) values ('Math1', 1);
insert into lessons(name, categories_id) values ('English1', 2);
insert into lessons(name, categories_id) values ('Science1', 2);
insert into lessons(name, categories_id) values ('Japanese', 2);
③マスターテーブルパターン(マスターテーブルが複数カラム)
-- テーブル定義
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED
);
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50),
description VARCHAR(50)
);
-- データを入れる
insert into categories(title, description) values ('理系', '難しい');
insert into categories(title, description) values ('文系', '簡単');
insert into lessons(name, categories_id) values ('Math1', 1);
insert into lessons(name, categories_id) values ('English1', 2);
insert into lessons(name, categories_id) values ('Science1', 2);
insert into lessons(name, categories_id) values ('Japanese', 2);
$ bin/cake bake all lessons
2カラム以上定義するとCategoriesでどのカラムの値を反映していいのかわからないからidが表示されるのかと思ったが、2カラム目のtitleが表示された。
これは、2カラム目が優先的に表示されているのか、それとも、title
というカラム名を見て表示してきているのか。④で確認する。
④
-- テーブル定義
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED
);
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
description VARCHAR(50),
title VARCHAR(50)
);
-- データを入れる
insert into categories(description, title) values ('難しい', '理系');
insert into categories(description, title) values ('簡単', '文系');
insert into lessons(name, categories_id) values ('Math1', 1);
insert into lessons(name, categories_id) values ('English1', 2);
insert into lessons(name, categories_id) values ('Science1', 2);
insert into lessons(name, categories_id) values ('Japanese', 2);
変わらず、titleが表示される。
ということは、titleというカラム名に意味を持たせられているっぽい。
適当なカラム名を設定してみる。
⑤
-- テーブル定義
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED
);
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
foo VARCHAR(50),
bar VARCHAR(50)
);
-- データを入れる
insert into categories(foo, bar) values ('難しい', '理系');
insert into categories(foo, bar) values ('簡単', '文系');
insert into lessons(name, categories_id) values ('Math1', 1);
insert into lessons(name, categories_id) values ('English1', 2);
insert into lessons(name, categories_id) values ('Science1', 2);
insert into lessons(name, categories_id) values ('Japanese', 2);
Categoriesにfoo, barの名前ではなく、そのままidが入っている。ということは、やっぱりtitle
やname
というカラム名に意味があり、勝手にやってくれているみたいだ。
ただし、生成されるcontrollerのindexアクション内は、特に変わりはなさそう。
public function index()
{
$this->paginate = [
'contain' => ['Categories'],
];
$lessons = $this->paginate($this->Lessons);
$this->set(compact('lessons'));
}
なので、viewを変更すれば、
<td><?= $lesson->has('category') ? $this->Html->link($lesson->category->id, ['controller' => 'Categories', 'action' => 'view', $lesson->category->id]) : '' ?></td>
を
<td><?= $lesson->has('category') ? $this->Html->link($lesson->category->foo, ['controller' => 'Categories', 'action' => 'view', $lesson->category->id]) : '' ?></td>
と変更すれば、表示も単純に変更される。
⑥マスターテーブルパターン(失敗)
lessonsテーブルで、subcategories_idというカラムを設定してみる。
ただしsubcategoriesテーブルは作成しない。
-- テーブル定義
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED,
subcategories_id INT UNSIGNED
);
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
-- データを入れる
insert into categories(name) values ('理系');
insert into categories(name) values ('文系');
insert into lessons(name, categories_id, subcategories_id) values ('Math1', 1, 1);
insert into lessons(name, categories_id, subcategories_id) values ('English1', 2, 2);
insert into lessons(name, categories_id, subcategories_id) values ('Science1', 2, 2);
insert into lessons(name, categories_id, subcategories_id) values ('Japanese', 2, 1);
subcategories
テーブルがないと怒られる。
特にリレーションを定義しているわけではないのに、勝手にリレーションがあるかのようになっている。
public function index()
{
$this->paginate = [
'contain' => ['Categories', 'Subcategories'],
];
$lessons = $this->paginate($this->Lessons);
$this->set(compact('lessons'));
}
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('lessons');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('Categories', [
'foreignKey' => 'categories_id',
]);
$this->belongsTo('Subcategories', [
'foreignKey' => 'subcategories_id',
]);
}
protected $_accessible = [
'name' => true,
'categories_id' => true,
'subcategories_id' => true,
'category' => true,
'subcategory' => true,
];
この3つを見ても、リレーションが勝手に組まれているっぽい。
なので、以下のように変更すると正常に表示できた。
public function index()
{
$this->paginate = [
'contain' => ['Categories'],
];
$lessons = $this->paginate($this->Lessons);
$this->set(compact('lessons'));
}
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('lessons');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('Categories', [
'foreignKey' => 'categories_id',
]);
}
.
.
.
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['categories_id'], 'Categories'));
return $rules;
}
protected $_accessible = [
'name' => true,
'categories_id' => true,
'subcategories_id' => true,
'category' => true,
];
⑦DB側でも外部キーの定義をする
-- テーブル定義
CREATE TABLE categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE lessons (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
categories_id INT UNSIGNED,
FOREIGN KEY (categories_id) REFERENCES categories(id) ON DELETE CASCADE
);
-- データを入れる
insert into categories(name) values ('理系');
insert into categories(name) values ('文系');
insert into lessons(name, categories_id) values ('Math1', 1);
insert into lessons(name, categories_id) values ('English1', 2);
insert into lessons(name, categories_id) values ('Science1', 2);
insert into lessons(name, categories_id) values ('Japanese', 2);
いくつかのファイルを見ると、多分普通にリレーションを定義しないときと同じ気がする。
ここかから外部テーブルsubcategories
を追加したい。
CREATE TABLE subcategories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
insert into subcategories(name) values ('数学');
insert into subcategories(name) values ('語学');
alter table lessons add subcategories_id INT UNSIGNED;
delete from lessons
insert into lessons(name, categories_id, subcategories_id) values ('Math1', 1, 1);
insert into lessons(name, categories_id, subcategories_id) values ('English1', 2, 1);
insert into lessons(name, categories_id, subcategories_id) values ('Science1', 2, 2);
insert into lessons(name, categories_id, subcategories_id) values ('Japanese', 2, 2);
subcategoriesを表示するために以下変更。
public function index()
{
$this->paginate = [
'contain' => ['Categories', 'Subcategories'],
];
$lessons = $this->paginate($this->Lessons);
$this->set(compact('lessons'));
}
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('lessons');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('Categories', [
'foreignKey' => 'categories_id',
]);
$this->belongsTo('Subcategories', [
'foreignKey' => 'subcategories_id',
]);
}
protected $_accessible = [
'name' => true,
'categories_id' => true,
'category' => true,
'subcategories_id' => true,
'subcategory' => true,
];
これでTemplateも修正すれば以下のように表示されます。
多分本当はLessonsFixture.php
とかも修正しないといけないっぽいが、それは一旦置いとく。