【保存版】Laravel CRUD

Laravel laravel
Laravel CRUD - Qiita
はじめにLaravelでCRUDを作っていきます。やらないこと認証関係は扱いません。削除の前の確認などJavaScriptで実現される機能は今回は作りません。つくるものindex(一覧表…
  1. はじめに
    1. やらないこと
    2. つくるもの
  2. 準備
    1. テーブルの作成
      1. 作成するテーブル
      2. modelファイルとmigrationファイルの作成
      3. migrationファイルの編集
      4. migrationの実行
    2. レコードの挿入
      1. seederファイルの作成
      2. seederファイルの編集
      3. DatabaseSeederへの登録
      4. seederファイルの実行
      5. controllerの作成
      6. controllerの編集
  3. 一覧画面(index)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 確認
  4. 新規登録(create)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 導線(index→create)
    5. 導線(create→index)
    6. 確認
  5. 新規保存(store)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 導線(create→store)
    5. 導線(store→index)
    6. 確認
  6. 詳細表示(show)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 導線(index→show)
    5. 導線(show→index)
    6. 確認
  7. 編集(edit)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 導線(show→edit)
    5. 導線(edit→show)
    6. 確認
  8. 更新(update)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 導線(edit→update)
    5. 導線(update→index)
    6. 確認
  9. 削除(destroy)
    1. routingの追加
    2. controllerの編集
    3. viewの新規作成
    4. 導線(show→destroy)
    5. 導線(destroy→index)
    6. 確認
  10. バリデーションの準備
    1. エラーメッセージの日本語化
  11. バリデーション(新規保存)
    1. form requestの作成
    2. form requestの編集
    3. controller
    4. view
  12. バリデーション(上書き保存)
    1. form requestの作成
    2. form requestの編集
    3. controller
    4. view
  13. ページネーション
    1. controller
    2. view
  14. 検索機能
    1. routingの追加
    2. controllerの編集
    3. viewの編集

はじめに

LaravelでCRUDを作っていきます。

やらないこと

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

つくるもの

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

流れ

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

準備

テーブルの作成

作成するテーブル

idnametelephoneemailcreated_atupdated_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:テーブル名指定のテーブルでユニーク
nullablenull許容
acceptedチェックされている
string文字列型
emailemail型
urlurl型

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" 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>
タイトルとURLをコピーしました