どこにでもある記事だけどね。基本は押さえておかないと。
#前提
自分で勝手に作ったadminテーブルに対してCRUDを作成する。
テーブルは下記の記事で作ったやつ。
LarabelでDBにテストデータを仕込む
ここまでに作ったものをまとめると、
App/Admin.php
database/Factories/AdminFactory.php
database/migrations/2019_09_21_104722_create_admin_table.php
database/seeds/AdminTablesSeeder.php
テストデータを作るところだけだね。
このテーブルをCRUDで一式やれるようにしてみるよ。
#何はともあれやること
CRUDの何を作るにしても、Controllerを作らないことには話にならない。
例によってartisanでControllerを作ってみる。
$php artisan make:controller AdminController --resource
--resourceをつけると、CRUDに必要なfunctionをあらかじめ入れた形でソースを生成してくれる。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
それぞれのfunctionについてはlaravelマニュアル見たほうがわかりやすいかも。
URI | Action | route | 内容 | |
---|---|---|---|---|
GET | /admin | index | admin.index | 一覧 |
GET | /admin/create | create | admin.create | 追加 |
POST | /admin | store | admin.store | 追加情報の保存 |
GET | /admin/{id} | show | admin.show | 表示 |
GET | /admin/{id}/edit | edit | admin.edit | 編集 |
PUT/PATCH | /admin/{id} | update | admin.update | 編集情報の更新 |
DELETE | /admin/{id} | destroy | admin.destroy | 削除 |
#一覧表示してみる
まずは一覧表示から。
作らなくちゃいけないものは、以下の通り。
・routes/web.phpにルートを追加する。
・一覧表示画面を作る(/admin/index.blade.php)。
・AdminController.phpのfunction indexに処理を追加する。
では、順番に。
##ルートを追加
/admin/indexへのルートを追加する。
--resourceをつけて生成したControllerのルートは、1行で全部設定できる。
Route::resource('admin', 'adminController');
こうしたときは、画面からジャンプ先を指定するrouteの設定内容は、前に書いた表の「Route」の内容になります。
indexに飛ばいしたいときは、「admin.index」を指定します。
<a href="{{ route('admin.index') }}">Admin</a>
##一覧画面を作る
とりあえず画面デザインをそろえるため、Login画面を基にして一覧画面を作ってみた。
Login用の項目などを削除して、代わりに一覧表示用のテーブルを追加しています。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Index</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{--成功時のメッセージ--}}
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
{{-- エラーメッセージ --}}
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
// この辺から追加
<div class="table-resopnsive">
<table class="table table-striped">
<thead>
<tr>
<th>{{__('ID')}}</th>
<th>{{__('admin_code')}}</th>
<th>{{__('name')}}</th>
<th>{{__('role')}}</th>
</tr>
</thead>
<tbody>
@if(isset($admins)) // $adminデータ存在チェック
@foreach ($admins as $admin) // テーブル作成
<tr>
<td>{{ $admin->id }}</td>
<td>{{ $admin->admin_code }}</td>
<td>{{ $admin->name }}</td>
<td>{{ $admin->role}}</td>
</tr>
@endforeach
@endif
</tbody>
</table>
// この辺まで追加
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
とりあえず単に一覧を表示するだけの画面です。
@ifで$adminsに値が入っているかチェックしています。
@foreachで取得したデータをすべてテーブルに設定しています。
##indexに処理を追加する
今度はadmincontroller.phpのindexにデータ取得処理を追加します。
public function index()
{
$admins = \App\Admin::all();
return view('admin/index', compact('admins'));
}
adminテーブルはEloquentを使っているので、allで全件抽出することができる。
全件取得したインスタンスをviewに渡してあげれば、bladeの中で一覧表を作ってくれる。
##とりあえず実行
上記までの処理を実装すると、こんな画面が表示される。はず。
ちなみにデータはシーダーで作りました。
#次はcreate…の前に
今回のadminに関する処理は、一覧画面から各機能へ遷移するようにします。今決めました。
追加→「追加」ボタンクリック→admin.create→admin.store
詳細→IDをクリック→admin.show
変更→詳細画面で「変更」ボタンをクリック→admin.edit→admin.update
削除→詳細画面で「削除」ボタンをクリック→admin.destroy
なので、createの処理を作る前に、一覧画面に「追加」ボタンを追加します。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Index</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{--成功時のメッセージ--}}
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
{{-- エラーメッセージ --}}
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
// ここから追加
<button type="button" class="btn btn-primary" onclick="location.href='{{ route('admin.create') }}'">
{{ __('追加') }}
</button>
// ここまで追加
<div class="table-resopnsive">
<table class="table table-striped">
<thead>
<tr>
<th>{{__('ID')}}</th>
<th>{{__('admin_code')}}</th>
<th>{{__('name')}}</th>
<th>{{__('role')}}</th>
</tr>
</thead>
<tbody>
@if(isset($admins))
@foreach ($admins as $admin)
<tr>
<td>{{ $admin->id }}</td>
<td>{{ $admin->admin_code }}</td>
<td>{{ $admin->name }}</td>
<td>{{ $admin->role}}</td>
</tr>
@endforeach
@endif
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
とりあえず追加ボタンだけ追加。
#createとstoreを作る
createはGET、storeはPOSTで送信される。てことは、createは参照だけ、storeで更新ということになります。
##controllerのcreateに処理を追加する
createで行うのは登録画面の表示だけなので、controllerには画面表示の処理だけ記述します。
// createのところだけ抜粋
public function create()
{
return view('admin.create');
}
##createの画面を作ってみる
表示する画面は下記のように作ってみた。基本/auth/register.blade.phpをぱくって作ってます。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Add</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{--成功時のメッセージ--}}
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
{{-- エラーメッセージ --}}
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('admin.store') }}" method="POST">
@csrf
<div class="form-group row">
<label for="admin_code" class="col-md-4 col-form-label text-md-right">{{ __('Admin Code') }}</label>
<div class="col-md-6">
<input id="admin_code" type="text" class="form-control" name="admin_code" value="{{ old('admin_code') }}">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}">
</div>
</div>
<div class="form-group row">
<label for="role" class="col-md-4 col-form-label text-md-right">{{ __('Role') }}</label>
<div class="col-md-6">
<input id="role" type="text" class="form-control" name="role" value="{{ old('role') }}">
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary" name='action' value='add'>
{{ __('追加') }}
</button>
<button type="submit" class="btn btn-primary" name='action' value='back'>
{{ __('戻る') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
エラーメッセージが出せる仕組みになっているが、今はまだvalidationしてないので何の役にも立ってません。そのうち何とかするから、待っとけ。
画面はこんな感じで表示される。
追加ボタンを押したら入力データを追加、戻るボタンを押したら一覧画面に戻るようにする。
これは、Formタグのaction属性でadmin.storeに遷移するよう設定している。そのため追加を押そうが戻るを押そうがすべてAdminControllerのstoreに飛ばされる。
##controllerのstoreに処理を追加する
createで入力されたものに対する処理を行うのがstoreという位置づけのようであります。
なのでvalidateしたり確認画面出したりテーブル更新したり、という処理は基本的にすべてstoreで行うことになります。
今回はとりあえずテーブルへのinsertだけしてみる。
// storeのところだけ抜粋
public function store(Request $request)
{
if($request->action === 'back') {
return redirect()->route('admin.index');
} else {
$admin = new Admin;
$admin->admin_code = $request->admin_code;
$admin->name = $request->name;
$admin->role = $request->role;
$admin->password = Hash::make($request->password);
$admin->save();
return redirect()->route('admin.index');
}
}
最初に押されたボタンの判定を行い、「戻る」ボタンの場合はindexにリダイレクトしています。
「戻る」ボタンが押されたのではない場合、画面に入力された内容をテーブルにセットしてinsertします。
テーブルにデータを追加したら、indexにリダイレクトします。
#詳細を表示する
一覧画面のIDにリンクを張って、IDをクリックしたらそのIDの内容を詳細画面に表示します。
詳細画面に「変更」ボタンを追加して、「変更」ボタンが押されたら編集画面を開くようにします。
まずは詳細画面を表示させます。
##一覧画面にリンクを追加する
IDにリンクを追加する。
//データ表示部分を抜粋
<tbody>
@if(isset($admins))
@foreach ($admins as $admin)
<tr>
<td><a href="/admin/{{ $admin->id }}">{{ $admin->id }}</a></td>
<td>{{ $admin->admin_code }}</td>
<td>{{ $admin->name }}</td>
<td>{{ $admin->role}}</td>
</tr>
@endforeach
@endif
</tbody>
##controllerのshowに処理を追加する
一覧でクリックしたIDを受け取ってadminテーブルを検索し、その内容を表示する。
書くのはこれだけ。
public function show($id)
{
$admins = \App\Admin::find($id);
return view('admin.show', compact('admins'));
}
画面はこんな感じで。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Add</div>
<div class="card-body">
@csrf
<div class="form-group row">
<label for="id" class="col-md-4 col-form-label text-md-right">{{ __('ID') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->id }}
</div>
</div>
<div class="form-group row">
<label for="admin_code" class="col-md-4 col-form-label text-md-right">{{ __('Admin Code') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->admin_code }}
</div>
</div>
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->name }}
</div>
</div>
<div class="form-group row">
<label for="role" class="col-md-4 col-form-label text-md-right">{{ __('Role') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->role }}
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="button" class="btn btn-primary" onclick="location.href='{{ route('admin.edit', $admins->id) }}'">
{{ __('変更') }}
</button>
<button type="button" class="btn btn-primary" onclick="history.back()">
{{ __('戻る') }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
一覧から詳細画面を出すのはこんな感じ。
IDをクリックすると。
詳細画面が表示される。
#editとupdateを作る
editとupdateはcreateとstoreの関係と同じで、editで変更用の画面を表示して、updateで更新をする。
変更画面は、さっき作った詳細画面にこっそり仕込んだ変更ボタンをクリックして遷移することにしていた。formのactionにeditへのリンクを書いておいたので、遡って見つけてくれ。
##Controllerのeditに処理を追加する
今回の処理では詳細画面から変更画面へ遷移するので、直前の処理でテーブル検索しているから、それを持ってきてもいいんだけれど、resourceで作成したひな形では$idを引数にしているので、そのままIDを渡すことにする。
なので、渡されたIDで再度テーブルを検索して、その内容を変更画面に表示させる。
public function edit($id)
{
$admins = \App\Admin::find($id);
return view('admin.edit', compact('admins'));
}
画面はこんな風に。
resourceでrouteを作成すると、updateはmethodがPUTになっているが、HTMLではPUTなんて指定できないので、FormのmethodではPOSTを指定しておき、Formの下に「@method('PUT')」と書いてhiddenの_methodにPUTを設定する。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Edit</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{--成功時のメッセージ--}}
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
{{-- エラーメッセージ --}}
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="/admin/{{ $admins->id }}" method="POST">
@csrf
@method('PUT')
<div class="form-group row">
<label for="id" class="col-md-4 col-form-label text-md-right">{{ __('ID') }}</label>
<div class="col-md-6">
<input id="id" class="input-group-text text-md-left" type="text" name="id" value="{{ old('$admins->id', $admins->id) }}">
</div>
</div>
<div class="form-group row">
<label for="admin_code" class="col-md-4 col-form-label text-md-right">{{ __('Admin Code') }}</label>
<div class="col-md-6">
<input id="admin_code" type="text" class="form-control" name="admin_code" value="{{ old('admin_code',$admins->admin_code) }}">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" value="{{ old('name',$admins->name) }}">
</div>
</div>
<div class="form-group row">
<label for="role" class="col-md-4 col-form-label text-md-right">{{ __('Role') }}</label>
<div class="col-md-6">
<input id="role" type="text" class="form-control" name="role" value="{{ old('role',$admins->role) }}">
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary" name='action' value='edit'>
{{ __('変更') }}
</button>
<button type="submit" class="btn btn-primary" name='action' value='back'>
{{ __('戻る') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
実際の画面はこんな風に表示される。
cssとかデフォルトのままなので、結構苦し紛れだ。
##Controllerのupdateに処理を追加する
中身はstoreに似ているが、追加ではなく更新なので、またテーブル読んじまった。
ここら辺はなんかできそうなので、各自宿題で考えておくように。
更新はsaveでできる。キーがなければinsert、存在したらupdateになる。
あと、passwordはHash化して保存しています。
public function update(Request $request, $id)
{
if($request->action === 'back') {
return redirect()->route('admin.index');
} else {
$admin = \App\Admin::find($id);
$admin->admin_code = $request->admin_code;
$admin->name = $request->name;
$admin->role = $request->role;
$admin->password = Hash::make($request->password);
$admin->save();
return redirect()->route('admin.index');
}
}
#最後にDestroy
最後に削除を追加する。
##Controllerのdestroyに処理を追加する
public function destroy($id)
{
$admins = \App\Admin::find($id);
$admins->delete();
return redirect()->route('admin.index');
}
削除ボタンは詳細画面に追加する。
DestroyはmethodにDELETEを指定しなくてはならないので、formでPOST指定した後、@methodでDELETEを指定する。
こんな感じ。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Add</div>
<div class="card-body">
@csrf
<div class="form-group row">
<label for="id" class="col-md-4 col-form-label text-md-right">{{ __('ID') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->id }}
</div>
</div>
<div class="form-group row">
<label for="admin_code" class="col-md-4 col-form-label text-md-right">{{ __('Admin Code') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->admin_code }}
</div>
</div>
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->name }}
</div>
</div>
<div class="form-group row">
<label for="role" class="col-md-4 col-form-label text-md-right">{{ __('Role') }}</label>
<div class="col-md-6 input-group-text">
{{ $admins->role }}
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="button" class="btn btn-primary" onclick="location.href='{{ route('admin.edit', $admins->id) }}'">
{{ __('変更') }}
</button>
<form style="display:inline" action="{{ route('admin.destroy', $admins->id) }}" method="post">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger">
{{ __('削除') }}
</button>
</form>
<button type="button" class="btn btn-primary" onclick="history.back()">
{{ __('戻る') }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
これでとりあえずCRUDが実装できました。
validationが未実装とか、確認画面出してないとか、まだまだ更新するところはいろいろありますが、まずは一段落。
今日のところはここまで。アディオス、アミーゴ!