Laravel CRUD



## はじめに
LaravelでCRUDを作っていきます。

### やらないこと

- 認証関係は扱いません。
- 削除の前の確認などJavaScriptで実現される機能は今回は作りません。

### つくるもの

|アクション|画面の有無|内容|
|---|---|---|
|index|画面あり|一覧表示画面|
|create|画面あり|新規入力フォーム|
|store|画面なし|追加処理(createの登録ボタン)|
|show|画面あり|詳細表示|
|edit|画面あり|変更フォーム(既存の値が入っている状態)|
|update|画面なし|変更処理(editの更新ボタン)|
|destroy|画面なし|削除処理(showの削除ボタン)|



```:流れ
index(一覧表示) ┳ create(新規作成画面) ━ store(新規保存)
      　　　　  ┗ show(詳細表示)  　　 ┳ edit(編集画面) ━ update(上書き保存)
　　　　　　　　　　　　　　　　        ┗ destroy(削除)　　　　　
```


## 準備

### テーブルの作成
#### 作成するテーブル

|id|name|telephone|email|created_at|updated_at|
|---|---|---|---|---|---|---|
|ID|名前|電話番号|メールアドレス|作成日時|更新日時|




#### modelファイルとmigrationファイルの作成

```:terminal
php artisan make:model Models/Member -m
```

-mオプションでマイグレーションファイルも一緒に作成します。

下記の2つのファイルが作成されます。
app\Models\Member.php
database\migrations\xxxx_xx_xx_xxxxxx_create_members_table.php


#### migrationファイルの編集

```:database\migrations\xxxx_xx_xx_xxxxxx_create_members_table.php
public function up()
  {
    Schema::create('members', function (Blueprint $table) {
      $table->id();
      $table->string('name',20);
      $table->string('telephone',13)->nullable()->unique();
      $table->string('email',255)->nullable()->unique();
      $table->timestamps();
    });
  }
 
public function down()
  {
    Schema::dropIfExists('members');
  }
``` 


#### migrationの実行

```:terminal
php artisan migrate
```
membersテーブルが作成されます。

### レコードの挿入
表示確認用にmembersテーブルにレコードを入れます。

#### seederファイルの作成

```:terminal
php artisan make:seeder MembersTableSeeder
```

database\seeds\MembersTableSeeder.phpが作成されます。

#### seederファイルの編集

```:database\seeds\MembersTableSeeder.php
    public function run()
     {
         DB::table('members')->insert(
             [
               [
                 'name'=>'山田',
                 'telephone'=>'xxxx-xxxxx',
                 'email'=>'yamada@example.com',
                 'created_at'=>now(),
                 'updated_at'=>now(),
               ],
 
               ]
         );
     }
```

#### DatabaseSeederへの登録
作成したMembersTableSeederをDatabaseSeederに登録します。

```:database\seeds\DatabaseSeeder.php
public function run()
  {
    $this->call(MembersTableSeeder::class);
  }
```

#### seederファイルの実行

```:terminal
php artisan db:seed
```

membersテーブルにレコードが入ります。


#### controllerの作成

```:terminal
php artisan make:controller MemberController --resource
```
--resourceオプションをつけると、7つのアクションの雛形が予め用意されます。

#### controllerの編集
7つのアクションの雛形が予め用意されています。

```:app\Http\Controllers\MemberController.php
//追加
use Illuminate\Support\Facades\DB;
use App\Models\Member;
 
public function index()
  {
    //
  }

public function create()
   {
     //
   }

public function store(Request $request)
  {
    //
  }
 
public function show($id)
  {
    //
  }

public function edit($id)
  {
    //
  }

public function update(Request $request, $id)
  {
    //
  }
   
public function destroy($id)
  {
    //
  }
```


## 一覧画面(index)
### routingの追加
/member/indexにアクセスした場合のルーティングを追加します。

```:route/web.php
//追記
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
});
```

### controllerの編集
membersテーブルからデータを取ってきて、viewに渡します。

```:app\Http\Controllers\MemberController.php
public function index()
  {
    //memberテーブルからname,telephone,emailを$membersに格納
    $members=DB::table('members')
      ->select('id', 'name', 'telephone', 'email')
      ->get();
     
    //viewを返す(compactでviewに$membersを渡す)
    return view('member/index', compact('members'));
  }
```

### viewの新規作成

```:resources\views\member\index.blade.php
<h1>一覧表示</h1>

<table>
<tr>
<th>ID</th>
<th>名前</th>
<th>電話番号</th>
<th>メールアドレス</th>
</tr>
@foreach($members as $member)
<tr>
<td>{{$member->id}}</td>
<td>{{$member->name}}</td>
<td>{{$member->telephone}}</td>
<td>{{$member->email}}</td>
</tr>
@endforeach
</table>
```

### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/index にアクセスし、一覧表示ができていることを確認します。
Ctrl+Cで簡易サーバーを終了します。








## 新規登録(create)
### routingの追加
/member/createにアクセスした場合のルーティングを追加します。

```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
      
  //追記
  Route::get('create', 'MemberController@create')->name('member.create');
});
```


### controllerの編集
create.blade.phpを返します。

```:app\Http\Controllers\MemberController.php
public function create()
  {
    return view('member/create');
  }
```

### viewの新規作成
新規作成画面を作ります。
名前だけ入力必須にしています。

```:resources\views\member\create.blade.php
<h1>新規作成</h1>

<form method="POST" action="">
  @csrf

  <div>
    <label for="form-name">名前</label>
    <input type="text" name="name" id="form-name" required>
  </div>
 
  <div>
    <label for="form-tel">電話番号</label>
    <input type="tel" name="telephone" id="form-tel">
  </div>
 
  <div>
    <label for="form-email">メールアドレス</label>
    <input type="email" name="email" id="form-email">
  </div>
 
  <button type="submit">登録</button>
 
</form>
```

### 導線(index→create)
一覧表示画面から新規作成画面へのリンクです。

```:resources\views\member\index.blade.php
<a href="{{ route('member.create') }}">{{ __('新規作成') }}</a>
```

### 導線(create→index)
新規作成画面から一覧表示画面へのリンクです。

```:resources\views\member\create.blade.php
//追加
<a href="{{ route('member.index') }}">{{ __('一覧へ戻る') }}</a>
```

### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/create にアクセスし、新規入力画面ができていることを確認します。
(この時点では、登録ボタンを押してもエラーになります。)
Ctrl+Cで簡易サーバーを終了します。








## 新規保存(store)
新規追加画面で入力した値をDBに保存します。

### routingの追加
/member/storeにアクセスした場合のルーティングを追加します。

```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
  Route::get('create', 'MemberController@create')->name('member.create');
     
  //追加
  Route::post('store', 'MemberController@store')->name('member.store');
});
```

### controllerの編集
フォームで入力された値をmembersテーブルに格納します。

```:app\Http\Controllers\MemberController.php
public function store(Request $request)
  {
    $member=new Member;
   
    $member->name=$request->input('name');
    $member->telephone=$request->input('telephone');
    $member->email=$request->input('email');
   
    $member->save();
   
    //一覧表示画面にリダイレクト
    return redirect('member/index');
  }
```

### viewの新規作成
なし


### 導線(create→store)
新規作成画面から新規保存へのリンクです。

```:resources\views\membercreate.blade.php
//<form method="POST" action="">
//↓フォームの送信先の変更
<form method="POST" action="{{route('member.store')}}">
```

### 導線(store→index)
controllerで、リダイレクトを記述済です。


### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/create にアクセスし、値を入力して送信すると、member/indexにリダイレクトされ、membersテーブルに値が入っている事を確認します。
Ctrl+Cで簡易サーバーを終了します。






## 詳細表示(show)

### routingの追加
/member/show/idにアクセスした場合のルーティングを追加します。

```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
  Route::get('create', 'MemberController@create')->name('member.create');
  Route::post('store', 'MemberController@store')->name('member.store');
     
  //追加
  Route::get('show/{id}', 'MemberController@show')->name('member.show');
});
```

### controllerの編集
指定したIDのメンバーの詳細ページを表示する処理をします。

```:app\Http\Controllers\MemberController.php
public function show($id)
  {
    $member=Member::find($id);
 
    return view('member/show', compact('member'));
  }
```

### viewの新規作成

```:resources\views\member\show.blade.php
<h1>詳細表示</h1>
 
<div>
名前
{{$member->name}}
</div>
 
<div>
電話番号
{{$member->telephone}}
</div>
 
<div>
メールアドレス
{{$member->email}}
</div>
``` 


### 導線(index→show)
一覧表示画面に、1カラム増やして詳細画面へのリンクを追加します。

```:resources\views\member\index.blade.php
<table>
<tr>
<th>ID</th>
<th>名前</th>
<th>電話番号</th>
<th>メールアドレス</th>
<th>詳細</th>
</tr>
@foreach($members as $member)
<tr>
<td>{{$member->id}}</td>
<td>{{$member->name}}</td>
<td>{{$member->telephone}}</td>
<td>{{$member->email}}</td>
<td><th><a href="{{route('member.show',['id'=>$member->id])}}">詳細</a></th></td>
</tr>
@endforeach
</table>
```

### 導線(show→index)

```:resources\views\member\show.blade.php
<a href="{{ route('member.index') }}">{{ __('一覧に戻る') }}</a>
```

### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細を押すと詳細画面が表示されることを確認します。
Ctrl+Cで簡易サーバーを終了します。










## 編集(edit)

### routingの追加
/member/edit/idにアクセスした場合のルーティングを追加します。

```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
  Route::get('create', 'MemberController@create')->name('member.create');
  Route::post('store', 'MemberController@store')->name('member.store');
  Route::get('show/{id}', 'MemberController@show')->name('member.show');
     
  //追加
  Route::get('edit/{id}', 'MemberController@edit')->name('member.edit');
});
```


### controllerの編集

```:app\Http\Controllers\MemberController.php
public function edit($id)
  {
    $member=Member::find($id);
 
    return view('member/edit', compact('member'));
  }
```

### viewの新規作成

```:resources\views\member\edit.blade.php
<h1>編集</h1>
 
 <form method="POST" action="">
  @csrf
 
 <div>
  名前
  <input type="text" name=name value="{{$member->name}}">
  </div>
 
  <div>
  電話番号
  <input type="text" name=telephone value="{{$member->telephone}}">
  </div>
  
  <div>
  メールアドレス
  <input type="text" name=email value="{{$member->email}}">
  </div>
 
 
  <input type="submit" value="更新する">
 
  </form>
```


### 導線(show→edit)

```:resources\views\member\show.blade.php
//追加
<a href="{{route('member.edit',['id'=>$member->id])}}">{{ __('編集') }}</a>
```

### 導線(edit→show)

```:resources\views\member\edit.blade.php
//追加
<a href="{{route('member.show',['id'=>$member->id])}}">{{ __('詳細に戻る') }}</a>
```

### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細→編集と進む編集フォームが表示されることを確認します。
(この時点では、更新ボタンを押してもエラーになります。)








## 更新(update)
編集画面で「更新」ボタンを押した時の動作を設定していきます。

### routingの追加
/member/update/idにアクセスした場合のルーティングを追加します。

```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
  Route::get('create', 'MemberController@create')->name('member.create');
  Route::post('store', 'MemberController@store')->name('member.store');
  Route::get('show/{id}', 'MemberController@show')->name('member.show');
  Route::get('edit/{id}', 'MemberController@edit')->name('member.edit');
     
  //追加
  Route::post('update/{id}', 'MemberController@update')->name('member.update');
});
```

### controllerの編集

```:app\Http\Controllers\MemberController.php
public function update(Request $request, $id)
  {
    $member=Member::find($id);
 
    $member->name=$request->input('name');
    $member->telephone=$request->input('telephone');
    $member->email=$request->input('email');
 
    //DBに保存
    $member->save();
 
    //処理が終わったらmember/indexにリダイレクト
    return redirect('member/index');
}
```

### viewの新規作成
なし

### 導線(edit→update)
editの送信ボタンの送信先を指定します。

```:resources\views\member\edit.blade.php
//<form method="POST" action="">
//↓
<form method="POST" action="{{route('member.update',['id' =>$member->id])}}">
```

### 導線(update→index)
controllerでリダイレクト済


### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細画面→編集画面と移動し、値を変更して「更新」ボタンを押し、/member/indexのデータが変更されていることを確認します。
Ctrl+Cで簡易サーバーを終了します。







## 削除(destroy)
### routingの追加
/member/destroy/idにアクセスした場合のルーティングを追加します。

```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index', 'MemberController@index')->name('member.index');
  Route::get('create', 'MemberController@create')->name('member.create');
  Route::post('store', 'MemberController@store')->name('member.store');
  Route::get('show/{id}', 'MemberController@show')->name('member.show');
  Route::get('edit/{id}', 'MemberController@edit')->name('member.edit');
  Route::post('update/{id}', 'MemberController@update')->name('member.update');
     
  //追加
  Route::post('destroy/{id}', 'MemberController@destroy')->name('member.destroy');
});
```

### controllerの編集
指定のIDを削除する処理を書きます。

```:app\Http\Controllers\MemberController.php
public function destroy($id)
  {
    $member=Member::find($id);
         
    $member->delete();
 
    return redirect('member/index');
  }
```

### viewの新規作成
なし

### 導線(show→destroy)

```:resources\views\member\show.blade.php
//追加
<form method="POST" action="{{route('member.destroy',['id'=>$member->id])}}">
  @csrf
  <button type="submit">削除</button>
</form>
```

### 導線(destroy→index)
controllerでリダイレクト済


### 確認
簡易サーバーを起動します。

```:terminal
php artisan serve
```

ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細画面に移動し、「削除」ボタンを押し、/member/indexのデータが削除されていることを確認します。
Ctrl+Cで簡易サーバーを終了します。






## バリデーションの準備

### エラーメッセージの日本語化
resources/lang/に下記のjaフォルダをenフォルダと同じ階層に配置します。
https://github.com/minoryorg/laravel-resources-lang-ja
※jaフォルダ内のファイルの編集でさらにメッセージのカスタマイズができます。

```:resources\lang\ja\validation.php
//'attributes' => [],
//↓
'attributes' => ['email'=>'メールアドレス',
'name'=>'名前'
],
```



## バリデーション(新規保存)

### form requestの作成

```:terminal
php artisan make:request StoreMember
```

app\Http\Requests\StoreMember.phpが新規作成されます。


### form requestの編集
バリデーションのルールを指定します。

```:app\Http\Requests\StoreMember.php
//追加
use Illuminate\Validation\Rule;
 
public function authorize()
  {
    //return false;
    //↓falseをtrueに変更
    return true;
  }
  
public function rules()
  {
    return [
         
      //追加
      'name' => [
        'string',
        'required',
        'max:20'
         ],
           
      'telephone' => [
        'string',
        'nullable',
        'max:13',
        'unique:members'
        ],
           
      'email' => [
        'nullable',
        'max:255',
        'email',
        'unique:members'
        ]
    ];
  }
```

|キーワード|内容|
|---|---|
|required|必須|
|max|最大長|
|unique:テーブル名|指定のテーブルでユニーク|
|nullable|null許容|
|accepted|チェックされている|
|string|文字列型|
|email|email型|
|url|url型|



### controller

```:app\Http\Controllers\MemberController.php
//追加
use App\Http\Requests\StoreMember;
 
 
//public function store(Request $request)
//↓変更
public function store(StoreMember $request)
```

### view
エラーメッセージを表示させたい場所にエラーメッセージを配置します。

```:resources\views\member\create.blade.php
<form method="POST" action="{{route('member.store')}}">
  @csrf
 
  <div>
    <label for="form-name">名前</label>
 
    // <input type="text" name="name" id="form-name" required>
    //↓valueを追加して入力値を保持させます。
    <input type="text" name="name" id="form-name" required value="{{old('name')}}">
 
    //追加
    @error('name')
    {{$message}}
    @enderror
 
  </div>
 
  <div>
    <label for="form-tel">電話番号</label>
 
    //<input type="tel" name="telephone" id="form-tel">
    //↓valueを追加して入力値を保持させます。
    <input type="tel" name="telephone" id="form-tel" value="{{old('telephone')}}">
 
   //追加
   @error('telephone')
   {{$message}}
   @enderror
 
  </div>
 
  <div>
 
    <label for="form-email">メールアドレス</label>
 
    //<input type="email" name="email" id="form-email">
    //↓valueを追加して入力値を保持させます。
    <input type="email" name="email" id="form-email" value="{{old('email')}}">
 
    //追加
    @error('email')
    {{$message}}
    @enderror
 
  </div>
 
  <button type="submit">送信</button>
 
</form>
```







## バリデーション(上書き保存)
新規登録のバリデーションのままでは、ユニークにする電話番号と、メールアドレスを弾いてしまうので、変更が必要です。
### form requestの作成

```:terminal
php artisan make:request UpdateMember
```
app\Http\Requests\UpdateMember.phpが新規作成されます。




### form requestの編集
新規作成のバリデーションとほぼ同じですが、既存の値も許可するように変更します。

```:app\Http\Requests\UpdateMember.php
//追加
use Illuminate\Validation\Rule;

public function authorize()
  {
    //return false;
    //↓falseをtrueに変更
    return true;
  }

public function rules()
  {
    return [
             
      //追加
      'name' => [
        'string',
        'required',
        'max:20'
        ],
 
      'telephone' => [
         'string',
         'nullable',
         'max:13',

         //既存の値も許可
         Rule::unique('members')->ignore($this->id)
       ],
 
      'email' => [
        'nullable',
        'max:255',
        'email',

         //既存の値も許可
         Rule::unique('members')->ignore($this->id)
         ]
             
     ];
   }
```

### controller

```:app\Http\Controllers\MemberController.php
//追加
use App\Http\Requests\UpdateMember;
 
//public function update(Request $request, $id)
//↓変更
public function update(UpdateMember $request, $id)
```

### view
エラーメッセージを表示させたい場所にエラーメッセージを配置します。

```:resources\views\member\edit.blade.php
<form method="POST" action="{{route('member.update',['id' =>$member->id])}}">
 @csrf

  <div>
    名前
    <input type="text" name=name value="{{$member->name}}">
    @error('name')
    {{$message}}
    @enderror
  </div>

  <div>
    電話番号
    <input type="text" name=telephone value="{{$member->telephone}}">
    @error('telephone')
    {{$message}}
    @enderror
  </div>

  <div>
    メールアドレス
    <input type="text" name=email value="{{$member->email}}">
    @error('email')
    {{$message}}
    @enderror
  </div>

  <input type="submit" value="更新する">

</form>
```

## ページネーション

### controller
一覧表示の件数が増えてきたら、ページを分けます。

```:app\Http\Controllers\MemberController.php
public function index()
  {
    $members=DB::table('members')
      ->select('id', 'name', 'telephone', 'email')
      //->get();
      //↓ 1ページに表示する件数を指定
      ->paginate(20);
 
      //viewを返す(compactでviewに$membersを渡す)
      return view('member/index', compact('members'));
  }
``` 
 

### view
一覧画面にページ送りのUIを追加します。

```:resources\views\member\index.blade.php
//追加
{{$members->links()}}
```

記述としては1行ですが、下記のようにhtmlが生成されますので、CSSで形を整えてください。

```:html
<nav>
  <ul class="pagination">
             
    <li class="page-item disabled" aria-disabled="true" aria-label="« Previous"><span class="page-link" aria-hidden="true">‹</span></li>
    <li class="page-item active" aria-current="page">
      <span class="page-link">1</span>
    </li>
    <li class="page-item">
      <a class="page-link" href="http://127.0.0.1:8000/member/index?page=2">2</a>
    </li>
    <li class="page-item">
      <a class="page-link" href="http://127.0.0.1:8000/member/index?page=2" rel="next" aria-label="Next »">›</a>
    </li>
     
  </ul>
</nav>
```







## 検索機能

### routingの追加
```:route/web.php
Route::group(['prefix'=>'member'], function () {
  Route::get('index' 'MemberController@index')->name('member.index');
  Route::get('create', 'MemberController@create')->name('member.create');
  Route::post('store', 'MemberController@store')->name('member.store');
  Route::get('show/{id}', 'MemberController@show')->name('member.show');
  Route::get('edit/{id}', 'MemberController@edit')->name('member.edit');
  Route::post('update/{id}', 'MemberController@update')->name('member.update');
  Route::post('destroy/{id}', 'MemberController@destroy')->name('member.destroy');
     
  //追加
  Route::get('search', 'MemberController@search')->name('member.search');
});
```


### controllerの編集

```:app\Http\Controllers\MemberController.php
public function search(Request $request)
  {
    $serach=$request->input('q');
 
    $query=DB::table('members');
 
    //検索ワードの全角スペースを半角スペースに変換
    $serach_spaceharf=mb_convert_kana($serach, 's');
 
 
    //検索ワードを半角スペースで区切る
    $keyword_array=preg_split('/[\s]+/', $serach_spaceharf, -1, PREG_SPLIT_NO_EMPTY);
 
    //検索ワードをループで回してマッチするレコードを探す
    foreach ($keyword_array as $keyword) {
        $query->where('name', 'like', '%'.$keyword.'%');
      }
 
    $query->select('id', 'name', 'telephone', 'email');
    $members=$query->paginate(20);
 
    return view('member/index', compact('members'));
  }
```

### viewの編集
一覧表示画面に検索フォームを追加します。

```:resources\views\member\index.blade.php
//追加
<form method="GET" action="{{route('member.search')}}">
  @csrf
  <div>
    <label for="form-search">検索</label>
    <input type="search" name="q" id="form-search">
  </div>
 
  <button type="submit">検索</button>
 
</form>
```