Tworzenie webowych aplikacji działających w czasie rzeczywistym zazwyczaj wymagało niemałej gimnastyki oraz sporej dozy cierpliwości. Na szczęście z pomocą przychodzi Laravel i socket.io, dzięki którym stworzenie takiej aplikacji to przysłowiowa bułka z masłem. W tym artykule przedstawię krok po kroku przepis na aplikację, która pozwala na wysyłanie wiadomości, które wyświetlane są użytkownikom owej aplikacji od razu, bez konieczności odświeżania strony.

Zanim zaczniemy, musimy przygotować środowisko pracy. W przypadku Laravela idealnie nadaje się do tego Homestead, który ma już wszystko zainstalowane. Na nim będę opierał przykłady zawarte w tym tekście, więc jeśli nie korzystacie z Homestead, upewnijcie się, że zainstalowaliście niezbędne narzędzia.

Przede wszystkim będziemy potrzebować Node.js (wraz z npm) oraz Redisa. Redis nie jest wymagany jeśli korzystamy z pushera.

Do obsługi socketów wykorzystamy biblioteki ioredis oraz socket.io. Zainstalujemy je poleceniem

npm install ioredis socket.io

Polecenie to powinniśmy wykonać w głównym katalogu naszego projektu.

W trakcie instalacji, która może zając trochę czasu, utworzymy serwer. Jest to prosty plik JavaScript, który nazwiemy server.js i umieścimy w głównym katalogu aplikacji. Zawartość pliku można znaleźć pod adresem https://laravel.com/docs/5.2/events#consuming-event-broadcasts w sekcji Redis. Wystarczy, że skopiujecie kod JavaScript z podlinkowanej dokumentacji i wkleicie go do pliku server.js. W rzeczywistej aplikacji należałoby dodać do niego kilka rzeczy, np. autoryzację.

Kolejnym krokiem będzie utworzenie klasy eventu, który będziemy chcieli wysyłać do utworzonego przed chwilą serwera. W tym celu musimy wykonać polecenie

php artisan make:event SomeEvent

Polecenie to utworzy event o nazwie SomeEvent. Aby móc korzystać z socketów, musimy wprowadzić w nim kilka drobnych zmian. Przede wszystkim musimy zaimplementować interface Illuminate\Contracts\Broadcasting\ShouldBroadcast oraz stworzyć metodę broadcastOn (powinna już znajdować się w klasie). Metoda powinna zwracać tablicę kanałów, do których chcemy wysłać wiadomość. Na koniec w konstruktorze odbieramy przesyłane dane i zapisujemy je do publicznej właściwości. Kluczowe jest, aby właściwość była publiczna, ponieważ tylko publiczne właściwości są serializowane jako payload wysyłanego wydarzenia. Dane te będą dostępne dla skryptu JavaScript będącego klientem socketa. Komplety kod klasy wygląda następująco (zamiast tablicy możemy odebrać obiekt, tablica została użyta w celu uproszczenia przykładu).

namespace App\Events;

use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class SomeEvent extends Event implements ShouldBroadcast
{
    use SerializesModels;

    public $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function broadcastOn()
    {
        return ['some-channel'];
    }
}

Następnym krokiem jest napisanie klienta, który będzie otrzymywał wiadomości w czasie rzeczywistym.

<div id="messages"></div>

<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
    var socket = io('http://192.168.10.10:6001');
    socket.on("some-channel:App\\Events\\SomeEvent", function(message){
        $('#messages').append('<p>id: '+message.data.id+', name: '+message.data.name+'</p>');
    });
</script>

Kilka słów wyjaśnienia do powyższego kodu. Adres IP jaki widzicie jest adresem Homesteada. Można go śmiało zastąpić adresem, z którego wy korzystacie. Port w adresie pokrywa się z portem z server.js. Metoda on wywołana na obiekcie socket przyjmuje jako parametr nazwę kanału, z którym chcemy się komunikować oraz nazwę klasy (wraz z przestrzenią nazw) interesującego nas wydarzenia. Zmienna message jest obiektem, gdzie „kluczami” są publiczne właściwości obsługiwanego eventu.

Na sam koniec zostało w pliku konfiguracyjnym broadcasting.php wskazanie Redisa jako domyślnego sterownika oraz uruchomienie serwera poleceniem

node server.js

Teraz, jeśli chcemy wysłać wiadomość do klienta, wystarczy w kodzie PHP wywołać wydarzenie.

event(new SomeEvent(['id' => rand(1, 1000), 'name' => str_random(5)]));

Wysłane dane wyświetlą się u każdego klienta niemal od razu.