Newer
Older
<?php
/**
* @file
* Definition of 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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
// entity properties.
$info = $entity->entityInfo();
if (!empty($info['fieldable'])) {
field_attach_form($entity->entityType(), $entity, $form, $form_state, $this->getFormLangcode($form_state));
}
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;
}
$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->entityType(), $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) {
Angie Byron
committed
$this->submitEntityLanguage($form, $form_state);
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
$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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/**
* Implements EntityFormControllerInterface::isDefaultFormLangcode().
*/
public function isDefaultFormLangcode($form_state) {
return $this->getFormLangcode($form_state) == $this->getEntity($form_state)->language()->langcode;
}
/**
* 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) {
// 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'];
}
$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().