lukaszstaniszewski.pl

blog programistyczny

lukaszstaniszewski.pl

blog programistyczny

Mercure + Symfony – wprowadzenie

W tym wpisie dowiesz się czym jest i jak można wykorzystać mercure wraz z Symfony. Nie przedstawię Ci tutaj wszystkich możliwości połączenia tych dwóch rzeczy (zresztą jest to niewykonalne) ale będą to dobre podstawy do dalszych działań.

Cały kod wpisu możesz znaleźć [TUTAJ].

Co to mercure

Zapewne nie raz nie dwa spotkałeś się z sytuacją gdy wywołałeś jakąś akcje i po pewnym czasie przyszło powiadomienie np. „Twój post został dodany”.  Aby takie powiadomienie wyświetliło się na twoim ekranie potrzebne jest narzędzie (np. cboden/ratchet) i technologia (np. WebSocket). Połączenie tych rzeczy daje nam możliwość wymiany informacji w czasie rzeczywistym między backend i klientem. Do takich właśnie działań służy temat dzisiejszego wpisu tj. mercure.

Technologią odpowiedzialną za wymianę informacji między backendem a klientem odpowiada ich autorski protokół mercure a dokładniej server sent events. SSE to pół dupleksowy (ang. half duplex) strumień zdarzeń, w którym klient subskrybuje zdarzenia a serwer je wysyła. W przeciwieństwie do wcześniej wspomnianego protokołu WebSocket w SSE klient nie może przesyłać żadnych danych do backendu tj. nie mamy full duplex.

Obrazowo jak to wszystko działa najlepiej widać za pomocą schematu w dokumentacji

mercure schemat
Źródło: https://mercure.rocks/docs/getting-started

Mercure hub

Aby wszystko mogło działać poprawnie będziemy potrzebować huba który będzie zarządzał eventami wysyłanymi do klientów/klienta. Samo postawienie huba jest banalnie proste. Cały docker-compose (do użytku tylko deweloperskiego, konfiguracja produkcyjna jest inna) możesz zobaczyć poniżej.

version: '3.7'

services:
  mercure:
    image: dunglas/mercure
    ports:
      - "9000:80"
    environment:
      SERVER_NAME: ':80'
      MERCURE_PUBLISHER_JWT_KEY: '!publisher_password!'
      MERCURE_SUBSCRIBER_JWT_KEY: '!subscriber_password!'
      MERCURE_EXTRA_DIRECTIVES: |
        cors_origins http://127.0.0.1:8000
    command: /usr/bin/caddy run -config /etc/caddy/Caddyfile.dev
    volumes:
      - mercure_data:/data
      - mercure_config:/config

volumes:
  mercure_data:
    driver: local
  mercure_config:
    driver: local

Publikowanie wiadomości

Do publikacji wiadomości będzie Ci również potrzebna nam paczka przygotowana przez symfony symfony/mercure-bundle. Abyśmy mogli przeprowadzić testy i zobaczyć działanie mercure przyda się również jakiś kontroler, command etc. który będzie wysyłał wiadomości na front aplikacji. Przykład takiego kontrolera możesz znaleźć poniżej.

<?php

declare(strict_types=1);

namespace App\Controller;

use App\DTO\MercureDTO;
use App\Form\MercureFormType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Routing\Annotation\Route;

#[Route('/mercure')]
class MercureController extends AbstractController
{
    #[Route('/publish', methods: ['GET'])]
    public function index(Request $request): Response
    {
        $form = $this->createForm(MercureFormType::class);
        $form->handleRequest($request);

        return $this->render('mercure.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    #[Route('/publish', methods: ['POST'])]
    public function publish(Request $request, HubInterface $hub): Response
    {
        $form = $this->createForm(MercureFormType::class);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /** @var MercureDTO $dto */
            $dto = $form->getData();

            $update = new Update(
                'http://127.0.0.1:8000/product/' . $dto->productId,
                json_encode(['status' => $dto->status->value], JSON_THROW_ON_ERROR)
            );

            $hub->publish($update);
        }

        return $this->forward(sprintf('%s::index', self::class), [
            'request' => $request,
        ]);
    }
}

Jak zapewne zauważyłeś wysyłka eventu jest bardzo prosta. Wystarczy pobrać z kontenera Symfony\Component\Mercure\HubInterface stworzyć obiekt klasy Symfony\Component\Mercure\Update a następnie przekazać go do metody publish. I tak naprawdę to tyle. Wiadomość trafiła do huba a następnie jest przekazywana do subskrybentów klucza który w naszym przypadku to http://127.0.0.1:8000/product/{ID PRODUKTU}.

Odbieranie danych

Tak samo jak w przypadku publikowania wiadomości, mercure jest banalny w odbieraniu ich. Przykład odbierania eventu możesz zobaczyć poniżej.

{% extends 'base.html.twig' %}

{% block body %}
    <div id="event-data"></div>

    <script>
        const eventSource = new EventSource("{{ mercure('http://127.0.0.1:8000/product/' ~ id)|escape('js') }}");
        eventSource.onmessage = event => {
            const data = JSON.parse(event.data);

            console.log(data);

            document.getElementById('event-data').innerText = data.status;
        }
    </script>
{% endblock %}

Celem przykładu było wyświetlenie statusu w konsoli oraz w divie o id event-data.  Jak sam widzisz to nic skomplikowanego, wystarczy że do funkcji twigowejmercure(dostarczanej w ramach paczki symfony/mercure-bundle) przekażemy klucz który chcemy subskrybować a rezultat funkcji tj. link do huba przekażemy doEventSource. Cała dalsza magia już odbywa się po stronie JS’a, my tylko musimy dodać obsługę onmessage.

Inne wsparcia

W ramach wprowadzenia do mercure nie będę przedstawiał Ci wszystkich możliwości integracji tego narzędzia z symfony bo sam wpis stałby się zbyt obszerny jak na pierwsze spotkanie ale chciałbym Ci przynajmniej wspomnieć o możliwościach jaka ta integracja daje.

M.in. możemy razem z api platform stworzyć powiadomienia każdorazowo gdy dojdzie do aktualizacji encji. Dzięki temu użytkownik np. administrator może wiedzieć o każdej nowej szansie sprzedaży, możemy aktualizować live dane klienta itp.
Kolejną bardzo fajną według mnie integracją jest możliwość wysyłania notyfikacji z symfony/notifier czyli wysyłanie różnych powiadomień do użytkownika np. o nowej promocji. Gdy odpowiednio skonfigurujemy notifier możemy za pomocą jednej notyfikacji wysłać event z mercure, sms, email itd.

O tym wszystkim możesz przeczytać [TUTAJ] i [TUTAJ].

Podsumowanie

Mam nadzieję że ten wpis pokazał Ci w jak prosty sposób możesz stworzyć komunikację live z twoim użytkownikiem. Poza samym działaniem mercura mam nadzieję że zapewne zauważyłeś jak bardzo mocno symfony wspiera integracje z mercure.

Zachęcam Cię również do przeczytania mojego wpisu na temat chain of responsibility który również może Ci się przydać wdrażając mercure w swoim projekcie [KLIKNIJ TUTAJ].

źródła

Mercure + Symfony – wprowadzenie
Przewiń na górę