Newer
Older
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityFormController.
namespace Drupal\Core\Entity;
/**
* Base class for entity form controllers.
*/
class EntityFormController implements EntityFormControllerInterface {
/**
* The name of the current operation.
*
* Subclasses may use this to implement different behaviors depending on its
* value.
*
* @var string
*/
protected $operation;
/**
Dries Buytaert
committed
* Constructs an EntityFormController object.
*
* @param string $operation
* The name of the current operation.
*/
public function __construct($operation) {
$this->operation = $operation;
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::build().
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
*/
public function build(array $form, array &$form_state, EntityInterface $entity) {
// During the initial form build, add the entity to the form state for use
// during form building and processing. During a rebuild, use what is in the
// form state.
if (!$this->getEntity($form_state)) {
$this->init($form_state, $entity);
}
// Retrieve the form array using the possibly updated entity in form state.
$entity = $this->getEntity($form_state);
$form = $this->form($form, $form_state, $entity);
// Retrieve and add the form actions array.
$actions = $this->actionsElement($form, $form_state);
if (!empty($actions)) {
$form['actions'] = $actions;
}
return $form;
}
/**
* Initialize the form state and the entity before the first form build.
*/
protected function init(array &$form_state, EntityInterface $entity) {
// Add the controller to the form state so it can be easily accessed by
// module-provided form handlers there.
$form_state['controller'] = $this;
$this->setEntity($entity, $form_state);
$this->prepareEntity($entity);
}
/**
* Returns the actual form array to be built.
*
* @see Drupal\Core\Entity\EntityFormController::build()
*/
public function form(array $form, array &$form_state, EntityInterface $entity) {
Dries Buytaert
committed
// @todo Exploit the Field API to generate the default widgets for the
// entity properties.
$info = $entity->entityInfo();
if (!empty($info['fieldable'])) {
field_attach_form($entity, $form, $form_state, $this->getFormLangcode($form_state));
if (!isset($form['langcode'])) {
// If the form did not specify otherwise, default to keeping the existing
// language of the entity or defaulting to the site default language for
// new entities.
$form['langcode'] = array(
'#type' => 'value',
'#value' => !$entity->isNew() ? $entity->langcode : language_default()->langcode,
);
}
return $form;
}
/**
* Returns the action form element for the current entity form.
*/
protected function actionsElement(array $form, array &$form_state) {
$element = $this->actions($form, $form_state);
// We cannot delete an entity that has not been created yet.
if ($this->getEntity($form_state)->isNew()) {
unset($element['delete']);
}
elseif (isset($element['delete'])) {
// Move the delete action as last one, unless weights are explicitly
// provided.
$delete = $element['delete'];
unset($element['delete']);
$element['delete'] = $delete;
Angie Byron
committed
$element['delete']['#button_type'] = 'danger';
}
if (isset($element['submit'])) {
// Give the primary submit button a #button_type of primary.
$element['submit']['#button_type'] = 'primary';
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
}
$count = 0;
foreach (element_children($element) as $action) {
$element[$action] += array(
'#type' => 'submit',
'#weight' => ++$count * 5,
);
}
if (!empty($element)) {
$element['#type'] = 'actions';
}
return $element;
}
/**
* Returns an array of supported actions for the current entity form.
*/
protected function actions(array $form, array &$form_state) {
return array(
// @todo Rename the action key from submit to save.
'submit' => array(
'#value' => t('Save'),
'#validate' => array(
array($this, 'validate'),
),
'#submit' => array(
array($this, 'submit'),
array($this, 'save'),
),
),
'delete' => array(
'#value' => t('Delete'),
// No need to validate the form when deleting the entity.
'#submit' => array(
array($this, 'delete'),
),
),
// @todo Consider introducing a 'preview' action here, since it is used by
// many entity types.
);
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::validate().
*/
public function validate(array $form, array &$form_state) {
Dries Buytaert
committed
// @todo Exploit the Field API to validate the values submitted for the
// entity properties.
$entity = $this->buildEntity($form, $form_state);
$info = $entity->entityInfo();
if (!empty($info['fieldable'])) {
field_attach_form_validate($entity, $form, $form_state);
}
// @todo Remove this.
// Execute legacy global validation handlers.
unset($form_state['validate_handlers']);
form_execute_handlers('validate', $form, $form_state);
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::submit().
*
* This is the default entity object builder function. It is called before any
* other submit handler to build the new entity object to be passed to the
* following submit handlers. At this point of the form workflow the entity is
* validated and the form state can be updated, this way the subsequently
* invoked handlers can retrieve a regular entity object to act on.
*
* @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.
*/
public function submit(array $form, array &$form_state) {
Dries Buytaert
committed
// Remove button and internal Form API values from submitted values.
form_state_values_clean($form_state);
$this->updateFormLangcode($form_state);
Angie Byron
committed
$this->submitEntityLanguage($form, $form_state);
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
$entity = $this->buildEntity($form, $form_state);
$this->setEntity($entity, $form_state);
return $entity;
}
/**
* Form submission handler for the 'save' action.
*
* @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.
*/
public function save(array $form, array &$form_state) {
// @todo Perform common save operations.
}
/**
* Form submission handler for the 'delete' action.
*
* @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.
*/
public function delete(array $form, array &$form_state) {
// @todo Perform common delete operations.
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::getFormLangcode().
public function getFormLangcode(array $form_state) {
$entity = $this->getEntity($form_state);
Angie Byron
committed
$translations = $entity->getTranslationLanguages();
if (!empty($form_state['langcode'])) {
$langcode = $form_state['langcode'];
}
else {
// If no form langcode was provided we default to the current content
// language and inspect existing translations to find a valid fallback,
// if any.
$langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
$fallback = language_multilingual() ? language_fallback_get_candidates() : array();
while (!empty($langcode) && !isset($translations[$langcode])) {
$langcode = array_shift($fallback);
}
}
// If the site is not multilingual or no translation for the given form
// language is available, fall back to the entity language.
return !empty($langcode) ? $langcode : $entity->language()->langcode;
Angie Byron
committed
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::isDefaultFormLangcode().
Angie Byron
committed
*/
public function isDefaultFormLangcode(array $form_state) {
Angie Byron
committed
return $this->getFormLangcode($form_state) == $this->getEntity($form_state)->language()->langcode;
}
/**
* Updates the form language to reflect any change to the entity language.
Angie Byron
committed
*
* @param array $form_state
* A keyed array containing the current state of the form.
Angie Byron
committed
*/
protected function updateFormLangcode(array $form_state) {
Angie Byron
committed
// Update the form language as it might have changed.
if (isset($form_state['values']['langcode']) && $this->isDefaultFormLangcode($form_state)) {
$form_state['langcode'] = $form_state['values']['langcode'];
}
Angie Byron
committed
/**
* Handle possible entity language changes.
*
* @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.
*/
protected function submitEntityLanguage(array $form, array &$form_state) {
Angie Byron
committed
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
$entity = $this->getEntity($form_state);
$entity_type = $entity->entityType();
if (field_has_translation_handler($entity_type)) {
$form_langcode = $this->getFormLangcode($form_state);
// If we are editing the default language values, we use the submitted
// entity language as the new language for fields to handle any language
// change. Otherwise the current form language is the proper value, since
// in this case it is not supposed to change.
$current_langcode = $entity->language()->langcode == $form_langcode ? $form_state['values']['langcode'] : $form_langcode;
foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
$previous_langcode = $form[$field_name]['#language'];
// Handle a possible language change: new language values are inserted,
// previous ones are deleted.
if ($field['translatable'] && $previous_langcode != $current_langcode) {
$form_state['values'][$field_name][$current_langcode] = $form_state['values'][$field_name][$previous_langcode];
$form_state['values'][$field_name][$previous_langcode] = array();
}
}
}
}
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::buildEntity().
*/
public function buildEntity(array $form, array &$form_state) {
$entity = clone $this->getEntity($form_state);
// @todo Move entity_form_submit_build_entity() here.
Dries Buytaert
committed
// @todo Exploit the Field API to process the submitted entity field.
entity_form_submit_build_entity($entity->entityType(), $entity, $form, $form_state);
return $entity;
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::getEntity().
*/
public function getEntity(array $form_state) {
return isset($form_state['entity']) ? $form_state['entity'] : NULL;
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::setEntity().
*/
public function setEntity(EntityInterface $entity, array &$form_state) {
$form_state['entity'] = $entity;
}
/**
* Prepares the entity object before the form is built first.
*/
protected function prepareEntity(EntityInterface $entity) {
// @todo Perform common prepare operations and add a hook.
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::getOperation().