digitalstate/platform-transport-bundle
Installation
composer require digitalstate/platform-transport-bundle
Ensure the bundle is registered in config/bundles.php:
return [
// ...
DigitalState\TransportBundle\TransportBundle::class => ['all' => true],
];
Database Setup
Run migrations to create transport and profile tables:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Use Case: Sending an SMS
Create a custom Transport class (e.g., TwilioTransport) implementing \Ds\Bundle\TransportBundle\Transport\Transport:
namespace App\Transport\Sms;
use Ds\Bundle\TransportBundle\Transport\Transport;
use Ds\Bundle\TransportBundle\Model\Message;
class TwilioTransport implements Transport {
public function send(Message $message) {
// Twilio logic here
}
}
Register the Transport
Define the transport in config/packages/transport.yaml:
transports:
twilio_sms:
class: App\Transport\Sms\TwilioTransport
profile: default_sms_profile
Send a Message
Inject the TransportManager and use it:
use Ds\Bundle\TransportBundle\Manager\TransportManager;
class MyService {
public function __construct(private TransportManager $transportManager) {}
public function sendWelcomeSms() {
$message = new Message('+1234567890', 'Welcome!');
$this->transportManager->send('twilio_sms', $message);
}
}
Transport Creation
Transport interface to implement custom logic (e.g., email, SMS, push notifications).class EmailTransport implements Transport {
public function __construct(private \Swift_Mailer $mailer) {}
public function send(Message $message) {
$this->mailer->send(...);
}
}
Profile Management
php bin/console make:entity Profile
config/transport.yaml:
transports:
twilio_sms:
profile: twilio_profile_id
Message Handling
Message class to standardize payloads:
$message = new Message(
recipient: 'recipient@example.com',
body: 'Hello!',
metadata: ['template' => 'welcome']
);
Message for custom fields if needed.Transport Manager
TransportManager to dynamically resolve transports:
$this->transportManager->send('email', $message); // Resolves to EmailTransport
TransportResolverInterface.Event-Driven Extensions
TransportEvent (e.g., PRE_SEND, POST_SEND) for logging, retries, or analytics:
use Ds\Bundle\TransportBundle\Event\TransportEvent;
$dispatcher->addListener(TransportEvent::PRE_SEND, function (TransportEvent $event) {
// Pre-send logic (e.g., validation)
});
Symfony Messenger Bridge Integrate with Symfony Messenger for async processing:
# config/packages/messenger.yaml
transports:
async_transport:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
retry_strategy:
max_retries: 3
Dispatch messages via Messenger:
$this->messageBus->dispatch(new SendMessage($message));
Doctrine ORM
Use repositories to manage Transport and Profile entities:
$profile = $this->profileRepository->find('default_sms_profile');
$transport = $this->transportRepository->findOneBy(['profile' => $profile]);
Configuration Validation
Validate transport configurations in config/validator/transport.yaml:
Ds\Bundle\TransportBundle\Entity\Transport:
properties:
class:
- NotBlank
- ClassExists
Testing Mock transports in tests:
$mockTransport = $this->createMock(Transport::class);
$mockTransport->method('send')->willReturn(true);
$this->transportManager->setTransport('test', $mockTransport);
Circular Dependencies
TransportManager to resolve transports by name, not directly instantiating them.Profile Misconfiguration
Transport "twilio_sms" not found for profile "missing_profile".
if (!$this->transportManager->hasTransport('twilio_sms')) {
throw new \RuntimeException('Transport not configured');
}
Message Serialization
Message metadata must be serializable. Non-serializable objects (e.g., closures) will fail.$message = new Message('recipient', 'body', ['data' => json_encode($complexObject)]);
Transport Lifecycle
Doctrine Events
Transport or Profile lifecycle callbacks (e.g., prePersist) may conflict with bundle logic.Enable Transport Logging
Add to config/packages/monolog.yaml:
handlers:
transport:
type: stream
path: "%kernel.logs_dir%/transport.log"
level: debug
channels: ["transport"]
Then log in your transport:
$this->logger->debug('Sending message', ['message' => $message->getBody()]);
Check Transport Resolution Debug transport resolution with:
$transport = $this->transportManager->getTransport('twilio_sms');
$this->logger->info('Transport class:', ['class' => get_class($transport)]);
Validate Config Use Symfony’s config validator:
php bin/console config:validate
Custom Transport Resolver
Implement TransportResolverInterface to override default resolution:
class CustomResolver implements TransportResolverInterface {
public function resolve(string $name): Transport {
// Custom logic (e.g., load from DB dynamically)
}
}
Register it in services.yaml:
Ds\Bundle\TransportBundle\Manager\TransportManager:
arguments:
$resolver: '@custom_resolver'
Transport Middleware Add middleware to transports (e.g., retry logic, analytics):
class RetryMiddleware {
public function __invoke(Transport $transport, Message $message) {
try {
$transport->send($message);
} catch (\Exception $e) {
// Retry logic
}
}
}
Wrap transports in TransportManager:
services:
App\Transport\RetryMiddleware:
tags: [ds_transport.middleware]
Custom Message Types
Extend the Message class for domain-specific needs:
class EmailMessage extends Message {
public function __construct(
string $to,
string $subject,
string $body,
array $attachments = []
) {
parent::__construct($to, $body);
$this->subject = $subject;
$this->attachments = $attachments;
}
}
Update transports to handle the new type.
Async Transport Queue Use Symfony Messenger with a queue transport:
# config/packages/messenger.yaml
transports:
async:
dsn: 'doctrine://default'
retry_strategy:
max_retries: 3
Dispatch messages:
$this->message
How can I help you explore Laravel packages today?