Commit b3301476 authored by catch's avatar catch

Issue #1913084 by tim.plunkett: Introduce a Form interface for building,...

Issue #1913084 by tim.plunkett: Introduce a Form interface for building, validating, and submitting forms.
parent 298db7a0
......@@ -6,6 +6,7 @@
*/
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Database\Database;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Utility\Color;
......@@ -106,8 +107,9 @@
* is not needed (i.e., when initially rendering the form) and is often
* used as a menu callback.
*
* @param $form_id
* The unique string identifying the desired form. If a function with that
* @param \Drupal\Core\Form\FormInterface|string $form_arg
* A form object to use to build the form, or the unique string identifying
* the desired form. If $form_arg is a string and a function with that
* name exists, it is called to build the form array. Modules that need to
* generate the same form (or very similar forms) using different $form_ids
* can implement hook_forms(), which maps different $form_id values to the
......@@ -126,36 +128,23 @@
*
* @see drupal_build_form()
*/
function drupal_get_form($form_id) {
function drupal_get_form($form_arg) {
$form_state = array();
$args = func_get_args();
// Remove $form_id from the arguments.
// Remove $form_arg from the arguments.
array_shift($args);
$form_state['build_info']['args'] = $args;
return drupal_build_form($form_id, $form_state);
}
// Determine the form ID.
if (is_object($form_arg) && $form_arg instanceof FormInterface) {
$form_state['build_info']['callback_object'] = $form_arg;
$form_id = $form_arg->getFormID();
}
else {
$form_id = $form_arg;
}
/**
* Returns a renderable form array using a specific callback.
*
* When using drupal_get_form(), the $form_id or information from hook_forms()
* is used to determine the callback used to build the form. If the callback
* needs to be explicitly specified, or if it is a method, use this function.
*
* @param string $form_id
* The unique string identifying the desired form.
* @param callable $callback
* A callback to be used for building the form.
* @param array $args
* (optional) An array of arguments to pass to the form callback. Defaults to
* an empty array.
*/
function drupal_get_callback_form($form_id, $callback, array $args = array()) {
$form_state = array();
$form_state['build_info']['args'] = $args;
$form_state['build_info']['callback'] = $callback;
return drupal_build_form($form_id, $form_state);
}
......@@ -773,7 +762,13 @@ function drupal_retrieve_form($form_id, &$form_state) {
// If an explicit form builder callback is defined we just use it, otherwise
// we look for a function named after the $form_id.
$callback = !empty($form_state['build_info']['callback']) ? $form_state['build_info']['callback'] : $form_id;
$callback = $form_id;
if (!empty($form_state['build_info']['callback'])) {
$callback = $form_state['build_info']['callback'];
}
elseif (!empty($form_state['build_info']['callback_object'])) {
$callback = array($form_state['build_info']['callback_object'], 'build');
}
// We first check to see if there is a valid form builder callback defined.
// If there is, we simply pass the arguments on to it to get the form.
......@@ -1081,8 +1076,11 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
if (!isset($form['#validate'])) {
// Ensure that modules can rely on #validate being set.
$form['#validate'] = array();
if (isset($form_state['build_info']['callback_object'])) {
$form['#validate'][] = array($form_state['build_info']['callback_object'], 'validate');
}
// Check for a handler specific to $form_id.
if (function_exists($form_id . '_validate')) {
elseif (function_exists($form_id . '_validate')) {
$form['#validate'][] = $form_id . '_validate';
}
// Otherwise check whether this is a shared form and whether there is a
......@@ -1095,8 +1093,11 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
if (!isset($form['#submit'])) {
// Ensure that modules can rely on #submit being set.
$form['#submit'] = array();
if (isset($form_state['build_info']['callback_object'])) {
$form['#submit'][] = array($form_state['build_info']['callback_object'], 'submit');
}
// Check for a handler specific to $form_id.
if (function_exists($form_id . '_submit')) {
elseif (function_exists($form_id . '_submit')) {
$form['#submit'][] = $form_id . '_submit';
}
// Otherwise check whether this is a shared form and whether there is a
......
<?php
/**
* @file
* Contains \Drupal\Core\Form\FormInterface.
*/
namespace Drupal\Core\Form;
/**
* Provides an interface for a Form.
*/
interface FormInterface {
/**
* Returns a unique string identifying the form.
*
* @return string
* The unique string identifying the form.
*/
public function getFormID();
/**
* Form constructor.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*
* @return array
* The form structure.
*/
public function build(array $form, array &$form_state);
/**
* Form validation handler.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function validate(array &$form, array &$form_state);
/**
* Form submission handler.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function submit(array &$form, array &$form_state);
}
......@@ -9,11 +9,12 @@
use Drupal\Core\Config\Entity\ConfigEntityListController;
use Drupal\block\Plugin\Core\Entity\Block;
use Drupal\Core\Form\FormInterface;
/**
* Defines the block list controller.
*/
class BlockListController extends ConfigEntityListController {
class BlockListController extends ConfigEntityListController implements FormInterface {
/**
* The regions containing the blocks.
......@@ -55,7 +56,7 @@ public function render($theme = NULL) {
// If no theme was specified, use the current theme.
$this->theme = $theme ?: $GLOBALS['theme_key'];
return drupal_get_callback_form('block_admin_display_form', array($this, 'form'));
return drupal_get_form($this);
}
/**
......@@ -92,9 +93,18 @@ protected function sort(Block $a, Block $b) {
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'block_admin_display_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::build().
*
* Form constructor for the main block administration form.
*/
public function form($form, &$form_state) {
public function build(array $form, array &$form_state) {
$entities = $this->load();
$form['#attached']['css'][] = drupal_get_path('module', 'block') . '/block.admin.css';
$form['#attached']['library'][] = array('system', 'drupal.tableheader');
......@@ -175,15 +185,23 @@ public function form($form, &$form_state) {
'#type' => 'submit',
'#value' => t('Save blocks'),
'#button_type' => 'primary',
'#submit' => array(array($this, 'submit')),
);
return $form;
}
/**
* Implements \Drupal\Core\Form\FormInterface::validate().
*/
public function validate(array &$form, array &$form_state) {
// No validation.
}
/**
* Implements \Drupal\Core\Form\FormInterface::submit().
*
* Form submission handler for the main block administration form.
*/
public function submit($form, &$form_state) {
public function submit(array &$form, array &$form_state) {
$entities = entity_load_multiple('block', array_keys($form_state['values']['blocks']));
foreach ($entities as $entity_id => $entity) {
$entity->set('weight', $form_state['values']['blocks'][$entity_id]['weight']);
......
......@@ -313,9 +313,7 @@ function field_ui_field_overview($entity_type, $bundle) {
$bundle = field_extract_bundle($entity_type, $bundle);
field_ui_inactive_message($entity_type, $bundle);
$field_overview = new FieldOverview($entity_type, $bundle);
return drupal_get_callback_form('field_ui_field_overview_form', array($field_overview, 'form'), array($entity_type, $bundle));
return drupal_get_form(new FieldOverview($entity_type, $bundle));
}
/**
......@@ -360,9 +358,7 @@ function field_ui_display_overview($entity_type, $bundle, $view_mode) {
$bundle = field_extract_bundle($entity_type, $bundle);
field_ui_inactive_message($entity_type, $bundle);
$display_overview = new DisplayOverview($entity_type, $bundle, $view_mode);
return drupal_get_callback_form('field_ui_display_overview_form', array($display_overview, 'form'), array($entity_type, $bundle, $view_mode));
return drupal_get_form(new DisplayOverview($entity_type, $bundle, $view_mode));
}
/**
......
......@@ -32,19 +32,16 @@ public function getRegions() {
}
/**
* Overrides Drupal\field_ui\OverviewBase::form().
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
*
* @return array
* The array containing the complete form.
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function form(array $form, array &$form_state) {
$form = parent::form($form, $form_state);
public function getFormID() {
return 'field_ui_display_overview_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::build().
*/
public function build(array $form, array &$form_state) {
// Gather type information.
$instances = field_info_instances($this->entity_type, $this->bundle);
$field_types = field_info_field_types();
......@@ -410,14 +407,9 @@ public function form(array $form, array &$form_state) {
}
/**
* Overrides Drupal\field_ui\OverviewBase::submit().
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
* Overrides \Drupal\field_ui\OverviewBase::submit().
*/
public function submit(array $form, array &$form_state) {
public function submit(array &$form, array &$form_state) {
$form_values = $form_state['values'];
$display = entity_get_display($this->entity_type, $this->bundle, $this->view_mode);
......
......@@ -43,19 +43,16 @@ public function getRegions() {
}
/**
* Overrides Drupal\field_ui\OverviewBase::form().
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
*
* @return array
* The array containing the complete form.
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function form(array $form, array &$form_state) {
$form = parent::form($form, $form_state);
public function getFormID() {
return 'field_ui_field_overview_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::build().
*/
public function build(array $form, array &$form_state) {
// When displaying the form, make sure the list of fields is up-to-date.
if (empty($form_state['post'])) {
field_info_cache_clear();
......@@ -423,14 +420,9 @@ public function form(array $form, array &$form_state) {
}
/**
* Overrides Drupal\field_ui\OverviewBase::validate().
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
* Overrides \Drupal\field_ui\OverviewBase::validate().
*/
public function validate(array $form, array &$form_state) {
public function validate(array &$form, array &$form_state) {
$this->validateAddNew($form, $form_state);
$this->validateAddExisting($form, $form_state);
}
......@@ -532,14 +524,9 @@ protected function validateAddExisting(array $form, array &$form_state) {
}
/**
* Overrides Drupal\field_ui\OverviewBase::submit().
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
* Overrides \Drupal\field_ui\OverviewBase::submit().
*/
public function submit(array $form, array &$form_state) {
public function submit(array &$form, array &$form_state) {
$form_values = $form_state['values']['fields'];
$bundle_settings = field_bundle_settings($this->entity_type, $this->bundle);
......
......@@ -7,10 +7,12 @@
namespace Drupal\field_ui;
use Drupal\Core\Form\FormInterface;
/**
* Abstract base class for Field UI overview forms.
*/
abstract class OverviewBase {
abstract class OverviewBase implements FormInterface {
/**
* The name of the entity type.
......@@ -59,43 +61,15 @@ public function __construct($entity_type, $bundle, $view_mode = NULL) {
}
/**
* Creates a field UI overview form.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
*
* @return array
* The array containing the complete form.
*/
public function form(array $form, array &$form_state) {
// Add the validate and submit behavior.
$form['#validate'] = array(array($this, 'validate'));
$form['#submit'] = array(array($this, 'submit'));
return $form;
}
/**
* Validate handler for the field UI overview form.
*
* @param array $form
* The root element or form.
* @param array $form_state
* The state of the form.
* Implements \Drupal\Core\Form\FormInterface::validate().
*/
public function validate(array $form, array &$form_state) {
public function validate(array &$form, array &$form_state) {
}
/**
* Submit handler for the field UI overview form.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* A reference to a keyed array containing the current state of the form.
* Implements \Drupal\Core\Form\FormInterface::submit().
*/
public function submit(array $form, array &$form_state) {
public function submit(array &$form, array &$form_state) {
}
/**
......
......@@ -2,7 +2,7 @@
/**
* @file
* Contains \Drupal\system\Tests\Form\CallbackBuilderTest.
* Contains \Drupal\system\Tests\Form\FormObjectTest.
*/
namespace Drupal\system\Tests\Form;
......@@ -10,9 +10,9 @@
use Drupal\simpletest\WebTestBase;
/**
* Tests form builder callbacks.
* Tests building a form from an object.
*/
class CallbackBuilderTest extends WebTestBase {
class FormObjectTest extends WebTestBase {
/**
* Modules to enable.
......@@ -23,20 +23,23 @@ class CallbackBuilderTest extends WebTestBase {
public static function getInfo() {
return array(
'name' => 'Form builder callbacks',
'description' => 'Tests form builder callbacks.',
'name' => 'Form object tests',
'description' => 'Tests building a form from an object.',
'group' => 'Form API',
);
}
/**
* Tests using a static method to build a form.
* Tests using an object as the form callback.
*/
function testStaticMethodCallback() {
$this->drupalGet('form-test/callback-builder');
$this->assertText('The Callbacks::buildForm() method was used for this form.');
$elements = $this->xpath('//form[@id="form-test-callback-builder-form"]');
$this->assertTrue(!empty($elements), 'The correct form ID was used even when it is not the callback function name.');
function testObjectFormCallback() {
$this->drupalGet('form-test/object-builder');
$this->assertText('The FormTestObject::build() method was used for this form.');
$elements = $this->xpath('//form[@id="form-test-form-test-object"]');
$this->assertTrue(!empty($elements), 'The correct form ID was used.');
$this->drupalPost('form-test/object-builder', NULL, t('Save'));
$this->assertText('The FormTestObject::validate() method was used for this form.');
$this->assertText('The FormTestObject::submit() method was used for this form.');
}
}
......@@ -3284,7 +3284,10 @@ function system_config_form($form, &$form_state) {
// primary submit handler, do that first, using the same logic.
if (!isset($form['#submit'])) {
$form['#submit'] = array();
if (function_exists($form_state['build_info']['form_id'] . '_submit')) {
if (isset($form_state['build_info']['callback_object'])) {
$form['#submit'][] = array($form_state['build_info']['callback_object'], 'submit');
}
elseif (function_exists($form_state['build_info']['form_id'] . '_submit')) {
$form['#submit'][] = $form_state['build_info']['form_id'] . '_submit';
}
elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) {
......
......@@ -6,6 +6,7 @@
*/
use Drupal\form_test\Callbacks;
use Drupal\form_test\FormTestObject;
/**
* Implements hook_menu().
......@@ -25,10 +26,9 @@ function form_test_menu() {
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['form-test/callback-builder'] = array(
'title' => 'Form callback builder test',
'page callback' => 'drupal_get_callback_form',
'page arguments' => array('form_test_callback_builder_form', '\Drupal\form_test\Callbacks::buildForm'),
$items['form-test/object-builder'] = array(
'title' => 'Form object builder test',
'page callback' => 'form_test_object_builder',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
......@@ -368,6 +368,13 @@ function form_test_permission() {
return $perms;
}
/**
* Page callback: Displays a form built from an object.
*/
function form_test_object_builder() {
return drupal_get_form(new FormTestObject());
}
/**
* Form submit handler to return form values as JSON.
*/
......
......@@ -48,12 +48,4 @@ public function validateName(&$element, &$form_state) {
}
}
/**
* Form constructor for the Form callback builder test form.
*/
public static function buildForm($form, &$form_state) {
$form['element'] = array('#markup' => 'The Callbacks::buildForm() method was used for this form.');
return $form;
}
}
<?php
/**
* @file
* Contains \Drupal\form_test\FormTestObject.
*/
namespace Drupal\form_test;
use Drupal\Core\Form\FormInterface;
/**
* Provides a test form object.
*/
class FormTestObject implements FormInterface {
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'form_test_form_test_object';
}
/**
* Implements \Drupal\Core\Form\FormInterface::build().
*/
public function build(array $form, array &$form_state) {
$form['element'] = array('#markup' => 'The FormTestObject::build() method was used for this form.');
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Implements \Drupal\Core\Form\FormInterface::validate().
*/
public function validate(array &$form, array &$form_state) {
drupal_set_message(t('The FormTestObject::validate() method was used for this form.'));
}
/**
* Implements \Drupal\Core\Form\FormInterface::submit().
*/
public function submit(array &$form, array &$form_state) {
drupal_set_message(t('The FormTestObject::submit() method was used for this form.'));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment