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'=>'[email protected]',
                 '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>