Search code examples
phplaraveleventslistener

Laravel event prevent updating a model


I have a Conversation which may be set to private. Setting to private means that user_id on conversations will get assigned a non-null value. User can set conversation to private if he has enough credits.

Frontend holds the logic for validation, however I want to do it on backend as well.

I've set the event to fire on model save:

protected $dispatchesEvents = [
    'saving' => ConversationSaving::class,
];

and that event is nothing special:

public function __construct(Conversation $conversation)
{
    Log::info("[Event] " . get_class());
    $this->conversation = $conversation;
}

Event service provider has the logic tied up like this:

ConversationSaving::class      => [
    PreventMakingPrivate::class
],
ConversationMadePrivate::class => [
    DeductCreditsForMakingPrivate::class
],

The idea was to trigger an update, and if update fails the ConversationMadePrivate event should never fire.

Controller looks like this:

$conversation->update(['user_id' => Auth::user()->id]);

Log::info('After update');
Log::info($conversation->user_id);
Log::info($conversation->user_id ? 't':'f');

if(!$conversation->user_id){
    return response([
        'error' => 'Error making private',
    ]);
}

event(new ConversationMadePrivate($conversation));

return response([
    'conversation' => $conversation->load('messages'),
]);

Now what I get in logs is:

[2020-05-16 07:34:41] local.INFO: [Event] App\Events\ConversationSaving 
[2020-05-16 07:34:41] local.INFO: [Listener] App\Listeners\PreventMakingPrivate  
[2020-05-16 07:34:41] local.INFO: [Listener] Not enough credits  
[2020-05-16 07:34:41] local.INFO: After update  
[2020-05-16 07:34:41] local.INFO: 1  
[2020-05-16 07:34:41] local.INFO: t  
[2020-05-16 07:34:41] local.INFO: [Event] App\Events\ConversationMadePrivate  
[2020-05-16 07:34:41] local.INFO: [Listener] App\Listeners\DeductCreditsForMakingPrivate  

So the code correctly enters the listener and returns false from PreventMakingPrivate, however the model still updates.

I tried also setting on Conversation updating instead of saving, but same thing happens.

How can I prevent the update?


Solution

  • Oh, I got it. In case someone also needs it, it looks as if update method will return 1 on success and nothing otherwise. This resolves my issue:

    $response = $conversation->update(['user_id' => Auth::user()->id]);
    
    if(!$response){
        return response([
            'error' => 'Error making private',
        ]);
    }