Is it possible to temporarily disable event in Laravel? - laravel

I have the following code in 'saved' model event:
Session::flash('info', 'Data has been saved.')`
so everytime the model is saved I can have a flash message to inform users. The problem is, sometimes I just need to update a field like 'status' or increment a 'counter' and I don't need a flash message for this. So, is it possible to temporarily disable triggering the model event? Or is there any Eloquent method like $model->save() that doesn't trigger 'saved' event?

Solution for Laravel 8.x and 9.x
With Laravel 8 it became even easier, just use saveQuietly method:
$user = User::find(1);
$user->name = 'John';
$user->saveQuietly();
Laravel 8.x docs
Laravel 9.x docs
Solution for Laravel 7.x, 8.x and 9.x
On Laravel 7 (or 8 or 9) wrap your code that throws events as per below:
$user = User::withoutEvents(function () use () {
$user = User::find(1);
$user->name = 'John';
$user->save();
return $user;
});
Laravel 7.x docs
Laravel 8.x docs
Laravel 9.x docs
Solution for Laravel versions from 5.7 to 6.x
The following solution works from the Laravel version 5.7 to 6.x, for older versions check the second part of the answer.
In your model add the following function:
public function saveWithoutEvents(array $options=[])
{
return static::withoutEvents(function() use ($options) {
return $this->save($options);
});
}
Then to save without events proceed as follow:
$user = User::find(1);
$user->name = 'John';
$user->saveWithoutEvents();
For more info check the Laravel 6.x documentation
Solution for Laravel 5.6 and older versions.
In Laravel 5.6 (and previous versions) you can disable and enable again the event observer:
// getting the dispatcher instance (needed to enable again the event observer later on)
$dispatcher = YourModel::getEventDispatcher();
// disabling the events
YourModel::unsetEventDispatcher();
// perform the operation you want
$yourInstance->save();
// enabling the event dispatcher
YourModel::setEventDispatcher($dispatcher);
For more info check the Laravel 5.5 documentation

There is a nice solution, from Taylor's Twitter page:
Add this method to your base model, or if you don't have one, create a trait, or add it to your current model
public function saveQuietly(array $options = [])
{
return static::withoutEvents(function () use ($options) {
return $this->save($options);
});
}
Then in you code, whenever you need to save your model without events get fired, just use this:
$model->foo = 'foo';
$model->bar = 'bar';
$model->saveQuietly();
Very elegant and simple :)

Call the model Object then call unsetEventDispatcher
after that you can do whatever you want without worrying about Event triggering
like this one:
$IncidentModel = new Incident;
$IncidentModel->unsetEventDispatcher();
$incident = $IncidentModel->create($data);

To answer the question for anyone who ends up here looking for the solution, you can disable model listeners on an instance with the unsetEventDispatcher() method:
$flight = App\Flight::create(['name' => 'Flight 10']);
$flight->unsetEventDispatcher();
$flight->save(); // Listeners won't be triggered

In laravel 8.x :
Saving A Single Model Without Events
Sometimes you may wish to "save" a given model without raising any events. You may accomplish this using the saveQuietly method:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();
See Laravel docs

In laravel 7.x you can do that as easy as
use App\User;
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
return User::find(2);
});
See more in Laravel 7.x Muting Events documentation

You shouldnt be mixing session flashing with model events - it is not the responsibility of the model to notify the session when something happens.
It would be better for your controller to call the session flash when it saves the model.
This way you have control over when to actually display the message - thus fixing your problem.

Although it's a long time since the question, I've found a way to turn off all events at once. In my case, I'm making a migration script, but I don't want any event to be fired (either from Eloquent or any other).
The thing is to get all the events from the Event class and remove them with the forget method.
Inside my command:
$events = Event::getRawListeners();
foreach ($events as $event_name => $closure) {
Event::forget($event_name);
}

The only thing that worked for me was using the trait WithoutEvents. This will be executed inside the setUp method and does prevent any dispatch you have added to the code.

Related

Laravel Eloquent model events on created user

I'm trying to automatically create a profile for a user when a user is created.
I'm using the created event and overriding the boot() method. But I when call the create() method on user->profile->create(), it says create was called on null. I checked and profile is null in this.
Here's the code:
static::created(function ($user) {
// it returns profile as null, thus create() can't be used on null.
$user->profile->create(['title' => $user->username,]);
});
Can anyone help me understand this? It's working in my tutor's code, and he is using Laravel 5.8 but I have version 7.1.
$user->profile returns the related model if any exists. You have to do $user->profile() which returns a query builder to query the relation. Try to do it like so:
$user->profile()->create(['title' => $user->username,]);

filter notifications from 'notifications' table 'data' field - Laravel

This is the default laravel notifications data field
{
"type":"Update Appointment",
"appointment_id":"379",
"date":null,
"updated_by":"Mahir",
"status":"2"
}
In controller i want to get all notifications with status = 2 and mark as read
Laravel 5.3 doc shows
$user = App\User::find(1);
foreach ($user->unreadNotifications as $notification) {
$notification->markAsRead();
}
How do i modify this to get all notifications with status = 2
Update : looking for something like this
$noti = $user->unreadNotifications->where('data->status',"2");
Note : my database doesn't support json data type.
According to laravel 5.6: JSON Where Clauses
and work with MySQL 5.7
I hope this answer help you
I think, you can create your own channel, (see also Illuminate\Notifications\Channels\DatabaseChannel), where you can override send() method, for saving your notifications. Before that, you can write your migration to update "notifications" table to add your custom filtering field.
See: https://laravel.com/docs/5.5/notifications#custom-channels
Sorry for my English. Hope, you'll understand.
$user = $user->unreadNotifications->where('data.complaint_id',1);
Its work fine for me, I hope it's helpful for you.

Laravel 5.2 - How to access an App Presenter method in EventServiceProvider?

I have a guitar lessons site where there is an exercises table. The original developers placed some functions in ExercisePresenter to retrieve other bits of data associated with an exercise, such as its url.
Here is a function in ExercisePresenter that returns url for an exercise:
public function url()
{
return '/guitar-lesson-ex/' . $this->urlName() . '/' . $this->id;
}
So now I am creating an event on new exercise created so I can use pusher notifications. In the EventServiceProvider I have this:
public function boot(DispatcherContract $events)
{
parent::boot($events);
Exercise::created(function ($exercise) {
// need to update lesson difficulty
$lesid = $exercise->lesson_id;
$les = \App\Lesson::find($lesid);
$dif = $les->difficulty();
DB::table('lessons')
->where('id', $lesid)
->update(['difficulty' => $dif]);
// lets trigger NewExerciseEvent to send pusher notifications
$url = $exercise->url;
event(new NewExerciseEvent($message));
});
}
I thought in above code $url = $exercise->url; would work since I see $exercise->url used successfully in exercise views. But it is returning $url as null. Now, there is no url column in the exercise database, so I figure somehow when $exercise->url; is used in a view, laravel is figuring out that the ExercisePresenter is able to return the url.
I am debugging through PHPStorm, but when I get to $url = $exercise->url; and step in, I am just taken through various bits of laravel code that looks for a method, etc. I am not savvy enough with laravel to figure out what it is doing here differently than in the view (too bad we can't debug views...), but each time I try this $url is returned as null.
Any idea how to get this to work properly in my EventServiceProvider?
Thanks!
Figured it out:
$url = $exercise->present()->url;
I had been searching for how to use presenters but having just read (Laravel View Presenters From Scratch), everything is clear!
Sorry for posting prematurely.

Laravel Event Listening

I have an issue similar to this post :
Laravel - last login date and time timestamp
In short, my purpose and question is :
I have a "logrecords" table in my database.
My event listeners in global.php are just working on default "users" table.
I want my event listeners are able to insert data on my "logrecords" table.
How can i do that :
Should i configure my database tables using which are using eloquent ?
Or should i change something in global.php ?
Thanks for your support.
--------------------Update--------------------
I realized that in my auth.php file, default authentication model has been set as :
'model' => 'User'
But i want to listen and work with both User and Logrecord model.So that when i try to listen events in my global.php file, laravel is automatically trying to work with User model. So that i had to configure my event listeners like that :
Part of my global.php file :
//First Example
Event::listen('auth.login', function($user)
{
$userLogRecord = Logrecord::firstOrCreate(array('user_id' => $user->id));
$userLogRecord->user_id = $user->id;
$userLogRecord->login_last_at = date('Y-m-d H:i:s');
$userLogRecord->save();
});
//Second Example
Event::listen('auth.logout', function($user)
{
$userLogRecord = Logrecord::firstOrCreate(array('user_id' => $user->id));
$userLogRecord->user_id = $user->id;
$userLogRecord->logout_last_at = date('Y-m-d H:i:s');
$userLogRecord->save();
});
It is working for now, but I am thinking that it's not a good idea to edit my listeners like that. My purpose is to listen and do some process with both User and Logrecord models. It serves my purpose right now but i feel like i have to improve.
Any ideas ?
#DemonDrake,
If I understand correctly you simply want to add data to a "log" table?
In the most simple form, the answer is "Yes you would use the eloquent ORM for that
class Log extends Eloquent {
protected $table = 'log';
}
or perhaps query builder even.
There are a number of ways this is possible. I personally suggest you check out https://laracasts.com/lessons

laravel remove model observer

Is there a way to remove a model observer that is added with
$model->observe(new ObserverObject)
Maybe something like
$model->observers['ObserverObject']->remove()
Thanks
You can check your event name by doing:
dd( $model->getEventDispatcher()->getListeners() );
And remove it using:
$model->getEventDispatcher()->forget($event);
Laravel 5.8
I have an AccountObserver, that observers the creating process on my AccountModel:
class AccountObserver
{
public function creating(AccountModel $account)
{
....
}
}
To disable this, first load the event dispatcher (via any eloquent model), then tell it what to forget. This works for any observer method:
$eventDispatcher = AccountModel::getEventDispatcher();
$eventDispatcher->forget('eloquent.creating: App\Models\AccountModel');
If you want to remember it again:
AccountModel::observe(AccountObserver::class);

Resources