5

I have Posts, Comments and notifications Table

Every Post hasMany comments

every comment hasMany Notifications

every Post hasMany Notifications

class Post extends Model
{

    public function notifications() {
        return $this->morphOne(Notification::class, 'to');
    }

    public function comments() {
        return $this->hasMany(Comment::class, 'post_id');
    }

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

        static::deleting(function($post) {
            $post->comments()->delete();
            $post->notifications()->delete();
        });
    } 
}
class Comment extends Model
{
    public function notifications() {
        return $this->morphOne(Notification::class, 'to');
    }

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

        static::deleting(function($comment) {
            $comment->notifications()->delete();
        });
    }
}

When I delete a post I should Delete The notifications and the comments as well, But the problem is When I delete the comments, The Notifications aren't deleted with it, They are deleted When I delete The Comment Directly but I need to delete the notifications of the comments When I delete the post !

julianstark999
  • 3,450
  • 1
  • 27
  • 41
  • Can you show the code on how you delete the post? – mrhn Nov 20 '19 at 14:12
  • Laravel does not instantiate the related models that it deletes, which is why notifications are deleted when you directly delete a comment, but not when comments are deleted by deleting a post. you would have to instantiate the comments when deleting a post to make it work – porloscerros Ψ Nov 20 '19 at 14:13

4 Answers4

4

Laravel does not instantiate the related models that it deletes, which is why notifications are deleted when you directly delete a comment, but not when comments are deleted by deleting a post. You would have to instantiate the comments when deleting a post to make it work.

class Post extends Model {
    
    public function notifications() {
        return $this->morphOne(Notification::class, 'to');
    }
    
    public function comments() {
        return $this->hasMany(Comment::class, 'post_id');
    }
    
    public static function boot() {
        parent::boot();
    
        static::deleting(function($post) {
            // here you could instantiate each related Comment
            // in this way the boot function in the Comment model will be called
            $post->comments->each(function($comment) {
                // and then the static::deleting method when you delete each one
                $comment->delete();
            });
            $post->notifications()->delete();
        });
    } 
}

Just for the records, I add what we discuss in the comments since it can serve others who encounter the same issue, and in the comments it can go unnoticed. Credit is for OP @Mahmoud Ben Jabir.

But if the post has 100 comments It will execute 100 query to delete them ! I will Figure out how to delete with minimum queries...

I already have onDelete on comments, but the notifications are polymorphic so it won't work on them...

The solution I will use is:
1- Get Ids of Comments that are related to the Post.
2- Delete from Notifications where type IS Comment AND id in (ids).
3- Delete Comments related to the Post.
4- Delete The Notifications Related to the Post
5- Delete The Post.

public static function boot() {
    parent::boot();
    static::deleting(function($post) {
        // 1- Get Ids of Comments that are related to the Post. 
        $ids = $post->comments()->pluck('id'); 
        // 2- Delete from Notifications where type IS Comment AND id in (ids). 
        Notification::where('entity_type', 'App\Comment')->whereIn('entity_id', $ids)->delete(); 
        // 3- Delete Comments related to the Post. 
        $post->comments()->delete();
        // 4- Delete The Notifications Related to the Post 
        $post->notifications()->delete();
    });
    // 5- Delete The Post.
}
Community
  • 1
  • 1
porloscerros Ψ
  • 4,808
  • 2
  • 11
  • 20
  • Ok This Will Work, But if the post has 100 comments It will execute 100 query to delete them ! I will Figure out how to delete with minimum queries, Thanks – Mahmoud Ben Jabir Nov 20 '19 at 14:31
  • @MahmoudBenJabir Yes, you are right. I think the more eficient way if to do it on DB level, with `->onDelete('cascade')`, but only will work with relationals databases and configuring foreign keys on DB level – porloscerros Ψ Nov 20 '19 at 14:38
  • I already have onDelete on comments, but the notifications are polymorphic so it won't work on them, I figured out a way to get the Ids of comments then delete the notifications Manually instead of relying on Eloquent relationships ... thanks man for the discussion – Mahmoud Ben Jabir Nov 20 '19 at 14:41
  • The solution I will use is: 1- Get Ids of Comments that are related to the Post. 2- Delete from Notifications where type IS Comment AND id in (ids). 3- Delete Comments related to the Cost. 4- Delete The Notifications Related to the Post 5- Delete The Post. Any suggestions about it ? – Mahmoud Ben Jabir Nov 20 '19 at 14:45
  • Looks good for me. I can't test it to be sure it works, but I imagine something like this in the deleting method of the Post ` $comments = $post->comments()->get(['id']); Notification::where('entity_type', 'App\Comment')->whereIn('entity_id', $comments)->delete(); $comments->delete(); $post->notifications()->delete();`. If that works, it would be just four queries – porloscerros Ψ Nov 20 '19 at 15:31
  • Or maybe this `$ids = $post->comments()->pluck('id'); Notification::where('entity_type', 'App\Comment')->whereIn('entity_id', $ids)->delete(); Comment::whereIn('id', $ids)->delete(); $post->notifications()->delete();` – porloscerros Ψ Nov 20 '19 at 15:33
  • 1
    Yes it's the same login I used And it worked, I used the second one, and just deleted comments in this way, $post->comments()->deleted(); – Mahmoud Ben Jabir Nov 20 '19 at 16:09
1

Chaining will not work, because the models aren't initialised.
Best solution is to loop and delete the individual comments

static::deleting(function($post) {
    foreach ($post->comments() as $comment){
        $comment->delete();
    }
});
MrEvers
  • 1,040
  • 1
  • 10
  • 18
1
static::deleting(function($comment) {
    $ids = $comment->notifications()->lists('id')->all();
    Notification::destroy($ids);
});
hemant
  • 156
  • 1
  • 6
0

Please use (assuming relation between post and comments)

$table->foreign('post_id')
      ->references('id')->on('posts')
      ->onDelete('cascade');

to make a foreign relation in your migration file. So then when you delete related post it's comment will be deleted also.

Mahmud hasan
  • 918
  • 10
  • 13