The Form API workflow

  • The Form API
  • Simplified workflow
  • Form API stages
  • Cache / Persistence
  • AJAX requests
  • Complete workflow
  • Key points

The Form API

Form API: What is
  • An abstraction of HTML forms
  • Handles submit, persistence, altering and rendering
  • Security!
  • Handles AJAX in forms
  • Allows alterations
  • Safety!
  • Build custom forms
Form API: How?
  • "form_builder" service handles forms
  • Many classes involved: FormBuilder, FormValidator, FormBase, etc.
  • A class that implements \Drupal\Core\Form\FormInterface
  • Usually extends Drupal\Core\Form\FormBase
  • A form array (usually $form)
  • A form state (usualy $form_state)
The Form Array
  • PHP array
  • Structure and behavior
  • Not exactly a Render Array
  • Available during all Form API stages
  • It is changed during stages
The Form State
  • Usually a Drupal\Core\Form\FormState instance
  • Implements Drupal\Core\Form\FormStateInterface
  • Contains state of the form
    • Inputs
    • Errors
    • Flags
  • Available during all Form API stages
  • It is changed during stages
Notable values
  • form_id: the form (like a class)
  • form_build_id: the specific form build (like an instance)
  • Token: ensures form was previously requested

Simplified Form API workflow diagrams

User requests a form
User requests a form
User submits a form
User submits a form
User submits a form that is rebuilt
Rebuilding loop
Rebuilding loop
Example of form rebuilding
On each request new form element appears
Form responses
Response can be:
  • Markup
  • Redirection
  • A rebuilt form
  • A form with errors
  • Whatever submits decide

Is that simple?

Yes and no
What else?
  • How Drupal handles security?
  • How Drupal keeps track of data between requests?
  • How is AJAX handled?
  • Validation in detail
  • Submission in detail
  • Rebuilding in detail
  • Workflow details

Form API stages

Retrieve and prepare (1/5)

1A - Retrieve
  • Form build function
  • Dynamic elems depends on $form_state
  • On rebuild: form is built again here
1B - Prepare
  • Build id added
  • Security token added for registered users (prevent CSRF)
  • Alter hooks!
  • First place to customize an existing form
Retrieve and prepare
Retrieve and prepare: output
  • A built form
  • All dynamics elements in place
  • All altertions in place
  • This is the unprocessed form

Form API stages

Recursive building (2/5)

Recursive building
  • Goals
    • Get a renderable array
    • Collect input values (defaults or sent by user)
    • Detect triggering button (if any)
    • Detect which validate handler to run
  • Last chance to modify/tweak the form
    • Process callback
    • After build callback
  • Not standard alters!
Recursive building
Process callbacks
  • Called top-bottom
  • Allow for elements to expand to multiple elements
  • More than 100 uses in core
  • You may need, specially for custom elems
  • Example: adding JS conditionally on field value
After build callbacks
  • Called bottom-to-top
  • Less than 10 uses in core
  • Unlikely you need it
  • Example: removing elements after file disk check
Handler detection
  • Button pressed
    if not then
  • Form-level validate
  • Button pressed
    if not then
  • Form-level submit

Form API stages

Validation (3/5)

Form is checked (security and valid values)
  • Check token (prevents CSRF)
  • Validate all elems bottom-to-top
  • Call elem's validate, if any
  • Validate form (button or form handler)
  • Can change $form or $form_state for next rebuild
  • Can enable rebuilding
Validation errors
  • Errors are added to rendered form
  • Submit handlers are skipped
  • Rebuilding is forbidden

Form API stages

Submit (4/5)

Form is executed, running any needed actions
  • Run submit handlers (button or form)
  • Return response, if any
  • Usually a redirect or markup is delivered
  • Can change $form or $form_state for next rebuild
  • Can enable rebuilding

Form API stages

Rebuild (5/5)

Rebuilding allows for complex forms
  • Multi-step forms
  • Dynamic elements (adding, removing, etc)
  • AJAX calls that modifies form
  • Runs building steps (retrieve, prepare and actual building)
  • Changes in $form and $form_state allow form changes when rebuilt

Cache / Persistence

Cache / Persistence
  • Named "cache" but it is persistence + cache
  • Allows for AJAX, mutistep and dynamic elements
  • Usually Drupal takes care of it (auto-enabled)
  • You may need to deal with it in special cases
Cache / Persistence: What?
  • $form and $form_state are saved
  • $form_state only given set of properties (see FormState::getCacheableArray)
  • Use FormState's functions to CRUD data that will be cached/persisted
    • get
    • set
    • has

AJAX requests

AJAX requests
  • They follow the same workflow
  • They return what #ajax_callback returns
  • They need cache/persistence (auto-enabled)
  • They can also return one or more AJAX commands
AJAX requests: How To
  • Element triggers an AJAX request
  • Form is build, validated and executed
  • During submit, some values are changed and rebuild enabled
  • During rebuild, these changed values cause the form to undergo changes (e.g.: more or less elements)
  • #ajax_callback returns changed part of the form, the form or AJAX commands
  • JS layer takes reponse and processes it (e.g: inserts markup into the HMTL)
AJAX requests: Don't
  • Don't change $form during #ajax_callback
  • #ajax_callback is not a submit, it is a result gatherer!
  • Any change should be done during rebuilding

The complete workflow illustration

Not the whole picture
  • Batch and programmed forms are not covered
  • Subforms
  • Details
  • How HTML id are handled
  • Some edge cases
  • Security details
  • More details

Key points

Key points
  • Form's build function and alters for big changes
  • Process and after builds callbacks for adjustments and tweaks
  • Use the rebuilding loop for dynamic forms
  • Use FormSate to store your own variables
  • Use #ajax_callback to return response, not to change the form
  • Check Drupal Examples module!