0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

cakephp3でDBのテーブルのリレーションの関係を整理する〜1対多編

Posted at

命名規則

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にアクセス

スクリーンショット 2022-06-14 16.58.55.png

特にリレーション組んでいないのに、勝手に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

スクリーンショット 2022-06-14 17.16.52.png

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);

スクリーンショット 2022-06-14 17.26.14.png

変わらず、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);

スクリーンショット 2022-06-14 17.30.50.png

Categoriesにfoo, barの名前ではなく、そのままidが入っている。ということは、やっぱりtitlenameというカラム名に意味があり、勝手にやってくれているみたいだ。

ただし、生成される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>

と変更すれば、表示も単純に変更される。

スクリーンショット 2022-06-14 17.38.21.png

⑥マスターテーブルパターン(失敗)

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);

スクリーンショット 2022-06-14 17.57.25.png

subcategoriesテーブルがないと怒られる。
特にリレーションを定義しているわけではないのに、勝手にリレーションがあるかのようになっている。

LessonsController.php
    public function index()
    {
        $this->paginate = [
            'contain' => ['Categories', 'Subcategories'],
        ];
        $lessons = $this->paginate($this->Lessons);

        $this->set(compact('lessons'));
    }
Table/LessonsTable.php
    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',
        ]);
    }

Entity/Lesson.php
    protected $_accessible = [
        'name' => true,
        'categories_id' => true,
        'subcategories_id' => true,
        'category' => true,
        'subcategory' => true,
    ];

この3つを見ても、リレーションが勝手に組まれているっぽい。
なので、以下のように変更すると正常に表示できた。

LessonsController.php
    public function index()
    {
        $this->paginate = [
            'contain' => ['Categories'],
        ];
        $lessons = $this->paginate($this->Lessons);

        $this->set(compact('lessons'));
    }
Table/LessonsTable.php
    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;
    }
Entity/Lesson.php
    protected $_accessible = [
        'name' => true,
        'categories_id' => true,
        'subcategories_id' => true,
        'category' => true,
    ];

スクリーンショット 2022-06-14 18.18.54.png

⑦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を表示するために以下変更。

LessonsController.php
    public function index()
    {
        $this->paginate = [
            'contain' => ['Categories', 'Subcategories'],
        ];
        $lessons = $this->paginate($this->Lessons);

        $this->set(compact('lessons'));
    }
Table/LessonsTable.php
    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',
        ]);
    }
Entity/Lesson.php
    protected $_accessible = [
        'name' => true,
        'categories_id' => true,
        'category' => true,
        'subcategories_id' => true,
        'subcategory' => true,
    ];

これでTemplateも修正すれば以下のように表示されます。
多分本当はLessonsFixture.phpとかも修正しないといけないっぽいが、それは一旦置いとく。
スクリーンショット 2022-06-14 19.36.57.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?