Form alapok Drupal 8-ban

mxr576 képe

CsatolmányMéret

demo-8.x.1.0-dev.tar.gz2.13 KB

Az előző blogbejegyzésből megtudhattuk, hogyan is változott meg a modulok alapfelépítése Drupal 8-ban a Drupal 7-hez képest. Úgy döntöttem, a mostani bemutatómhoz is az előző hello modult fogom használni apróbb változtatások mellett, ezzel is bemutatva, hogy egy modulon belül milyen jól külön lehet választani a modul által szolgáltatott funkciókat a D8 új fájlstruktúrájában.

Az előző modulunk új neve demo lett és a routing.yml fájlban használt elnevezésekkel próbáltam egyértelműbbé tenni, hogy mi mit jelöl. Íme az új modul felépítése, amelyben már láthatóak az új fájljaink is, amelyeket ebben a leírásban meg fogunk nézni:

Vágjunk is bele

Ahogy az előző példában is láthattuk, elsőre talán semmi sem tűnik annyira egyszerűnek Drupal 8-ban, mint ahogy az a 7-esben volt előtte. Nem fogok kertelni, elsőre az űrlapok is ilyenek lesznek… A Symfony itt is megváltoztatta az űrlapok készítésének és hívásának módját, természetesen mindezt objektum-orientált alapokon.

Most kétféle űrlapot fogunk megnézni: a SimpleExampleForm-ot, amely egy egyszerű űrlap megvalósítását mutatja be, illetve a ConfigExampleForm-ot, amely olyan űrlapot mutat be, amellyel rendszerszintű beállításokat olvashatunk és menthetünk el (lásd Drupal 7-ben: system_settings_form()).

Szokásos első lépések

Vegyük fel a SimpleExampleForm-ot a routing.yml-be illetve a hook_menu()-be.

demo.module.php

<?php
/**
 * @file
 * Provides hook implementations for the hello module.
 */
 
/**
 * Implements hook_menu().
 */
function demo_menu() {
  $items['demo-form/simple-form-example'] = array(
    'title' => 'Simple form',
    'route_name' => 'formdemo.simple_form_example',
    'weight' => 0,
  );
  return $items;
}

demo.rouiting.yml

formdemo.simple_form_example:
  pattern: 'demo-form/simple-form-example'
  defaults:
    _form: '\Drupal\demo\Form\SimpleExampleForm'
  requirements:
    _permission: 'access content'

Csak a releváns részeket másoltam ki, ugyanis sok új dolog most nincs itt számunkra. Amire felhívnám a figyelmet, hogy _content helyett, most _form -ot használunk a routing.yml-ben.

SimpleExampleForm.php

<?php
/**
 * @file
 * Contains \Drupal\demo\Form\SimpleExampleForm.
 */
 
namespace Drupal\demo\Form;
 
use Drupal\Core\Form\FormInterface;
 
/**
 * Simple form example with validation and submit handler.
 */
class SimpleExampleForm implements FormInterface {
 
  /**
   * {@inheritdoc}
   */
  public function getFormID() {
    return 'simple_example_form';
  }
 
  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, array &$form_state) {
    $form['foo'] = array(
      '#type' => 'select',
      '#title' => t('Foobar'),
      '#default_value' => 'bar',
      '#description' => t('Trust me, "bar" will be the proper answer!'),
      '#options' => array(
        'foo' => t('foo'),
        'bar' => t('bar'),
      ),
      '#required' => TRUE,
    );
    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = array(
      '#name' => 'op',
      '#type' => 'submit',
      '#value' => t('Save'),
      '#button_type' => 'primary',
    );
    $form['actions']['cancel'] = array(
      '#name' => 'op',
      '#type' => 'submit',
      '#value' => t('Cancel'),
    );
    return $form;
  }
 
  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, array &$form_state) {
    if ($form_state['values']['foo'] != 'bar') {
      form_set_error('foo', t('You should choose "bar"!'));
    }
  }
 
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, array &$form_state) {
    if ($form_state['values']['op'] == t('Save')) {
      drupal_set_message(t('The form has been saved.'));
    }
  }
}

Láthatjuk, hogy űrlapok készítéséhez a FormInterface-t kell implementálunk saját osztályunkban. Ez az interfész 4 függvényt szolgáltat számunkra, amelyeket kötelezőek vagyunk felüldefiniálni:

  • getFormID() - saját egyedi nevet kell adnunk (prefixként még használható például modulunk neve is (demo_), amit ebben az egyszerű esetben kihagytam).
  • buildForm() - az űrlapunk leírását tartalmazza, egy Form API tömbbel tér vissza.
  • validateForm() - az űrlapunkhoz tartozó validáció, ugyanaz, mint Drupal 7-ben.
  • submitForm() - az űrlap sikeres elküldésekor történő esemény leírása, ugyanaz, mint Drupal 7-ben.

Megszokhattuk már Drupal 7-ben is, hogy az űrlapunkhoz írhatunk saját függvényeket is, amelyek például kiszámolják az inputként kapott összeg ÁFÁ-val terhelt értékét. Ezeknek régen a .module vagy .inc fájlokban volt a helyük. Volt! Mostantól ezeknek a függvényeknek a helye ugyanabban az osztályban van, amely ezeket felhasználja.

A másik űrlapunk kódja is kísértetiesen hasonlít az előzőekben taglaltakra, hiszen a SystemConfigBase is a FormInterface-t implementálja. Az OOP szemlélet alapjának egyik fő gondolata, hogy ne kódoljuk le mindent újra és újra, dolgozzunk újrafelhasználható komponensekkel. Ezt nyújtja számunkra a SystemConfigBase kiterjesztése, amely olyan űrlapot definiál nekünk előre, amelynek alapból van egy „Beállítások mentése” funkciója. Számunkra egyetlen új és fontos property-je van, amelyet kiemelnék: a $configFactory… de csak a reklám után… azaz nézzük előbb a kódot:

ConfigExampleForm.php

<?php
/**
 * @file
 * Contains \Drupal\demo\Form\ConfigExampleForm.
 */
 
namespace Drupal\demo\Form;
 
use Drupal\system\SystemConfigFormBase;
 
/**
 * Config form example that can read and store important settings.
 */
class ConfigExampleForm extends SystemConfigFormBase {
 
  /**
   * {@inheritdoc}
   */
  public function getFormID() {
    return 'config_example_form';
  }
 
  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, array &$form_state) {
    /**
     * Read default settings from file.
     * @see config/demo.form.settings.yml
     */
    $config = $this->configFactory->get('demo.form.settings');
 
    $form = parent::buildForm($form, $form_state);
    $form['example'] = array(
      '#type' => 'textfield',
      '#title' => t('Example'),
      '#description' => t('Get the default value from demo.form.settings.yml'),
      '#default_value' => $config->get('default'),
      '#required' => TRUE,
    );
    return $form;
  }
 
  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, array &$form_state) {
    parent::validateForm($form, $form_state);
  }
 
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, array &$form_state) {
    parent::submitForm($form, $form_state);
 
    // Save the new value.
    $this->configFactory->get('demo.form.settings')
      ->set('default', $form_state['values']['example'])
      ->save();
  }
}

Oké, megvagyunk, van getFormID()-nk, buildForm()-unk, validate… várjunk csak… mi volt az ott a buildForm()-ban?!

<span style="color: #0000ff;">$config</span> = <span style="color: #0000ff;">$this</span>-<span style="color: #66cc66;">&amp;</span>gt;configFactory-<span style="color: #66cc66;">&amp;</span>gt;get<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'demo.form.settings'</span><span style="color: #66cc66;">&#41;</span>;

Azt mondja, hogy: a $config értéke legyen az ezen objektum configFactory objektumának get paramétere által visszaadott érték (spoiler: ami egy configuration objektum lesz). De mi az a paraméter ott a get-ben?!

Amit ott a get() paraméteréül láthatunk, az egy Symfony settings.yml-re való hivatkozás. A Symfony-val újabb újdonságot kaptunk Drupal 8-ban, mégpedig, hogy a rendszer ilyen settings.yml fájlokkal is konfigurálható lett, ezzel is megkönnyítve a dev/staging/prod site-ok beállításának szinkronban tartását, költöztetését. (Mert mint tudjuk, sajnos nem mindenre jó kedvenc Drupal 7-es Features modulunk.)

Ilyen alapbeállításokat tartalmazó fájlokat tartalmazhatnak moduljaink is, amelyek általános helye a

modul_neve/config/modul_neve.(funkcio).settings.php

Miután bekértük e fájl tartalmát, a benne található változók értékét elérhetjük a $config objektum részeként, illetve mivel ezzel létrejött a rendszeren belül egy hivatkozás az adott változó-érték párosra, ezért rendszer szinten módosíthatjuk is azt (lásd submitForm()).

Az utolsó trükk mára

Szeretném a SimplExampleForm űrlapot egy másik oldalon vagy blokkban is megjeleníteni.

A drupal_get_form() fogja nyújtani a választ minden óhajunkra, ugyanis ennek segítségével bárhol és bármikor megjeleníthetünk egy adott űrlapot. Most már a kontrollerek és routerek mellett nem szükséges a drupal_get_form() használata ahhoz, hogy egy űrlapot egy oldalon megjelenítsünk, azonban blokkok esetében továbbra is hasznos lehet. A példánk a FormController.php-ban található, amelyből a fontosabb részeket emelném ki:

use Drupal\demo\Form\SimpleExampleForm;

A namespace használata, ahol a SimpleExampleForm-unk található.

class FormController implements ControllerInterface {
  public function formDemoDrupalGetFormPage() {
    // Creating a new SimpleExampleForm form object that described in our "Drupal\demo\Form\SimpleExampleForm" namespace.
    return drupal_get_form(new SimpleExampleForm());
  }
}

Illetve új SimpleExampleForm() objektum létrehozása a drupal_get_form() függvényben.

Pár beépített form típus ízelítőnek, amelyeket még kiterjeszthetünk

  1. SystemConfigFormBase
  2. ConfirmFormBase
  3. BulkFormBase
  4. FieldInstanceFormBase
  5. ViewsFormBase

Források

http://getlevelten.com/blog/ian-whitcomb/drupal-8-module-development-part-2-forms

Technológia: Drupal 8MVCForm API