Hello World! Hello Drupal 8! Hello OOP!

mxr576 képe

CsatolmányMéret

hello-8.x.1.0-dev.tar.gz790 byte

Túl vagyunk a Drupal 8 code freeze-en, most már elkezdhetünk dolgozni az eddigi Drupal 7-es moduljaink portolásán. Megszokhattuk már Drupal berkekben, hogy a verziószám váltás nem kevés újdonságot szokott magával hozni… spoiler alert: ez most is így lesz!

Azoknak, akik követik a Drupal 8-cal kapcsolatos híreket, most sok újat nem mondtam, és őszintén szólva ezeknek az újdonságoknak a részletezésébe nem is szeretnék ezen bejegyzés kapcsán belemenni. Amik számunkra most fontosak lesznek, azok az alábbi kulcsszavak: Symfony, OOP, MVC, PSR-0, namespace

De mik is ezek? Mit jelentenek számunkra?

Drupal 8 alapjául felhasználásra került a Symfony framework és túlnyomórészt neki köszönhetjük az összes többi csúnya kifejezést, amit feljebb felsoroltam.

A Symfony egy tisztán objektum orientált (OOP) keretrendszer, amely osztályait névterekbe (namespace), fájljait pedig a  Modell-View-Controller (MVC) struktúra és a PSR-0 szabvány szerint nevezi el és rendezi hierarchiába.

Lássuk mit is jelent ez számunkra a gyakorlatban egy egyszerű Hello Word Drupal 8-as modul megírásakor:

1. Új fájlstruktúrát a modul felépítésében

Igen, ennyire robosztus és mondhatni már-már bonyolult a Drupal 8 mappastruktúrája. Köszönhető ez a PSR-0 szabványnak, amely a névtereknek megfelelően rendezi az osztályainkat tartalmazó fájljainkat a lib mappán belül az alábbi szabály szerint:

\<Vendor Name>\(<Namespace>)*\<Class Name>

A Symfony autoloadere ezen struktúra alapján tölti be automatikusan a szükséges fájlokat (lsd. később).

Miért jó ez? Mert mindig csak azok a fájlok vannak a memóriában, amelyek valóban szükségesek. Kimelendő: az osztályok elkülönítve találhatóak a modulunk többi fájljaitól (YAML fájlok, képek, css, stb.) a lib mappán belül!

Van arra kezdeményezés egyébként, hogy ezt a bonyolult PSR-0 szabványt leváltsák a kevésbé bonyolult PSR-4-re, ezzel kiküszöbölve a most akár végtelennek is tűnő mappahierarchiát.

2. Megújult az .info fájl is

Az eddig megszokott .info fájljaink lecserélésre kerültek Symfonyban is használatos YAML (.yml) fájlokra, és mostantól az új kiterjesztésük .info.yml lett.  Megváltozott a fájlban használt szintaxis is, az alábbiak szerint:

3. …és most jön az igazi fekete mágia…

De lássuk előtte hogyan is nézett ki a kódunk Drupal 7-ben:

<span><strong>hello.module</strong></span>

<?php
/**
 * @file
 * Provides functionality to our demo module.
 */
 
/**
 * Implements hook_menu().
 */
function hello_menu() {
  return array(
    'hello' => array(
      'title' => 'Hello',
      'page callback' => 'hello_page',
      'access callback' => 'user_access',
      'access arguments' => array('access content'),
      'file' => 'hello.pages.inc',
    ),
  );
}

<span><strong>hello.pages.inc</strong></span>

<?php
/**
 * @file
 * Menu callbacks for hook_menu().
 */
 
/**
 * Custom page callback to say "Hello".
 */
function hello_page() {
  return array(
    '#type' => 'markup',
    '#markup' => t('Hello.'),
  );
}

…és ennyi… Elég egyszerű, ugye?

Van egy hook_menu()-nk, ami definiálja a /hello útvonalra az oldal címét, hogy hol keresse a rendszer az adott oldal tartalmát és kik férhetnek hozzá az adott oldalhoz. Illetve van egy hello_page()-ünk, amely pedig magát az oldal tartalmát tartalmazza.

Ehhez képest Drupal 8-ban…

Megmaradt számunkra a hook_menu() ugyancsak, amely leegyszerűsődött, hiszen csak az oldal címét és az útvonalat definiálja, illetve egy bizonyos route_name-et.

Mi is ez a route_name? A route_name alapján tudja a rendszer, hogy a Symfonyból átvett routing.yml-ben milyen néven van definiálva az aktuális útvonal leírása. Mostantól a routing.yml mondja meg, hogy az adott útvonal tartalmát hol keresse a rendszer és milyen jogosultságok szükségesek a tartalom megnézéséhez. Ezen belül a tartalom elérése szintén nem mappákkal és fájlokkal van megadva mostantól, hanem ugyancsak névterekkel.
 
<span><strong>hello.module</strong></span>

<?php
/**
 * @file
 * Provides hook implementations for the hello module.
 */
 
/**
 * Implements hook_menu().
 */
function hello_menu() {
  return array(
    'hello' => array(
      'title' => 'Hello',
      'route_name' => 'hello',
    ),
  );
}

<span><strong>hello.routing.yml</strong></span>
 

hello:
  pattern: '/hello'
  defaults:
    _content: '\Drupal\hello\Controller\HelloController::helloPage'
  requirements:
    _permission: 'access content'

…és volt még valami ott a fenti fájl listában…
 
<span><strong>lib/Drupal/hello/Controller/HelloController.php</strong></span>

<?php
/**
 * @file
 * Contains \Drupal\hello\Controller\HelloController.
 */
 
namespace Drupal\hello\Controller;
 
use Drupal\Core\Controller\ControllerInterface;
 
/**
 * Controller routines for hello routes.
 */
class HelloController {
  /**
   * Description of our page to say "Hello".
   */
  public function helloPage() {
    return array(
      '#type' => 'markup',
      '#markup' => t('Hello.'),
    );
  }
}

Akkor ez mi is?! Ez itt kérem szépen az MVC-ből a C, azaz a Controller, amely a Model és a View réteg között képez egy középréteget. Itt szokás megvalósítani az úgynevezett üzleti logikát. A Controller feladata lekezelnie és reagálnia a különböző URL kérésekre. (Nem, nem a View rétegbe hányunk bele mindenféle csúnya logikai kódot (pl.: .tpl.php) vagy a Model réteg kezeli le, hogy Pista bá nyugdíjas = TRUE, mert 65 év feletti!)

Eddig kerülgettem a forró kását, egészen a fájlstruktúra óta, de most már vizsgáljuk meg részletesen az alábbi sztringet:

Drupal\hello\Controller\HelloController

Ez itt egy komplex névtér, pontosabban al-névterek összessége.

  • Drupal: azaz a 1. pontban említett hasonló sztringből a <Vendor Name>. Ennek használata azt szolgálja, hogy kódunk véletlen se ütközzön a szerveren található bármely más rendszer részét képező kóddal.
  • hello: mielőtt a modul írásához kezdtünk, gondosan ellenőriztük, hogy a miénkkel megegyező nevű projekt ne létezzen a Drupal világon belül, ugyanis nem szeretnénk, ha saját modulunk bármely más azonos nevű modullal ütközne.
  • Controller: feljebb már kifejtésre került jelentősége.
  • HelloController: az utolsó része a névterünknek, amely tartalmazza az osztályt, amelyben az oldalunk definíciója található.
    Ahogy az 1. pontban is láthattuk, a névterek egy az egyben leképezhetőek jól szeparált fájlstruktúrává. A HelloController onnan kapta a nevét, mert nyilvánvalóan megtalálható benne a “hello” és a “Controller” névtér. Ha a modulom több oldalt tartalmazna, akkor azokat valószínűleg további controller osztályokba rendezném, amelyeknek kapnának egy hasonlóan beszédes nevet maguknak.

Látható még a kód elején a Drupal core-ban definiált ControllerInterface implementálása. Ennek kihagyása nem számít hibának, mivel annyira egyszerű, hogy nem hív magával semmilyen más külső kódot. Azonban mégis hiba, ugyanis ha már OOP-ben programozunk, akkor használjuk is ki annak dependency injection funkcióját.

Foglaljuk össze, mi változott a Drupal 8-ban

  • Modulunk kódjai mostantól osztályokba vannak szervezve, nem globálisak. (Kivételek a hook-ok a .module fájlban.) A kódunk objektumorientált, így egyszerűbbé vált robosztus rendszerek készítése, tesztelése és karbantartása. Ráadásul egy halom hasznos funkciót is kaptunk az objektumorientáltsággal (pl. dependency injection).
  • Az osztályaink névterekbe vannak rendezve, amely lehetővé teszi, hogy kódunk se Drupalon belül (modulok), se Drupalon kívül ne ütközzön más kóddal a szerverünkön.
  • A névterekben létrehozhatunk újabb al-névtereket, ezzel egy fastruktúrát létrehozva a névtereinkből.
  • Minden osztály külön fájlba kerül, amelyek helye a fájlrendszerben követi a névterek hierarchiáját.

Források

Drupal 8: Hello OOP, Hello world! | effulgentsia

Drupal 8 Module Development, Part 1: Getting Started | LevelTen Dallas, TX

Technológia: Drupal 8OOPSymfonyMVC