はじめに
データベースを触り始めると確実に一度は躓きますよね。。
私も最近までデータベース設計がちんぷんかんぷんでした。
少しずつ理解出来てきたので今回は備忘録としてまとめつつ、初心者の方の助けになればと思いまとめてみました。
Laravel6.0の実装でお困り中の方は下記も見てみて下さい。
初歩的なことは下記でできるようになるかと思います。
1対1, 1対多, 多対多ってなんやねん
まずは誰もが思うであろう、こちらから解説します。
1対1
まずこれはほとんど使わないので覚える必要はありません。
双方のレコードが1対1になるもしくは双方のPKが同じケースとなります。
例えるならば、
ユーザーとマイナンバーとかでしょうか。
1人のユーザーに対して1つのマイナンバーが割り振られます。
こうなるとまず思うのが、テーブル分ける必要ってある?という疑問です。
わざわざ分けるメリットもなく普通に同一テーブル内に入れろやということでほぼ使いません。
既存のテーブル構成を変えずに項目を追加したいときは採用するかも・・・
1対多
ここからはよく使うので具体的なLaravelのコードも書いていきます。
Aテーブルのレコードは、Bテーブルの複数レコードと関連するが、
BテーブルのレコードはAテーブルのレコードの1つのみ関連するケースを指します。
例えるならば、
ユーザーと投稿でしょうか。
Twitterなどでも1人のユーザーが複数の投稿を行います。
これは国語力を活かして考えるとおすすめです。
上記の例で言うと、
1人のユーザーが複数の投稿を行う → 日本語として正しい
1つの投稿が複数のユーザーで〜〜 → おかしい
ということでこれは、ユーザーテーブルが親で、投稿テーブルが子となります。
他の例としては、顧客と注文で考えてみましょう。
1人の顧客が複数の注文をする → 日本語として正しい
1つの注文が複数の顧客で〜〜 → おかしい
このように考えてみるとわかりやすいのではないでしょうか??
Laravelコード解説
新たにマイグレーションファイルとモデルを作成する
php artisan make:model Models/Post --migration
Migration
親
public function up()
{
Schema::create('registers', function (Blueprint $table) {
// 符号なしBIGINTを使用した自動増分ID
$table->bigIncrements('id')->comment('id');
// 20文字以内の文字列
$table->string('name', 20)->comment('名前');
// 255文字以内で独自の文字列
$table->string('email', 255)->unique('email')->comment('メールアドレス');
// 時間
$table->timestamps();
});
}
子
public function up()
{
Schema::create('registers', function (Blueprint $table) {
// 符号なしBIGINTを使用した自動増分ID
$table->bigIncrements('id')->comment('id');
// 255文字以内の文字列
$table->string('body', 255)->comment('本文');
// 1対多リレーション(整数しか入らないのでunsignedBigIntegerを使用)
$table->unsignedBigInteger('user_id')->comment('ユーザーID');
// 時間
$table->timestamps();
// 外部キー制約
$table->foreign("user_id")->references("id")->on("users");
});
}
マイグレーションを実行する。
php artisan migrate
Model
親にhasMany、子にbelongsToを使います。
親
class User extends Authenticatable
{
public function posts() {
return $this->hasMany("App\Models\Post");
}
}
子
class Post extends Model
{
public function user() {
return $this->belongsTo("App\Models\User");
}
}
Controller
コントローラーでのアクセス方法についてです。
// model読み込み
use App\Models\User;
use App\Models\Post;
public function index() {
// 親から子
// ユーザーのIDが1の投稿すべて
User::find(1)->posts;
// 子から親
// 投稿のIDが1のユーザーの名前
Post::find(1)->user->name;
// 子から親
// 投稿すべてを取得
// viewファイルで親にアクセスする際は、{{ $post->user->name }}でuserのnameが取得可能です
Post::all();
// よりスマートなのはwithを使いましょう。(親から子)
User::with("posts");
}
多対多
AテーブルのレコードもBテーブルのレコードも、
双方の複数レコードと関連するケースを指します。
例えるならば、
投稿と投稿のカテゴリでしょうか。
1つの投稿に複数のカテゴリーがあり、
1つのカテゴリーに複数の投稿があります。
これも国語力を活かして考えるとおすすめです。
1対多の場合は入れ替えると片方は意味が通じて、もう一方は日本語として正しくない形となりました。
今回は多対多は入れ替えてもどちらとも日本語として正しくなります。
上記の例で言うと、
1つの投稿に複数のカテゴリーがあります → 日本語として正しい
1つのカテゴリーに複数の投稿があります → 日本語として正しい
1つの投稿に複数のカテゴリーが紐付き、
このLaravelのタグは私以外の様々な方がこちらのタグを紐づけています。
このように考えていくと理解しやすいのではないでしょうか?
では、Laravelのコード解説に移ります。
Laravelコード解説
多対多の場合は中間テーブルというものを使って構築していきます。
なぜ中間テーブルを使うのかは図解などがあった方がわかりやすいのですが自分に作成するスキルがないので割愛しますw
しかし他の方の説明で素晴らしいものがあったので中間テーブルを詳しく知りたい方はこちらをご確認ください。
新たにマイグレーションファイルとモデルを作成しましょう。
今回のケースは、
親がPostとCategoryとなり、子がpost_categoryです。
// カテゴリー
php artisan make:model Models/Category --migration
// 中間テーブルのマイグレーションファイル
php artisan make:migration create_posts_categories_table
Migration
親
public function up()
{
Schema::create('registers', function (Blueprint $table) {
// 符号なしBIGINTを使用した自動増分ID
$table->bigIncrements('id')->comment('id');
// 20文字以内の文字列
$table->string('category', 20)->comment('カテゴリー');
// 時間
$table->timestamps();
});
}
public function up()
{
Schema::create('registers', function (Blueprint $table) {
// 符号なしBIGINTを使用した自動増分ID
$table->bigIncrements('id')->comment('id');
// 255文字以内の文字列
$table->string('body', 255)->comment('本文');
// 1対多リレーション(整数しか入らないのでunsignedBigIntegerを使用)
$table->unsignedBigInteger('user_id')->comment('ユーザーID');
// 時間
$table->timestamps();
// 外部キー制約
$table->foreign("user_id")->references("id")->on("users");
});
}
子
public function up()
{
Schema::create('posts_categories', function (Blueprint $table) {
$table->unsignedBigInteger('post_id')->comment('投稿ID');
$table->unsignedBigInteger('category_id')->comment('カテゴリーID');
// プライマリーキー制約(PK)
$table->primary(["post_id", "category_id"]);
});
}
マイグレーションを実行する。
php artisan migrate
Model
親にhasMany、子にbelongsToを使います。
親
class Post extends Model
{
public function user() {
return $this->belongsTo("App\Models\User");
}
// belongsToMany('関係するモデル', '中間テーブルのテーブル名', '中間テーブル内で対応しているID名', '関係するモデルで対応しているID名');
public function categories() {
return $this->belongsToMany("App\Models\Category", "posts_categories", "post_id", "category_id");
}
}
class Post extends Model
{
// belongsToMany('関係するモデル', '中間テーブルのテーブル名', '中間テーブル内で対応しているID名', '関係するモデルで対応しているID名');
public function posts() {
return $this->belongsToMany("App\Models\Post", "posts_categories", "category_id", "post_id");
}
}
子
モデルファイルは必要ありません。
Controller
コントローラーでのアクセス方法についてです。
// model読み込み
use App\Models\Post;
use App\Models\Category;
public function index() {
// 親から親
// 投稿のIDが1のカテゴリーすべて
Post::find(1)->categories;
// 親から親
// 投稿のIDが1のカテゴリー名
Post::find(1)->categories->category;
}