リレーションとは
例えば、投稿記事articleが表示された時に「投稿者の名前も知りたい!」と思うのは当然だろう。そのためには何をするのか?articlesテーブルにuser_nameの様なカラムを追加するのか?
しかし、投稿者情報を引き出すために既存のusersテーブルを活用できれば簡単だ。それを可能にしてくれるのが、リレーション
という仕組みだ。
公式ドキュメントより(一部抜粋)
データベーステーブルは大抵の場合他のものと関連しています。たとえばブログ投稿(ポスト)は多くのコメントを持つか、それを投稿したユーザーと関連しています。Eloquentはそうしたリレーションを簡単に管理し操作できるようにするとともに、様々なタイプのリレーションをサポートしています。
Eloquentのリレーション(関係)とはEloquentモデルクラスのメソッドとして定義します。
今回はそのための設定を行っていく。
主キーと外部キー
今まで2つのテーブルusersとarticles
を作成した。この2つのテーブルで共通の値が入るカラム
を設定し、これを元に結びつければ良いのだ。これをidカラムとuser_idカラム
にする。
この場合、usersテーブルのid
は主キー
と呼ばれ、articlesテーブルのuser_id
は外部キー
と呼ばれる。articlesテーブルは、user_idを元にusersテーブルから必要なuser情報を引き出すことができるのだ。
実はLaravelでは、既にuser_id
にはusersテーブルのidが入る仕組みになっているのだ。
公式ドキュメントより(一部抜粋)
Eloquentは、Commentモデルに対する外部キーを自動的に決めることを心に留めてください。規約によりEloquentは、自分自身のモデル名の「スネークケース」に
_id
のサフィックスをつけた名前と想定します。ですから今回の例でEloquentは、Commentモデルの外部キーをpost_id
であると想定します。
モデル名が異なるので、CommentモデルをArticleモデル、PostモデルをUserモデルとして読んで欲しい。
モデル編集
ここで注意するのはモデル同士の結びつき方だ。UserとArticleの関係は1対多
と呼ばれるものだ。
- ユーザー(投稿者)は複数の記事を投稿できる。よって、
Userは複数のArticleを持つことができる
。 - 1つの記事の投稿者は1人しか存在しない。よって、
Articleは1つのUserしか持つことができない
。
上記の関係に基づき、各モデルにはこのように関数を追記する。
public function articles()
{
return $this->hasMany('App\Article');
}
public function user()
{
return $this->belongsTo('App\User');
}
コントローラ編集
Articleコントローラの全体像はこうなる。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Article;
class ArticleController extends Controller
{
public function __construct()
{
$this->middleware('auth')->except(['index']);
}
public function index()
{
$articles = Article::All();
return view('article.index', ['articles' => $articles]);
}
public function add()
{
return view('article.add');
}
public function create(Request $request)
{
$article = new Article;
$article->user_id = $request->user()->id;
$article->title = $request->title;
$article->content = $request->content;
$article->save();
return redirect('/');
}
public function edit(Request $request)
{
$article = Article::find($request->id);
return view('article.edit', ['article' => $article]);
}
public function update(Request $request)
{
$article = Article::find($request->id);
$article->title = $request->title;
$article->content = $request->content;
$article->save();
return redirect('/');
}
public function delete(Request $request)
{
$article = Article::find($request->id);
return view('article.delete', ['article' => $article]);
}
public function remove(Request $request)
{
$article = Article::find($request->id);
$article->delete();
return redirect('/');
}
}
アクセス制限
ゲスト(ログインしていない人)の場合は、投稿記事の閲覧のみ可能とし、新規投稿などの機能は使わせたくない。
この場合、役に立つのがコンストラクターだ。これがmiddleware('auth')
を呼び出し、アクセス制限をかけてくれる。さらにexcept(['index'])
を記載すれば、アクセス制限の対象にindexアクション
を除いてくれる。
コンストラクターやミドルウェアについては・・・ほとんど知らない。これらは今度の課題とする。
変更点
実は、変更があるのはcreateアクション
だけだ。$article->user_id = $request->user()->id;
でArticleモデルのuser関数
を呼び出し、関連するUserインスタンスのidプロパティ
をArticleインスタンスのuser_idプロパティとして保存
している。
その他のアクションについてもビューでユーザー名を表示させたい箇所があるが、ビューからの呼び出しが簡単にできるため、コントローラ部分の編集はほとんどないのだ。
ビュー編集
リンクの部分を編集する。@authディレクティブ
でユーザーとゲストの判別を行い、表示する項目を分けている。
<a href="{{ url('/') }}">トップページ</a>
<a href="{{ route('user_index') }}">ユーザー一覧</a>
<a href="{{ route('article_index') }}">記事一覧</a>
<a href="{{ route('article_add') }}">記事作成</a>
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">ログイン</a>
<a href="{{ route('register') }}">会員登録</a>
@endauth
article.indexビュー
こちらは大幅に編集した。
@extends('...layouts.layout')
@section('article_index')
投稿一覧<br>
<table>
<tr><th>ユーザーID</th><th>名前</th><th>タイトル</th><th>内容</th></tr>
@foreach($articles as $article)
<tr>
<td>{{ $article->user_id }}</td>
<td>{{ $article->user->name }}</td>
<td>{{ $article->title }}</td>
<td>{{ $article->content }}</td>
@auth
@if( ( $article->user_id ) === ( Auth::user()->id ) )
<td><a href="{{ route('article_edit') }}?id={{ $article->id }}">編集</a></td>
<td><a href="{{ route('article_delete') }}?id={{ $article->id }}">削除</a></td>
@endif
@endauth
</tr>
@endforeach
</table>
@endsection
-
{{ $article->user_id }}
の部分は、{{ $article->user->id }}
と記述しても同様の結果が得られる。 -
{{ $article->user->id }}
では、ArticleのUserモデル
をuser動的プロパティ
により取得している。(動的プロパティ?) -
@authディレクテブの
中で@ifディレクティブ
を使用し、現在のユーザーにのみ表示される部分を作成している。 -
@ifディレクティブ
中では、条件を{{ }}
ではなく( )
を使用して記述する。 -
Auth::user()->id
は、現在ログインしているユーザーのidプロパティを取得する。 - 結果、記事の投稿者にのみ編集・削除メニューの表示がされる。
ビューの中でメソッドが直接呼び出せるのは便利だ。
あと、動的プロパティ
についてはこちらから。commentsをarticlesに読み替えよう。分かったような分からないような気がする。
リレーションを定義したら、commentsプロパティによりコメントのコレクションへアクセスできます。Eloquentは「動的プロパティ」を提供しているので、モデルのプロパティとして定義したリレーションメソッドへアクセスできることを覚えておきましょう。
article.addビュー
必要のないユーザーID:<input type='text' name='user_id'><br>
は削除した。しかし、どうやってuser_id
を取得しているのだろうか?
createアクション
では$article->user_id = $request->user()->id;
の記述がされている。HTTPリクエストインスタンスが代入されている$request
には、現在ログインしているユーザーの情報も入っているのだろうか?だとしたら、つじつまが合うのだか。
@extends('...layouts.layout')
@section('article_add')
投稿作成<br>
<form action='{{ route('article_create') }}' method='post'>
{{ csrf_field() }}
タイトル:<input type='text' name='title'><br>
内容:<input type='text' name='content'><br>
<input type='submit' value='投稿'>
</form>
@endsection
article.editビュー
とarticle.deleteビュー
については省略する。特に変更を加える必要はないのだが、ユーザー名を表示させたい場合は適宜{{ $article->user->name }}
を追記しよう。
ユーザーの一覧表示
以前のCRUD実装で、投稿記事の一覧表示機能は作成した。ユーザーも同様に一覧表示ができるようにしたい。
コントローラ作成
Userコントローラ
を作成する。
$ php artisan make:controller UserController
Controller created successfully.
早速編集する。ログインしていなくても一覧表示機能を使用させるために、 $this->middleware('auth')->except(['index']);
と記述する。(現在はindexアクションしか作成していないので、この記述がなくても同様の動きはする。)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('auth')->except(['index']);
}
public function index()
{
$users = User::All();
return view('user.index', ['users' => $users]);
}
}
ルーティング設定
Route::get('/user/index', 'UserController@index')->name('user_index');
ビュー作成
resources/viewsディレクトリ
にuserディレクトリ
を用意し、index.blade.php
を作成する。
@extends('...layouts.layout')
@section('user_index')
ユーザー一覧<br>
<table>
<tr><th>ユーザーID</th><th>名前</th></tr>
@foreach($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
</tr>
@endforeach
</table>
@endsection
こちらも忘れずに。
@yield('user_index')
入門編終了
CRUDとリレーションを実装することができたので、これで入門編は終了とする。他にも付け足したい機能はあるし、デザインも整えたい。AWSも使いたい。それらについては、まとまったら別記事として投稿する予定だ。
拙い記事ではあったが、誰かの何かのお役に立てれば、さらにベテランの方々にも初心を思い出していただければ、これ以上の喜びはない。
最後までお読みいただき、ありがとうございました!