Po niezbyt owocnym początku mojej „przyjaźni” z systemem zarządzania treścią Drupal nadszedł czas zmierzenia się z rozszerzeniami dla CMS’a. W niniejszym artykule przedstawię, jak krok po kroku stworzyć rozszerzenie pełniące rolę REST API.

Szczerze praca na kodzie Drupala, operowanie na własnych szablonach i wszelkie funkcje opierające się na bardziej zaawansowanych zmianach w bazie danych, nie należą do przyjemności. Aby przynajmniej na poziomie podstawowym zrozumieć mechanizm (nie tylko w teorii) należy poświęcić mnóstwo cennego czasu. W moim mniemaniu dużo za dużo czasu.

Ale wróćmy do tematu. Zapraszam na mały tutorial tworzenia REST API.

Teoria, a praktyka

Niezbędne informacje można (i należy) odszukać na stronie Drupala – creating custom modules. Znajdziecie tam mnóstwo informacji. Nie mam pojęcia, czy wszystkie, ale na pewno jest to lektura obowiązkowa.

Niezbędne elementy, aby skonstruować moduł REST API:

  1. Katalog rozszerzenia
  2. Metadane
  3. Skórka
  4. Routing
  5. Kontroler

Na wyżej wskazanej stronie, jako źródle informacji można znaleźć zdecydowanie więcej informacji nt. rozszerzeń. Zauważcie, że nasz REST API nie będzie mieć żadnego bloku zarządzania ustawieniami itp., a także nie zajmiemy się w nim szeroko pojętą obsługą prawami dostępu do API – pozostawimy je publiczne. Te i pochodne tematy pozostawiam Wam. Sam jedynie nakieruję, gdzie można zajrzeć, by dowiedzieć się więcej.

 

REST API

Z katalogiem rozszerzenia nie powinno być najmniejszego problemu. Moduł należy utworzyć w jednej z poniższych lokalizacji:

  • /modules/<your-module>   (najczęstsza lokalizacja)
  • /sites/all/modules/<your-module>

Nazwa modułu dla zachowania logiki najlepiej, aby była tożsama z nazwą maszynową, wykorzystywaną przez Drupala. Nazwa maszynowa to nic innego, jak nazwa plików konfiguracyjnych (*.info.yml, *.routing.yml, *.module). Drupal nakłada pewne restrykcje na nazwy maszynowe:

  • musi się rozpoczynać od litery
  • musi zawierać wyłącznie małe litery i podkreślnik (nie może zawierać nic innego, w tym spacji)
  • musi być unikalna względem innych modułów, skórek i profili używanych w CMS’ie
  • musi być inna niż następujące słowa: src, lib, vendor, assets, css, files, images, js, misc, templates, includes, fixtures, Drupal.

Metadane to nic innego, jak plik <nazwa_maszynowa>.info.yml. Stanowi on dane informacyjne, identyfikujące nasze rozszerzenie. Na potrzeby REST API stworzymy: rest_api.info.yml

name: Publiczne API
type: module
description: Pierwsze publiczne API.
package: Api
author: Damian Kosiorowski
core: 8.x
version: '8.x-1.0-alpha'

Nazwa i opis to przyjazne informacje nt. naszego modułu wyświetlane na stronie pozwalającej zarządzać modułami w Drupalu. Pakiet pozwala pozycjonować nasz moduł w określonym bloku rozszerzeń. Więcej informacji tutaj.

Skórka, czyli plik <nazwa_maszynowa>.module jest to plik analogiczny do plików *.theme. Pozwalają one nadzorować haki (ang. hooks) w obrębie rozszerzenia. Na nasze potrzeby plik pozostawiamy pusty. Zainteresowanych szczegółami odsyłam tutaj.

Plik routingu jest już niezbędny w następującej formie (<nazwa_maszynowa>.routing.yml):

rest_api.add_like:
 path: '/rest-api/item/{node}/addLike'
 defaults: { _controller: '\Drupal\rest_api\Controller\RestAPIController::addLike' }
 methods: [POST]
 requirements:
 _access: 'TRUE'
 options:
 no_cache: 'TRUE'

Myślę, że nic tutaj nie trzeba tłumaczyć. Klucze właściwości są bardzo intuicyjne. Jest jednak ich dużo więcej. Wszystkie znajdziecie opisane bardzo szczegółowo na stronie: https://www.drupal.org/docs/8/api/routing-system/structure-of-routes.

W powyższym przykładzie zwróćcie uwagę, że path oznacza ścieżkę w adresie url, której wywołanie spowoduje uruchomienie kodu – funkcji addLike we wskazanym kontrolerze. Oczywiście w tym przypadku wyłącznie poprzez metodę POST.

Takim sposobem dotarliśmy do kluczowego momentu, mianowicie logiki kontrolera – /modules/rest_api/src/Controller/RestAPIController.php:

<?php
/**
 * @file
 * Contains \Drupal\rest_api\Controller\RestAPIController.
 */
namespace Drupal\rest_api\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\node\Entity\Node;

/**
 * Controller routines for rest_api routes.
 */
class RestAPIController extends ControllerBase {

 /**
 * Callback for `/rest-api/item/{node}/addLike` API method.
 */
 public function addLike( Request $request, $node ) {

     $response['result'] = true;
 
     if(!($item = Node::load($node))) {
         $response['message'] = t('Node is not exist.');
         $response['result'] = false;
     }
 
     if($response['result']) {
         $item->field_clicks_counter->value++;
 
         try {
             if($item->save() !== SAVED_UPDATED) {
                 throw new \Exception('Increment unsuccessful');
             }
             $response['counter'] = $item->field_likes->value;
         }
         catch(\Exception $e) {
             $response['message'] = $e->getMessage();
             $response['node'] = $item->toArray();
             $response['result'] = false;
         }
     }
     return new JsonResponse( $response );
 }
}

 

To wszystko. Teraz wystarczy zainstalować rozszerzenie i korzystać.

 

Podsumowując chciałbym nadmienić, że akurat pisanie i korzystanie z rozszerzeń w Drupalu, nie jest aż tak kłopotliwe i można je tolerować o ile dotyczy aplikacji-systemu blogowego. Jeśli aplikacja jest na tyle złożona, że powiązanie jej z typowym blogiem jest trudne do zidentyfikowania to wystarczy mieć doświadczenie w tworzeniu systemów w natywnych frameworkach PHP, by w dużo krótszym czasie ją stworzyć. Ba i dużo lżejszą – piszę o tym dlatego, że to co dzieje się „z tyłu” systemu (tzw. backend) jest w mojej ocenie mocno nieoptymalne dla złożonych systemów. Liczba zapytań do bazy danych, ich sekwencje dla pojedynczego żądania są często bardzo wysokie, nieoptymalne, aby nie powiedzieć, że nie do zaakceptowania … a przecież każdy backendowiec wie, że podstawą jest optymalizacja liczby zapytań i samych zapytań do bazy.

 

Pozdrawiam!