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

Messaging Interface Laravel Package

toneflix-code/messaging-interface

toneflix-code/messaging-interface provides a set of PHP contracts for building consistent messaging systems in Laravel apps. Define drivers and channels behind a common interface so SMS, email, or custom transports can be swapped without changing application code.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a contract-first abstraction: it defines the interfaces (and typically a manager/facade) that a Laravel app codes against, while concrete SMS/Voice providers live behind that contract. That means you rarely call a provider SDK directly — you resolve the manager, hand it a message, and swap providers by config.

Minimum setup:

composer require toneflix-code/messaging-interface
php artisan vendor:publish --tag="messaging-interface-config"   # or the tag listed in the README
php artisan vendor:publish --tag="messaging-interface-migrations" # only if the package ships a table

Then open config/messaging-interface.php (or whatever the publish tag drops). This is your first stop — read every key: default driver, per-driver credentials, from/sender ID, and any queue/retry settings. The second stop is vendor/toneflix-code/messaging-interface/src/Contracts/ — this tells you the actual method signatures (send, sendBulk, voice, etc.) more reliably than the README.

First use case — a single transactional SMS on user signup:

use ToneflixCode\MessagingInterface\Facades\Messaging;
// or app(\ToneflixCode\MessagingInterface\Contracts\MessagingInterface::class)

Messaging::driver('termii')
    ->to('+2348012345678')
    ->message('Your OTP is 123456')
    ->send();

Voice is usually the same surface, just a different verb (->call(...) or a VoiceMessage object) — check the contract before assuming.

Implementation Patterns

Bind against the contract, not the facade. In service providers or action classes, inject MessagingInterface rather than importing the facade. This keeps unit tests trivial:

public function __construct(private MessagingInterface $messaging) {}

public function notify(Order $order): void
{
    $this->messaging->to($order->customer->phone)->message("Order #{$order->id} shipped")->send();
}

Wrap your own thin domain service. Don't scatter Messaging::to(...) calls across controllers. Create an SmsNotifier (or similar) that owns the "which driver, which sender ID, which template" decisions. When Termii goes down or you add Africa's Talking, you change one file.

Leverage Laravel notifications. The strongest long-term pattern is a custom notification channel that speaks this contract. Then domain code just does $user->notify(new OrderShipped($order)) and the SMS/voice choice is configuration, not code:

public function via($notifiable) { return [MessagingChannel::class]; }
public function toMessaging($notifiable)
{
    return (new SmsMessage)
        ->to($notifiable->phone)
        ->content("Order {$this->order->id} shipped");
}

Swap drivers per-message when the use case dictates. OTPs often need a cheaper local provider, while critical alerts go through your premium provider. Since the manager exposes ->driver(...), pick per call rather than relying only on the default:

$messaging->driver(config('services.sms.critical_driver'))->to($phone)->message($msg)->send();

Queue the send. SMS/voice APIs are network-bound; wrap delivery in a ShouldQueue job or notification so your HTTP request doesn't block on a third-party response. Combine with Laravel's retryUntil and a dead-letter table for failed sends.

Test with fakes / a null driver. The package usually ships (or supports) a fake or null driver for local/test environments. Set the default driver to it in phpunit.xml so test suites never hit a provider. If there's no fake, implement the contract in a FakeMessaging class yourself and swap it in a test service provider.

Log every attempt with metadata. Wrap send() in a small decorator that records the driver, recipient, message id returned by the provider, and outcome. This is invaluable for debugging "why didn't this user get an OTP" and for reconciliation when a provider bills you for a message you can't find a record of.

Gotchas and Tips

Read the source, not the docs. With 0 stars and 0 dependents, the README may lag behind the code. src/Contracts/, src/Drivers/, and the service provider's register()/boot() are the ground truth. The bound class names and config keys are defined there.

Config cache bites. After editing published config, run php artisan config:clear (or config:cache) in every environment. Driver credentials read at boot through config() will silently be stale otherwise.

Phone number formatting is on you. The package almost certainly forwards the number as-is. Normalize to E.164 (+234...) before handing it in, since most providers reject or misroute local formats. Consider a small PhoneNumber value object in your app layer.

Sender ID / from address is often provider-specific and pre-registered. Termii, Africa's Talking, and Twilio treat sender IDs differently (alphanumeric, prior approval, country rules). Don't assume a config value works across drivers — verify per provider.

Provider error shapes leak through. Most of these abstractions throw a generic exception wrapping the provider's response. Inspect both the exception message and any getResponse()/getPrevious() payload, because rate-limit vs. invalid-number vs. auth errors need different handling (retryable vs. not).

Rate limits are per provider and per account. The abstraction won't save you from provider throttling. Implement a per-provider RateLimiter in your notifier, and use Bus::batch() or Laravel's queue backoff for retries — don't hammer the API in a loop.

Extension points. The intended way to add a provider is a custom driver class implementing the contract, registered via the config's drivers array or a Messaging::extend()-style call in a service provider. If you need custom behavior (retries, DLR callbacks, voice XML), subclass the base driver rather than reimplementing the contract.

Delivery receipts are usually a separate webhook. The package may or may not ship webhook routing. Check routes/ in the vendor folder; if it exists, publish/exclude CSRF for that route (VerifyCsrfToken::$except). Otherwise, wire the provider's webhook endpoint yourself and correlate via the message ID returned from send().

Upgrade cautiously. Last release July 2025 with no dependents means it hasn't been battle-tested across the ecosystem. Pin the version in composer.json, commit the lock file, and watch for breaking changes in the contract — if a send() signature changes, your notifier wrapper is what saves you.

Watch for the naming trap. The package is "messaging-interface"; several similarly named packages exist (laravel-notification-channels/*, toneflix-code/messaging, etc.). Confirm in composer show that you installed the one whose namespace matches your use statements.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib