はじめに
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ファイルの作成
php artisan make:model Models/Member -m
-mオプションでマイグレーションファイルも一緒に作成します。
下記の2つのファイルが作成されます。
app\Models\Member.php
database\migrations\xxxx_xx_xx_xxxxxx_create_members_table.php
migrationファイルの編集
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の実行
php artisan migrate
membersテーブルが作成されます。
レコードの挿入
表示確認用にmembersテーブルにレコードを入れます。
seederファイルの作成
php artisan make:seeder MembersTableSeeder
database\seeds\MembersTableSeeder.phpが作成されます。
seederファイルの編集
public function run()
{
DB::table('members')->insert(
[
[
'name'=>'山田',
'telephone'=>'xxxx-xxxxx',
'email'=>'yamada@example.com',
'created_at'=>now(),
'updated_at'=>now(),
],
]
);
}
DatabaseSeederへの登録
作成したMembersTableSeederをDatabaseSeederに登録します。
public function run()
{
$this->call(MembersTableSeeder::class);
}
seederファイルの実行
php artisan db:seed
membersテーブルにレコードが入ります。
controllerの作成
php artisan make:controller MemberController --resource
--resourceオプションをつけると、7つのアクションの雛形が予め用意されます。
controllerの編集
7つのアクションの雛形が予め用意されています。
//追加
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::group(['prefix'=>'member'], function () {
Route::get('index', 'MemberController@index')->name('member.index');
});
controllerの編集
membersテーブルからデータを取ってきて、viewに渡します。
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の新規作成
<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>
確認
簡易サーバーを起動します。
php artisan serve
ブラウザで http://127.0.0.1:8000/member/index にアクセスし、一覧表示ができていることを確認します。
Ctrl+Cで簡易サーバーを終了します。
新規登録(create)
routingの追加
/member/createにアクセスした場合のルーティングを追加します。
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を返します。
public function create()
{
return view('member/create');
}
viewの新規作成
新規作成画面を作ります。
名前だけ入力必須にしています。
<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)
一覧表示画面から新規作成画面へのリンクです。
<a href="{{ route('member.create') }}">{{ __('新規作成') }}</a>
導線(create→index)
新規作成画面から一覧表示画面へのリンクです。
//追加
<a href="{{ route('member.index') }}">{{ __('一覧へ戻る') }}</a>
確認
簡易サーバーを起動します。
php artisan serve
ブラウザで http://127.0.0.1:8000/member/create にアクセスし、新規入力画面ができていることを確認します。
(この時点では、登録ボタンを押してもエラーになります。)
Ctrl+Cで簡易サーバーを終了します。
新規保存(store)
新規追加画面で入力した値をDBに保存します。
routingの追加
/member/storeにアクセスした場合のルーティングを追加します。
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テーブルに格納します。
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)
新規作成画面から新規保存へのリンクです。
//<form method="POST" action="">
//↓フォームの送信先の変更
<form method="POST" action="{{route('member.store')}}">
導線(store→index)
controllerで、リダイレクトを記述済です。
確認
簡易サーバーを起動します。
php artisan serve
ブラウザで http://127.0.0.1:8000/member/create にアクセスし、値を入力して送信すると、member/indexにリダイレクトされ、membersテーブルに値が入っている事を確認します。
Ctrl+Cで簡易サーバーを終了します。
詳細表示(show)
routingの追加
/member/show/idにアクセスした場合のルーティングを追加します。
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のメンバーの詳細ページを表示する処理をします。
public function show($id)
{
$member=Member::find($id);
return view('member/show', compact('member'));
}
viewの新規作成
<h1>詳細表示</h1>
<div>
名前
{{$member->name}}
</div>
<div>
電話番号
{{$member->telephone}}
</div>
<div>
メールアドレス
{{$member->email}}
</div>
導線(index→show)
一覧表示画面に、1カラム増やして詳細画面へのリンクを追加します。
<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)
<a href="{{ route('member.index') }}">{{ __('一覧に戻る') }}</a>
確認
簡易サーバーを起動します。
php artisan serve
ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細を押すと詳細画面が表示されることを確認します。
Ctrl+Cで簡易サーバーを終了します。
編集(edit)
routingの追加
/member/edit/idにアクセスした場合のルーティングを追加します。
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の編集
public function edit($id)
{
$member=Member::find($id);
return view('member/edit', compact('member'));
}
viewの新規作成
<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)
//追加
<a href="{{route('member.edit',['id'=>$member->id])}}">{{ __('編集') }}</a>
導線(edit→show)
//追加
<a href="{{route('member.show',['id'=>$member->id])}}">{{ __('詳細に戻る') }}</a>
確認
簡易サーバーを起動します。
php artisan serve
ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細→編集と進む編集フォームが表示されることを確認します。
(この時点では、更新ボタンを押してもエラーになります。)
更新(update)
編集画面で「更新」ボタンを押した時の動作を設定していきます。
routingの追加
/member/update/idにアクセスした場合のルーティングを追加します。
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の編集
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の送信ボタンの送信先を指定します。
//<form method="POST" action="">
//↓
<form method="POST" action="{{route('member.update',['id' =>$member->id])}}">
導線(update→index)
controllerでリダイレクト済
確認
簡易サーバーを起動します。
php artisan serve
ブラウザで http://127.0.0.1:8000/member/index にアクセスし、詳細画面→編集画面と移動し、値を変更して「更新」ボタンを押し、/member/indexのデータが変更されていることを確認します。
Ctrl+Cで簡易サーバーを終了します。
削除(destroy)
routingの追加
/member/destroy/idにアクセスした場合のルーティングを追加します。
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を削除する処理を書きます。
public function destroy($id)
{
$member=Member::find($id);
$member->delete();
return redirect('member/index');
}
viewの新規作成
なし
導線(show→destroy)
//追加
<form method="POST" action="{{route('member.destroy',['id'=>$member->id])}}">
@csrf
<button type="submit">削除</button>
</form>
導線(destroy→index)
controllerでリダイレクト済
確認
簡易サーバーを起動します。
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フォルダ内のファイルの編集でさらにメッセージのカスタマイズができます。
//'attributes' => [],
//↓
'attributes' => ['email'=>'メールアドレス',
'name'=>'名前'
],
バリデーション(新規保存)
form requestの作成
php artisan make:request StoreMember
app\Http\Requests\StoreMember.phpが新規作成されます。
form requestの編集
バリデーションのルールを指定します。
//追加
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型 | |
url | url型 |
controller
//追加
use App\Http\Requests\StoreMember;
//public function store(Request $request)
//↓変更
public function store(StoreMember $request)
view
エラーメッセージを表示させたい場所にエラーメッセージを配置します。
<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の作成
php artisan make:request UpdateMember
app\Http\Requests\UpdateMember.phpが新規作成されます。
form requestの編集
新規作成のバリデーションとほぼ同じですが、既存の値も許可するように変更します。
//追加
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
//追加
use App\Http\Requests\UpdateMember;
//public function update(Request $request, $id)
//↓変更
public function update(UpdateMember $request, $id)
view
エラーメッセージを表示させたい場所にエラーメッセージを配置します。
<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
一覧表示の件数が増えてきたら、ページを分けます。
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を追加します。
//追加
{{$members->links()}}
記述としては1行ですが、下記のようにhtmlが生成されますので、CSSで形を整えてください。
<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::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の編集
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の編集
一覧表示画面に検索フォームを追加します。
//追加
<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>