The form state build info supports a new 'callback' key that can be used to explicitly set a form builder callback. Moreover now any callable can be used as a form handler/callback. This allows the new Entity form controllers to work, and can also be used to collect all the required functions for a form into a class more easily. The old syntax is still valid.
Before:
class MyForm {
function build($form, &$form_state) {
// ...
$form['#validate'] = 'my_oo_form_validate';
$form['#submit'] = 'my_oo_form_submit';
return $form;
}
}
function my_oo_form_page(MyForm $object)
return drupal_get_form('my_oo_form', $object);
}
function my_oo_form($form, &$form_state, $object)
$form_state['object'] = $object;
return $object->build($form, $form_state);
}
function my_oo_form_validate($form, &$form_state) {
$form_state['object']->validate($form, $form_state);
}
function my_oo_form_submit($form, &$form_state) {
$form_state['object']->submit($form, $form_state);
}
After:
class MyForm {
function build($form, &$form_state) {
// The ::methodName syntax is automatically expanded to array($this, 'validate').
// This only works for the form object, not partial forms in widgets, plugins or form alters.
$form['#validate'][] = '::validate';
$form['#submit'][] = '::submit';
}
}
function my_oo_form_page(MyForm $object)
$form_state = array();
$form_state['build_info']['callback'] = array($object, 'build');
return drupal_build_form('my_oo_form', $form_state);
}
Additionally, '#element_validate' and '#ajax' callbacks can now defined as methods, as well.
Before:
class MyField {
function build($form, &$form_state) {
$form['foo'] = array(
// ...
'#element_validate' = array('my_element_validator'),
);
$form['bar'] = array(
// ...
'#ajax' => array(
'callback' => 'my_ajax_callback',
'wrapper' => 'foo',
),
);
return $form;
}
}
function my_ajax_callback() {
// Do something fancy.
}
function my_element_validator() {
// Validate something.
}
After:
use Drupal\Core\Form\FormStateInterface;
class MyField {
function build($form, &$form_state) {
$form['foo'] = array(
// ...
'#element_validate' = array(array($this, 'myElementValidator')),
);
$form['bar'] = array(
// ...
'#ajax' => array(
'callback' => array($this, 'myAjaxCallback'),
'wrapper' => 'foo',
),
);
return $form;
}
function myAjaxCallback() {
// Do something fancy.
}
/**
* Validates my element.
*
* @param array $element
* The form element to process.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
* @param array $complete_form
* The complete form structure.
*/
public static function myElementValidator(&$element, FormStateInterface $form_state, &$complete_form) {
// Validate something.
}
}
See core's Url::validateUrl() for an example.
If operating outside of a class context (say in a form alter hook), a fully-qualified class can be specified instead of $this. In this case, it would be 'Drupal\my_module\Path\MyField'.
Comments
Note that #value_callback
Note that #value_callback does not yet support methods. See #2040559: Replace function_exists() with is_callable() in FormBuilder.