Playing with form API

Drupal Summer 2017

Register

name

Samuel Solís

nick

@estoyausente

company

Bodeboca

http://www.bodeboca.com

submit

Some background

D6 Form

function form_example_tutorial_6(&$form_state) {
  $form['description'] = array(
    '#type' => 'item',
    '#title' => t('A form with a validation handler'),
  );
  $form['data'] = array(
    '#type' => 'fieldset',
    '#title' => t('Data'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['data']['name'] = array(
    '#type' => 'textfield',
    '#title' => t('First name'),
    '#required' => TRUE,
    '#default_value' => "First name",
    '#description' => "Please enter your first name.",
    '#size' => 20,
    '#maxlength' => 20,
  );
  $form['data']['year_of_birth'] = array(
    '#type' => 'textfield',
    '#title' => "Year of birth",
    '#description' => 'Format is "YYYY"',
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
  );
  return $form;
}

D6 Form

function form_example_tutorial_6_ahah(&$form_state) {

  $initial_markup = '<div>' 
       . t('This box will be replaced') 
       . '</div>';

  $form['box'] = array(
    '#type' => 'markup',
    '#prefix' => '<div id="box">',
    '#suffix' => '</div>',
    '#value' => $initial_markup,
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#ahah' => array(
      'path' => 'examples/ahah_example/simplest_ahah/callback',
      'wrapper' => 'box',
    ),
    '#value' => t('Click Me to change box color'),
  );

  return $form;
}

D7 Form

function form_example_tutorial_7 ($form, &$form_state) {
  $form['drupal_summer'] = array(
    '#type' => 'radios',
    '#options' => array(
      'go_to_the_beach'   => t('To the beach'),
      'go_this_session' => t('To this awesome session'),
      'hangover' => t('Sorry, I have a huge hangover'),
    ),
    '#title' => t('Where do you want to go this Saturday Morning?'),
  );

  $form['learn_something'] = array(
    '#type' => 'textfield',
    '#title' => t('What do you want to learn?'),
    '#states' => array(
      'visible' => array(
        ':input[name="drupal_summer"]' => array('value' => 'go_this_session'),
      ),
    ),
  );
}

D7 Form

//Exists in D6 too
function _form_example_7_steps() {
  return array(
    1 => array(
      'form' => 'form_example_wizard_7_1',
    ),
    2 => array(
      'form' => 'form_example_wizard_7_2',
    ),
  );
}

function form_example_wizard($form, &$form_state) {

  if (empty($form_state['step'])) {
    $form_state['step'] = 1;
    $form_state['step_information'] = _form_example_steps();
  }
  $step = &$form_state['step'];
  drupal_set_title(t('Extensible Wizard: Step @step', array('@step' => $step)));
  $form = $form_state['step_information'][$step]['form']($form, $form_state);
  if ($step > 1) {
    $form['prev'] = array(
      '#type' => 'submit',
      '#value' => t('Previous'),
      '#name' => 'prev',
      '#submit' => array('form_example_wizard_previous_submit'),
      '#limit_validation_errors' => array(),
    );
  }

  if ($step < count($form_state['step_information'])) {
    $form['next'] = array(
      '#type' => 'submit',
      '#value' => t('Next'),
      '#name' => 'next',
      '#submit' => array('form_example_wizard_next_submit'),
    );
  }
  else {
    $form['finish'] = array(
      '#type' => 'submit',
      '#value' => t('Finish'),
    );
  }

  // Include each validation function defined for the different steps.
  if (function_exists($form_state['step_information'][$step]['form'] . '_validate')) {
    $form['next']['#validate'] = array($form_state['step_information'][$step]['form'] . '_validate');
  }

  return $form;
}

D7 Form

//Exists in D6 too
function _form_example_element_7_info() {
  $types['form_beach'] = array(
    '#input' => TRUE ,
    '#process' => array('form_beach_process'),
    '#element_validate' => array('form_beach_validate'),
    '#autocomplete_path' => FALSE,
    '#value_callback'   => 'form_beach_value',
    '#default_value' => array(
      'extension' => '',
      'location' => '',
      'water_temperature' => '',
    ),
    '#theme_wrappers' => array('form_beach_form_element'),
  );
  return $types;
}

That's all

And what about D8?

Same concept but ...

  • New types
    • HTML5 types
    • "Drupal" types
  • POO developed
  • Ajax more powerful

New #types

Special text #types

  • tel
  • email
  • url
  • search
  • entity_autocomplete

Number

Range

Weight

Color

Date

Some grouping types

  • vertical_tabs
  • dropbutton
  • operations
  • details
  • datelist
  • ...

Defining forms

FormBase

namespace Drupal\fapi_example\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class BuildDemo extends FormBase {
    
  public function getFormId() {
    return 'fapi_example_simple_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {}
  public function validateForm(array &$form, FormStateInterface $form_state) {}
  public function submitForm(array &$form, FormStateInterface $form_state) {}

}

ConfigFormBase

namespace Drupal\summer\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

class SummerConfigurationForm extends ConfigFormBase {

  public function getFormId() {
    return 'your_module_admin_settings';
  }
  
  protected function getEditableConfigNames() {
    return [
      'summer.settings',
    ];
  }
 
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('summer.settings');
    $form['summer_config'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Set why this summer is awesome'),
      '#default_value' => $config->get('summer_config'),
    );
    return parent::buildForm($form, $form_state);
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $values = $form_state->getValues();
    $this->config('summer.settings')
      ->set('summer_config', $values['summer_config'])
      ->save();
  }

  

ConfirmFormBase

namespace Drupal\ban\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;

class ConfirmParty extends ConfirmFormBase {

  public function getFormId() {
    return 'confirm_party_form';
  }

  public function getQuestion() {
    return $this->t('Are you sure you want to go to the party this nigth?');
  }

  public function getConfirmText() {
    return $this->t('Yes, give me some beers');
  }

  public function getCancelUrl() {
    return new Url('party.at_home');
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    return parent::buildForm($form, $form_state);
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    //Go to party
  }
}

Validating Forms

validateForm()

  public function validateForm(array &$form, FormStateInterface $form_state) {
    $rings = $form_state->getValue('rings_number');
    if ($rings > 1) {
      // Set an error for the form element with a key of "rings_number".
      $form_state->setErrorByName(
        'rings_number', 
        $this->t('This isn\'t the One Ring.')
      );
    }

Validate entities
!=
Validate forms

Entity Form

Alter entity

Validation

Entity

Save entity

Validation

Form

Load entity

Submitting Forms / Processing Form Data

submitForm()

  public function submitForm(array &$form, FormStateInterface $form_state) {
    /*
     * This would normally be replaced by code that actually does something
     * with the value.
     */
    $title = $form_state->getValue('title');
    drupal_set_message(
        $this->t('You specified a title of %title.', 
        ['%title' => $title])
    );
  }

Integrate the form in a request

Routing

fapi_example.simple_form:
  path: 'examples/fapi-example/simple-form'
  defaults:
    _form:  '\Drupal\fapi_example\Form\SimpleForm'
    _title: 'Simple Form'
  requirements:
    _permission: 'access content'

Retrieving a form outside of routes

Retrieving a form

$extra = '612-123-4567';
$form = \Drupal::formBuilder()
            ->getForm('Drupal\mymodule\Form\ExampleForm', $extra);

...

public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL)
  $form['phone_number'] = array(
    '#type' => 'tel',
    '#title' => $this->t('Your phone number'),
    '#value' => $extra,
  );
  return $form;
}

Altering a form

form_alter

<?php
/**
 * Implements hook_form_FORM_ID_alter().
 */
function example2_form_example_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $form['important_question']['#description'] = 
      t('Why did not we call the eagles at the beginning of the movie?');
}

States

#states

$form['drink_wine'] = [
      '#type' => 'checkbox',
      '#title' => 'Do you like wine?',
    ];

    $form['wine_type'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => 'accommodation',
      ],
      '#states' => [
        'invisible' => [
          'input[name="drink_wine"]' => ['checked' => FALSE],
        ],
      ],
    ];

    $form['wine_type']['type'] = [
      '#type' => 'radios',
      '#options' => [
        'white' => $this->t('White wine'),
        'red' => $this->t('Red wine'),
        'sparkling ' => $this->t('Sparkling wine'),
      ],
      '#title' => t('Wine type'),
    ];

    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    return $form;

#states

Ajax

#ajax

public function buildForm(array $form, FormStateInterface $form_state) {
    $form['temperature'] = [
      '#title' => $this->t('Temperature'),
      '#type' => 'select',
      '#options' => $this->getColorTemperatures(),
      '#empty_option' => $this->t('- Select a color temperature -'),
      '#ajax' => [
        // Could also use [get_class($this), 'updateColor'].
        'callback' => '::updateColor',
        'wrapper' => 'color-wrapper',
      ],
    ];
    $form['color_wrapper'] = [
      '#type' => 'container',
      '#attributes' => ['id' => 'color-wrapper'],
    ];

    $temperature = $form_state->getValue('temperature');
    if (!empty($temperature)) {
      $form['color_wrapper']['color'] = [
        '#type' => 'select',
        '#title' => $this->t('Color'),
        '#options' => $this->getColorsByTemperature($temperature),
      ];
    }

    $form['actions'] = [
      '#type' => 'actions',
      'submit' => [
        '#type' => 'submit',
        '#value' => $this->t('Submit'),
      ],
    ];

    return $form;
  }

#ajax

public function updateColor(array $form, FormStateInterface $form_state) {
    return $form['color_wrapper'];
  }

  protected function getColorsByTemperature($temperature) {
    return $this->getColors()[$temperature]['colors'];
  }

  protected function getColorTemperatures() {
    return array_map(function ($color_data) {
      return $color_data['name'];
    }, $this->getColors());
  }

  protected function getColors() {
    return [
      'warm' => [
        'name' => $this->t('Warm'),
        'colors' => [
          'red' => $this->t('Red'),
          'orange' => $this->t('Orange'),
          'yellow' => $this->t('Yellow'),
        ],
      ],
      'cool' => [
        'name' => $this->t('Cool'),
        'colors' => [
          'blue' => $this->t('Blue'),
          'purple' => $this->t('Purple'),
          'green' => $this->t('Green'),
        ],
      ],
    ];
  }

#ajax

Ajax explained

Ajax anatomy

'#ajax' => [
  'callback' => 'Drupal\module\Type\ClassName::Ajaxmethod', //path or method
  'event' => 'keyup', //The JavaScript event to respond to
  'wrapper' => 'aValidId',
  'method' => 'replaceWith', //'replaceWith', 'append', 'prepend' ...
  'effect' => 'fade',
  'progress' => array(
    'type' => 'throbber',
    'message' => NULL,
  ),
];

Response

public function Ajaxmethod() {
    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand(
      '#edit-date-format-suffix',
      '<small id="edit-date-format-suffix">' . $format . '</small>'));
    return $response;
}

AjaxCommands

  • OpenDialogCommand
  • CloseDialogCommand
  • AjaxResponse
  • ReplaceCommand
  • ...

https://api.drupal.org/api/drupal/core!core.api.php/group/ajax/8.2.x

Documentation

  • https://api.drupal.org/api/drupal/elements/8.2.x
  • https://www.drupal.org/docs/8/api/form-api/introduction-to-form-api
  • https://www.drupal.org/docs/8/api/form-api/configformbase-with-simple-configuration-api
  • https://www.drupal.org/project/examples

Form API is your friend!

name

Samuel Solís

nick

@estoyausente

company

Bodeboca

http://www.bodeboca.com

Playing whit Form API in D8

By Samuel Solís

Playing whit Form API in D8

  • 1,734