Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ErikRobles's avatar

Laravel 7 image not deleting from disk but is deleted from db

I am able to successfully add multiple images for the task (post if it were a blog) and delete the task. The task is successfully deleted from the db as well as its subsequent images. However, in the disc (by the way, I am working local at the moment) the files remain untouched. I am unsure how to handle this request as I am new to Laravel. Any help would be greatly appreciated. Here is what I have in my various files. TaskController.php showing both my store and delete functions:

public function store(Request $request)
    {
        $this->validate($request, [
            'task_name' => 'required',
            'task_description' => 'required',
        ]);

        // Create Task
        $user = Auth::user();
        $task = new Task();
        $data = $request->all();
        $task->user_id = $user->id;
        $task = $user->task()->create($data);
        if ($request->hasFile('images')) {
            $files = $request->file('images');
            foreach ($files ?: [] as $file) {
                $name = time() . '-' . $file->getClientOriginalName();
                $name = str_replace(' ', '-', $name);
                $file->move('task-images', $name);
                $task->image()->create(['name' => $name]);
                $images = new Image;
                $images->name = $name;
            }
        }
        $task->task_name = $request->input('task_name');
        $task->task_description = $request->input('task_description');
        $task->task_priority = $request->input('task_priority');
        $task->task_assigned_by = $request->input('task_assigned_by');
        $task->task_assigned_to = $request->input('task_assigned_to');
        $task->task_to_be_completed_date = $request->input('task_to_be_completed_date');
        $task->task_notes = $request->input('task_notes');
        $task->task_status = $request->task_status;
        $task->save();



        return redirect('/home')->with('success', 'Task Created');
    }

public function destroy($id)
    {
        $task = Task::findOrFail($id);
        $task->delete();
        return redirect('home')->with('success', 'Task Deleted');
    }

Where I am attempting to delete is in my show.blade.php (just the delete form)

<form style="display: inline;" action="/tasks/{{ $task->id }}" method="POST" class="">
              @csrf
              @method('DELETE')
            <button type="submit" class="btn btn-danger btn-sm ml-1 mr-1">
              <i class="fa fa-trash"></i> Delete
            </button>
 </form>

In my web.php, I am using a resource controller for TasksController.php.

In my Image.php Model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use App\Task;

class Image extends Model
{
    protected $fillable = [
        'task_id',
        'name',
    ];

    protected $uploads = '/task-images/';

    public function getFileAttribute($image)
    {
        return $this->uploads . $image;
    }


    public function task()
    {
        // return $this->belongsTo('App\Task', 'task_id');
        return $this->belongsTo(Task::class);
    }

    public static function boot()
    {
        parent::boot();
        self::deleting(function ($image) {
            Storage::delete(Storage::path($image->name));
        });
    }
}

and my Task.php Model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Image;
use Illuminate\Support\Facades\Storage;


class Task extends Model
{
    protected $fillable = [
        'task_name', 'task_priority', 'task_assigned_to', 'task_assigned_by', 'task_description', 'task_to_be_completed_date', 'task_status',
        'task_notes'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function image()
    {
        // return $this->hasMany('App\Image');
        return $this->hasMany(Image::class);
    }

    public static function boot()
    {
        parent::boot();
        self::deleting(function ($task) {
            foreach ($task->image ?: [] as $image) {
                $image->delete();
            }
        });
    }
}

Finally, in my filesystem.php, for the disk section, I have:

'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

If I have missed something, please let me know and I will edit my question. Thank you in advance for your help.

0 likes
38 replies
Sinnbeck's avatar

Do you have a foreign key with on delete?

Sinnbeck's avatar

Try changing the delete command

Storage::delete('task-images/'. $image->name));
ErikRobles's avatar

thank you @jlrdw and @sinnbeck. I have tried with both th unlink option and changing the delete option you guys provided. No joy yet. I tried using $image->name but that threw an error so I tried with $task->image and ran a dd on that request. This is what I got in return: "task-images/[{"id":64,"name":"1600350578-newEgypt.jpg","task_id":22,"created_at":"2020-09-17T13:49:38.000000Z","updated_at":"2020-09-17T13:49:38.000000Z"},{"id":65,"name":"1600350578-scifi1.jpg","task_id":22,"created_at":"2020-09-17T13:49:38.000000Z","updated_at":"2020-09-17T13:49:38.000000Z"},{"id":66,"name":"1600350578-scifi1.webp","task_id":22,"created_at":"2020-09-17T13:49:38.000000Z","updated_at":"2020-09-17T13:49:38.000000Z"}] ◀"

ErikRobles's avatar

I am using, I belive the foreign key of task_id in the image model to relate to the task model:

 Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->string('name')->default('image.png')->nullable();
            $table->unsignedBigInteger('task_id');
            // $table->string('image_path');
            $table->timestamps();
        });
jlrdw's avatar

You of course have to get the name of that image. After getting the name, i.e.,

myimage.jpg, you should then be able to delete it.

Snapey's avatar

experiment in tinker and make sure you have the basic building blocks in place.

For instance, test you can delete the file as

>>> $image = Image::find(1)    // use a valid image id

>>> Storage::delete('task-images/'.$image->filename)

Then when you know this works copy any changes into your code then move up a layer by deleting the image model using eloquent. If that works, then move up to deleting the task etc

Make sure you quit and reload tinker between code edits

jlrdw's avatar

To add, backup first in case you delete the wrong thing.

ErikRobles's avatar

Here is what I get after the first Tinker test:

$ php artisan tinker
Psy Shell v0.10.4 (PHP 7.3.0 — cli) by Justin 
Hileman
>>> $image = Image::find(1);
[!] Aliasing 'Image' to 'App\Image' for this Tinker session.
=> null
>>> Storage::delete('task-images/' . $image->name);
PHP Notice:  Trying to get property 'name' of 
non-object in Psy Shell code on line 1        
=> false
>>>

So, I obviously have something wrong or am clueless on how to access my disk. the db gets deleted just fine but the storage disk remains intact.

If I run $image = Image::all('name'); and run a dd($image); I get:

Illuminate\Database\Eloquent\Collection {#1217 ▼
  #items: array:7 [▼
    0 => App\Image {#1218 ▼
      #fillable: array:2 [▶]
      #uploads: "/task-images/"
      #connection: "mysql"
      #table: "images"
      #primaryKey: "id"
      #keyType: "int"
      +incrementing: true
      #with: []
      #withCount: []
      #perPage: 15
      +exists: true
      +wasRecentlyCreated: false
      #attributes: array:1 [▼
        "name" => "1600371770-egypt2.jpg"
      ]
      #original: array:1 [▶]
      #changes: []
      #casts: []
      #classCastCache: []
      #dates: []
      #dateFormat: null
      #appends: []
      #dispatchesEvents: []
      #observables: []
      #relations: []
      #touches: []
      +timestamps: true
      #hidden: []
      #visible: []
      #guarded: array:1 [▶]
    }
    1 => App\Image {#1219 ▶}
    2 => App\Image {#1220 ▶}
    3 => App\Image {#1221 ▶}
    4 => App\Image {#1222 ▶}
    5 => App\Image {#1223 ▶}
    6 => App\Image {#1224 ▶}
  ]
}
Snapey's avatar
>>> $image = Image::find(1);
[!] Aliasing 'Image' to 'App\Image' for this Tinker session.
=> null

ok stop at this point because 1 is not the valid id number of an image. Look in the database and choose an image model. make sure you know its filename so you can check it is deleted later

ErikRobles's avatar

Here is the new result failing but at least I got the right id number:

>>> $image = Image::find(74);
=> App\Image {#4121
     id: 74,
     name: "1600371770-egypt2.jpg",
     task_id: 24,
     created_at: "2020-09-17 19:42:50",       
     updated_at: "2020-09-17 19:42:50",       
   }
>>> Storage::delete('task-images/'.$image->name);
=> false
>>>

Could it be that I am trying to delete an array of objects at the same time but unclear on how to go about it?

Snapey's avatar

The point of the tinker is that you are not doing anything more complicated than you can see in tinker. You are getting the name from the image model and performing a delete.

So if its false then the file is missing or not in the task-images folder.

ErikRobles's avatar

Thank you @Snapey. This is strange as I am looking in the public folder and in that is the task-images folder and in that I have 1600371770-egypt2.jpg which corresponds to id 74 name: "1600371770-egypt2.jpg", which is in my db. I am scratching my head on this one. Let me dig a bit deeper and comment tomorrow if I have luck or not. Thank you @Snapey. If you think of anything in the meantime, I certainly do appreciate it.

Snapey's avatar

Storage disk will store the files in /storage/app

Your code indicates the image would be stored as /storage/app/task-images/1600371770-egypt2.jpg

You should not see it in public, unless you have created a symlink (and not the default one)

newbie360's avatar

i think Laravel dev team should change the default setting to use public disk instead of local disk ;)

filesystems.php

'default' => env('FILESYSTEM_DRIVER', 'public'),

let the user use Storage::disk('local') when they needs ;)

many questions about the storage disk per day over the internet ;)

jlrdw's avatar

You can actually store files anywhere you like. But one big question do they need to be protected user files or can anyone view them.

ErikRobles's avatar

In my case they are not sensitive images so I don't really think it matters at this point weather they are accessible to the public or not.

ErikRobles's avatar

Yes, I am using a symlink but what I find strange is that I don't see task-images in the storage/app/public folder. I would assume as I have seen in other projects that there would be those files there and in my public folder as well. Maybe I am wrong.

Snapey's avatar

You have not put them in the storage/app/public folder. you have put them in storage/app/task-images

ErikRobles's avatar

So, I have changed the storage folder and it is now showing up in the two locations like one would expect. In my main app public/storage/upload I have all the uploaded files that are also in the db as well as in storage/app/public/upload that the symlink refers to contain all the same files. In my TasksController, I changed the if statement to the following:

 if ($request->hasFile('images')) {
            $files = $request->file('images');
            foreach ($files ?: [] as $file) {
                $name = time() . '-' . $file->getClientOriginalName();
                $name = str_replace(' ', '-', $name);
                $file->storeAs('public/upload', $name);
                $task->image()->create(['name' => $name]);
                $images = new Image;
                $images->name = $name;
            }

This is what made the folder structure better as per @Snapey 's suggestion. I still am having a problem in my delete call as I am not sure how to create that method. If I use $task->image I don't get an error and the db gets deleted but the disk does not. If I use $image->name I get an error:

Property [name] does not exist on this collection instance.

Here is my destroy method (which I am sure is all messed up)

public function destroy($id)
    {
        $task = Task::findOrFail($id);
        $image = Image::all();
        $name = $task->image;

        File::delete('public/upload', $name);


        $task->delete();
        return redirect('home')->with('success', 'Task Deleted');
    }

Any help on this would be greatly appreciated. Thank you.

Snapey's avatar

go back to testing the basic building blocks as suggested earlier

ErikRobles's avatar

OK. I got the following after running some tests:

>>> File::delete('storage/app/public/upload/'.$image->name);
=> true

resulted in true and the file was deleted from my disk. My next dilemma is how to delete them all as most of my tasks(posts) have 3 or more images. If I try the same thing in my Controller, I get Property [name] does not exist on this collection instance

jlrdw's avatar

You can get their names in an array or collection, loop, delete as needed.

ErikRobles's avatar

Thank you @jlrdw. It got rid of the error and I am still able to delete them from the db but the following code does not delete them from the disk. How frustrating. What am I possibly missing. Any help greatly appreciated.

 public function destroy($id)
    {
        $task = Task::findOrFail($id);
        $images = Image::find($id);

        foreach ((array) $images as $image) {
            File::delete('storage/app/public/upload/', $image->name);
        }


        $task->delete();
        return redirect('home')->with('success', 'Task Deleted');
    }
jlrdw's avatar

In the loop your variable is $image, so

       foreach ((array) $images as $image) {
            File::delete('storage/app/public/upload/', $image);
        }

I meant if multiple images, before deleting names from DB, capture the names and put in array.

Make sure you only have the ones you wish to delete, BACKUP.

newbie360's avatar

in your migration, i guess image is belongs to task ?

 Schema::create('images', function (Blueprint $table) {
            $table->id();
            ...
            $table->unsignedBigInteger('task_id');
            ...
            ...
        });

can you see the problem ? ;)

 public function destroy($id)
    {
        $task = Task::findOrFail($id);
        $images = Image::find($id);

put dd($images); before the loop

ErikRobles's avatar

It comes back as null and when I run dd($task->image) it gives me back the array. so my question is, how do I access that in my Controller to delete all the images that belong to that particular task?

Snapey's avatar
Snapey
Best Answer
Level 122

Your task has multiple images. each Image is a single model and a single file

You need to get all images associated with task model, then iterate over them, deleting by name

It would help understanding if you changed the name of the relationship from image to images to reflect that there are more than one.

1 like
jlrdw's avatar

Did you see my last reply above.

Basically you have to find those child id's where parent ID is whatever parent ID is.

Next

Please or to participate in this conversation.