⚠️ Work in progress so keep calm. The good news: this is maintained!
- php ^7.4 || ^8.0
- symfony ^4.4 || ^5.0
- mercure
composer require jul6art/push-bundle
Then Download the mercure hub depending on your operating system and install it in the root of your project. For each release, the assets section list operating systems implementations. The folder must contain the mercure bin. Rename this folder mercure.
npm install event-source-polyfill
and import it on client side to make push works on IE and Edge
Go to jwt.io and put your future mercure secret key (default it's !ChangeMe!) in the verify signature textarea and this array in the payload textarea
{
"mercure": {
"publish": []
}
}
Because the array is empty, the Symfony app will only be authorized to publish public updates (see the authorization section of symfony/mercure-bundle for further information).
THen store the generated token in your .env file as MERCURE_JWT_TOKEN parameter
The default token is signed with the secret key: !ChangeMe!
CORS_ALLOWED_ORIGINS is the client URL and port. It can be * or a list of domains ADDR is the server url and 3000 is the port for mercure server
JWT_KEY='!ChangeMe!' ADDR='localhost:3000' ALLOW_ANONYMOUS=1 CORS_ALLOWED_ORIGINS="http://localhost:80" ./mercure/mercure
⚠️ By default, push messages are async so you need to launch a crawler in a terminal to dequeue messages and send it
bin/console messenger:consume async_priority_high --time-limit 600
Server side
/**
* @ApiResource(mercure=true)
*/
class SomeTopic {}
Client side
import {EventSourcePolyfill} from "../polyfills/Polyfills";
export default class MercureProvider {
provide = () => {
const publishUrl = new URL('http://publish.url:3000/hub');
publishUrl.searchParams.append("topic", "/some_topic");
publishUrl.searchParams.append("topic", "/some_topic/{id}");
const es = new EventSourcePolyfill(publishUrl, {
headers: {
'Authorization': 'Bearer ' + YOUR_MERCURE_JWT_TOKEN
}
});
es.onmessage = e => {
const data = JSON.parse(e.data);
const regex = /\/api\/(?<type>\w+)\//gm;
const match = regex.exec(data['@id']);
if (null !== match) {
const event = new CustomEvent(match.groups.type, { "data": data });
document.dispatchEvent(event);
}
};
}
};
// somewhere else
document.addEventListener('...', function() {
// what you need
});
Server side
use Jul6Art\PushBundle\Service\Traits\PusherAwareTrait;
/**
* Class RequestEventSubscriber
*/
class SomeService
{
use PusherAwareTrait;
public function function(): void
{
$this->pusher->push('/some/topic', ['test' => true]);
}
}
push:
async: false
push:
routing:
'PathToSomeAsyncMessage': async_priority_high
Can be async_priority_high or async_priority_low or sync
My Entity
/**
* @ORM\Entity(repositoryClass=MyClassRepository::class)
* @Asyncable(eventClass="App\Event\MyClassEvent")
*/
class MyClass
{
}
My EntityEvent
<?php
namespace App\Event;
use App\Entity\MyClass;
use Jul6Art\CoreBundle\Event\AbstractEvent;
/**
* Class MyClassEvent
*/
class MyClassEvent extends AbstractEvent
{
public const CREATED = 'event.my_class.created';
public const DELETED = 'event.my_class.deleted';
public const EDITED = 'event.my_class.edited';
public const VIEWED = 'event.my_class.viewed';
/**
* @var MyClass
*/
private $myClass;
public function __construct(MyClass $myClass)
{
parent::__construct();
$this->myClass = $myClass;
}
public function getMyClass(): MyClass
{
return $this->myClass;
}
public function setMyClass(MyClass $myClass): MyClassEvent
{
$this->myClass = $myClass;
return $this;
}
}
All actions in listeners who listen these event class consts will be async
You can also specify which doctrine events you want to track
/**
* @ORM\Entity(repositoryClass=MyClassRepository::class)
* @Asyncable(eventClass="App\Event\MyClassEvent", events={"postLoad", "postPersist"})
*/
class MyClass
{
}
Available events are
- postLoad
- postPersist
- postUpdate
- preRemove
The Push Bundle is open-sourced software licensed under the MIT license.
© 2023 dev in the hood