Rainieren's avatar

Laravel Delete all with relation?

Hello, I'm trying to figure out how I can delete everything that is connected to a theme, Currently, I have a topic which has replies and to delete those 2 from the database I do something like this in the destroy method

$topic = Topic::find($id);

$topic->replies()->delete();

$topic->delete();

return redirect('/');

But a topic is within a theme. So what I'm trying to do is, add a function which allows the user to delete a theme, But that function also needs to delete ALL the topics within that theme and then ALL the replies of all those topics.

I maybe figured that it needs to look something like this but I can be completely wrong.

$theme = Theme::find($id);
$theme->topics()->replies->delete();
$theme->delete();

return redirect('/')

The relations between all the things are okay but I can list them here, A theme has many Topics A Topic has many replies

How do I do this? Thanks in advance

PS: Sorry for the weird title, I don't know how to explain this (Tips always appreciated)

0 likes
11 replies
rob897's avatar

Did you setup any foreign keys in your database schemas? If so you can set them to cascade on delete.

Rainieren's avatar

@rob897 I don't quite understand what ur saying. My database is correct. Can you maybe explain or show me what u man?

Rainieren's avatar

I looked at that one @rob897 and if I read it correctly he just wants to delete 2 things related to each other, I'm trying to delete 3 things related to each other, A theme, All the topics within that theme and all the replies to all those topics.

rob897's avatar

And what I meant about the database schemas, if you use laravels migrations you can setup the foreign key restraints like this: https://laravel.com/docs/5.4/migrations#foreign-key-constraints Something like this:


// Your Topics migration you would have
$table->foreign('theme_id')
      ->references('id')->on('themes')
      ->onDelete('cascade');

// Your Replies migration you would have
$table->foreign('topic_id')
      ->references('id')->on('topics')
      ->onDelete('cascade');

Does that make sense?

Rainieren's avatar

For me, it doesn't because I'm not that familiar with foreign keys? I thought I could just do this in the destroy method using some similar code that I was already using. but I guess that is a no go? I'll try to figure it out if its the only option?

rob897's avatar

If you are using eloquent just setup the relationships in the models. I always try to use all of the built in goodness Laravel has to offer which is why I setup the migrations with any project I work on.

corndoglet's avatar

I've been trying to do something similar as @rainieren. Even though I set up foreign keys and cascade on delete in the migration, I couldn't get the replies to be deleted when a theme is deleted.

Here is what works for me in Laravel 5.8, for anyone else who finds this thread 2 years on... This assumes Eloquent relationships have been set up where a Theme has many Topics, and a Topic has many Replies.

In the ThemeController:

public function destroy(Theme $theme)
{    
    $topics = $theme->topics();
        
    foreach($topics->get() as $topic) {
        $topic->replies()->delete();
    }
        
    $topics->delete();
        
    $theme->delete();
        
    return redirect('/');
}

It doesn't work if one just does $theme->topics()->replies()->delete(), one has to loop through each topic.

2 likes
Jelmer de Jong's avatar

I found this thread 3 years later as i was hoping to find if Laravel had an easy way to for this. As I believe Laravel hasn't. I went about it an other way and want to post it for anyone looking for a cleaner way and keep the logic outside your controller.

In the Theme Model:

// overwriting the model boot method to extend it with a addition to the deleting event
public static function boot()
{
    parent::boot();
    static::deleting(function($theme){
        $theme->topics->each(function($topic){
           $topic->delete();
        });
    });
}

And in the Topic Model:

// here we do the same as in the Theme model but then for the replies
public static function boot()
{
    parent::boot();
    static::deleting(function($topic){
        $topic->replies->each(function($reply){
           $reply->delete();
        });
    });
}

And in your ThemeController:

public function destroy(Theme $theme)
{
    $theme->delete();
    return redirect('/');
}

In this way any time the delete() method is called on a theme all the topics with their replies are removed. Same goes for removing just a topic, it deletes the related replies without writing any additional code.

HelloChrisBrown's avatar

You can actually make that a little shorter.

static::deleting(function($topic){
        $topic->replies()->delete()
    });
Jelmer de Jong's avatar

You are right. And it does not only make the code shorter and cleaner, I guess it will also make for faster execution time and less datebase calls. Instead of iterating through all the relationships and deleting them one by one (with a database query for every instance), you make one query and delete them all at once. So you addition is definitely an improvement. But unfortunately in my case i need to remove associated files on the server on the last delete() call (in my case these are mail attachments instead of topic replies). And in if i call $mail->attachments()->delete() the deleting event in my Attachment model will not fire and all the files are left on the server. So I need to iterate through the attachments and remove them one by one.

Please or to participate in this conversation.