troccoli's avatar

Is the Notification Facade macroable?

My app sends quite a few notifications on Slack, so I thought I could create a macro to clean up the code and reduce duplication.

namespace App\Providers;

use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Notification as NotificationFacade;
use Illuminate\Support\ServiceProvider;

class NotificationServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        NotificationFacade::macro('slack', function (Notification $notification) {
            NotificationFacade::route('slack', $notification->routeNotificationForSlack())
                ->notify($notification);
        });
    }
}

and I add the new provider to boostrap/providers.php. But now all my tests fail with

Error: Call to undefined method Illuminate\Notifications\Channels\MailChannel::macro()
at vendor/laravel/framework/src/Illuminate/Support/Manager.php:186
at vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:361
at app/Providers/NotificationServiceProvider.php:17
at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
at vendor/laravel/framework/src/Illuminate/Container/Util.php:43
at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:96
at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35
at vendor/laravel/framework/src/Illuminate/Container/Container.php:754
at vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1150
at vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1131
at vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1130
at vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/BootProviders.php:17
at vendor/laravel/framework/src/Illuminate/Foundation/Application.php:341
at vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:473
at vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:32
at vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:54
at vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php:91
at vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:44
at tests/TestCase.php:25

I don't understand what is wrong in my macro, and why Laravel is trying to call the macro() method on MailChannel.

Does anyone have any idea?

I'm using Laravel 12.20.0

0 likes
2 replies
LaryAI's avatar
Level 58

The issue here is that the Notification Facade is not macroable. In Laravel, only classes that use the Macroable trait support the macro() method. The Illuminate\Support\Facades\Notification facade does not use this trait, which is why you're seeing the error:

Call to undefined method Illuminate\Notifications\Channels\MailChannel::macro()

This error occurs because the facade is forwarding the macro call to its underlying manager, which doesn't have the macro method either.

What Can You Do Instead?

If you want to encapsulate your Slack notification logic, you have a few options:

1. Create a Helper Function

You can define a global helper function (e.g., in app/helpers.php):

if (! function_exists('notify_slack')) {
    function notify_slack($notifiable, $notification)
    {
        \Illuminate\Support\Facades\Notification::route('slack', $notifiable->routeNotificationForSlack())
            ->notify($notification);
    }
}

Then use it like:

notify_slack($user, new MySlackNotification());

2. Create a Custom Service

You can create a service class to encapsulate this logic:

namespace App\Services;

use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Notification as NotificationFacade;

class SlackNotifier
{
    public function send($notifiable, Notification $notification)
    {
        NotificationFacade::route('slack', $notifiable->routeNotificationForSlack())
            ->notify($notification);
    }
}

Register it in the service container and inject/use it where needed.

3. Use a Trait

If you want to use this in multiple places, a trait might be helpful:

namespace App\Traits;

use Illuminate\Support\Facades\Notification;

trait SendsSlackNotifications
{
    public function sendSlackNotification($notifiable, $notification)
    {
        Notification::route('slack', $notifiable->routeNotificationForSlack())
            ->notify($notification);
    }
}

Then use the trait in your classes.


Summary:
The Notification facade is not macroable, so you can't add macros to it. Instead, use a helper, service, or trait to encapsulate your Slack notification logic and reduce duplication.

Please or to participate in this conversation.