概要
公開領域のディスクに保存された画像ファイルを変更・削除する手順を整理する。
- アップロード画像処理~準備
- 画像ファイルの入力
- アップロード画像ファイルの保存・登録
- アップロード画像の表示
- アップロード画像の変更・削除
変更は現在のファイルを削除して新しいファイルを保存するので、画像ファイルの削除手順がキーになる。
一般的なアップロードファイルの削除はこちらを参照。
ファイルの削除手順
storage
ディレクトリーのディスクに保存された画像ファイルの削除は、以下で行う。
1 |
\Storage::disk('ディスク')->delete('ファイルパス'); |
ここで'ディスク'
と'ファイルパス'
は、保存・登録でstore()
メソッドの引数に指定したものと同じ。
データの変更
例題のアプリケーションを、画像ファイルのほか、商品名や価格も併せて変更できるように修正する。
ルーティング
- 画像データの編集は、
GET
メソッドでedit
アクションにルーティングされる。 - 編集されたデータの更新登録は、
PATCH
メソッドでupdate
アクションにルーティングされる。
1 2 3 4 5 6 7 8 9 |
$ php artisan route:list +--------+-----------+-------------------+----------------+--------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+-------------------+----------------+--------------------------------------------+------------+ ........ | | PUT|PATCH | items/{item} | items.update | App\Http\Controllers\ItemController@update | web | | | GET|HEAD | items/{item}/edit | items.edit | App\Http\Controllers\ItemController@edit | web | ........ +--------+-----------+-------------------+----------------+--------------------------------------------+------------+ |
ビューからのリンク~index.blade.php
index
ページで一覧された各商品に、編集のリンクを配置する。要点としては、
- a要素の
href
の遷移先でroute('items.edit', $item)
をしている - 第1引数はeditのルート名、第2引数は編集対象のデータインスタンス
GET
のURLは以下の様に展開されるアプリケーションルート/{$id}/edit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!DOCTYPE html> <html lang="ja" dir="ltr"> <head> <meta charset="utf-8"> <title>商品一覧・入力</title> </head> <body> <h1>商品一覧・入力</h1> ........ {{-- 商品データ・画像表示 --}} @foreach ($items as $item) <p>{{ $item->name }} : {{ $item->price }}円</p> <div> @if ($item->image !=='') <img src="{{ \Storage::url($item->image) }}" width="25%"> @else <img src="{{ \Storage::url('items/no_image.png') }}"> @endif </div> <a href="{{ route('items.edit', $item) }}">内容編集</a> @endforeach </body> </html> |
コントローラー~edit
GET
メソッドでルーティングされたedit
アクションでは、URLに含まれる$id
に対応するインスタンス$item
がデータベースから引数として渡され、このインスタンスをそのままedit
ビューに渡している。
1 2 3 4 5 6 7 8 9 |
class ItemController extends Controller { ........ public function edit(Item $item) { return view('items.edit', ['item' => $item]); } ........ } |
ビュー~edit.blade.php
edit
ビューでは渡された$item
インスタンスの内容を各input
要素に表示し、画像ファイルのパスから画像を表示している。
- 画像を扱うため、
form
のenctype
オプションを指定している form
要素で指定できるメソッドはGET
かPOST
だけだが、update
ルートはPATCH
メソッドを期待しているので、@method
ディレクティブで'patch'
を指定している- 新規入力時と同じく、フォームリクエストで編集時の入力バリデーションを行っている
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<!DOCTYPE html> <html lang="ja" dir="ltr"> <head> <meta charset="utf-8"> <title>商品データ編集</title> </head> <body> <h1>商品データ編集</h1> <p>商品ID: {{ $item->id }}</p> <form method="post" action="{{ route('items.update', $item) }}" enctype="multipart/form-data"> @csrf @method('patch') <div> <label>商品名: <input type="text" name="name" value="{{ $item->name }}"> </label> </div> <div> <label>価 格: <input type="text" name="price" value="{{ $item->price }}">円 </label> </div> <div> @if ($item->image !=='') <img src="{{ \Storage::url($item->image) }}" width="25%"> @else <img src="{{ \Storage::url('items/no_image.png') }}"> @endif </div> <div> <input type="file" name="image"> </div> <input type="submit" value="更新"> </form> <a href="{{ route('items.index') }}">一覧に戻る</a> {{-- バリデーションエラー表示 --}} @foreach ($errors->all() as $error) <p>{{ $error }}</p> @endforeach </body> </html> |
コントローラー~update
PATCH
メソッドでルーティングされたupdate
アクションでは、編集入力内容によって、商品データの内容と画像ファイルを更新している。
- 編集時に画像が選択されていれば、現在のファイルを削除して、選択されたファイルを保存
- このときにファイルのパスも取得
- 商品内容、画像ファイルへのパスでデータの内容を更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class ItemController extends Controller { ........ public function update(ItemRequest $request, Item $item) { // 画像ファイルインスタンス取得 $image = $request->file('image'); // 現在の画像へのパスをセット $path = $item->image; if (isset($image)) { // 現在の画像ファイルの削除 \Storage::disk('public')->delete($path); // 選択された画像ファイルを保存してパスをセット $path = $image->store('items', 'public'); } // データベースを更新 $item->update([ 'name' => $request->name, 'price' => $request->price, 'image' => $path, ]); return redirect()->route('items.edit', $item); } ........ } |
データの削除
ルーティング
index
ページで一覧表示された商品の削除ボタンを押すと、DELETE
メソッドでdestroy
にルーティングされる。
1 2 3 4 5 6 7 8 |
$ php artisan route:list +--------+--------+--------------+---------------+---------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+--------+--------------+---------------+---------------------------------------------+------------+ ........ | | DELETE | items/{item} | items.destroy | App\Http\Controllers\ItemController@destroy | web | ........ +--------+--------+--------------+---------------+---------------------------------------------+------------+ |
ビューへの削除ボタン配置~index.blade.php
index
ページの各商品表示にフォームと削除ボタンを加える。
form
要素ではmethod="post"
を指定し、@method
ディレクティブで'delete'
メソッドを指定している- 削除する商品を指定して
destroy
にルーティングするだけなので、フォームにはsubmit
ボタンだけが配置されている
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{{-- 商品データ・画像表示 --}} @foreach ($items as $item) <p>{{ $item->name }} : {{ $item->price }}円</p> <div> @if ($item->image !=='') <img src="{{ \Storage::url($item->image) }}" width="25%"> @else <img src="{{ \Storage::url('items/no_image.png') }}"> @endif </div> <a href="{{ route('items.edit', $item) }}">内容編集</a> <form method="post" action="{{ route('items.destroy', $item) }}"> @csrf @method('delete') <input type="submit" value="データ削除"> </form> @endforeach |
コントローラー~destroy
destroy
アクションでは、ファイルが登録されていればpublic
ディスクから削除し、商品データをデータベースから削除している。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public function destroy(Item $item) { // 商品画像ファイルへのパスを取得 $path = $item->image; // ファイルが登録されていれば削除 if ($path !== '') { \Storage::disk('public')->delete($path); } // データベースからデータを削除 $item->delete(); return redirect()->route('items.index'); } |