From 6cd81219122828b371ebdb216d17dbd6832a34b6 Mon Sep 17 00:00:00 2001 From: Derek MacDonald Date: Fri, 20 Jul 2018 23:58:50 -0400 Subject: [PATCH] Notifications can choose locale Queued notifications can run in a locale other than the application default. --- .../Notifications/ChannelManager.php | 24 ++- src/Illuminate/Notifications/Notification.php | 20 ++ .../Notifications/NotificationSender.php | 28 ++- .../Support/Facades/Notification.php | 1 + .../Testing/Fakes/NotificationFake.php | 21 ++ .../Notifications/Fixtures/greeting.blade.php | 1 + ...otificationsViaAnonymousNotifiableTest.php | 8 +- .../SendingNotificationsWithLocaleTest.php | 202 ++++++++++++++++++ 8 files changed, 295 insertions(+), 10 deletions(-) create mode 100644 tests/Integration/Notifications/Fixtures/greeting.blade.php create mode 100644 tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php diff --git a/src/Illuminate/Notifications/ChannelManager.php b/src/Illuminate/Notifications/ChannelManager.php index eb8c4f29d007..d8a8de735163 100644 --- a/src/Illuminate/Notifications/ChannelManager.php +++ b/src/Illuminate/Notifications/ChannelManager.php @@ -21,6 +21,13 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr */ protected $defaultChannel = 'mail'; + /** + * Locale used when sending notifications. + * + * @var string|null + */ + protected $locale; + /** * Send the given notification to the given notifiable entities. * @@ -31,7 +38,7 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr public function send($notifiables, $notification) { return (new NotificationSender( - $this, $this->app->make(Bus::class), $this->app->make(Dispatcher::class)) + $this, $this->app->make(Bus::class), $this->app->make(Dispatcher::class), $this->locale) )->send($notifiables, $notification); } @@ -46,7 +53,7 @@ public function send($notifiables, $notification) public function sendNow($notifiables, $notification, array $channels = null) { return (new NotificationSender( - $this, $this->app->make(Bus::class), $this->app->make(Dispatcher::class)) + $this, $this->app->make(Bus::class), $this->app->make(Dispatcher::class), $this->locale) )->sendNow($notifiables, $notification, $channels); } @@ -168,4 +175,17 @@ public function deliverVia($channel) { $this->defaultChannel = $channel; } + + /** + * Set the locale of notifications. + * + * @param string $locale + * @return $this + */ + public function locale($locale) + { + $this->locale = $locale; + + return $this; + } } diff --git a/src/Illuminate/Notifications/Notification.php b/src/Illuminate/Notifications/Notification.php index 9b258b7dc1f6..64f68e4c43bf 100644 --- a/src/Illuminate/Notifications/Notification.php +++ b/src/Illuminate/Notifications/Notification.php @@ -15,6 +15,13 @@ class Notification */ public $id; + /** + * Locale used when sending the notification. + * + * @var string|null + */ + public $locale; + /** * Get the channels the event should broadcast on. * @@ -24,4 +31,17 @@ public function broadcastOn() { return []; } + + /** + * Set the locale to send this notification in. + * + * @param string $locale + * @return $this + */ + public function locale($locale) + { + $this->locale = $locale; + + return $this; + } } diff --git a/src/Illuminate/Notifications/NotificationSender.php b/src/Illuminate/Notifications/NotificationSender.php index c9731fab77a4..cca14ae62cb3 100644 --- a/src/Illuminate/Notifications/NotificationSender.php +++ b/src/Illuminate/Notifications/NotificationSender.php @@ -5,11 +5,14 @@ use Illuminate\Support\Str; use Illuminate\Support\Collection; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Traits\Localizable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Database\Eloquent\Collection as ModelCollection; class NotificationSender { + use Localizable; + /** * The notification manager instance. * @@ -31,19 +34,28 @@ class NotificationSender */ protected $events; + /** + * Locale used when sending notifications. + * + * @var string|null + */ + protected $locale; + /** * Create a new notification sender instance. * * @param \Illuminate\Notifications\ChannelManager $manager * @param \Illuminate\Contracts\Bus\Dispatcher $bus * @param \Illuminate\Contracts\Events\Dispatcher $events + * @param string|null $locale * @return void */ - public function __construct($manager, $bus, $events) + public function __construct($manager, $bus, $events, $locale = null) { $this->bus = $bus; $this->events = $events; $this->manager = $manager; + $this->locale = $locale; } /** @@ -83,11 +95,13 @@ public function sendNow($notifiables, $notification, array $channels = null) continue; } - $notificationId = Str::uuid()->toString(); + $this->withLocale($notification->locale ?? $this->locale, function () use ($viaChannels, $notifiable, $original) { + $notificationId = Str::uuid()->toString(); - foreach ((array) $viaChannels as $channel) { - $this->sendToNotifiable($notifiable, $notificationId, clone $original, $channel); - } + foreach ((array) $viaChannels as $channel) { + $this->sendToNotifiable($notifiable, $notificationId, clone $original, $channel); + } + }); } } @@ -153,6 +167,10 @@ protected function queueNotification($notifiables, $notification) $notification->id = $notificationId; + if (! is_null($this->locale)) { + $notification->locale = $this->locale; + } + $this->bus->dispatch( (new SendQueuedNotifications($notifiable, $notification, [$channel])) ->onConnection($notification->connection) diff --git a/src/Illuminate/Support/Facades/Notification.php b/src/Illuminate/Support/Facades/Notification.php index d13ec82ae30e..425520d852e3 100644 --- a/src/Illuminate/Support/Facades/Notification.php +++ b/src/Illuminate/Support/Facades/Notification.php @@ -10,6 +10,7 @@ * @method static void send(\Illuminate\Support\Collection|array|mixed $notifiables, $notification) * @method static void sendNow(\Illuminate\Support\Collection|array|mixed $notifiables, $notification) * @method static mixed channel(string|null $name = null) + * @method static \Illuminate\Notifications\ChannelManager locale(string|null $locale) * * @see \Illuminate\Notifications\ChannelManager */ diff --git a/src/Illuminate/Support/Testing/Fakes/NotificationFake.php b/src/Illuminate/Support/Testing/Fakes/NotificationFake.php index 8093a4fb7968..72b6ab9bab38 100644 --- a/src/Illuminate/Support/Testing/Fakes/NotificationFake.php +++ b/src/Illuminate/Support/Testing/Fakes/NotificationFake.php @@ -17,6 +17,13 @@ class NotificationFake implements NotificationFactory, NotificationDispatcher */ protected $notifications = []; + /** + * Locale used when sending notifications. + * + * @var string|null + */ + public $locale; + /** * Assert if a notification was sent based on a truth-test callback. * @@ -203,6 +210,7 @@ public function sendNow($notifiables, $notification) 'notification' => $notification, 'channels' => $notification->via($notifiable), 'notifiable' => $notifiable, + 'locale' => $notification->locale ?? $this->locale, ]; } } @@ -217,4 +225,17 @@ public function channel($name = null) { // } + + /** + * Set the locale of notifications. + * + * @param string $locale + * @return $this + */ + public function locale($locale) + { + $this->locale = $locale; + + return $this; + } } diff --git a/tests/Integration/Notifications/Fixtures/greeting.blade.php b/tests/Integration/Notifications/Fixtures/greeting.blade.php new file mode 100644 index 000000000000..1eceadd36906 --- /dev/null +++ b/tests/Integration/Notifications/Fixtures/greeting.blade.php @@ -0,0 +1 @@ +{{ __('hi') }} diff --git a/tests/Integration/Notifications/SendingNotificationsViaAnonymousNotifiableTest.php b/tests/Integration/Notifications/SendingNotificationsViaAnonymousNotifiableTest.php index 153d60dd6c1f..ab9235ea756b 100644 --- a/tests/Integration/Notifications/SendingNotificationsViaAnonymousNotifiableTest.php +++ b/tests/Integration/Notifications/SendingNotificationsViaAnonymousNotifiableTest.php @@ -46,14 +46,16 @@ public function test_faking() ->route('testchannel', 'enzo') ->route('anothertestchannel', 'enzo@deepblue.com'); - NotificationFacade::send( + NotificationFacade::locale('it')->send( $notifiable, new TestMailNotificationForAnonymousNotifiable() ); NotificationFacade::assertSentTo(new AnonymousNotifiable(), TestMailNotificationForAnonymousNotifiable::class, - function ($notification, $channels, $notifiable) { - return $notifiable->routes['testchannel'] == 'enzo' && $notifiable->routes['anothertestchannel'] == 'enzo@deepblue.com'; + function ($notification, $channels, $notifiable, $locale) { + return $notifiable->routes['testchannel'] === 'enzo' && + $notifiable->routes['anothertestchannel'] === 'enzo@deepblue.com' && + $locale === 'it'; } ); } diff --git a/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php b/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php new file mode 100644 index 000000000000..741249e51717 --- /dev/null +++ b/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php @@ -0,0 +1,202 @@ +set('app.debug', 'true'); + + $app['config']->set('mail.driver', 'array'); + + $app['config']->set('app.locale', 'en'); + + $app['config']->set('database.default', 'testbench'); + + $app['config']->set('database.connections.testbench', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + + View::addLocation(__DIR__.'/Fixtures'); + + app('translator')->setLoaded([ + '*' => [ + '*' => [ + 'en' => ['hi' => 'hello'], + 'fr' => ['hi' => 'bonjour'], + ], + ], + ]); + } + + public function setUp() + { + parent::setUp(); + + Schema::create('users', function ($table) { + $table->increments('id'); + $table->string('email'); + $table->string('name')->nullable(); + }); + } + + public function test_mail_is_sent_with_default_locale() + { + $user = NotifiableLocalizedUser::forceCreate([ + 'email' => 'taylor@laravel.com', + 'name' => 'Taylor Otwell', + ]); + + NotificationFacade::send($user, new GreetingMailNotification); + + $this->assertContains('hello', + app('swift.transport')->messages()[0]->getBody() + ); + } + + public function test_mail_is_sent_with_facade_selected_locale() + { + $user = NotifiableLocalizedUser::forceCreate([ + 'email' => 'taylor@laravel.com', + 'name' => 'Taylor Otwell', + ]); + + NotificationFacade::locale('fr')->send($user, new GreetingMailNotification); + + $this->assertContains('bonjour', + app('swift.transport')->messages()[0]->getBody() + ); + } + + public function test_mail_is_sent_with_notification_selected_locale() + { + $users = [ + NotifiableLocalizedUser::forceCreate([ + 'email' => 'taylor@laravel.com', + 'name' => 'Taylor Otwell', + ]), + NotifiableLocalizedUser::forceCreate([ + 'email' => 'mohamed@laravel.com', + 'name' => 'Mohamed Said', + ]), + ]; + + NotificationFacade::send($users, (new GreetingMailNotification())->locale('fr')); + + $this->assertContains('bonjour', + app('swift.transport')->messages()[0]->getBody() + ); + + $this->assertContains('bonjour', + app('swift.transport')->messages()[1]->getBody() + ); + } + + public function test_mailable_is_sent_with_selected_locale() + { + $user = NotifiableLocalizedUser::forceCreate([ + 'email' => 'taylor@laravel.com', + 'name' => 'Taylor Otwell', + ]); + + NotificationFacade::locale('fr')->send($user, new GreetingMailNotificationWithMailable); + + $this->assertContains('bonjour', + app('swift.transport')->messages()[0]->getBody() + ); + } + + public function test_mail_is_sent_with_locale_updated_listeners_called() + { + Carbon::setTestNow(Carbon::parse('2018-07-25')); + + Event::listen(LocaleUpdated::class, function ($event) { + Carbon::setLocale($event->locale); + }); + + $user = NotifiableLocalizedUser::forceCreate([ + 'email' => 'taylor@laravel.com', + 'name' => 'Taylor Otwell', + ]); + + $user->notify((new GreetingMailNotification())->locale('fr')); + + $this->assertContains('bonjour', + app('swift.transport')->messages()[0]->getBody() + ); + + $this->assertContains('dans 1 jour', + app('swift.transport')->messages()[0]->getBody() + ); + + $this->assertTrue($this->app->isLocale('en')); + + $this->assertSame('en', Carbon::getLocale()); + } +} + +class NotifiableLocalizedUser extends Model +{ + use Notifiable; + + public $table = 'users'; + public $timestamps = false; +} + +class GreetingMailNotification extends Notification +{ + public function via($notifiable) + { + return [MailChannel::class]; + } + + public function toMail($notifiable) + { + return (new MailMessage) + ->greeting(__('hi')) + ->line(Carbon::tomorrow()->diffForHumans()); + } +} + +class GreetingMailNotificationWithMailable extends Notification +{ + public function via($notifiable) + { + return [MailChannel::class]; + } + + public function toMail($notifiable) + { + return new GreetingMailable; + } +} + +class GreetingMailable extends Mailable +{ + public function build() + { + return $this->view('greeting'); + } +}