## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require bernard/bernard-bundle
Add to AppKernel.php:
$bundles[] = new Bernard\BernardBundle\BernardBundle();
Configure Driver (e.g., config/packages/bernard.yaml):
bernard:
driver: file
options:
directory: "%kernel.project_dir%/var/bernard"
Tested drivers: file, doctrine, phpamqp, phpredis, ironmq, sqs, pheanstalk.
First Use Case:
Create a receiver service (e.g., src/Message/Handler/SendNewsletterHandler.php):
namespace App\Message\Handler;
use Bernard\MessageHandlerInterface;
class SendNewsletterHandler implements MessageHandlerInterface
{
public function __invoke($message)
{
// Handle message (e.g., send newsletter)
return true;
}
}
Register it in config/services.yaml:
services:
App\Message\Handler\SendNewsletterHandler:
tags:
- { name: bernard.receiver, message: SendNewsletter }
Produce a Message:
php bin/console bernard:produce SendNewsletter '{"to":"user@example.com"}'
Consume Messages (in a separate terminal):
php bin/console bernard:consume --no-debug
Message Production:
bernard:produce for CLI or inject Bernard\MessageProducerInterface in controllers/services:
use Bernard\MessageProducerInterface;
class NewsletterController
{
public function __construct(private MessageProducerInterface $producer) {}
public function send()
{
$this->producer->send(new SendNewsletter('user@example.com'));
}
}
Message Consumption:
supervisord or systemd to run bernard:consume persistently.
Example systemd service (/etc/systemd/system/bernard.service):
[Unit]
Description=Bernard Message Consumer
After=network.target
[Service]
ExecStart=/usr/bin/php /path/to/bin/console bernard:consume --no-debug
User=www-data
Restart=always
[Install]
WantedBy=multi-user.target
Bernard\Middleware\BatchMiddleware).Error Handling:
Bernard\MessageHandlerInterface with exception handling:
public function __invoke($message)
{
try {
// Logic
} catch (\Exception $e) {
// Log or retry (e.g., throw $e or return false)
}
}
bernard:debug to inspect registered receivers and middleware.Middleware Integration:
# config/services.yaml
App\Middleware\LoggingMiddleware:
tags:
- { name: bernard.middleware, priority: 100 }
namespace App\Middleware;
use Bernard\Middleware\MiddlewareInterface;
class LoggingMiddleware implements MiddlewareInterface
{
public function handle($message, callable $next)
{
\Log::info('Processing message', ['message' => $message]);
return $next($message);
}
}
Testing:
MessageProducerInterface in unit tests:
$producer = $this->createMock(MessageProducerInterface::class);
$producer->expects($this->once())->method('send');
$controller = new NewsletterController($producer);
$controller->send();
Bernard\Test\ConsumerTestCase for integration tests.Debug Mode Memory Leaks:
--no-debug for long-running consumers to avoid memory bloat from Symfony’s debug toolbar and profiler.Driver-Specific Quirks:
connection option matches your doctrine.dbal.connections name.logging: false) to avoid RedisException issues.prefetch: 1 to avoid invisibility timeout errors (default is >1).Message Serialization:
Bernard\Serializer\SerializerInterface. For custom types, implement a normalizer:
use Bernard\Serializer\Normalizer\NormalizerInterface;
class CustomNormalizer implements NormalizerInterface
{
public function normalize($object, $format = null, array $context = [])
{
return ['custom' => 'data'];
}
public function denormalize($data, $class, $format = null, array $context = [])
{
return new $class();
}
}
Register it in services.yaml:
App\Serializer\CustomNormalizer:
tags:
- { name: bernard.normalizer }
Receiver Registration:
bernard.receiver are public (public: true in YAML or public: true in PHP config).bernard:debug to verify receivers are registered:
php bin/console bernard:debug
Middleware Order:
tags:
- { name: bernard.middleware, priority: 50 } # Runs after priority 100
Consumer Stuck?:
--verbose for detailed output:
php bin/console bernard:consume --verbose
connection_timeout in SncRedisBundle config.Message Not Processed?:
Performance Issues:
prefetch and monitor visibility timeouts.Bernard\Middleware\BatchMiddleware to reduce database/Redis calls.Custom Drivers:
Bernard\Driver\DriverInterface and register via bernard.driver tag in services.yaml.Dynamic Receivers:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class DynamicReceiverPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$receivers = $this->fetchReceiversFromDatabase();
foreach ($receivers as $serviceId => $message) {
$container->getDefinition($serviceId)
->addTag('bernard.receiver', ['message' => $message]);
}
}
}
Register the pass in BernardBundle’s extension.Async Consumption:
Messenger component alongside Bernard for hybrid async/sync workflows:
# config/packages/messenger.yaml
framework:
messenger:
transports:
bernard: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'App\Message\SendNewsletter': bernard
Monitoring:
Laravel Horizon (via php-amqplib) or Prometheus by extending Bernard’s driver metrics.Bernard\Driver\FileDriver:
use Prometheus\CollectorRegistry;
class MetricsFileDriver extends FileDriver
{
public function __construct(CollectorRegistry $registry)
{
$this->registry = $registry;
}
public function pop()
{
$this->registry->getOrRegisterCounter('bernard_messages_popped')->inc();
return parent::pop();
}
}
Cache Directory:
%kernel.cache_dir% for file driver—it’s cleared on cache:clear. Use var/bernard instead.Symfony 3+:
bernard/bernard-bundle:^2.0 for Symfony 3+ compatibility (v1.x supports Symfony 2.7+).Environment Variables:
%env() for sensitive options (e.g., Redis passwords):
bernard:
driver
How can I help you explore Laravel packages today?