Phil Cross
Phil Cross

Reputation: 9302

Laravel 5.1 Model Delete Event

I have a load of Models. Users can comment on some of the model records. For example, a user can comment on a Notice model record, or a user can comment on a Calendar Model record.

I've created a trait called Commentable. This contains all the methods needed to retrieve comments based on a model, to add / delete comments, create the comment create form and so on.

Whenever I want a model to be commentable, all I need to do is use that trait within the model.

Because that comment is a polymorphic relationship with the model record, I can't do an onCascade = delete migration.

Whenever the parent model is deleted (such as a notice or calendar item), then I want all associated records to be deleted as well, but I'd prefer not to have to rely on the developer writing the deleteRelatedComments() method call into an overridden delete function.

I thought I would create a service provider that listened for any model deletion event, check to see if that model was commentable, and delete any associated comments, but the event doesn't fire correctly.

This is my service provider code:

Model::deleting(function (Model $record) {
    if(in_array('App\Libraries\Traits\Commentable', class_uses($record))) {
        $record->deleteRelatedComments();
    }

    return true;
});

As you can see, all it does is check the deleted model to see if it uses the Commentable trait. If it does, it calls the deleteRelatedComments() method.

My Question

Is there any way to automatically delete related polymorphic related content on the deletion of it's parent record:

If I delete a Notice record, is there any way to delete any associated polymorphic Comment records?

Upvotes: 1

Views: 4132

Answers (1)

Phil Cross
Phil Cross

Reputation: 9302

I have previously created a service provider to listen for any Model deletes using Model Events as specified at http://laravel.com/docs/5.1/eloquent#events

However, this doesn't work if you're trying to listen to the Model class instead of a specific class such as Notice.

Instead, when you delete each model, a eloquent.deleting event is fired. I can then manually create a listener within the EventServiceProvider to listen for any model being deleted, and perform my checks accordingly, but without having the rely on the user overloading the delete() method and manually deleting the polymorphic relations.

Answer

If you want to delete polymorphic content automatically when you delete a model, make sure you either implement an interface or use a trait in the model.

For example, I want to delete all polymorphic comments when I delete the parent model record (If I delete a Notice record delete all polymorphic comments).

I created a Commentable trait which I use in any model (but you can just as easily use an empty interface on your model).

Then, in your EventServiceProvider.php alter the boot() method accordingly:

public function boot(DispatcherContract $events)
{
    parent::boot($events);

    /**
     * If you use a trait in your model:
     */
    $events->listen('eloquent.deleting*', function ($record) {
        if (in_array('App\Libraries\Traits\Commentable', class_uses($record))) {
            $record->deleteRelatedComments();
        }
    });


    /**
     * Or if you use an interface:
     */

    $events->listen('eloquent.deleting*', function ($record) {
        if ($record instanceof SomeInterface) {
            $record->deleteSomePolymorphicRelation();
        {
    });
}

Use case:

Commentable Trait

namespace App\Libraries\Traits;


trait Commentable 
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'content');
    }

    public function deleteRelatedComments()
    {
        $this->comments()->delete();
    }
}

Commentable Model

class Notice extends Model
{
    use Commentable;

    /** ... **/

    /**
     * NOTE: You do not need to overload the delete() method
     */
}

EventServiceProvider.php

public function boot(DispatcherContract $events)
{
    parent::boot($events);

    $events->listen('eloquent.deleting*', function ($record) {
        if (in_array('App\Libraries\Traits\Commentable', class_uses($record))) {
            $record->deleteRelatedComments();
        }
    });
}

Now, the developer creating a new content type that wants it to be commentable doesn't need to worry about creating the relations, managing the relation methods, or even deleting the polymorphic contents by overloading delete(). To make a model entirely commentable, they just need to use the Commentable trait and the framework handles everything.

This method can be used to automatically delete any polymorphic record, as long as the parent model implements some interface or uses a related trait.

Upvotes: 3

Related Questions