Laravel5.7: usersのCRUD機能を実装する

usersテーブルに関するCRUD機能を実装します。

# 親記事
[Laravel 5.7で基本的なCRUDを作る - Qiita](http://qiita.com/sutara79/items/ef30fcdfb7afcb2188ea)


# usersのEloquentモデルを生成する
:link: readouble.com: [モデル定義](https://readouble.com/laravel/5.7/ja/eloquent.html#defining-models)

なお、usersテーブルの場合は`app/User.php`が既に存在するので、モデル生成は不要です。

# usersのコントローラーを生成する
今回は、典型的なCRUD機能を提供してくれるリソースコントローラを利用させてもらいます。
:link: readouble.com: [リソースコントローラ](https://readouble.com/laravel/5.7/ja/controllers.html#resource-controllers)

```text:PowerShell
> php artisan make:controller UserController --resource
```

`app/Http/Controllers/UserController.php`が生成され、CRUDに関するアクションメソッドも自動で定義されています。
あとは中身の処理を書くだけです。


## ルーティングを設定する
```php:routes/web.php
Route::resource('users', 'UserController');
```

上の一行で、下記のようにCRUDに関するルートがまとめて設定されます。

|動詞|URI|アクション|ルート名|
|:--|:--|:--|:--|
|GET      |/users            |index  |users.index|
|GET      |/users/create     |create |users.create|
|POST     |/users            |store  |users.store|
|GET      |/users/{user}     |show   |users.show|
|GET      |/users/{user}/edit|edit   |users.edit|
|PUT/PATCH|/users/{user}     |update |users.update|
|DELETE   |/users/{user}     |destroy|users.destroy|

Artisanコマンドでルートの設定を確認することもできます。

```text:PowerShell
> php artisan route:list
```


## Eloquentモデルをインポートする
コントローラ内で頻繁に使うことになるので、モデルをインポートしておきます。

```diff:app/Http/Controllers/UserController.php
  use Illuminate\Http\Request;
+ use App\User;
 
  class UserController extends Controller
  {
```

次からは、CRUDの機能をひとつひとつ実装していきます。

# index
:link: readouble.com: [モデルの取得](https://readouble.com/laravel/5.7/ja/eloquent.html#retrieving-models)

ユーザーを一覧表示します。
[以前の記事](https://qiita.com/sutara79/items/e19fb386451ab9a4a5fa)で生成したデータが表示されます。

URL例: `http://<APP_URL>/users`
![001.png](https://qiita-image-store.s3.amazonaws.com/0/27816/fceed655-8fa6-8243-cae8-097bcace1bac.png)

## コントローラ
```php:app/Http/Controllers/UserController.php
    public function index()
    {
        $users = User::all();
        return view('users.index', ['users' => $users]);
    }
```

## ビュー
[以前の記事](https://qiita.com/sutara79/items/3ae420f756573a4a2f93)で作ったレイアウトを利用します。

```html:resources/views/users/index.blade.php
@php
    $title = __('Users');
@endphp
@extends('layouts.my')
@section('content')
<div class="container">
    <h1>{{ $title }}</h1>
    <div class="table-responsive">
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>{{ __('ID') }}</th>
                    <th>{{ __('Name') }}</th>
                </tr>
            </thead>
            <tbody>
                @foreach ($users as $user)
                    <tr>
                        <td>{{ $user->id }}</td>
                        <td><a href="{{ url('users/'.$user->id) }}">{{ $user->name }}</a></td>
                    </tr>
                @endforeach
            </tbody>
        </table>
    </div>
</div>
@endsection
```

手軽に見栄えを整えるために[Bootstrap](http://getbootstrap.com/)を利用しています。
また、後で多言語化に対応させるので、あらかじめ`__`メソッド内に文章を記述するようにしています。
:link: readouble.com: [翻訳文字列の取得](https://readouble.com/laravel/5.7/ja/localization.html#retrieving-translation-strings)

```diff:例
-    Name
+    {{ __('Name') }}
```

# show
:link: readouble.com: [1モデル／集計の取得](https://readouble.com/laravel/5.7/ja/eloquent.html#retrieving-single-models)

1件だけを表示します。

URL例: `http://<APP_URL>/users/show/1`
![002.png](https://qiita-image-store.s3.amazonaws.com/0/27816/f34edb76-bf63-7bdd-b4b8-6a63ba60e335.png)

## コントローラ
```php:app/Http/Controllers/UserController.php
    /**
     * Display the specified resource.
     *
     * @param  \App\User $user
     * @return \Illuminate\Http\Response
     */
    public function show(User $user)
    {
        return view('users.show', ['user' => $user]);
    }
```

「モデル結合ルート」を利用して簡潔に記述するため、`show`メソッドの引数は`$id`ではなく`User $user`とします。
:link: readouble.com: [モデル結合ルート (Route Model Binding)](https://readouble.com/laravel/5.7/ja/routing.html#route-model-binding)

## ビュー
```html:resources/views/users/show.blade.php
@php
    $title = __('User') . ': ' . $user->name;
@endphp
@extends('layouts.my')
@section('content')
<div class="container">
    <h1>{{ $title }}</h1>

    {{-- 編集・削除ボタン --}}
    <div>
        <a href="{{ url('users/'.$user->id.'/edit') }}" class="btn btn-primary">
            {{ __('Edit') }}
        </a>
        {{-- 削除ボタンは後で正式なものに置き換えます --}}
        <a href="#" class="btn btn-danger">
            {{ __('Delete') }}
        </a>
    </div>

    {{-- ユーザー1件の情報 --}}
    <dl class="row">
        <dt class="col-md-2">{{ __('ID') }}</dt>
        <dd class="col-md-10">{{ $user->id }}</dd>
        <dt class="col-md-2">{{ __('Name') }}</dt>
        <dd class="col-md-10">{{ $user->name }}</dd>
        <dt class="col-md-2">{{ __('E-Mail Address') }}</dt>
        <dd class="col-md-10">{{ $user->email }}</dd>
    </dl>
</div>
@endsection
```


# create, store
:link: readouble.com: [Inserts](https://readouble.com/laravel/5.7/ja/eloquent.html#inserts)

1件を新規追加します。
後の章でバリデーション・ルールを設定するまでは、パスワードの2回入力による確認は機能しません。

URL例: `http://<APP_URL>/users/create`
![090.png](https://qiita-image-store.s3.amazonaws.com/0/27816/f4a73d3e-7418-258e-e10d-662e4e4af679.png)

## コントローラ
```php:app/Http/Controllers/UserController.php
    // 追加用のフォーム画面へ移動
    public function create()
    {
        return view('users.create');
    }

    // 実際の追加処理
    // 終わったら、作ったばかりのユーザーのページへ移動
    public function store(Request $request)
    {
        $user = new User;
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = $request->password;
        $user->save();
        return redirect('users/'.$user->id);
    }
```

## ビュー
```html:resources/views/users/create.blade.php
@php
    $title = __('Create User');
@endphp
@extends('layouts.my')
@section('content')
<div class="container">
    <h1>{{ $title }}</h1>
    <form action="{{ url('users') }}" method="post">
        @csrf {{-- CSRF保護 --}}
        @method('POST') {{-- 疑似フォームメソッド --}}
        <div class="form-group">
            <label for="name">{{ __('Name') }}</label>
            <input id="name" type="text" class="form-control" name="name" required autofocus>
        </div>
        <div class="form-group">
            <label for="email">{{ __('E-Mail Address') }}</label>
            <input id="email" type="email" class="form-control" name="email" required>
        </div>
        <div class="form-group">
            <label for="password">{{ __('Password') }}</label>
            <input id="password" type="password" class="form-control" name="password" required>
        </div>
        <div class="form-group">
            <label for="password_confirmation">{{ __('Confirm Password') }}</label>
            <input id="password_confirmation" type="password" class="form-control" name="password_confirmation" required>
        </div>
        <button type="submit" name="submit" class="btn btn-primary">{{ __('Submit') }}</button>
    </form>
</div>
@endsection
```

:link: readouble.com: [CSRF保護](https://readouble.com/laravel/5.7/ja/csrf.html)
:link: readouble.com: [疑似フォームメソッド](https://readouble.com/laravel/5.7/ja/routing.html#form-method-spoofing)

# edit, update
:link: readouble.com: [Updates](https://readouble.com/laravel/5.7/ja/eloquent.html#updates)

1件を更新します。
今のところ、メールアドレスとパスワードは変更できません。

URL例: `http://<APP_URL>/users/2/edit`
![010.png](https://qiita-image-store.s3.amazonaws.com/0/27816/e134fa51-92c7-920e-fd28-51c4e0c9915c.png)

## コントローラ
```php:app/Http/Controllers/UserController.php
    // 更新用フォーム画面へ移動
    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\User $user
     * @return \Illuminate\Http\Response
     */
    public function edit(User $user)
    {
        return view('users.edit', ['user' => $user]);
    }

    // 実際の更新処理
    // 終わったら、そのユーザのページへ移動
    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\User $user
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, User $user)
    {
        $user->name = $request->name;
        $user->save();
        return redirect('users/'.$user->id);
    }
```

## ビュー
`method_field()`に`PUT`を指定する以外は`create.blade.php`と同じです。

```php:resources/views/users/edit.blade.php
@php
    $title = __('Edit').': '.$user->name;
@endphp
@extends('layouts.my')
@section('content')
<div class="container">
    <h1>{{ $title }}</h1>
    <form action="{{ url('users/'.$user->id) }}" method="post">
        @csrf
        @method('PUT')
        <div class="form-group">
            <label for="name">{{ __('Name') }}</label>
            <input id="name" type="text" class="form-control" name="name" value="{{ $user->name }}" required autofocus>
        </div>
        <button type="submit" name="submit" class="btn btn-primary">{{ __('Submit') }}</button>
    </form>
</div>
@endsection
```

# destroy
:link: readouble.com: [モデル削除](https://readouble.com/laravel/5.7/ja/eloquent.html#deleting-models)

1件を削除します。
現時点では、確認も何もなくいきなり削除します。

## コントローラ
```php:app/Http/Controllers/UserController.php
    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\User $user
     * @return \Illuminate\Http\Response
     */
    public function destroy(User $user)
    {
        $user->delete();
        return redirect('users');
    }
```

## ビュー
削除ボタンは色々な場所で使うので、コンポーネントとしてまとめておきます。
:link: readouble.com: [コンポーネントとスロット](https://readouble.com/laravel/5.7/ja/blade.html#components-and-slots)

```html:resources/views/components/btn-del.blade.php
<form style="display:inline" action="{{ url($table.'/'.$id) }}" method="post">
    @csrf
    @method('DELETE')
    <button type="submit" class="btn btn-danger">
        {{ __('Delete') }}
    </button>
</form>
```

先ほど作ったshowページの削除ボタンと置き換えます。

```diff:resources/views/users/show.blade.php
-         {{-- 削除ボタンは後で正式なものに置き換えます --}}
-         <a href="#" class="btn btn-danger">
-             {{ __('Delete') }}
-         </a>
+         @component('components.btn-del')
+             @slot('table', 'users')
+             @slot('id', $user->id)
+         @endcomponent
```

# 完了
ログインもバリデーションもありませんが、とりあえずCRUDの根幹はできあがりました。