Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Notification Api Client Bundle Laravel Package

idci/notification-api-client-bundle

A Symfony bundle that provides an API client for the IDCI Notification service, letting PHP applications send and manage notifications through a simple, configurable integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: idci/notification-api-client-bundle is a Symfony bundle, not a Laravel package. It has no ServiceProvider, no extra.laravel.providers, and Laravel's package discovery will not register it. There is no bundles.php in a Laravel app, so the bundle's DependencyInjection/Extension class never runs and its service definitions are never loaded. You are not "installing a package" so much as pulling in a library that expects a kernel you don't have.

That said, you can still get value from it — by treating the bundle as documentation and using the underlying client directly.

Minimal steps

  1. Pull the code so you can read it (it's 2 stars / 0 dependents — assume you will read the source, not the docs):

    composer require --no-scripts idci/notification-api-client-bundle
    

    Expect symfony/http-kernel, symfony/dependency-injection, and symfony/config to land in composer.lock. That's tolerable on modern Laravel (Symfony components are already present transitively), but it also means nothing will execute.

  2. Where to look first, in this order:

    • vendor/idci/notification-api-client-bundle/DependencyInjection/Configuration.php — this is the authoritative config schema. Every knob the author thought about (endpoint/base URI, credentials, timeout, default sender, transport) is a node in this tree. This file is your integration checklist.
    • vendor/idci/notification-api-client-bundle/Resources/config/services.xml (or .yaml) — the service ids, class names, and constructor arguments. This tells you exactly which concrete class to app()->bind() and what it needs.
    • vendor/idci/notification-api-client/ (the sibling, non-bundle package) — this is the part you actually want. The bundle is a thin DI wrapper; the client is the transport, the DTOs, and the API contract.
    • The bundle's README.md, last: it is likely Symfony-flavoured YAML examples (config/packages/idci_notification_api_client.yaml) that you will translate, not copy.
  3. First use case — send one transactional notification, wired by hand:

    // config/services.php
    return [
        'notification_api' => [
            'endpoint' => env('NOTIFICATION_API_ENDPOINT'),
            'key'      => env('NOTIFICATION_API_KEY'),
        ],
    ];
    
    // app/Providers/NotificationApiServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(NotificationApiClient::class, function ($app) {
            $config = $app['config']['services.notification_api'];
    
            return new NotificationApiClient(
                httpClient: app(\GuzzleHttp\Client::class),
                baseUri: $config['endpoint'],
                apiKey: $config['key'],
            ); // -> constructor signature comes from services.xml; match it exactly
        });
    }
    

    Then in a controller or job: app(NotificationApiClient::class)->send($payload);

The whole "getting started" phase is roughly 30 minutes of source reading. Budget for it; there is no quickstart path that doesn't involve opening Configuration.php.

Implementation Patterns

Pattern 1 — Extract the client, discard the bundle (recommended)

The bundle's only job is to build the client and register it as a service. Laravel's container does that in ten lines. Concretely:

  • Read services.xml to get the client's FQCN and constructor args.
  • Bind it as a singleton (it's likely stateless and Guzzle-backed — a singleton is correct).
  • If the bundle wraps the client in a decorator/manager class, bind that instead and alias the client interface to it: $this->app->alias(NotificationApiManager::class, NotificationApiClientInterface::class);
  • Consider composer require idci/notification-api-client directly and dropping the bundle from require entirely. You lose nothing in Laravel and shed the Symfony DI surface.

Pattern 2 — Translate the Symfony config tree

Symfony bundles declare config in YAML; you declare config in config/services.php + .env. Map them one-to-one and do not try to emulate the config tree:

Symfony (config/packages/*.yaml) Laravel
%env(NOTIFICATION_API_KEY)% env('NOTIFICATION_API_KEY') inside config/services.php
%kernel.debug% config('app.debug')
bundle-level defaults defaults in the array, overridable in config/services.php
validation of required nodes a ServicesConfig rule or a one-off check in the provider

Keep the raw env() calls only in config/ files so php artisan config:cache behaves. A common failure here is calling env() at resolve time inside the singleton closure — that breaks under config:cache.

Pattern 3 — Wire it into Laravel's Notification system

This is the highest day-to-day leverage, because it lets the rest of your app stay idiomatic:

// app/Channels/NotificationApiChannel.php
class NotificationApiChannel
{
    public function __construct(private NotificationApiClient $client) {}

    public function send($notifiable, Notification $notification): void
    {
        if (! method_exists($notification, 'toNotificationApi')) {
            return;
        }

        $this->client->send(
            $notification->toNotificationApi($notifiable)
        );
    }
}

Register it in config/services.php? No — in the notification itself:

public function via($notifiable): array
{
    return [NotificationApiChannel::class];
}

Now every notification in the app (InvoicePaid, PasswordReset, DeploymentFailed) routes through one place, and you can swap the transport later without touching call sites.

Pattern 4 — Queue everything, always

Sending notifications synchronously in a request cycle is the classic mistake. Implement ShouldQueue on the notification, or wrap the client call in a dedicated job:

final class SendNotification implements ShouldQueue
{
    use Queueable;

    public int $tries = 5;
    public array $backoff = [5, 15, 60, 300];

    public function __construct(private array $payload) {}

    public function handle(NotificationApiClient $client): void
    {
        $client->send($this->payload); // throws on non-2xx -> queue retries
    }

    public function middleware(): array
    {
        return [new WithoutOverlapping($this->payload['idempotency_key'])];
    }
}

The client almost certainly throws on HTTP errors (Guzzle behaviour), which is exactly what the queue wants — no extra wrapper needed. Add failed() for a dead-letter table or alert.

Pattern 5 — Test doubles

Two layers, depending on how deep the client sits:

  • If the client uses Laravel's Http facade internally (unlikely for a Symfony bundle — it's probably Guzzle), Http::fake() works.
  • Realistically it's Guzzle. Use GuzzleHttp\Handler\MockHandler + HandlerStack and inject that client into your singleton, or build a FakeNotificationApiClient implementing the same interface and bind it in TestCase::setUp().
$this->app->instance(NotificationApiClient::class, new FakeNotificationApiClient([
    'status' => 'queued',
]));

Keep an interface (or a thin one you own) in front of the vendor class so you can assert on calls.

Pattern 6 — Decorate for observability

If you need logging, metrics, or idempotency headers, wrap the vendor client rather than editing it:

$this->app->extend(NotificationApiClient::class, function ($client) {
    return new LoggingNotificationApiClient($client, app(LoggerInterface::class));
});

This is the Laravel equivalent of a Symfony CompilerPass/service decorator and is the correct extension point.

Gotchas and Tips

License is the biggest issue. GPL-3.0 is copyleft. Pulling it into a proprietary Laravel SaaS is a legal question, not a technical one, but it is materially different from the MIT/Apache libraries you're used to. Many teams have a blanket "no GPL in require" policy that this package trips immediately. Get an answer before you build on it; otherwise reimplement the HTTP calls (Http::withToken(...)->post(...)) and skip the dependency entirely.

The bundle cannot boot. Do not add Idci\NotificationApiClientBundle\...\NotificationApiClientBundle anywhere — there is nowhere to add it. Any README instructions referencing AppKernel::registerBundles(), config/bundles.php, or bundles.php are inapplicable. Similarly, extra.symfony.endpoint and framework bundle registration metadata are invisible to Composer in a Laravel context.

Symfony service ids ≠ class names. The bundle registers services under identifiers like idci.notification_api.client with aliases, often with public: false and autowiring. In Symfony you'd inject the interface and autowiring picks it up. In Laravel, app(SomeInterface::class) fails unless you explicitly alias() it. Expect the first attempt to yield a BindingResolutionException targeting an interface, not a class — that's the signal to add the alias.

%parameter% and %env()% placeholders are unresolved strings if you copy config. Those syntaxes are resolved by Symfony's ContainerBuilder. If they leak into a constructor argument you pass manually from a YAML you copied, you'll send the literal string %env(NOTIFICATION_API_KEY)% to the API and get a 401 that looks like a credentials problem.

Configuration is validated by the Symfony Configuration class, and you're bypassing it. That means required-node errors become TypeError: $apiKey must be string, null given at container-resolve time instead of a clear "you forgot the api_key node" message. Add an explicit guard or a config test.

Zero ecosystem support. 2 stars, 0 dependents, GPL-3.0, score 0.07 — treat it as an in-house package that happens to be on Packagist. Pin an exact version (composer require idci/...:x.y.z), commit the lock, and consider forking to vendor if you rely on it. There is no Stack Overflow corpus for this package; every error message is yours to debug from source.

Error semantics are opaque. The client will surface non-2xx responses as Guzzle RequestException/ClientException. Laravel won't render those nicely. Either catch and convert in your job ($e->getResponse()->getBody()) or add a handler in bootstrap/app.php if you call it synchronously anywhere. Log the response body — notification APIs usually return a JSON reason (invalid_recipient, template_not_found) that is worth surfacing.

No php artisan config:cache awareness. Symfony bundles are typically built to read %env()% at container compile time, which is the opposite of Laravel's model where env() calls are compiled into a cached config file. Never call env() from inside the client factory or a job — always go through config().

The bundle's data collector / profiler integration, if any, does nothing in Laravel. Use Telescope or a Guzzle Middleware::log() instead:

$stack = HandlerStack::create();
$stack->push(Middleware::log($logger, new MessageFormatter(...)));

Extension points map like this: Symfony CompilerPass → $this->app->extend(); Symfony tagged services → $this->app->tag() + tagged(); Symfony Configuration validation → Laravel config validation or a config test; Symfony bundle Resources/config/services.yaml → your service provider. Nothing here requires you to learn Symfony's DI internals — you just need to read them once.

Consider not using it at all. For a single-endpoint notification sender, a 40-line Laravel service around Http::withToken() with retry() and a NotificationApiResponse DTO gives you better observability, no copyleft, no dead Symfony code, and typed errors. The only reason to reach for this package is if the client hides genuinely fiddly protocol details (multipart templates, signing, pagination) that you'd rather not reimplement. Verify that before committing.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console