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

octoxan's avatar

How to run a function every time a certain class is deleted?

Ran into a weird problem with a project I'm working on. A class, we'll call it Page has a relationship to a Drawing class. This Drawing class has a table where I store what the drawing/images path is. I'm not using the default Laravel storage stuff because of the weird way I have to import these images over curl and do all sorts of weird unrelated-to-this-issue stuff.

So when I delete a Drawing from the controller, I'm checking if the file exists from the path column and if it does I use unlink() to delete it.

Now when I delete a Page from a controller, it deletes it's Drawings from the database, but it doesn't go through the process of deleting the files.

Is there some sort of function naming I can do on the Drawing class like onDelete() or something that fires every time it's deleted no matter how it's deleted? I know Laravel has all sorts of function naming conventions to magically do stuff, wasn't sure if there was one for this and I couldn't find one from Googling.

Or is my only option to on the Page controller, before deleting it, loop through its Drawings and delete the files?

0 likes
9 replies
sutherland's avatar

You are going to have to loop through the drawings somewhere and individually call delete on them if you want it to get rid of the files. I wouldn't do it in the controller, instead I'd listen to the deleting event on the Page model then individually delete each Drawing model. That way it will always delete the related records no matter where you call $page->delete() from.

https://laravel.com/docs/5.6/eloquent#events

Edit: To further explain, right now your database schema must be using on delete cascade to remove the related records, which is a nice convenience, but that happens on the database layer so Laravel isn't aware it needs to call your delete logic on those records.

octoxan's avatar

Ah haven't actually had to mess with events yet. Thank you, I'll look into that!

sutherland's avatar

@mrbadr observers are just another way to tie into the same Eloquent events, but like I said you'll need to listen to the Page's model events first to call delete on each Drawing. Otherwise the Drawing's deleting event (regardless of how it's registered) never gets called if it's left solely to the database schema.

octoxan's avatar

@sutherland Should I still call $drawing->delete() in there, or just delete the file, since the database will be deleting the drawing row anyways?

sutherland's avatar

@octoxan no, I'd still call $drawing->delete() and make another listener/observer for the Drawing that does the file deletion. That way you don't end up having to repeat the logic in multiple places if a user needs to delete a single drawing without deleting the entire page.

octoxan's avatar

@sutherland Am I supposed to return true when trying the deleting event?

Here's my code...

Page::deleting(function($page) {
            $drawings = $page->drawings;
        
            foreach ($drawings as $drawing) {
                $path = app()->basePath() . '/public' . $drawing->path;
    
                if (file_exists($path)) {
                    unlink($path);
                }
    
                $drawing->delete();
            }
            
            return true;
        });

Drawing::deleting(function($drawing) {
            $path = app()->basePath() . '/public' . $drawing->path;
    
            if (file_exists($path)) {
                unlink($path);
            }
    
            $drawing->delete();
            
            return true;
        });

It deletes the files if they exist, but my page that deletes Pages now throws a 500 error and oddly won't even give me any sort of error message.

sutherland's avatar

No, you don't need to return anything.

You're making it a little more complicated than necessary, here's closer to what you should do:

Page::deleting(function ($page) {
    foreach ($page->drawings as $drawing) {
        $drawing->delete();
    }
});

Drawing::deleting(function ($drawing) {
    $path = app()->basePath() . '/public' . $drawing->path;

    if (file_exists($path)) {
        unlink($path);
    }
});

Please or to participate in this conversation.