draw/doctrine-bus-message-bundle
Symfony bundle that integrates Doctrine with the Messenger/command bus pattern, making it easy to dispatch bus messages from Doctrine entities and listeners. Helps coordinate persistence and message handling with clean separation of concerns.
Bridge Symfony Messenger to Laravel Since this bundle is Symfony-focused, use Symfony Messenger as a microservice or abstract its logic into a Laravel-compatible layer. Start with a Laravel-native alternative:
composer require symfony/messenger laravel-horizon
First Use Case: Dispatch a Message on Entity Save
Replace Doctrine lifecycle events with Laravel’s Model::saved():
// app/Models/User.php
use Illuminate\Database\Eloquent\Model;
use App\Jobs\SendWelcomeEmail;
class User extends Model
{
protected static function booted()
{
static::saved(function ($user) {
SendWelcomeEmail::dispatch($user);
});
}
}
Key Configuration
.env (e.g., QUEUE_CONNECTION=redis).app/Console/Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->job(new SendWelcomeEmail($user))->everyMinute()->withoutOverlapping();
}
Where to Look First
Illuminate\Database\Eloquent\Model observers and Illuminate\Bus\Queueable.Queue::fake() in tests to verify message dispatch.Event-Driven Messaging Pattern
@DispatchMessage annotations on entities.
/**
* @DispatchMessage(event="postPersist", message="App\Message\UserRegistered")
*/
class User {}
class UserObserver {
public function saved(User $user) {
dispatch(new UserRegistered($user));
}
}
Message Handling
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
class UserRegisteredHandler {
public function __invoke(UserRegistered $message) {
// Send email, log analytics, etc.
}
}
class SendWelcomeEmail implements ShouldQueue {
use Dispatchable, InteractsWithQueue;
public function handle() {
Mail::to($this->user->email)->send(new WelcomeEmail());
}
}
Integration with Laravel Services
php artisan horizon
EventServiceProvider:
protected $listen = [
'eloquent.saved: App\Models\User' => [
'App\Listeners\LogUserActivity',
],
];
postUpdate), batch messages to reduce queue load:
// Use Laravel's chunk() for bulk operations
User::chunk(100, function ($users) {
foreach ($users as $user) {
SendUpdateNotification::dispatch($user);
}
});
route() helper or Symfony’s Router to dynamically resolve message handlers based on entity type.public function test_user_creation_dispatches_email()
{
Queue::fake();
User::create(['name' => 'John']);
Queue::assertPushed(SendWelcomeEmail::class);
}
Symfony Dependency Overhead
Transaction Rollback Scenarios
DoctrineTransport with transactional handlers.DB::transaction(function () {
$user = User::create([...]);
SendWelcomeEmail::dispatch($user);
});
Message Serialization Issues
serialize() by default, which may not work with Laravel’s queue payloads (expects json or php serialization). Fix: Customize the serializer in Laravel’s queue config:
'serializers' => [
'php' => Illuminate\Queue\Serializers\PhpSerializer::class,
],
Event Ordering
prePersist → postPersist). In Laravel, ensure observers/jobs respect this order by using priority queues or event ordering in EventServiceProvider.Debugging Queue Jobs
messenger:consume command won’t work in Laravel. Use:
php artisan queue:work
Log::debug('Dispatching message for entity', ['entity' => $entity->id]);
php artisan horizon
php bin/console debug:event-dispatcher
php bin/console messenger:consume async -vv
Custom Message Handlers
MessageHandlerInterface or Laravel’s ShouldQueue to add business logic:
class CustomHandler implements ShouldQueue {
public function handle() {
// Custom logic
}
}
Dynamic Event Mapping
// Laravel example: Dynamic observer registration
Event::listen('eloquent.saved:*', function ($model) {
if ($model instanceof User) {
SendWelcomeEmail::dispatch($model);
}
});
Transport Abstraction
class LaravelMessengerTransport implements TransportInterface {
public function send(Message $message) {
Queue::push(new ProcessMessageJob($message));
}
}
Testing Strategies
Queue::fake();
$user = User::create([...]);
Queue::assertPushed(SendWelcomeEmail::class);
Queue::flush() to process jobs in tests:
Queue::flush();
$this->assertDatabaseHas('notifications', [...]);
lifecycle_callbacks are enabled in config/packages/doctrine.yaml:
orm:
entity_managers:
default:
lifecycle_callbacks:
prePersist: true
# ... other events
config/packages/messenger.yaml:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'App\Message\UserRegistered': async
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
How can I help you explore Laravel packages today?