Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

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:

<?php
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:

<?php
class MyForm {
  function
build($form, &$form_state) {
   
// ...
   
$form['#validate'] = array(array($this, 'validate'));
   
$form['#submit'] = array(array($this, '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:

<?php
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:

<?php
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.
 
}
  function
myElementValidator() {
    
// Validate something.
 
}
}
?>
Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

It needs one altering though to be a valid callback.

<?php
class MyForm {
  function
build($form, &$form_state) {
   
// ...
   
$form['#validate'] = array(array($this, 'validate'));
   
$form['#submit'] = array(array($this, 'submit'));
  }
}
?>

I updated the example, thanks.

Is there any similar method to this for form elements using AJAX? For example:

$form['my_element'] = array(
  #type => 'radios',
  #title => t('My AJAX Element'),
  ...
  #ajax => array(
    'callback' => array(array($this, 'my_method')),
    'wrapper' => 'my-wrapper',
  ),
);

Let me rephrase my question...I'm actually curious about using callbacks that are not located in the current class (so not using $this, but some other class object). Is that possible? If so, how might that look?

1. Should the callback methods be public, protected, or private?
2. Must the callback methods be connected to a route in .routing.yml?