Avoid execution of Laravel boot() function's events in eloquent model - php

I have a boot() function in my model that looks like this,
public static function boot() {
parent::boot();
// new item created
static::created(function($proof) {
//Fire an event
}
}
the created function fires everytime I fire something like this, $proof->save()
is there a way to by pass firing the created function, there are some scenarios where I do not want to run the functionality within that method?
If it makes any difference I am running Laravel 4.2

Run flushEventListeners() before you do the create. Example:
$data = [];
if ( //SOMETHING ) {
User::flushEventListeners();
}
User:create($data);
Note that this will prevent all events from firing, not just created.

Related

Pass variable to laravel model boot

I use laravel 5.8 for my application. I have variables call "lot" and when I delete one, I want to perform other actions.
So I use deleting function on my model, everything works OK.
But know I have a function on my controller to delete many "lots" and I want actions to be perform only when all "lots" have been delete and not on every "lots" delete.
So I wonder if there is a way to achieve this ? Maybe we can pass a variable to boot functions to trigger or not the function ?
My model looks like this :
protected static function boot()
{
parent::boot();
static::deleted(function($modele) {
Etage::doesntHave('lots')->delete();
}
}
Model events are been designed to work for single models.
You should use Laravel Events instead, not tied to Models.
In EventServiceProvider.php register a new Event/Listener in the $listens array, like
LotsDeleted::class => PerformOtherAction::class
Create those classes with php artisan event:generate
Then, when you have finished deleting all your Lot objects, trigger the event with
event(new LotsDeleted());
The handle() function of your listener will be called and you can perform other actions.

Laravel model event won't fire

I'm using ajax to update my model User, the ajax part works fine since the data is updated successfully in the database, inside my controller action the update performed by :
$user->update($data);
The part that doesn't work:
I've used boots method updated inside my model like :
class User extends BaseModel
{
...
public static function boot()
{
parent::boot();
self::updated(function($model){
Log::info("updated");
dd($model);
});
}
}
The event was never reached I'm not sure why.
Problem:
I'm trying to perform an action after the model update but the event doesn't fire.
Here's what the manual states with update()
When issuing a mass update via Eloquent, the saved and updated model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
You need to use save to trigger events. Something like:
$user->fill($data);
$user->save();
This of course is assuming that $user is a model and not a query builder instance.
You are accessing the function statically:
instead try using
self::updated(function($model){
Log::info("updated");
dd($model);
});

Eager loading has some unexpected side effects on Model events/ booting in Laravel

I'm trying to create some tests.
Here's my Test Class:
class ExampleTest extends TestCase {
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
$this->seed();
Auth::loginUsingId(1);
}
public function testActionUpdateNew()
{
$action = new Action(Array());
$action->save();
var_dump($action->id);
Action::with('reponses','contact','user','etudiant','entreprise','etude')->findOrFail($action->id);
}
public function testEtudes()
{
$etudes=Etude::all()->toArray();
$this->assertCount(10, $etudes, "Nombre d'études incorrectes");
$numEtudes=count($etudes);
//Buggy part
$etude= Etude::create(Array());
var_dump($etude->id);
$etudes=Etude::all()->toArray();
$this->assertCount(11, $etudes, "Nombre d'études incorrectes");
//10+1 should equal to 11 but it hasnt updated
}
}
The test that is not passing is the second one: I count the number of eloquent Objects Etudes, which are of 10 at the beginning, I then add one etude to the database (using Etude::create()) , the object is created, because $etude->id gives out a real number. Howewer, the number of Etude hasn't updated.
The problem does go away when I remove the 'etude' from the eager loading in Action::with('reponses',...)
Here is the etudes relationship in the Action class:
public function etude() {
return $this->belongsTo('Etude');
}
Do you guys have any idea if eager-loading in laravel can have such strange behavior and how to fix that ?
EDIT
I found out that calling with('etude') had the action to remove the events registered to the Eloquent Model:
boot Method of Etude:
public static function boot()
{
parent::boot();
static::creating(function($etude)
{
var_dump("creating etude"); //This doesn't get executed even when I run Etude::create(Array());
}
);
}
So If I add Etude::boot() at the beginning of testEtudes, it works again. This is still strange.
Does eager loading has any effect on events or the boot method ? Or is the boot method not called automatically after each test ?
In Laravel tests, the event dispatcher is reset between each test, but the models are still only booted once as they live a pretty independent life. This means that between each test, the model listeners are erased but never re-registered. The solution is to not use boot() for registering model events, but rather but them in a separate file - either a service provider or a file included from app/start/global.php (app/events.php is a common one).

Laravel: How to prevent eloquent model save using events inside a package?

I am trying to prevent a model from saving to database, if it has certain value in a property.
Inside service provider:
public function register()
{
$this->registerSaveRelated();
$this->registerEvents();
}
public function registerEvents()
{
$app = $this->app;
$app['events']->listen('eloquent.saving*', function ($model) use ($app) {
$app['my_service']->checkModel($model);
});
}
public function registerSaveRelated(){
// More code...
checkModel($model) gets fired, as expected. However, nothing happens if I return false. The model continues to save. What is the correct way to do it?
It needs to be done in the package, not model. So Model::saving() is not an option.
There are two solutions that I can think of.
A. Use Model::saving() and just differentiate between the packages using a boolean or other simple logic.
B. Override the save method and put the logic in that function itself.
I think you are on the right track. You are correct that checkModel needs to return true or false. The important part, however, is what the closure passed to listen returns. That is what determines whether it saves or not. Right now its not returning anything. Try adding return like so:
public function registerEvents()
{
$app = $this->app;
$app['events']->listen('eloquent.saving*', function ($model) use ($app) {
return $app['my_service']->checkModel($model);
});
}

laravel model callbacks after save, before save, etc

Are there callbacks in Laravel like:
afterSave()
beforeSave()
etc
I searched but found nothing. If there are no such things - what is best way to implement it?
Thanks!
The best way to achieve before and after save callbacks in to extend the save() function.
Here's a quick example
class Page extends Eloquent {
public function save(array $options = [])
{
// before save code
parent::save($options);
// after save code
}
}
So now when you save a Page object its save() function get called which includes the parent::save() function;
$page = new Page;
$page->title = 'My Title';
$page->save();
Adding in an example for Laravel 4:
class Page extends Eloquent {
public static function boot()
{
parent::boot();
static::creating(function($page)
{
// do stuff
});
static::updating(function($page)
{
// do stuff
});
}
}
Actually, Laravel has real callback before|after save|update|create some model. check this:
https://github.com/laravel/laravel/blob/3.0/laravel/database/eloquent/model.php#L362
the EventListener like saved and saving are the real callbacks
$this->fire_event('saving');
$this->fire_event('saved');
how can we work with that? just assign it to this eventListener example:
\Laravel\Event::listen('eloquent.saving: User', function($user){
$user->saving();//your event or model function
});
Even though this question has already been marked 'accepted' - I'm adding a new updated answer for Laravel 4.
Beta 4 of Laravel 4 has just introduced hook events for Eloquent save events - so you dont need to extend the core anymore:
Added Model::creating(Closure) and Model::updating(Closure) methods for hooking into Eloquent save events. Thank Phil Sturgeon for finally pressuring me into doing this... :)
In Laravel 5.7, you can create a model observer from the command line like this:
php artisan make:observer ClientObserver --model=Client
Then in your app\AppServiceProvider tell the boot method the model to observe and the class name of the observer.
use App\Client;
use App\Observers\ClientObserver;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Client::observe(ClientObserver::class);
}
...
}
Then in your app\Observers\ you should find the observer you created above, in this case ClientObserver, already filled with the created/updated/deleted event hooks for you to fill in with your logic. My ClientObserver:
namespace App\Observers;
use App\Client;
class ClientObserver
{
public function created(Client $client)
{
// do your after-model-creation logic here
}
...
}
I really like the simplicity of this way of doing it. Reference https://laravel.com/docs/5.7/eloquent#events
Your app can break using afarazit solution*
Here's the fixed working version:
NOTE: saving or any other event won't work when you use eloquent outside of laravel, unless you require the events package and boot the events. This solution will work always.
class Page extends Eloquent {
public function save(array $options = [])
{
// before save code
$result = parent::save($options); // returns boolean
// after save code
return $result; // do not ignore it eloquent calculates this value and returns this, not just to ignore
}
}
So now when you save a Page object its save() function get called which includes the parent::save() function;
$page = new Page;
$page->title = 'My Title';
if($page->save()){
echo 'Page saved';
}
afarazit* I tried to edit his answer but didn't work
If you want control over the model itself, you can override the save function and put your code before or after __parent::save().
Otherwise, there is an event fired by each Eloquent model before it saves itself.
There are also two events fired when Eloquent saves a model.
"eloquent.saving: model_name" or "eloquent.saved: model_name".
http://laravel.com/docs/events#listening-to-events

Categories