概要
公開領域のディスクに保存された画像ファイルを変更・削除する手順を整理する。
- アップロード画像処理~準備
- 画像ファイルの入力
- アップロード画像ファイルの保存・登録
- アップロード画像の表示
- アップロード画像の変更・削除
変更は現在のファイルを削除して新しいファイルを保存するので、画像ファイルの削除手順がキーになる。
一般的なアップロードファイルの削除はこちらを参照。
ファイルの削除手順
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');     } |