Ext.Direct i Zend – wprowadzenie

Ext.Direct i Zend – wprowadzenie

Jeżeli nie wiesz czym jest Ext.Direct i jakie możliwości nam daje jego użycie w swoich aplikacjach zapraszam do przejrzenia wpisów na oficjalnej stronie Sencha. W tym wpisie natomiast zajmiemy się omówieniem jego zastosowania z pomocą PHP-owskiego frameworka jakim jest Zend.

Najpopularniejsze protokoły komunikacji

W każdej webowej aplikacji potrzebujemy narzędzia/platformy, która pozwoli nam na łatwą i szybką komunikację z bazą danych. Najczęściej wykorzystywane sposoby to serwery typu SOAP lub JSON. Ten pierwszy komunikuje się za pomocą XML’i, drugi używa naturalnego javascriptowego formatu JSON (JavaScript Object Notation). Ciężko rozstrzygnąć który ze sposobów jest lepszy, oba mają pewne wady i zalety. SOAP serwer umożliwia nam walidację danych zaraz po odebraniu wiadomości, dzięki temu wiemy, że XML który wysłaliśmy jest poprawny i zwróci nam odpowiednie informacje. W przypadku serwera JSON-owego o taką walidację musimy zadbać sami. Przewagą jest natomiast rozmiar informacji. XML-e często są przerostem formy nad treścią. Obiekt JSON jest lekki i zawiera tylko to co nas interesuje. Dodatkowo przetworzenie go i zwrócenie w odpowiedniej formie również jest procesem mniej czasochłonnym.

Myślę, że te parę słów wstępu i Ciebie przekonało aby do połączeń typu client->serwer posłużyć się komunikacją opartą na formacie JSON.

CRUD

Zanim jeszcze zagłębimy się w ćwiczenia praktyczne, dobrze było by wyjaśnić sobie czym jest CRUD czyli jedna z konwencji zarządzania danymi.
CRUD w rozszerzeniu create (tworzyć), read (odczytać), update (aktualizować), destroy(niszczyć) odwzorowywuje podstawowy przepływ informacji i czynności którym podlegają dane. Oczywiście nic nie stoi na przeszkodzić, aby zaimplementować znacznie szerszy wachlarz metod, jednak CRUD to coś co posiada każdy obiekt. Gdzie tkwi największa siła takiego rozwiązania? Przede wszystkim automatyzacja. Bazując na jednym szablonie oraz np definicji schemy jesteśmy w stanie wygenerować lwią cześć pracy, którą inaczej musielibyśmy wykonać ręcznie.

Ext.Direct i ZendFramework

W tutorialu jak używać Direct’a  posłużymy się paczką która znajduje się na naszym githubie:

Ściągnij pliki z repo

Gdy będziemy mieli już pliki na dysku, nie zapomnijmy o skonfigurowaniu vhost’ów.
W aplikacji, tak jak i często w życiu, aby pogodzić dwie “zwaśnione” strony potrzebny jest mediator. W przypadku Ext.Direct’a kimś/czymś takim jest klasa provider’a, która odpowiada za odczytanie mapy metod przesłanych z serwera oraz późniejszą komunikacje pomiędzy końcówkami.

zfp

Pierwszym krokiem jaki musimy podjąć jest rejestracja naszej klasy odpowiadającej za komunikację w obiekcie menadżera Ext.Direct:

Ext.require(
    [
        'Ext.direct.*',
        'MyApp.lib.direct.ZendFrameworkProvider'
    ], function () {
        Ext.direct.Manager.addProvider(
            //Ext.app.REMOTING_API
            {
                'type': 'zf',
                'url': Ext.app.JSONRPC_API.target,
                'actions': Ext.app.JSONRPC_API.services,
                'format': 'json'
            });
    });

Aby to zrobić użyjemy wbudowanego loadera podając jako argumenty tablice z niezbędnymi klasami do załadowania (linijka 3,4) oraz callback’iem który ma się wykonać po ich wczytaniu. To właśnie tam dodajemy nasz provider do zbioru aktywnych zasobów.
W gwoli wyjaśnień w linijce 9 znajduję się skrótowo zapisany typ, który w klasie ZendFrameworkProvider’a odpowiada aliasowi (Ext JS sam doda przedrostek “direct” oraz końcówkę “provider“).
Linijka 10 definiuje adres końcówki na którą wysyłane będą dane. W naszym przypadku wykorzystaliśmy odpowiednią zmienną, która zawiera adres url. O tym gdzie i jak została zadeklarowana napiszę parę linijek niżej.
Linijka 11 to obiekt ze zbiorem dostępnych metod po stronie serwera, ich definicja tak jak definicja powyższego adresu znajduje się w specjalnie przygotowanej mapie.

Przedstawiony kod znajdziemy w pliku app.js:

zfp2

Tak jak już wspomniałem, aby sprawnie komunikować się z backendem Ext JS potrzebuje swoistej mapy czyli spisu metod i argumentów które przyjmują. W naszym przypadku jest ona automatycznie konstruowana poprzez JSON serwer. Pliki odpowiedzialne za jej generowanie znajdują się tutaj:

zfp3

Na załączonym screen’ie widzimy dwa pliki, które nie różnią się ostatecznie wyświetlaną zawartością, jednakże różnią się typem i budową. Plik php jest przydatny podczas procesu pisania aplikacji, ponieważ dynamicznie skanuje istniejące klasy i generuje z nich mapę. Ta na stałe zapisywana jest do pliku javascriptowego, którego możemy później użyć przy budowaniu aplikacji lub bezpośrednio w środowisku produkcyjnym.

Poniżej przykładowa mapa:

Ext.define('Ext.app.JSONRPC_API', { // nazwa klasy
    "transport": "POST", // sposób w jaki przesyłamy dane
    "envelope": "JSON-RPC-2.0", // wersja wykorzystywanego protokołu komunikacji
    "contentType": "application\/json", // deklaracja typu odpowiedzi
    "SMDVersion": "2.0", // standard zaimplementowanego mechanizmu do budowania mapy
    "target": "\/api\/json\/1.0\/jsonrpc.php", //nasza końcówka do komunikacji
    "services": { // deklaracja usług, dostępnych metod
        "Game.Player.read": { // nazwa obiektu reprezentującego metodę "read" po stronie klienta
            "envelope": "JSON-RPC-2.0",
            "transport": "POST",
            "parameters": [ // argumenty jakie przyjmuje funkcja
                {
                    "type": "array",
                    "name": "params",
                    "optional": false
                }
            ],
            "returns": "array"
        },
        "Game.Player.create": { // analogicznie jak w przypadku pierwszej metody
            "envelope": "JSON-RPC-2.0",
            "transport": "POST",
            "parameters": [
                {
                    "type": "array",
                    "name": "params",
                    "optional": false
                }
            ],
            "returns": "array"
        },
        "Game.Player.update": {
            "envelope": "JSON-RPC-2.0",
            "transport": "POST",
            "parameters": [
                {
                    "type": "array",
                    "name": "params",
                    "optional": false
                }
            ],
            "returns": "array"
        },
        "Game.Player.destroy": {
            "envelope": "JSON-RPC-2.0",
            "transport": "POST",
            "parameters": [
                {
                    "type": "array",
                    "name": "params",
                    "optional": false
                }
            ],
            "returns": "array"
        }
    },
    "singleton": true
});

Jak widać załączony kod to nic innego jak definicja statycznej klasy Ext JS. Jako, że ilość kodu jest znacząca wyjaśnienia zostały zawarte w komentarzach.
Aby przypadkiem po przeładowaniu aplikacji nie ujrzeć długiej listy błędów warto pamiętać, aby powyższy plik załączyć przed definicją aplikacji. Gdzie dokładnie powinien zostać umieszczony możemy sprawdzić w pliku index.html (katalog /public).

Ok, wiemy już jak zainicjować niezbędne biblioteki, przejdźmy zatem do analizy metod, które posłużą nam do komunikacji z klientem. Te znajdują się w folderze pokazanym poniżej:

zfp4

Zawartość przykładowego pliku nie powinna stanowić dla nas problemu w rozszyfrowaniu. Warunkiem poprawności jest zawarcie metody publicznej oraz dostarczenie odpowiedniego bloku dokumentacji. Zawiera ona opisy parametrów oraz zwracanego typu. Metoda “read” poniżej:

/**
     * @param array $params
     * @return array
     */
    public function read($params)
    { // metoda zwracająca listę obiektów typu Player
        return array(
            'success'=>true,
            'Player'=>array(
                array(
                    'id' => 0,
                    'name' => 'Sebastian',
                    'surname' => 'Widelak',
                    'age' => 24,
                    'nickname' => 'mkalafior'
                ),
                array(
                    'id' => 1,
                    'name' => 'Gal',
                    'surname' => 'Anonim',
                    'age' => 999,
                    'nickname' => 'anonymous'
                )
            )
        );
    }

Ważna jest też struktura zwracanych danych. Zmienną kluczem jest tutaj “success“, to od jej wartości (true/false) będzie zależeć czy Ext.Direct zinterpretuje odpowiedź jako poprawną czy błędną. Warto zanotować, że dostępna w repozytorium implementacja serwera, dostarcza również mechanizm wyłapywania wyjątków, tak aby odpowiedź została zwrócona nawet w przypadku wewnętrznego błędu. Jeżeli chodzi już stricte o dane, każdy ich zestaw podpinamy pod klucz opisujący konkretny model (w naszym przypadku “Player“). Gdy umieścimy pod nim tablice asocjacyjną po stronie klienta otrzymamy obiekt, jeżeli będzie to tablica tablic, otrzymamy listę obiektów.

Ostatnią czynnością niezbędną do zrozumienia działania Direct’a jest przyjrzenie się modelom po stronie klienta. W Ext JS każda tabela/mapper po stronie serwera ma swoją reprezentację w postaci odpowiedniego modelu po stronie klienta. Dla “Player’a” będzie on wyglądał następująco:

Ext.define('MyApp.model.Player', { //nazwa modelu
    extend: 'Ext.data.Model',
    fields: [ //definicja pól
        { name: 'id', type: 'integer' },
        { name: 'name', type: 'string' },
        { name: 'surname', type: 'string' },
        { name: 'age', type: 'integer' },
        { name: 'nickname', type: 'string' }
    ],
    proxy : { // najbardziej interesująca dla nas część
        type : 'direct', // konfigurujemy proxy, a jako typ ustawiamy 'direct'
        api : { // definiujemy api, przyporządkowując metody serwera do
                // odpowiadających im metod po stronie klienta
                // dzieki CRUD-owi mapowanie jest jasne i czytelne
            read : Game.Player.read,
            destroy : Game.Player.destroy,
            create : Game.Player.create,
            update : Game.Player.update
        },
        // definiujemy reader'a
        reader : {
            // ustalilismy, że nasze dane będą w formacie json
            type : 'json',
            // a kluczem danych w zwracanym obiekcie będzie nazwa obiektu
            root: 'Player'
        }
    }
});

Komentarze jak poprzednio w kodzie.

Tak skonfigurowana aplikacja pozwoli nam w pełni wykorzystać potencjał opisywaneo rozwiązania. Oczywiście to co przedstawiłem powyżej to nie całe możliwości Direct’a. Szczegóły postaram się przekazać w następnym, bardziej praktycznym artykule.

Komentarze:

Zostaw odpowiedź

Proszę wypełnij wymagane pola aby dodać komentarz.


Drag circle to the rectangle on the right!

Wyślij