laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.21k stars 10.9k forks source link

SendEmailVerificationNotification listener is registered by default and there is no (easy) way to disable it #52601

Closed JaZo closed 3 weeks ago

JaZo commented 3 weeks ago

Laravel Version

11.21.0

PHP Version

8.2.22

Database Driver & Version

No response

Description

Issue

Laravel registers the \Illuminate\Auth\Listeners\SendEmailVerificationNotification listener for the \Illuminate\Auth\Events\Registered event by default, and there is no (easy) way to disable this.

What I've tried:

1. Replace EventServiceProvider

As suggested in #51187. I've created a custom EventServiceProvider where I've overwritten the configureEmailVerification function with an empty function. After that I tried to replace this service provider in config/app.php:

'providers' => ServiceProvider::defaultProviders()->replace([
    \Illuminate\Foundation\Support\Providers\EventServiceProvider::class => \App\Providers\EventServiceProvider::class,
])->toArray(),

But this won't work as this provider is registered dynamically in \Illuminate\Foundation\Configuration\ApplicationBuilder::withEvents. Replacing it with a custom binding also isn't possible as service providers aren't resolved from the container.

Conclusion: not viable

2. Stop event propagation

I can stop propagation in my own listener to prevent execution of the default one. But that is highly unstable as the order might not always be guaranteed and what if I need an extra listener that is triggered afterwards?

Conclusion: not viable

3. Use a custom Registered event

I can create a custom registered event and dispatch that instead of the default one. But several (1st party) packages, such as fortify, rely on this event by either dispatching it or listening to it. That would mean I have to find a way to swap the event in these packages.

Conclusion: viable, but highly inconvenient

4. Overwrite the listener

I can "disable" the listener by creating a no-op listener and swapping it out in the container.

$this->app->bind(\Illuminate\Auth\Listeners\SendEmailVerificationNotification::class, \App\Listeners\Noop::class);

Conclusion: viable, but not so elegant

5. Patch Laravel

Use something like cweagans/composer-patches to patch the issue away.

Conclusion: viable, but 🤢

Context

We have a custom listener for the \Illuminate\Auth\Events\Registered event where we dispatch a job that, eventually, sends the email verification notification ($user->sendEmailVerificationNotification()), but first checks some prerequisites. We do this in a (delayed) job because the user has to be present in an external system before we can send the notification. This worked all fine, but after we upgraded to Laravel 11, users get two of these notifications. I tracked it down and found that this specific event and its listener are hard coded in the Laravel source in \Illuminate\Foundation\Support\Providers\EventServiceProvider::configureEmailVerification.

Related

Steps To Reproduce

  1. Create a listener for the \Illuminate\Auth\Events\Registered event;
  2. Send the email verification notification in that listener ($user->sendEmailVerificationNotification());
  3. Register a user;
  4. See you get two email verification notifications.
driesvints commented 3 weeks ago

I'm sorry but as Taylor closed the PR it doesn't seems like we're going to take action here. You can always re-attempt the PR or present another solution and try to build a stronger use case for Taylor to review.