Laravel. Disable observer methods if the database is seeding - php

I have an observer for my User model. Inside my observer->created event i have some code.
public function created(User $user)
{
sendEmail();
}
So, the idea is, when a user is created, the system will send for the user email notification that the account was created.
Question: When the database is seeding, it also calls this method 'created' and sends users (that are in the seeds) email notification.
So, my question is, how can i check, probably inside this 'created' method if at the moment laravel is seeding data -> do not send email of do not run the 'created' observer method.
Tried to google, found something, but not working correct.
Something like YourModel::flushEventListeners();

You can use YourModel::unsetEventDispatcher(); to remove the event listeners for a model temporary.
If you need them after seeding in the same execution, you can read the dispatchers, unset them and then set them again.
$dispatcher = YourModel::getEventDispatcher();
// Remove Dispatcher
YourModel::unsetEventDispatcher();
// do stuff here
// Re-add Dispatcher
YourModel::setEventDispatcher($dispatcher);

namespace Database\Seeders;
use App\Models\Blog;
use Illuminate\Database\Seeder;
class BlogsTableSeeder extends Seeder
{
public function run()
{
Blog::withoutEvents(function () {
// normally
Blog::factory()
->times(10)
->hasUploads(1) //hasOne
->hasComments(2) //hasMany
->create();
});
}
}

You may mute event with WithoutModelEvents trait
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class SomeSeeder extends Seeder
{
use WithoutModelEvents;
public function run()
{
User::factory( 30 )->create();
}
}
or you may try createQuietly method of a factory, for example
class SomeSeeder extends Seeder
{
public function run()
{
User::factory( 30 )->createQuietly();
}
}

You could use the saveQuietly() function https://laravel.com/docs/8.x/eloquent#saving-a-single-model-without-events
This allows you to disable all events for a single model.
If you wanna disable a single event for a single model, read about it here: http://derekmd.com/2019/02/conditionally-suppressing-laravel-event-listeners/

Related

Call method of another controller in laravel

I have to call the method of another controller.
I use following code to make a call.
app('App\Http\Controllers\ApiUserController')->getList();
This is working fine.
But I want to try using use function so that I dont have to repeat all line
use App\Http\Controllers\ApiUserController;
class MyMethods
{
public function index()
{
app('ApiUserController')->getList()
Did I made some mistake here?
Instead of using app function, you will need to go through OOP way like so:
use App\Http\Controllers\ApiUserController;
class MyMethods
{
public function index()
{
$apiUserController = new ApiUserController();
$apiUserController->getList();
However, as many people have mentioned here, it is not really the best practice to call a method of one controller from the another.
So if I were at your place, I would create a helper, register its alias in config and use that helper to get the list in both places.
I hope it helps
Calling controller from other controller or other objects is not a good practice. Here is a good article explaining why. Also "fat" controllers is less preferable than "thin" controllers.
You should define a service layer object with common logic and use it. Create a service object and register it with one of service providers.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\YourUserService;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(YourUserService::class);
}
}
After that you can use your service in DI style.
use App\Services\YourUserService;
class MyMethods
{
protected $userService;
public function __construct(YourUserService $userService)
{
$this->userService = $userService;
}
public function index()
{
$this->userService->foo();
}
}
Why should I use dependency injection?
I agree with the answer Learner has given above, however, I wouldn't recommend it in terms of code organisation and testability.
Looking at the code, I can see that you need to get list of users and thats why you have to call the api user controller from another controller. However, you can easily extract the logic out to a service or even a trait.
if you were to use a trait then you could do some thing like following,
trait ApiUser {
public function getList()
{
// get the list for users from api
}
}
//Then you can simply use this trait any where you want,
class SomeController
{
// correct namespace for ApiUser trait
use ApiUser;
}
Another way of doing it, which i love to use again and again depending on the scenario; is to stick with the principle of coding to interface not to implementation. That would be some thing like follow.
interface ApiUserInterface
{
public function getList();
}
class ApiUser implements ApiUserInterface
{
public function getList()
{
// logic to get users from api
}
}
Make sure that when application requires the interface, it knows where to find its implementation. If you using Laravel, then you could register your interface to class in AppServiceProvider
Once that's done, you can use this service any where you want as a contract.
class OneController
{
protected $apiUserContract;
public function __construct(ApiUserInterface $apiUserContract)
{
$this->apiUserContract = $apiUserContract;
}
public function index()
{
// You can retrieve the list of the contract
$this->apiUserContract->getList();
}
}
// you could also just typehint the contact in method without requiring
// it in constructor and it will get resolved out of IOC i.e. container
class AnotherController
{
public function index(ApiUserInterface $apiUserContract)
{
// You can retrieve the list of the contract
$apiUserContract->getList();
}
}
Let me know if you need further explanation and hope it helps

Is it possible to prevent Laravel running Model Events when the database is being Seeded?

Laravel's seeder runs a variety of Model Events on my models which trigger New Order notification emails, among other things, from the Product::saved() Model Event.
This significantly slows down database seeding. Is it possible to detect whether a Seed is being ran and if so, tell Laravel not to run the Model Events?
There are functions on the Model class which will allow you to ignore events.
Before using a model to seed, you will need to do something like this...
YourModel::flushEventListeners();
I recommend to remove the Dispatcher in this Case from the Eloquent Model.
For example.
// Check Dispatcher
Model::getEventDispatcher()
// Remove Dispatcher
Model::unsetEventDispatcher()
// Add Dispatcher
Model::setEventDispatcher(new \Illuminate\Events\Dispatcher);
Add WithoutModelEvents Trait to your seeder
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
public function run(): void
{
// Silent eloquent queries ...
}
}

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 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

Symfony2 - Is there a way to hook into an event for user confirmation?

I'm using the FOS user bundle for symfony2 and want to run some custom code to log the event when a user confirms his registration at /register/confirm/{token}
However, there does not seem to be an event for when a user is confirmed, so I'd like to know the best way to hook into execution when a user account is confirmed.
You can override RegistrationController (see doc) to add logging functionality.
If you're able to use the dev-master (2.0.*#dev), than you can use the new controller events in the FOSUserBundle. See the documentation at github for details: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/controller_events.md
Here is a little example for the confirm event. Don't forget to define the service as mentioned in the link above.
<?php
namespace Acme\UserBundle\Security\Listener;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RegistrationListener implements EventSubscriberInterface
{
public function __construct(/** inject services you need **/)
{
// assign services to private fields
}
public static function getSubscribedEvents()
{
return array(FOSUserEvents::REGISTRATION_CONFIRM => 'onRegistrationConfirm',);
}
/**
* GetResponseUserEvent gives you access to the user object
**/
public function onRegistrationConfirm(GetResponseUserEvent $event)
{
// do your stuff
}
}

Categories