暇なときに予定を探せるアプリyoteiPickerをリリースしました。
削除機能を作成します。
#開発環境
Docker 20.10.7
PHP 7.4.22
Laravel 8.53.1
mySQL 5.7
データベースのツール phpmyadmin
以下の記事の続きになります。
【Laravel実務に使える】マイグレーションの作成から一覧画面表示まで
【Laravel8】レイアウト共通化 layoutsファイルのapp.blade.phpのコード例
【Laravel】画面遷移させる方法 例:一覧画面から登録画面に遷移
【実務で使えるLaravel】登録画面フォームの作成と新規登録処理を解説
【実務で使えるLaravel】詳細画面の表示 例:一覧画面から詳細画面に遷移
【実務で使えるLaravel】編集画面の表示・更新処理
#想定
今回は本を管理するアプリを想定し、一覧画面から本のidを削除することを想定します。
削除ボタンを押す→ブックナンバー1のレコードが削除される。
一覧画面のビューは以下のようになっています。
@extends('layouts.app')
@section('content')
<h1>本を管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ブックナンバー</th>
<th>ブック名</th>
<th>作成日</th>
<th>詳細</th>
<th>編集</th>
<th>削除</th>
</tr>
</thead>
<tbody>
@foreach ($books as $book)
<tr>
<td>{{ $book->book_id }}</td>
<td>{{ $book->book_name }}</td>
<td>{{ $book->created_at }}</td>
<td><a href="{{ route('book.show', ['id'=>$book->book_id]) }}" class="btn btn-primary">詳細</a></td>
<td><a href="{{ route('book.edit', ['id'=>$book->book_id]) }}" class="btn btn-info">編集</a></td>
</tr>
@endforeach
</tbody>
</table>
<a href="{{ route('book.create') }}">本の登録へ</a>
@endsection
#実装の流れ
ルーティングの記述
コントローラーにロジックをかく
ビューに削除の処理を追加
#実際にコードを書く
①まずは削除処理のルーティングを記述する
routes>web.phpに以下を追加
// 本の削除
Route::post('/destroy{id}', [BookController::class, 'destroy'])->name('book.destroy');
コントローラーに削除処理を追加
今回のアプリではBookController.phpに一連の処理を書いています。他の機能については記事トップの記事リンクから覗いてください。
app>Http>Controllers>BookController.php
/**
* 削除処理
*/
public function destroy($id)
{
// Booksテーブルから指定のIDのレコード1件を取得
$book = Book::find($id);
// レコードを削除
$book->delete();
// 削除したら一覧画面にリダイレクト
return redirect()->route('book.index');
}
一覧画面のビューにボタンをクリックしたら削除できるように処理を追加(resources>views>book>index.blade.php)
<td>
<form action="{{ route('book.destroy', ['id'=>$book->book_id]) }}" method="POST">
@csrf
<button type="submit" class="btn btn-danger">削除</button>
</form>
</td>
詳細ボタンや編集ボタンは以下のような記述でも大丈夫でしたが、削除する場合はaタグではなくformタグになる点に注意です。
<td><a href="{{ route('book.show', ['id'=>$book->book_id]) }}" class="btn btn-primary">詳細</a></td>
<td><a href="{{ route('book.edit', ['id'=>$book->book_id]) }}" class="btn btn-info">編集</a></td>
一覧画面全体
@extends('layouts.app')
@section('content')
<h1>本を管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ブックナンバー</th>
<th>ブック名</th>
<th>作成日</th>
<th>詳細</th>
<th>編集</th>
<th>削除</th>
</tr>
</thead>
<tbody>
@foreach ($books as $book)
<tr>
<td>{{ $book->book_id }}</td>
<td>{{ $book->book_name }}</td>
<td>{{ $book->created_at }}</td>
<td><a href="{{ route('book.show', ['id'=>$book->book_id]) }}" class="btn btn-primary">詳細</a></td>
<td><a href="{{ route('book.edit', ['id'=>$book->book_id]) }}" class="btn btn-info">編集</a></td>
<td>
<form action="{{ route('book.destroy', ['id'=>$book->book_id]) }}" method="POST">
@csrf
<button type="submit" class="btn btn-danger">削除</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
<a href="{{ route('book.create') }}">本の登録へ</a>
@endsection
これで削除ボタンを押したときに削除処理が実行されます。
これでも動作的には問題ないですが、実務レベルになるために削除処理をコントローラーからモデルに移行して、コントローラーの記述量を少なくしましょう。
実務ではidをもとにレコードを取得するとか削除処理とか、ロジックに関してはモデルに書くことが望ましいです。理由は、何回もロジックを書く必要がなくなり、コード量が減るから。
例えば、idをもとにデータを取得する場合、
$book = Book::where('book_id', '>' 3)
->orderBy('book_id', 'desc')
->get();
毎回こんなの書いてたらコントローラーの記述超増えるし、同じコード何回も書いててダサいですよね。
なので、モデルに上記のようなコードを1回書いておいて、コントローラーではロジックを呼び出すだけにします。
$book = $this->book->findBook();
1行で済みます。
では実際に今回の削除処理をモデルに移していきます。
今回対象のコード
/**
* 削除処理
*/
public function destroy($id)
{
// Booksテーブルから指定のIDのレコード1件を取得
$book = Book::find($id);
// レコードを削除
$book->delete();
// 削除したら一覧画面にリダイレクト
return redirect()->route('book.index');
}
コントローラーを以下のように修正
/**
* 削除処理
*/
public function destroy($id)
{
// 指定されたIDのレコードを削除
$deleteBook = $this->book->deleteBookById($id);
// 削除したら一覧画面にリダイレクト
return redirect()->route('book.index');
}
$this->bookは、コンストラクトで自動的にインスタンスが生成されるように書いています。
※後ほどコード全文も載せます。
public function __construct()
{
$this->book = new Book();
}
そしてモデルに削除処理を記載。
app>models>Book.php
/**
* 削除処理
*/
public function deleteBookById($id)
{
return $this->destroy($id);
}
destroyを使っていますが、deleteではなくdestroyにしている理由は、$id指定があるからです。
delete() = 複数削除、destroy() = 単体削除って感じ。詳細は、以下。
delete() と destroy() の違いについて
https://woinc.jp/differences-between-delete-and-destroy-on-laravel/
これでモデルとコントローラーで処理を分けられました。
最後にコード全文を載せておきます。
app>models>Book.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
// モデルに関連付けるテーブル
protected $table = 'books';
// テーブルに関連付ける主キー
protected $primaryKey = 'book_id';
// 登録・更新可能なカラムの指定
protected $fillable = [
'book_id',
'user_id',
'category_id',
'book_name',
'created_at',
'updated_at'
];
/**
* 一覧画面表示用にbooksテーブルから全てのデータを取得
*/
public function findAllBooks()
{
return Book::all();
}
/**
* リクエストされたIDをもとにbooksテーブルのレコードを1件取得
*/
public function findBookById($id)
{
return Book::find($id);
}
/**
* 登録処理
*/
public function InsertBook($request)
{
// リクエストデータを基に管理マスターユーザーに登録する
return $this->create([
'book_name' => $request->book_name,
]);
}
/**
* 更新処理
*/
public function updateBook($request, $book)
{
$result = $book->fill([
'book_name' => $request->book_name
])->save();
return $result;
}
/**
* 削除処理
*/
public function deleteBookById($id)
{
return $this->destroy($id);
}
}
app>Http>controllers>BookController.php
<?php
namespace App\Http\Controllers;
use App\Models\Book;
use Illuminate\Http\Request;
class BookController extends Controller
{
public function __construct()
{
$this->book = new Book();
}
/**
* 一覧画面
*/
public function index()
{
$books = $this->book->findAllBooks();
return view('book.index', compact('books'));
}
/**
* 登録画面
*/
public function create(Request $request)
{
return view('book.create');
}
/**
* 登録処理
*/
public function store(Request $request)
{
$registerBook = $this->book->InsertBook($request);
return redirect()->route('book.index');
}
/**
* 詳細画面の表示
*/
public function show($id)
{
$book = Book::find($id);
return view('book.show', compact('book'));
}
/**
* 編集画面の表示
*/
public function edit($id)
{
$book = Book::find($id);
return view('book.edit', compact('book'));
}
/**
* 更新処理
*/
public function update(Request $request, $id)
{
$book = Book::find($id);
$updateBook = $this->book->updateBook($request, $book);
return redirect()->route('book.index');
}
/**
* 削除処理
*/
public function destroy($id)
{
// 指定されたIDのレコードを削除
$deleteBook = $this->book->deleteBookById($id);
// 削除したら一覧画面にリダイレクト
return redirect()->route('book.index');
}
}
resources>views>book>index.blade.php
@extends('layouts.app')
@section('content')
<h1>本を管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ブックナンバー</th>
<th>ブック名</th>
<th>作成日</th>
<th>詳細</th>
<th>編集</th>
<th>削除</th>
</tr>
</thead>
<tbody>
@foreach ($books as $book)
<tr>
<td>{{ $book->book_id }}</td>
<td>{{ $book->book_name }}</td>
<td>{{ $book->created_at }}</td>
<td><a href="{{ route('book.show', ['id'=>$book->book_id]) }}" class="btn btn-primary">詳細</a></td>
<td><a href="{{ route('book.edit', ['id'=>$book->book_id]) }}" class="btn btn-info">編集</a></td>
<td>
<form action="{{ route('book.destroy', ['id'=>$book->book_id]) }}" method="POST">
@csrf
<button type="submit" class="btn btn-danger">削除</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
<a href="{{ route('book.create') }}">本の登録へ</a>
@endsection
routes>web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BookController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// 一覧画面の表示
Route::get('/', [BookController::class, 'index'])->name('book.index');
// 本の登録画面の表示
Route::get('/create', [BookController::class, 'create'])->name('book.create');
// 本の登録処理
Route::post('/store', [BookController::class, 'store'])->name('book.store');
// 本の詳細
Route::get('/show/{id}', [BookController::class, 'show'])->name('book.show');
// 本の編集画面
Route::get('/edit/{id}', [BookController::class, 'edit'])->name('book.edit');
// 本の更新処理
Route::post('/update/{id}', [BookController::class, 'update'])->name('book.update');
// 本の削除
Route::post('/destroy{id}', [BookController::class, 'destroy'])->name('book.destroy');
以上です。
ご質問があればどしどしお願いいたします。
参考になりましたら忘れずLGTMをしていただけると励みになります。
暇なときに予定を探せるアプリyoteiPickerをリリースしました。