Laravel 6.x 【画像アップロード】 ファイルの削除、データベースへのレコード削除


制作環境

Windows 10
Laravel : 6.18.35
Laravel/ui : 1.0
Laravel-mix : 5.0.1
Bootstrap : 4.0.0
MDBootstrap : 4.19.1
chart.js : 2.9.3
XAMPP
PHP : 7.4.3
Visual Studio Code

関連記事

Laravel 6.x 【画像アップロード】 ディレクトリの作成、ファイルの保存、データベースへの登録、表示
Laravel 6.x 【画像アップロード】 トランザクションを使用する

参考サイト

http://hand28.hatenadiary.jp/entry/2018/09/27/175813
https://reffect.co.jp/laravel/laravel-storage-manipulation-master
https://fippiy.hatenablog.jp/entry/2019/03/22/173054

はじめに

この記事はプログラミングをはじめたばかりの素人が、できたことをメモするのに利用しています。
内容には誤りがあるかもしれません。

はじめる前に

この記事は、以前掲載した内容(関連記事参照)の続編として作成しております。
その為、記述については追記で掲載していますので、わからない方はまず以前の記事を参照してください。

アップロードした画像の削除に特化して掲載してます。
極力シンプルに記述する為、あえてバリデーション等は無視し、やりたい事に必要な最小限のコードだけ書いております。

やりたい事

・フォームで削除したい画像のIDを手動で指定できる。
・file_imagesテーブルから、指定した画像を削除する。
・テーブルから削除すると、保存してある画像本体も削除される。
・成功時のメッセージを表示する。

フォームのイメージ

登録用のフォームと削除用のフォームの2つがある。
登録済みの画像が下に表示される。
IDを入れて削除ボタンを押すと、画像が削除され、残った画像だけが下に表示される。

フォームの編集

※以前の記事への追記になります。

resources>views> 内のfile.blade.phpを開き</form>と@foreachの間に以下を追記します。

file.blade.php
  <form action="/file" method="post">
    @method('delete')
    @csrf
    <label>削除ID <input type="text" name="del_id" size="5"></label>
    <input type="submit" value="削除">
  </form>

★ポイント★

ルーティングをRoute::delete()とする為、@methodでメソッドを指定してます。

コントローラの編集

※失敗する可能性を考慮してないので、エラーメッセージ等はでません。
app>Http>Controllers 内のFileUpController.phpを開き、以下のアクションを追記します。

FileUpController.php
// 先頭に追記
use Illuminate\Support\Facades\Storage;

    public function delete(Request $request)
    {
        // フォームで指定されたIDの情報をデータベースから取得する
        $delItem = FileImage::find($request->del_id);
        // 取得したデータからfile_nameカラム(ファイルの名前)の情報を取得する
        $delFileName = $delItem->file_name;
        // storage/app/public/imagesから、画像ファイルを削除する
        Storage::delete('public/images/' . $delFileName);
        // データベースのテーブルからレコードを削除する
        $delItem->delete();

        // /fileにリダイレクトする その際にフラッシュメッセージを渡す
        return redirect('/file')->with('success', '削除完了しました。');
    }

ルーティングの追加

routes 内のweb.phpを開き、以下を追記する。

web.php
Route::delete('/file', 'FileUpController@delete');

記述はこれで終了です。

動作確認

フォームでIDを指定し、削除ボタンを押して下さい。
※事前にID番号とファイル名を確認しておいて下さい。数字は半角で入力します。

指定した画像が下の表示から削除され、「削除完了しました」と表示されていればOKです。

ファイルの確認

画像本体のファイルが削除されているか確認します。
storage>app>public>imagesディレクトリを確認し、削除した画像ファイルがない事と、データベースに登録してあるファイル数があっていることを確認してください。

併せてpublic>storage>imagesディレクトリを確認し、同じようにファイルがない事と、ファイル数を確認してください。
また、storage>app>public>imagesディレクトリの内容と同じかも確認してください。

データベースの確認

tinkerを使い、データが削除されているか確認します。

プロジェクトのディレクトリでターミナルを起動し、以下を実行して下さい。

php artisan tinker

次に以下を実行して下さい。

use App\Models\FileImage;

次に以下を実行して下さい。

FileImage::all();

全データが表示されるので、指定したIDのデータがない事を確認してください。

確認が終わったら、に以下のどちらかを実行し、tinkerを終了してください。

exit;  Ctrl+c

どうでもいいことですが、exit;を選択するとtinkerからお返事がきます。
以上で終了です。

作成時に発生した問題について

この内容を実際に作成している時、画像ファイル本体を削除するところで少しハマリました。
データベースからの削除はすんなりいくのですが、どうしても画像ファイル本体の削除がうまくいきませんでした。
以下がその時のコントローラの記述で、囲んでいるところが完成したコードと違うところです。

FileUpController.php
    public function delete(Request $request)
    {
        $delItem = FileImage::find($request->del_id);
        $delFileName = $delItem->file_name;

        // ここから
        $delPath = storage_path('app/public/images/' . $delFileName);
        Storage::delete($delPath);
        // ここまで

        $delItem->delete();

        return redirect('/file')->with('success', '削除完了しました。');
    }

改善のために試したこと

dd()を使用して$delPathを確認。

\$delPath = storage_path('app/public/images/' . $delFileName);の後に以下を記述しフォームを送信。

dd($delPath);

以下が表示された内容のサンプルです。

当方の環境について
当方はXAMPPを使用しておりますが、プロジェクトはXAMPPのhtdocs内には作成せず、デスクトップに学習用のフォルダを作成し、その中でプロジェクトを作成して作業をしております。

htdocsを使用していない理由ですが、当方はSSDそ使用している為、極力データを増やさないようにする為と、ダメージを抑える為、また自作PCなので障害が発生した時にデータを守る為です。
確認時はphp artisan serveを利用しております。

また、ドライブがCでないのも同じ理由で、デスクトップを他のHDDに移行しているためです。
その為、この問題は当方の環境が影響している可能性もあります。

dd()を使用して気になった点の修正

表示されたパスですが、appの前までがバックスラッシュで、以降がスラッシュになっているのが気になったので、記述を以下に変更してみました。

$delPath = storage_path('app\public\images\\' . $delFileName);

結果: 何の意味もありませんでした。

ヘルパ関数を変更

ヘルパ関数をstorage_pathから、public_pathに変更して記述も修正しました。

結果: 改善しませんでした。

改善策

ヘルパ関数は完全パスを返すのですが、完全パスは必要ないのではないか、というか本来http://localhost~ のパスを返さないとだめではないのかと考え、パスを事前に取得するのをやめ、Storage::delete();でパスを指定することにし、自分の環境ではうまくいきました。

リファレンス
https://readouble.com/laravel/6.x/ja/helpers.html?header=storage_path()