Piotr Rybałtowski

Programista PHP, Symfony

PHPProgramowanieZend Framework

REST w ZF2 – metody PUT i DELETE w formularzach

Architektura dostępu do danych REST staje się coraz bardziej popularna w Internecie. Coraz częściej trafiam na serwisy działające w ten sposób, coraz więcej frameworków obsługuje ten wzorzec. W Internecie dużo już napisano na temat samego sposobu implementacji, ale w skrócie chodzi w głównym stopniu o sposobie dostępu dodanych przez protokół HTTP wykorzystując 4 typy (metody) zapytań i odpowiednie formatowanie adresów URI. Zazwyczaj standardowe serwisy korzystają z zapytań GET i POST. REST proponuje wykorzystanie też PUT i DELETE (HTTP definiuje jeszcze kilka kolejnych, jeszcze rzadziej wykorzystywanych).

Najcześciej wykorzystywane i obsługiwane przez frameworki zapytania to (podane na przykładzie zasobu users):

  • GET /users – pobranie listy zasobów (tutaj użytkowników)
  • GET /users/5 – pobranie danych konkretnego zasobu o podanym identifykatorze
  • POST /users – utworzenie nowego zasobu
  • PUT /users/5 – aktulizacja (edycja) konkretnego zasobu
  • DELETE /users/5 – usunięcie konkretnego zasobu

Jak widać zapytania te odpowiadają standardowemu modelowi CRUD (create, read/retrieve, update, delete/destroy) – czynnościom wykonywanym na danych w 99% przypadków działania aplikacji.

Cały czas rozwijany Zend Framework 2 jest tworzony z całkiem niezłą obsługą REST. Przygotowany został specjalny typ kontrolera – RestfulController – w którym znajdziemy abstrakcyjne metody getList, get, create, update i delete odpowiadające wyżej wymienionym RESTowym akcjom. Niestety, problem cały czas stanowią przeglądarki, które niebardzo chcą RESTowe zapytania obsługiwać. A dokładniej wysyłać dane z typem zapytania PUT lub DELETE. Wiele innych frameworków obsługuje takie czy inne sztuczki do pominięcia tego ograniczenia, jednak w ZF2 niczego takiego nie udało mi się znaleźć. Być może zostanie to dodane w przyszłości, ale już teraz poradzenie sobie z tym nie wymaga wiele pracy.

Zapytania PUT i DELETE zmieniają dane w bazie (a PUT dodatkowo przesyła dane do serwera), więc bliżej im do POST niż do GET. Wykorzystamy więc standardowy formularz HTMLowy, ale z dodatkowym ukrytym parametrem _method o wartości metodą, którą chcemy „przemycić”:

<form action="/users/5" method="POST">
<input type="hidden" name="_method" value="PUT">
<!-- tutaj pozostałe elementy formularza -->
</form>

Następnie musimy przechwycić te dane i wskazać klasie żądania nowy sposób działania. Robimy to na zdarzeniu bootstrap na wysokim priorytecie (żeby kod został wykonany jak najwcześniej). Akcję definiujemy w dowolnym module, gdzie najlepiej będzie to pasowało do konkretnej aplikacji, może to być na przykład standardowy moduł Application. Edytujemy plik Module.php z katalogu głównego modułu i wpisujemy w nim metodę init:

public function init()
{
  $events = \Zend\EventManager\StaticEventManager::getInstance();
  $events->attach('bootstrap', 'bootstrap', array($this, 'overrideMethod'), 2000);
}

Jeżeli w konkretnym module metoda już istnieje dopisujemy tylko brakujące linie. Spowoduje to uruchomienie na bardzo wczesnym etapie zdarzenia bootstrap metody overrideMethod z bieżącego obiektu (trzeci parametr metody attach definuje callback). Musimy tę metodę stworzyć (w tej samej klasie Module):

public function overrideMethod(Event $e)
{
    $request = $e->getParam('application')->getRequest();

    if($request->isPost()) {
        switch($request->post()->get('_method')) {
            case Request::METHOD_PUT:
                $request->setMethod(Request::METHOD_PUT);
                $request->setContent(file_get_contents('php://input'));
                break;
            case Request::METHOD_DELETE:
                $request->setMethod(Request::METHOD_DELETE);
                break;
        }
    }
}

Metoda kolejno:

  • pobiera obiekt Request z Eventu (linia 3),
  • sprawdza, czy rządanie jest typu POST (linia 5),
  • testuje zawartość pola _method (linia 6),
  • nadpisuje odpowiednią metodę zapytania (linie 8 i 12),
  • przepisuje dane wejściowe (klasa Request z ZF2 spodziewa się tylko formatu POST, linia 9).

Od tej pory w aplikacji można normalnie używać kontrolerów RestfulControler i ich standardowych akcji łącznie z parametrem $data dla create i update.

Skomentuj lub zadaj pytanie

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.