【Laravel入門】リレーションとユーザーの一覧表示

# リレーションとは

例えば、投稿記事articleが表示された時に「投稿者の名前も知りたい！」と思うのは当然だろう。そのためには何をするのか？articlesテーブルにuser_nameの様なカラムを追加するのか？

しかし、投稿者情報を引き出すために既存のusersテーブルを活用できれば簡単だ。それを可能にしてくれるのが、``リレーション``という仕組みだ。

公式ドキュメントより（一部抜粋）

>データベーステーブルは大抵の場合他のものと関連しています。たとえばブログ投稿（ポスト）は多くのコメントを持つか、それを投稿したユーザーと関連しています。Eloquentはそうしたリレーションを簡単に管理し操作できるようにするとともに、様々なタイプのリレーションをサポートしています。

>Eloquentのリレーション（関係）とはEloquentモデルクラスのメソッドとして定義します。

今回はそのための設定を行っていく。

# 主キーと外部キー

今まで２つのテーブル``usersとarticles``を作成した。この２つのテーブルで``共通の値が入るカラム``を設定し、これを元に結びつければ良いのだ。これを``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の関係は``１対多``と呼ばれるものだ。

- ユーザー（投稿者）は複数の記事を投稿できる。よって、``Userは複数のArticleを持つことができる``。
- １つの記事の投稿者は１人しか存在しない。よって、``Articleは１つのUserしか持つことができない``。

上記の関係に基づき、各モデルにはこのように関数を追記する。

```User.php
public function articles()
{
    return $this->hasMany('App\Article');
}
```

```Article.php
public function user()
{
    return $this->belongsTo('App\User');
}
```

# コントローラ編集

Articleコントローラの全体像はこうなる。

```ArticleController.php
<?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ディレクティブ``でユーザーとゲストの判別を行い、表示する項目を分けている。

```views/layouts/layout.blade.php
<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ビュー

こちらは大幅に編集した。

```resources/views/article/index.blade.php
@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``には、現在ログインしているユーザーの情報も入っているのだろうか？だとしたら、つじつまが合うのだか。

```views/article/add.blade.php
@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アクションしか作成していないので、この記述がなくても同様の動きはする。）

```UserController.php
<?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]);
    }
}
```

## ルーティング設定

```web.php
Route::get('/user/index', 'UserController@index')->name('user_index');
```

## ビュー作成

``resources/viewsディレクトリ``に``userディレクトリ``を用意し、``index.blade.php``を作成する。

```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
```

こちらも忘れずに。

```views/layouts/layout.blade.php
@yield('user_index')
```

# 入門編終了

CRUDとリレーションを実装することができたので、これで入門編は終了とする。他にも付け足したい機能はあるし、デザインも整えたい。AWSも使いたい。それらについては、まとまったら別記事として投稿する予定だ。

拙い記事ではあったが、誰かの何かのお役に立てれば、さらにベテランの方々にも初心を思い出していただければ、これ以上の喜びはない。

最後までお読みいただき、ありがとうございました！