## Getting Started
### Minimal Setup
1. **Installation**
```bash
composer require symfony/mercure-bundle
Add the bundle to config/bundles.php:
return [
// ...
Symfony\Mercure\Bundle\MercureBundle::class => ['all' => true],
];
Configure .env
Add your Mercure Hub URL (e.g., a public hub like https://mercure.rocks or a self-hosted instance):
MERCURE_URL=https://your-mercure-hub/.well-known/mercure
MERCURE_PUBLIC_URL=https://your-mercure-hub/.well-known/mercure
MERCURE_JWT_SECRET=your-secret-key # Required for private hubs
First Push
Inject MercureUpdatePublisherInterface in a controller or service:
use Symfony\Mercure\Update\MercureUpdatePublisherInterface;
public function publishUpdate(MercureUpdatePublisherInterface $publisher)
{
$publisher->publish(
new Update('https://example.com/updates', 'New update!', new \DateTimeImmutable())
);
}
Subscribe in JavaScript Use the Mercure client to listen for updates:
const eventSource = new EventSource('https://your-mercure-hub/.well-known/mercure?topic=https://example.com/updates');
eventSource.onmessage = (e) => console.log(e.data);
Real-Time Notifications
afterSave in Doctrine events):
$entityManager->getEventManager()->addEventListener(
[YourEntity::class, 'postPersist'],
function ($event) use ($publisher) {
$publisher->publish(new Update(
'https://example.com/updates/' . $event->getObject()->getId(),
'New item created!'
));
}
);
Topic-Based Updates
https://example.com/updates/{resource}) to segment updates by resource/type.$publisher->publish(new Update(
'https://example.com/dashboard/updates',
json_encode(['status' => 'active', 'data' => $newData])
));
Authentication & Private Topics
use Symfony\Mercure\HubInterface;
use Symfony\Mercure\JWT\StaticJWTGenerator;
$hub = $container->get(HubInterface::class);
$jwt = StaticJWTGenerator::createJWT(
['sub' => 'user:123'],
$container->getParameter('mercure.jwt_secret')
);
$hub->publish(new Update(
'https://example.com/private-updates',
'Secret data',
new \DateTimeImmutable(),
$jwt
));
Batching Updates
EventDispatcher to batch multiple updates (e.g., after bulk operations):
$dispatcher->dispatch(new BatchUpdateEvent($updates), BatchUpdateEvent::NAME);
Symfony Messenger Combine with Messenger for async publishing:
$message = new MercureUpdateMessage(
new Update('https://example.com/updates', 'Async update')
);
$bus->dispatch($message);
ReactPHP
For non-HTTP clients (e.g., CLI tools), use the MercureHubClient:
$hub = new MercureHubClient('https://your-mercure-hub');
$hub->subscribe('https://example.com/updates', function ($update) {
// Handle update
});
Caching Responses Cache frequent updates (e.g., with Symfony Cache component) to reduce hub load:
$cache = $container->get('cache.app');
if (!$cache->has('last_update')) {
$publisher->publish(new Update('https://example.com/updates', 'Cached update'));
$cache->set('last_update', true, 3600);
}
Testing
Use MercureTestHub in PHPUnit:
$hub = new MercureTestHub();
$publisher = new MercureUpdatePublisher($hub);
$publisher->publish(new Update('https://example.com/test', 'Test data'));
$this->assertCount(1, $hub->getUpdates());
Twig Extension Registration (Symfony Mercure 0.7+)
symfony/mercure-bundle v0.4.2 fixes a regression where the Twig extension might not register automatically.php bin/console cache:clear
services.yaml:
Symfony\Mercure\Twig\MercureExtension:
tags: ['twig.extension']
JWT Validation
mercure.jwt_secret matches the hub’s configuration. Mismatches will cause 401 Unauthorized errors..env and hub settings.CORS Issues
mercure-hub Docker image) with:
MERCURE_CORS_ALLOWED_ORIGINS=http://localhost:3000,https://your-app.com
* may not work due to Mercure’s security model.Topic URL Encoding
urlencode() or Symfony’s UrlGenerator:
$topic = 'https://example.com/updates/' . urlencode($resourceId);
Connection Limits
https://example.com/user:123/updates) for per-user topics.EventSource Retries
eventSource.onerror = () => {
setTimeout(() => {
eventSource = new EventSource(url);
}, 1000 * Math.pow(2, errorCount++));
};
Hub Logs
config/packages/mercure.yaml:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
jwt: '%env(MERCURE_JWT_SECRET)%'
debug: true # Enable debug logs
var/log/dev.log) for hub communication errors.Network Tab
EventSource connection in Chrome DevTools (Network > WS). Look for:
401 Unauthorized: JWT or URL issues.404 Not Found: Incorrect topic URL or hub misconfiguration.Mercure Hub CLI
curl:
curl -H "Authorization: Bearer YOUR_JWT" https://your-mercure-hub/.well-known/mercure
200 OK with hub metadata.Twig Extension Verification
mercure_hub() or mercure_update() functions are available in your Twig templates. If not, the extension may not be registered.Custom Update Classes
Extend Update to add metadata:
class CustomUpdate extends Update {
public function __construct(string $topic, string $data, \DateTimeImmutable $date, array $metadata = []) {
parent::__construct($topic, $data, $date);
$this->metadata = $metadata;
}
}
Middleware Add processing before publishing (e.g., sanitize data):
$publisher = new MercureUpdatePublisher($hub);
$publisher->addMiddleware(function
How can I help you explore Laravel packages today?