こんにちは!
PHPのLaravelやJavaScriptでWeb開発をしているフリラーンスエンジニアのファドと申します!
こちらの記事はLaravel教材の第6回目の記事になります。
その他のLaravel教材を学習したい方は下記リンクから直接教材へ飛ぶことができます。
-
Laravel教材
【Laravel教材①】ブログシステムを実装しながらLaravelの基礎を徹底的に学ぶ!〜環境構築編〜
-
Laravel教材
【Laravel教材②】Laravelでブログシステム構築!〜DB接続からテストデータの作成編〜
-
Laravel教材
【Laravel教材③】Laravelでブログシステム構築!〜MVCモデル解説から画面の共通化編〜
-
Laravel教材
【Laravel教材④】Laravelでブログシステム構築!〜全記事一覧画面実装から記事詳細画面実装編〜
-
Laravel教材
【Laravel教材⑤】Laravelでブログシステム構築!〜記事投稿機能実装編〜
-
Laravel教材
【Laravel教材⑦】Laravelでブログシステム構築!〜記事削除機能実装編〜
記事編集
記事詳細画面表示と記事投稿ができるようになったので、次は投稿した記事の編集をする機能の作成をしていきます。
記事編集機能は記事詳細画面表示と記事投稿を合わせたような機能になっているので、それらの実装方法を思い出しながら実装してみましょう!
記事編集の流れは下記のようになっています。
- 記事詳細からIDに紐付けたリンクを作成して編集フォームへ遷移
- 編集フォームからPOSTで編集内容を送る
- コントローラーとモデルを使用してデータを編集する
- フォームリクエストを使用してバリデーション
- エラー処理
データの更新メソッド
リンクの作成方法やバリデーション、エラー処理などについてはこれまでの機能実装時に説明したので、まだ説明していないデータ更新のメソッドについて説明します。
実は記事投稿用メソッドの際に2つの方法を説明したと思いますが、データの更新にも2つの方法があります。
それがfillメソッド + saveメソッド
とupdateメソッド
です。
まずはfillメソッド + saveメソッド
の使い方から説明します。
実は記事投稿用メソッドを紹介した時にfillメソッド + saveメソッド
の使い方も紹介しています。
その時の内容とほとんど一緒ですが、今回は記事編集なので編集するための記事の取得が必要になります。
使い方は下記の通りです。
$article = Article::find(1);
$article->fill([
'title' => 'タイトル',
'content' => 'コンテンツ',
]);
$article->save();
まず、編集したい記事の情報をfindメソッド
を使用して取得します。
今回は例として記事IDが1
の記事を取得しています。
次にfillメソッド
を使用して、編集したカラム名と編集内容を=>
で結びつけます。
今回は例としてタイトル
、コンテンツ
とテキストをそのまま入力していますが、実際はフォームで入力された値をこちらに設定します。
そして最後にsaveメソッド
を使用して保存します。
updateメソッド
の使い方はfillメソッド + saveメソッド
とほとんど同じです。
使い方は下記の通りです。
$article = Article::find(1);
$article->update([
'title' => 'タイトル',
'content' => 'コンテンツ'
]);
イメージとしては、fillメソッド + saveメソッド
を一気に行えるのがupdateメソッド
です。
fillメソッド + saveメソッド
とupdateメソッド
の違いは、fillメソッド + saveメソッド
が差分更新なのに対して、updateメソッド
はメソッドが実行されると差分があるかないか関係なく毎回更新します。
データベースへ負荷をなるべくかけないためにも、データベースに登録されている値と登録しようとしている値に差分があったときのみ更新するfillメソッド + saveメソッド
を使用するのが一般的です。
実装
fillメソッド + saveメソッド
以外の内容は記事詳細画面表示機能と記事投稿機能とほぼ同じなので、さっそく実装してみましょう。
記事詳細からIDに紐付けたリンクを作成して編集フォームへ遷移
まずは編集画面表示と編集処理用のルートを作成していきます。
first-app/routes/web.php
を下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;
Route::get('/', function () {
return view('welcome');
});
// 投稿一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
// 記事投稿フォームを表示
Route::get('/article/create', [ArticleController::class, 'createArticle'])->name('createArticle');
// 記事登録処理
Route::post('/article/store', [ArticleController::class,'storeArticle'])->name('storeArticle');
// 記事編集フォームを表示
Route::get('/article/edit/{id}', [ArticleController::class, 'editArticle'])->name('editArticle'); // ここを追加
// 記事編集処理
Route::post('/article/update/{id}', [ArticleController::class,'updateArticle'])->name('updateArticle'); // ここを追加
// 投稿詳細を表示
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle');
追加したのは下記の2行です。
// 記事編集フォームを表示
Route::get('/article/edit/{id}', [ArticleController::class, 'editArticle'])->name('editArticle'); // ここを追加
// 記事編集処理
Route::post('/article/update/{id}', [ArticleController::class,'updateArticle'])->name('updateArticle'); // ここを追加
記事編集フォームを表示させるためには記事IDが必要なので、/article/edit/{id}
としています。
また、記事編集処理ではフォームを使用してPOSTで内容を送信しているのでRoute::post
としています。
次にルートで作成した記事編集画面表示のためのeditArticleメソッド
を作成します。
first-app/app/Http/Controllers/ArticleController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
追加したコードは下記の通りです。
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
記事詳細画面を表示させる時とほぼ同じコードですね!
違うのは遷移させるのがedit.blade.php
になっているだけです。
次に、記事詳細画面から編集画面に遷移できるようにボタンを作成していきましょう。
first-app/resources/views/articles/detail.blade.php
を下記の通り編集してください。
@extends('layout')
@section('title')
記事詳細
@endsection
@section('content')
<h2>{{ $article->title }}</h2>
<div class="d-flex">
<span class="mr-2">作成日:{{ $article->created_at }}</span>
<span>更新日:{{ $article->updated_at }}</span>
</div>
<p class="mt-4">{{ $article->content }}</p>
<a href="{{ route('showArticles') }}" class="mt-3 btn btn-secondary">戻る</a>
<a href="{{ route('editArticle', $article->id) }}" class="mt-3 btn btn-primary">編集</a>
@endsection
追加したのは下記コードです。
<a href="{{ route('editArticle', $article->id) }}" class="mt-3 btn btn-primary">編集</a>
これで編集ボタンをクリックすると記事編集画面へ遷移し、Articleコントローラー
のeditArticleメソッド
へ引数として記事IDを渡すことができます。
編集フォームからPOSTで編集内容を送る
次に編集フォームからPOSTで編集内容を送信するためにedit.blade.php
を作成していきます。first-app/resources/views/articles
ディレクトリにedit.blade.php
をコマンドかVSCodeで作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-app
ディレクトリ上で実行してください。
$ touch resources/views/articles/edit.blade.php
作成したedit.blade.php
を下記の通り編集してください。
@extends('layout')
@section('title')
記事編集
@endsection
@section('content')
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2>記事編集フォーム</h2>
<form method="POST" action="{{ route('updateArticle', $article->id) }}">
@csrf
<div class="form-group">
<label for="title">
タイトル
</label>
<input id="title" name="title" class="form-control" value="{{ old('title', $article->title) }}" type="text">
@if ($errors->has('title'))
<div class="text-danger">
{{ $errors->first('title') }}
</div>
@endif
</div>
<div class="form-group mt-3">
<label for="content">
内容
</label>
<textarea id="content" name="content" class="form-control" rows="4">{{ old('content', $article->content) }}</textarea>
@if ($errors->has('content'))
<div class="text-danger">
{{ $errors->first('content') }}
</div>
@endif
</div>
<div class="mt-3">
<a class="btn btn-secondary mr-2" href="{{ route('showArticle', $article->id) }}">
キャンセル
</a>
<button type="submit" class="btn btn-primary">
更新
</button>
</div>
</form>
</div>
</div>
@endsection
http://localhost/article/edit/1へアクセスして、下記画像のように表示されればOKです!
今回は例として記事IDが1
の記事の編集画面を表示しています。
編集フォームの内容はほとんど登録フォームと同じですが、変更した部分について解説します。
まず、ページタイトルを記事編集
、ページの見出しを記事編集フォーム
としています。
次に、フォームで送信する先をaction属性
の属性値として、編集処理のメソッドであるupdateArticleメソッド
に指定しました。
また、記事IDを渡すためにrouteメソッド
の第二引数に$article->id
と指定しています。
次にinput要素
のvalue
とtextarea要素
の内容
をそれぞれ編集しています。
編集した理由は、データベースに保存されている記事タイトルと記事内容のデータをそれぞれのフォームに反映させるためです。oldメソッド
の第二引数に値を設定することで、編集フォームが表示された直後はデフォルト値としてデータベースに保存されたデータをフォームに表示してくれます。
つまり、記事IDが1
の編集画面を表示すると、下記画像のようにデータベースに保存されている記事ID1
の記事タイトルと記事内容がフォームに表示されるということです。
一方でバリデーションに引っかかった場合はデフォルトの値ではなく、バリデーションで引っかかった時に入力された内容がフォームに反映されて、さらにバリデーションのエラーメッセージがその下に赤文字で出力されるようになります。
次に、キャンセルボタンの遷移先を記事詳細画面にするためにhref属性
の属性値を記事詳細画面を表示するルート名showArticle
に変更しています。
もちろん記事詳細画面への遷移には、どの記事の詳細画面なのかを指定しなければならないので、routeメソッド
の第二引数に記事IDを渡しています。
最後に投稿ボタンを編集用に更新ボタン
にして記事編集フォームの実装は終わりです。
コントローラーとモデルを使用してデータを編集する
編集フォームからPOSTで送信されたデータをデータベースに保存するための処理を実装していきましょう。
すでにルートには編集処理用のルーティングをしているので、そのルーティングに従ってArticleコントローラー
にupdateArticleメソッド
を作成していきましょう。
first-app/app/Http/Controllers/ArticleController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(Request $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
追加したコードは下記の通りです。
/**
* 記事編集処理
*/
public function updateArticle(Request $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
return redirect()->route('showArticle', $article->id);
}
記事登録時の時と同じ様に、POSTで送信された値はupdateArticleメソッド
の引数でRequest $request
と記述することで受け取る事ができます。
また、今回は編集する記事IDもフォームから送られてきているので、$id
と記述して引数を受け取ります。
これでPOSTで送信されてきた値と記事IDをupdateArticleメソッド
内で使用できるようになりました。
次は渡されてきた記事IDを使用して記事データを取得していきます。
取得するにはいつも通りfindメソッド
を使用するだけです。
先ほど説明したfillメソッド + saveメソッド
を使用して編集処理をしています。$request->title
と記述することでフォームで入力したタイトルを取得することができます。
また、$request->content
と記述することでフォームで入力した内容を取得することができます。
これも記事登録処理と同じですね。
最後にリダイレクト先を記事詳細画面に指定すれば終わりです。
記事詳細画面を表示するためには記事IDが必要になるので、routeメソッド
の第二引数に$article->id
として渡しておきましょう。
これで投稿を編集することができました。
しっかり記事が編集できるかの動作確認は各自行ってください!
フォームリクエストを使用してバリデーション
次にフォームリクエストを使用してバリデーションを実装しましょう。
今回は記事編集用のバリデーションなのでArticleUpdateRequest.php
を作成しましょう。
それでは、ターミナルで下記コマンドをfirst-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:request ArticleUpdateRequest
first-app/app/Http/Requests
配下にArticleUpdateRequest.php
が作成されていればOKです。
それではfirst-app/app/Http/Requests/ArticleUpdateRequest.php
を下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ArticleUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true; // trueへ変更
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'title' => 'required|string|max:100',
'content' => 'required|string',
];
}
public function attributes()
{
return [
'title' => 'タイトル',
'content' => '内容',
];
}
}
内容はArticleStoreRequest.php
と全く同じなので、説明は省略します。
次にArticleコントローラー
のupdateArticleメソッド
にArticleUpdateRequest
のバリデーションを反映させましょう。
first-app/app/Http/Controllers/ArticleController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use App\Http\Requests\ArticleUpdateRequest; // ここを追加
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
まずはArticleUpdateRequest
をArticleコントローラー
内で使用できるように、use文
を使用して下記のように記述しましょう。use App\Http\Requests\ArticleUpdateRequest;
次にupdateArticleメソッド
の第一引数をRequest $request
からArticleUpdateRequest $request
に変更します。
これで記事編集時にArticleUpdateRequest.php
で設定したバリデーションが実行されるので、記事編集フォームで何も入力しないで更新ボタンをクリックしたり、タイトルを100文字以上にすると、エラーメッセージが出力されるようになりました。
エラー処理
最後にエラー処理を実装しておきましょう。
実装する内容は下記の通りです。
- try-catch文
- トランザクション
- ログ出力
- エラー画面への遷移
これも登録処理とほとんど変わりません。
first-app/app/Http/Controllers/ArticleController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use App\Http\Requests\ArticleUpdateRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// トランザクション開始
DB::beginTransaction();
try {
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
変更したコードは下記の通りです。
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// トランザクション開始
DB::beginTransaction();
try {
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticle', $article->id);
}
実装内容自体は記事登録時と同じですね!
もしわからない箇所がある場合は、記事登録部分を読み直してください!
★検索ワード
・Laravel 編集機能実装
・Laravel fill save update
次の教材
次の教材は下記から簡単に飛ぶことができます!
引き続きプログラミングを楽しんでいきましょう!
プログラミング学習サポートについて
「独学で挫折した。。。」
「一人でのプログラミング学習がしんどい。。。」
「未経験からエンジニア転職をしたいけど何をしたら良いかわからない。。。」
このような悩みをお持ちの方向けに、本教材作成者のファドがMENTAという学習サイトにてあなたのプログラミング学習とエンジニア転職を徹底サポートいたします!
サポート価格はなんと1日あたりたったの約300円!
教材で分からない箇所のサポートはもちろんのこと、本サイトで公開しているすべての課題の解答も公開しております。
また、MENTAで学習を終わらせていただいた方限定で懇意にしていただいている企業さんを紹介することもあります!
なお、サポート内容の詳細は下記の通りです。
- 目標設定
- マインドセット
- オリジナル教材見放題
- オリジナル課題見放題
- オリジナル課題の解答見放題
- 課題コードレビュー
- 教材への無制限質問
- 課題への無制限質問
- ポートフォリオ作成アドバイス
- 褒めのコーチング
いくつかのプランを用意させていただいておりますので、下記より一度ご覧ください!
コメント