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

Zoul's avatar
Level 5

static boot() deleting with multiple conditions not working

Hi,

When a user is deleted, i want also to delete all its related photos, a user has OneToOne relation with user_meta which is working, the photo is being deleted, however, its not working with posts and tribes, which it has oneToMany relation,

Here boot() function in User model


    public static function boot() {
        parent::boot();
//user_meta
        static::deleting(function($user) {
            if($user->user_meta->photo ?? false) {
                $image = Str::after($user->user_meta->photo, 'images/');
                File::delete('images/'.$image);
                $user->user_meta()->delete(); // your don't have a $meta variable
            }  
        });

//tribes
        static::deleting(function($user) {
                $user->tribes->each(function($tribe) {
                $image = Str::after($tribe->photo, 'images/');
                File::delete('images/'.$image);
                $tribe->delete();
            }
        });

//posts
        static::deleting(function($user) {
                $user->posts->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                File::delete('images/'.$image);
                $post->delete();
            }
    });
    } 
0 likes
39 replies
SilenceBringer's avatar

@zoul I think you define it wrong way. Use just 1 deleting

    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
			//user_meta
			if($user->user_meta->photo ?? false) {
                $image = Str::after($user->user_meta->photo, 'images/');
                File::delete('images/'.$image);
                $user->user_meta()->delete(); // your don't have a $meta variable
            }  
			//tribes
            $user->tribes->each(function($tribe) {
                $image = Str::after($tribe->photo, 'images/');
                File::delete('images/'.$image);
                $tribe->delete();
			}
			//posts
            $user->posts->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                File::delete('images/'.$image);
                $post->delete();
            }
    	});
    } 
Zoul's avatar
Level 5

@SilenceBringer thanks a lot for your support !

it looks right , i tried it but its throwing this error:

``
which is pointing  this line in posts
//posts
  $user->posts->each(function($post) {
SilenceBringer's avatar

@Zoul sure, syntax errors

    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
			//user_meta
			if($user->user_meta->photo ?? false) {
                $image = Str::after($user->user_meta->photo, 'images/');
                File::delete('images/'.$image);
                $user->user_meta()->delete(); // your don't have a $meta variable
            }  
			//tribes
            $user->tribes->each(function($tribe) {
                $image = Str::after($tribe->photo, 'images/');
                File::delete('images/'.$image);
                $tribe->delete();
			});
			//posts
            $user->posts->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                File::delete('images/'.$image);
                $post->delete();
            });
    	});
    } 
Zoul's avatar
Level 5

@SilenceBringer thanks a gain, after modifying i got only user_meta photo is deleted, posts and tribes are still not deleted

tykus's avatar

@Zoul are the Tribe and Post models deleted, but their images not??? Do you have the correct image path??

Zoul's avatar
Level 5

@tykus yes the posts and tribes model are deleted, only the photos are not deleted, i guess the path is correct, since i can update photos with replacing the old ones

tykus's avatar

Move the Tribe and Post related model event listeners to their own classes (realistically, you could/should also do this for the UserMeta model as well!!!)

public static function boot() {
    parent::boot();
    static::deleting(function($user) {
        if($user->user_meta->photo ?? false) {
            $image = Str::after($user->user_meta->photo, 'images/');
            File::delete('images/'.$image);
            $user->user_meta()->delete();
        }  
        $user->tribes->each(fn ($tribe) => $tribe->delete());
        $user->posts->each(fn ($post) => $post->delete());
    });
} 
// Tribe.php
public static function boot()
{
    parent::boot();
    static::deleting(function ($tribe) {
        $image = Str::after($tribe->photo, 'images/');
        File::delete('images/'.$image);
    })
}
// Post.php
public static function boot()
{
    parent::boot();
    static::deleting(function ($post) {
        $image = Str::after($post->photo, 'images/');
        File::delete('images/'.$image);
    })
}

This makes the clean-up logic reusable whenever you delete a Tribe or Post model; not only when deleting a User.

Also, are you storing a full URL for the image path; why else is it necessary get the string after images/ and then prepend that segment again?

Zoul's avatar
Level 5

@tykus thanks a lot for your help ! I moved the event listeners to their own classes but only user_meta photo is deleted, posts and tribes are not deleted,

the image is saved in posts folder, under public/images/posts/ the result of dd() with $image = Str::after($post->photo, 'images/'); gives me

"posts/0c034f74-1175-43e2-9c76-3ebb20c4f490.jpg"

then with dd(File::delete('images/'.$image)); returning a true since it could find the the correct path to the photo when 'images' folder added before , now its "images/posts/0c034f74-1175-43e2-9c76-3ebb20c4f490.jpg"

tykus's avatar

@Zoul curious why you're using File and not Storage in your application?

You could make the deletion of files and relations much more efficient.

Zoul's avatar
Level 5

@tykus

  • curious why you're using File and not Storage in your application? Because i'm storing photos in public which is subdirectory of my laravel project and not in storage folder, if i store photos in storage folder, then i could check with Storage() to look for photos,

  • You could make the deletion of files and relations much more efficient.

I should start using Storage, since it takes me directory to the folder, i also used File() because i know how it works, but its less efficient, and requires more works as you stated

AlexElementarteilchen's avatar

Hi, I recently had the same issue.

The problem is that File::delete() expects the absolute path while Storage:: works with your configured disks (and relative paths).

On my machine / setup this shows the difference:

echo storage_path() ;

gives the absolute path:

/Users/alex/Documents/web-projects/laravel/myapp/storage

We now can list the files in the app/public dir (which is a subdirectory of storage) with

$files = Storage::files('/public');

This gives in my case:

print_r($files);

Array
(
    [0] => public/.gitignore
    [1] => public/KHJvb6Zi2HYDnx1XlzOHhpaHF1LWntVtA3Rhyn32.jpeg
)

Now let's check if the file public/KHJvb6Zi2HYDnx1XlzOHhpaHF1LWntVtA3Rhyn32.jpeg exists:

Storage::exists( $files[1] );
true

Let's do the same with File::exists:

File::exists( $files[1] );
false

But if we add the storage path so that we get the absolute path to the file it works:

 File::exists( storage_path() . '/app/' . $files[1] );
true

Hope this helps!

1 like
Zoul's avatar
Level 5

@AlexElementarteilchen thanks a lot for your support ! Of course this helps a lot understanding the Storage(), that's well expalined, when i check with Storage() it takes me directory to storage folder, however, i'm storing my photos in public/ which is subdirectory of my laravel project, so which Storage() i never reach there, laravel/public/posts/ i thinks i should refactor my code for using Storage() or dig deeper for a solution ;)

1 like
Zoul's avatar
Level 5

After changing to use storage, i can't still delete posts' photo when deleting a user

store method in postcontroller

    public function store(PostRequest $request)
    {
            $post = new Post();
            $fileName = "";
            if ($request->hasFile('photo')) {
               $image = $request->file('photo');
               $fileName = $image->getClientOriginalName();
               $image_resize = Image::make($image->getRealPath())->resize(860, 500);
                $image->storeAs('images/posts/', $fileName);
                   }

            $post->cat_id = $request->cat_id;
           $post->user_id = Auth::user()->id;
            $post->photo = $fileName;
            $post->title = ['en' => $request->title, 'ar' => $request->title_ar, 'fr' => $request->title_fr];
            $post->description = ['en' => $request->description, 'ar' => $request->description_ar, 'fr' => $request->description_fr];
            $post->save();
           return redirect()->route('all.post')->with(['success' => 'Saved Successfully']);
    }

boot static in User model

    public static function boot() {
        parent::boot();
        static::deleting(function($user) {
            $user->posts->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                File::delete('storage/images/'.$image);
                $post->delete(); 
            });
            
        });
    } 

i used this Str with File to delete photo because its working with updating photo

                $image = Str::after($post->photo, 'images/');
                File::delete('storage/images/'.$image);

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

photo path:

"http://127.0.0.1:8000/storage/images/posts/1fccef12-c5d5-4cd4-96ca-f92f32e2d194.jpg"

Snapey's avatar

@Zoul File::delete needs an absolute path and pointing at the app/public folder. You are not using the disk you created.

File::delete(storage_path('app/public/images/'.$image));
Zoul's avatar
Level 5

@Snapey thanks for your support ! When updating i used

    File::delete('storage/images/'.$image);

and the old photo is being deleted, that's why i'm using it in boot static method, posts photos are saved in

  • storage/app/public/images/posts
  • public/storage/images/posts

i'm still missing something, because its not working after adding File::delete(storage_path('app/public/images/'.$image));

    public static function boot() {
        parent::boot();
        static::deleting(function($user) {
            $user->post->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                File::delete(storage_path('app/public/images/'.$image));
                $post->delete(); 
            });
        });
    } 
Snapey's avatar

try dd after this line

$image = Str::after($post->photo, 'images/');
dd($image);
Zoul's avatar
Level 5

@Snapey its not fired in boot static method

    public static function boot() {
        parent::boot();
        static::deleting(function($user) {
            $user->post->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                dd($image);
                File::delete(storage_path('app/public/images/'.$image));
                $post->delete(); 
            });
        });
    } 

but in update method in PostController dd($image) =

"posts/07c7c843-1c86-47dd-aa07-bdd1d4fe38f0.jpg" 

before dd($image) the $post->photo gives the full path, so with Str i'm only taking the photo path after images word dd($post->photo); =

"http://127.0.0.1:8000/storage/images/posts/07c7c843-1c86-47dd-aa07-bdd1d4fe38f0.jpg"
Snapey's avatar

not fired in boot then you are not calling delete or your user has no posts

and is your relation really called post and not posts ?

Zoul's avatar
Level 5

@Snapey this is weird, the user has manyToone relation with posts, and posts are saved in db with user_id

and is your relation really called post and not posts ? Yes, it was called posts, and i changed it to post to see if this can solve the problem as it was working in another project, I will switch it back to posts to be persistent

Zoul's avatar
Level 5

@Snapey yes i changed it in relation

   public function posts() 
    {
        return $this->hasMany(Post::class,'user_id');
    }

and in UserController where i delete user too,

 $user->posts()->delete();
Snapey's avatar

@Zoul no still not right

public static function boot() {
        parent::boot();
        static::deleting(function($user) {
            $user->posts->each(function($post) {
                $image = Str::after($post->photo, 'images/');
                dd($image);
                File::delete(storage_path('app/public/images/'.$image));
                $post->delete(); 
            });
        });

but if you are not hitting dd, move it out of the loop

public static function boot() {
        parent::boot();
        static::deleting(function($user) {
          dd($user);
           $user->posts->each(function($post) {
                $image = Str::after($post->photo, 'images/');
 
                File::delete(storage_path('app/public/images/'.$image));
                $post->delete(); 
            });
        });
}

1 like
Zoul's avatar
Level 5

@Snapey for dd($user) it showing

App\Models\User {#1632 ▼
  #fillable: array:4 [▶]
  #hidden: array:2 [▶]
  #casts: array:1 [▶]
  #connection: "mysql"
  #table: "users"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  +preventsLazyLoading: false
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:9 [▶]
  #original: array:9 [▶]
  #changes: []
  #classCastCache: []
  #dates: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  #visible: []
  #guarded: array:1 [▶]
  #rememberTokenName: "remember_token"

and for dd($user->posts()); its showing

Illuminate\Database\Eloquent\Relations\HasMany {#1633 ▼
  #foreignKey: "posts.user_id"
  #localKey: "id"
  #query: Illuminate\Database\Eloquent\Builder {#669 ▶}
  #parent: App\Models\User {#1632 ▶}
  #related: App\Models\Post {#672 ▼
    #guarded: []
    #table: "posts"
    +translatable: array:2 [▶]
    #connection: "mysql"
    #primaryKey: "id"
    #keyType: "int"
    +incrementing: true
    #with: []
    #withCount: []
    +preventsLazyLoading: false
    #perPage: 15
    +exists: false
    +wasRecentlyCreated: false
    #attributes: []
    #original: []
    #changes: []
    #casts: []
    #classCastCache: []
    #dates: []
    #dateFormat: null
    #appends: []
    #dispatchesEvents: []
    #observables: []
    #relations: []
    #touches: []
    +timestamps: true
    #hidden: []
    #visible: []
    #fillable: []
    #translationLocale: null
  }
}
Snapey's avatar

posts() is a relationship. You are not proving anything by doing that, and not doing what I suggested

Zoul's avatar
Level 5

@Snapey sorry I thought you wanted to see what dd($user) show ! I did not get your suggestion then

Snapey's avatar

@Zoul You are saying you cannot delete images

I asked you to dd the image name

You said that the dd was never called

I said this is because your deleting method is not called or your user has no posts and to dd the user to see if the deleting function is called

You didn't confirm that the dd was called but you did say it dumped the user from which i ASSUME you are talking about within the deleting method.

So back to, maybe the user does not have any posts, but instead of dd($user->posts) you did dd($user->posts()) but all that does is confirms that the user model has a posts relationship defined

You should instead dd($user->posts) or dd($user->posts()->get()) or to save time, just go straight to dd($user->posts()->pluck('image'))

1 like
Zoul's avatar
Level 5

@Snapey sorry for missing out and misunderstanding all of this, i saw in your comments you asked to add dd($user) before a loop, yes its called now, i should have confirmed you that sorry, then i added dd($user->posts()); as to give you more options of seeing the problem, without knowing this does not help, i know it may be hard to communicate with beginner like me :) I appreciate your patience and valuable time !

dd($user->posts()->pluck('photo')); this show empty array,

Illuminate\Support\Collection {#1658 ▼
  #items: []
}

Is it empty because the posts are deleted before this boot stateic method is called ?

Snapey's avatar

Is this deleting in the User model or the Post model?

Why not delete the image within the post model deleting function?

But anyway, this should work if the user's post contains an image. Bear in mind that dd will STOP on the first post, so the first post may have no photo, but others may have photo.

Only you can say because it depends whats in your database

Zoul's avatar
Level 5

@Snapey

  • Is this deleting in the User model or the Post model? Its in User model
  • Why not delete the image within the post model deleting function? I did not know if this was possible ! Do i need to implement boot deleting method too ?

It works when updating a post, the old photo is being removed, when deleting a user, thier posts are also deleted, but their photos are still there in storage folder, all your solutions look right to remove these photos but its not working with me, this is weird,

in my relation user migration table

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->boolean('approved')->default(0);
            $table->rememberToken(); 
            $table->timestamps();
        });
    }

posts migration table

    public function up() 
    { 
        Schema::create('posts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id')->unsigned();
            $table->unsignedBigInteger('cat_id')->unsigned();
            $table->longText('title');
            $table->longText('description');
            $table->string('photo')->nullable();
            $table->boolean('approved')->default(0);
            $table->bigInteger('views')->default(0);
            $table->timestamps();
            $table->foreign('cat_id')->references('id')->on('categories')->onDelete('cascade');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
    }
Zoul's avatar
Level 5

@Snapey its manyToone relation a user can have many posts and post belong to one user, when a user is deleted, all their posts are deleted, and this is working fine, only posts' photo are left in storage folder in user Model

  public function posts() 
    {
        return $this->hasMany(Post::class,'user_id');
    }

in post model

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

i hop i answered your question

newbie360's avatar

i saw in your migration has a foreign key, the post is auto deleted by database layer, not your backend code ?

Zoul's avatar
Level 5

@newbie360 thanks for your help ! i'm deleting all related posts to the user in destroy() in UserController, and to performe this i need a fk i guess, actually, i don't have a problem with related posts being deleted, this works fine, the problem how to get rid of posts's photos in storage folder after deleting a user, this is what we have been trying to find a solution,

in UserController, destroy(); method

   public function destroy($id)
    {


        $user = User::findOrFail($id);
        if(!$user){
            return redirect()->route('all.user')->with(['error' => 'the record does not exist']);
        }
        $user->comments()->delete();
        $user->posts()->delete();
        $user->tribes()->delete();
        $user->delete();
        return redirect()->route('all.user')->with(['success' => 'Deleted Successfully']);
    }
Snapey's avatar
Snapey
Best Answer
Level 122

wish you had shown this before....

Your deleting method runs at the end, after you had deleted the users posts (which is also unnecessary because you have $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); on your posts table

remove this from your controller $user->posts()->delete(); and fix the deleting method, OR just delete the images in the controller where you are deleting other stuff

1 like
Zoul's avatar
Level 5

@Snapey it works now After removing $user->posts()->delete(); from the destroy() in UserController, and following the path of the photo by dd() in boot static in User model until i got true return , now the post's photo is deleted after deleting a user

public static function boot() {
        parent::boot();
        static::deleting(function($user) {
           $user->posts->each(function($post) {
                 $image_delete = Str::after($post->photo, 'images/');
                File::delete('storage/images/'.$image_delete);
                $post->delete(); 
            });
        });
}

thank you all of you for your help and valuable time, i appreciate it !

newbie360's avatar

i guess you store the photo as 'images/posts/07c7c843-1c86-47dd-aa07-bdd1d4fe38f0.jpg' in database ?

and seems you using public disk ?

backup all your models file to somewhere and remove all the event in all models, in controller try to do this

   public function destroy($id)
    {
        $user = User::find($id);

        if(!$user){
            return redirect()->route('all.user')->with(['error' => 'the record does not exist']);
        }

        // get an array of the photos path before delete the user
        $postPhotos = $user->posts->pluck('photo')->toArray();

        if ($user->delete()) {
            Storage::disk('public')->delete($postPhotos); // delete method accept an array
        }

        return redirect()->route('all.user')->with(['success' => 'Deleted Successfully']);
    }
1 like
Zoul's avatar
Level 5

@newbie360 thanks a lot for your help ! @snapey suggestion works fine, but i will keep suggestion for future use ! Thanks again for your valuable time !

newbie360's avatar

@Zoul actually i prefer doing any database action before any physical file action

because you can rollback database record, but can't rollback deleted file...

1 like
Zoul's avatar
Level 5

@newbie360 you are right ! that's why we apply softDeletes() for any variable that we want to keep track to i guess

Please or to participate in this conversation.