初めてのLaravel ⑥。外部キー制約の使い方

初めてのLaravel ⑥。外部キー制約の使い方
この記事はこんな悩みを解決します
  • 外部キー制約って何?
  • 複数のテーブルを連動させる方法が知りたい
  • Laravelでデータベースを扱う基本を学びたい

データベースを使っていると複数のテーブルを連動させて使用したい、という事があるかと思います。例えば、特定のユーザーのデータを抽出したい、このコメントを書いたユーザーの情報が知りたい、などです。そんな時には外部キー制約をかけると便利にデータを扱えます。

この記事を読む事で外部キー制約の使い方が分かります。分からないところが多い場合は以前の記事を読んで見てください。

初めてのLaravel ⑥。外部キー制約の使い方

今回はcommentテーブルとuserテーブルの外部キー制約を行います。外部キー制約とは複数のテーブルを紐付けして使うことを言います。今回は1つのユーザーに対して複数のコメントがあると想定して設定していきます。

いわゆる、1対多のリレーションというやつです。他にも1対1のリレーションや多対多のリレーションなどもあります。ただ、1対多を覚えればほぼ同じなので問題ないかと思います。

まずはTablePlusで今のテーブルの状態を把握します。

usersテーブルはデフォルトの設定になっています。

commentテーブルはこんな感じになっています。

usersテーブルと紐づけるためにcommentテーブルuser_idカラムを作っていきましょう。さらに、user_idに外部キー制約をつける呪文も書いていきます

commentテーブルを作るmigrationファイルを編集します。

 public function up()
    {
        Schema::create('comment', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id'); //追加 user_idの作成
            $table->text('title');
            $table->string('comment');
            $table->timestamps();
       
            $table->foreign('user_id')  //追加 user_idに外部キー制約をつけますよ。usersテーブルのidカラムを参照してそのカラムがなくなったらカスケード的に処理します。
                  ->references('id')
                  ->on('users')
                  ->onDelete('cascade');
        });
    }
カスケードとは?

カスケードとはusersテーブルの特定の人のidが削除されたらcommentテーブルのそのuser_idを持ったデータも削除します、という事です。ユーザーがいないのにコメントだけあるという状況を避ける事ができます。

ここで注意ですが、user_idはusersテーブルのidと紐づけるため、同じデータ型でなければなりません。今回はunsingedBigIntegerにしています。

では、php artisan migrate:fresh でテーブルを作り直します。

テーブルの構造も見ておきます。

うまくいってますね。user_idにusersテーブルの外部キー制約がかかっているのが分かります。

データの削除

ユーザーとコメントの紐付けができたのでユーザーが削除された時にコメントも削除されるか見ていきましょう。まずはデータを入れていきます。

mbp:training user$ php artisan tinker
Psy Shell v0.10.4 (PHP 7.3.19 — cli) by Justin Hileman
>>> factory(App\User::class, 3)->create();
//省略

usersテーブルに3つのデータを入れました。

さらに、commentテーブルにもデータを入れていきます。
user_idを2に指定してコメントを作ります。

>>> factory(App\Comment::class, 3)->create(['user_id' => 2]);
//省略

TablePlusで確認します。

うまくできてますね。

では、ユーザーを削除してコメントも削除されるか確認していきます。

うまく削除できてますね。

データの取り出し

せっかく紐付けができたので、特定のユーザーがどんなコメントを書いているか一覧が出るような機能をつけていきたいと思います。

もう一度、php artisan migrate:fresh してデータを入れ直しました。

さらに、UserモデルとCommentモデルに下記の呪文を追加します。

public function comments(){
        return $this->hasMany(Comment::class);
    }

Userモデルでcommentを唱えるとCommentのクラスを作ってコメントテーブルにアクセスします、という意味です。

public function user(){
        return $this->belongsTo(User::class);
    }

CommentモデルはUserクラスに紐づいてますよ、という意味です。これもCommentモデルからuserを唱えるとユーザーにアクセスできる、という意味です。

少し分かりにくいですが、ターミナルで確認していきましょう。

mbp:training user$ php artisan tinker
Psy Shell v0.10.4 (PHP 7.3.19 — cli) by Justin Hileman
>>> App\User::find(2)->comments; //Userモデルからcommentコマンドを唱えています。
//省略

これでidが2のユーザーに紐づいているコメントが取れてきます。

ビューへの埋め込み

では、実用化するためにビューに埋め込んでいきましょう。

今回は特定のユーザーをクリックするとそのユーザーのコメントが表示される、というプログラムにしたいと思います。

まずはユーザー一覧のビューです。

@forelse($users as $user)
		<li>
			<a href="{{ action('TrainingController@UserDetail', $user->id) }}">{{ $user->name }}</a>
		</li>
		@empty
		<p>コメントはありません</p>
@endforelse

次にコントローラーの設定です。

ここで躓くのが DB class bypasses Eloquent を使わないといけないというところです。クラスが作られないため stdClass::$comments  というエラーになります。

use App\User; //これがないとDB class bypasses Eloquentが使えない

public function UserDetail($id){
    	//$user = \DB::table('users')->find($id);
    $user = User::find($id); //自動でモデルクラスを作りデータを引き出します。

    	return view('detail', [
    	'user' => $user
    ]);
    }

DB class bypasses Eloquent で$userの中にクラスモデルが作られているので下記のようにコメントを引き出す事ができます。

@forelse($user->comments as $comment) //Userモデルからコメントにアクセスしている
		<li>
			<p>{{$comment->title}}</p>
		</li>
		@empty
		<p>コメントはありません</p>
@endforelse	
	

もう1つ躓くポイントですがUserモデルを下記のように設定しておきましょう。

// protected $fillable = [
//     'name', 'email', 'password',
// ];
protected $fillable = ['users'];

fillableを設定すると$userを作った時に設定したものしか引き出せません。上記の例では’name’, ‘email’, ‘password’だけという事ですね。これを’user’とする事でリレーションしているデータも取り出せるようになります。

確認して見ます

これで上から2つ目のリンクをクリックして見ます。

スタイルが崩れていますが目的のデータが取れてきています。

まとめ

今回は外部キー制約の使い方を紹介しました。

ブログやショッピングサイトなどを作って行く際に重要な知識なので使えるようにしておきましょう。ややこしいかもしれませんが、一度設定してしまえばほぼ考えずに使えるのでかなり便利です。

関連記事