flosch/messagebird-bundle
Symfony bundle that integrates the MessageBird API, exposing the official PHP client as configurable services and adding a Swiftmailer transport so applications can send SMS messages through MessageBird using familiar mailer APIs and YAML configuration.
Reality check first. flosch/messagebird-bundle is a Symfony 3 bundle, not a Laravel package. Symfony 3 has been end-of-life since 2021, the bundle has zero stars and zero dependents, and the vendor itself rebranded from MessageBird to Bird. Composer may install it into a Laravel app, but there is no package discovery, no service provider, and no Laravel-facing API. Day-to-day, you "leverage" this package by using the SDK it wraps — not the bundle itself.
Install the underlying SDK instead:
composer require messagebird/php-rest-api
Put the credential in .env:
MESSAGEBIRD_ACCESS_KEY=live_xxxxxxxxxxxxxxxx
MESSAGEBIRD_ORIGINATOR=MyApp
Wire it via a config entry (config/services.php):
return [
'messagebird' => [
'access_key' => env('MESSAGEBIRD_ACCESS_KEY'),
'originator' => env('MESSAGEBIRD_ORIGINATOR', 'MyApp'),
],
];
Bind the client as a singleton (dedicated MessageBirdServiceProvider or AppServiceProvider):
use MessageBird\Client;
$this->app->singleton(Client::class, fn () => new Client(
config('services.messagebird.access_key')
));
First use case — a queued transactional SMS:
namespace App\Services;
use MessageBird\Client;
use MessageBird\Objects\Message;
class SmsSender
{
public function __construct(private Client $client) {}
public function send(string $to, string $body, ?string $originator = null): Message
{
$message = new Message();
$message->originator = $originator ?? config('services.messagebird.originator');
$message->recipients = [$to]; // E.164: +31612345678
$message->body = $body;
return $this->client->messages->create($message);
}
}
Dispatch it from a job (SendSmsJob::dispatch($user, $code)), never inline in a login request.
If you must read the Symfony bundle (e.g., inheriting a Symfony 3 service), open, in order:
DependencyInjection/Configuration.php — exact config keys (usually just access_key).Resources/config/services.xml — the service ID and class (expect messagebird.client → MessageBird\Client).DependencyInjection/*Extension.php — how the key reaches the constructor.The entire surface is one service definition and one config node; ten minutes of reading beats guessing.
Adapter over the SDK, always. The MessageBird\Client is a third-party object with a frozen API. Inject it only into one class, and have the rest of the app depend on an interface:
interface SmsGateway { public function send(string $to, string $body): void; }
// AppServiceProvider
$this->app->bind(SmsGateway::class, MessageBirdSmsGateway::class);
When the vendor changes or you migrate, you rewrite one class.
Use a Laravel Notification channel. For anything user-facing (OTP, alerts), model it as a notification and add a MessageBirdChannel that delegates to SmsGateway. You get queues, toMessageBird() shaping, and per-notification routing for free.
Queue every outbound send. SMS is a network call with 100–500ms latency and a hard rate limit. SendSmsJob implements ShouldQueue with $tries = 3 and backoff() keeps the request path clean and gives you retry semantics.
Idempotency to survive retries. A retried job will send twice if the first call succeeded but the response was lost. Persist an sms_log row keyed by a message reference before dispatch, and have the job check-and-set status before calling messages->create().
Webhooks for delivery receipts. MessageBird posts status updates to a URL you configure. Add a route that verifies the signature, returns 200 fast, and dispatches a job to update the sms_log row. Do not process receipts synchronously.
Testing. Don't hit the network. Bind SmsGateway to a FakeSmsGateway in tests (or Http::fake() if you wrap the REST endpoints), and assert the job is dispatched with the right recipient and body.
Extension point when the bundle is in the tree. The bundle's value is a service definition you can override — alias MessageBird\Client to your own decorated instance, or decorate messagebird.client — without touching third-party code. That is also the seam you use to swap vendors later.
composer update. If you see it in a dependency tree, treat it as a migration line item, not something to adopt.messagebird/php-rest-api is effectively frozen. Pin the version and assume no upstream fixes; plan a wrapper swap if you need WhatsApp or voice.MessageBird\Client throws on an empty key. Failing at container boot is better than failing at send time, but it means a missing env var crashes startup — validate MESSAGEBIRD_ACCESS_KEY in a config check and fail loudly in CI.php artisan config:cache freezes env() calls. Only read MESSAGEBIRD_ACCESS_KEY inside config/services.php; calling env() from a service class returns null in production.0612345678 fails; +31612345678 works. Normalize (e.g., via propaganistas/laravel-phone) before it reaches the gateway, and reject rather than let the SDK 400.MessageBird\Exceptions\* subclasses. Catch broad Throwable in the job, log the API code, and mark the row failed — don't let an uncaught SMS exception take out a queued login job's siblings.message_id + status uniqueness constraint, or you'll get spoofed "delivered" receipts.MessageBird\Client into controllers, models, or notifications directly. The bundle's biggest trap is that it makes the SDK look first-party — it isn't. One adapter class in, one out.How can I help you explore Laravel packages today?