Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
9.09% |
2 / 22 |
CRAP | |
11.76% |
12 / 102 |
EntityForm | |
0.00% |
0 / 1 |
|
13.04% |
3 / 23 |
1436.08 | |
11.76% |
12 / 102 |
setOperation | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
getBaseFormId | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
getFormId | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
buildForm | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
init | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
form | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
processForm | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
afterBuild | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
actionsElement | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 15 |
|||
actions | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 15 |
|||
submitForm | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
save | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
buildEntity | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 6 |
|||
copyFormValuesToEntity | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
getEntity | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
setEntity | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getEntityFromRouteMatch | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
prepareEntity | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
prepareInvokeAll | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 7 |
|||
getOperation | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
setModuleHandler | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
setEntityManager | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
setEntityTypeManager | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Entity\EntityForm. | |
*/ | |
namespace Drupal\Core\Entity; | |
use Drupal\Core\Form\FormBase; | |
use Drupal\Core\Extension\ModuleHandlerInterface; | |
use Drupal\Core\Form\FormStateInterface; | |
use Drupal\Core\Render\Element; | |
use Drupal\Core\Routing\RouteMatchInterface; | |
/** | |
* Base class for entity forms. | |
* | |
* @ingroup entity_api | |
*/ | |
class EntityForm extends FormBase implements EntityFormInterface { | |
/** | |
* The name of the current operation. | |
* | |
* Subclasses may use this to implement different behaviors depending on its | |
* value. | |
* | |
* @var string | |
*/ | |
protected $operation; | |
/** | |
* The module handler service. | |
* | |
* @var \Drupal\Core\Extension\ModuleHandlerInterface | |
*/ | |
protected $moduleHandler; | |
/** | |
* The entity manager. | |
* | |
* @var \Drupal\Core\Entity\EntityManagerInterface | |
* | |
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. | |
*/ | |
protected $entityManager; | |
/** | |
* The entity type manager. | |
* | |
* @var \Drupal\Core\Entity\EntityTypeManagerInterface | |
*/ | |
protected $entityTypeManager; | |
/** | |
* The entity being used by this form. | |
* | |
* @var \Drupal\Core\Entity\EntityInterface | |
*/ | |
protected $entity; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setOperation($operation) { | |
// If NULL is passed, do not overwrite the operation. | |
if ($operation) { | |
$this->operation = $operation; | |
} | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getBaseFormId() { | |
// Assign ENTITYTYPE_form as base form ID to invoke corresponding | |
// hook_form_alter(), #validate, #submit, and #theme callbacks, but only if | |
// it is different from the actual form ID, since callbacks would be invoked | |
// twice otherwise. | |
$base_form_id = $this->entity->getEntityTypeId() . '_form'; | |
if ($base_form_id == $this->getFormId()) { | |
$base_form_id = NULL; | |
} | |
return $base_form_id; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getFormId() { | |
$form_id = $this->entity->getEntityTypeId(); | |
if ($this->entity->getEntityType()->hasKey('bundle')) { | |
$form_id .= '_' . $this->entity->bundle(); | |
} | |
if ($this->operation != 'default') { | |
$form_id = $form_id . '_' . $this->operation; | |
} | |
return $form_id . '_form'; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function buildForm(array $form, FormStateInterface $form_state) { | |
// During the initial form build, add this form object to the form state and | |
// allow for initial preparation before form building and processing. | |
if (!$form_state->has('entity_form_initialized')) { | |
$this->init($form_state); | |
} | |
// Ensure that edit forms have the correct cacheability metadata so they can | |
// be cached. | |
if (!$this->entity->isNew()) { | |
\Drupal::service('renderer')->addCacheableDependency($form, $this->entity); | |
} | |
// Retrieve the form array using the possibly updated entity in form state. | |
$form = $this->form($form, $form_state); | |
// 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(FormStateInterface $form_state) { | |
// Flag that this form has been initialized. | |
$form_state->set('entity_form_initialized', TRUE); | |
// Prepare the entity to be presented in the entity form. | |
$this->prepareEntity(); | |
// Invoke the prepare form hooks. | |
$this->prepareInvokeAll('entity_prepare_form', $form_state); | |
$this->prepareInvokeAll($this->entity->getEntityTypeId() . '_prepare_form', $form_state); | |
} | |
/** | |
* Gets the actual form array to be built. | |
* | |
* @see \Drupal\Core\Entity\EntityForm::processForm() | |
* @see \Drupal\Core\Entity\EntityForm::afterBuild() | |
*/ | |
public function form(array $form, FormStateInterface $form_state) { | |
// Add #process and #after_build callbacks. | |
$form['#process'][] = '::processForm'; | |
$form['#after_build'][] = '::afterBuild'; | |
return $form; | |
} | |
/** | |
* Process callback: assigns weights and hides extra fields. | |
* | |
* @see \Drupal\Core\Entity\EntityForm::form() | |
*/ | |
public function processForm($element, FormStateInterface $form_state, $form) { | |
// If the form is cached, process callbacks may not have a valid reference | |
// to the entity object, hence we must restore it. | |
$this->entity = $form_state->getFormObject()->getEntity(); | |
return $element; | |
} | |
/** | |
* Form element #after_build callback: Updates the entity with submitted data. | |
* | |
* Updates the internal $this->entity object with submitted values when the | |
* form is being rebuilt (e.g. submitted via AJAX), so that subsequent | |
* processing (e.g. AJAX callbacks) can rely on it. | |
*/ | |
public function afterBuild(array $element, FormStateInterface $form_state) { | |
// Rebuild the entity if #after_build is being called as part of a form | |
// rebuild, i.e. if we are processing input. | |
if ($form_state->isProcessingInput()) { | |
$this->entity = $this->buildEntity($element, $form_state); | |
} | |
return $element; | |
} | |
/** | |
* Returns the action form element for the current entity form. | |
*/ | |
protected function actionsElement(array $form, FormStateInterface $form_state) { | |
$element = $this->actions($form, $form_state); | |
if (isset($element['delete'])) { | |
// Move the delete action as last one, unless weights are explicitly | |
// provided. | |
$delete = $element['delete']; | |
unset($element['delete']); | |
$element['delete'] = $delete; | |
$element['delete']['#button_type'] = 'danger'; | |
} | |
if (isset($element['submit'])) { | |
// Give the primary submit button a #button_type of primary. | |
$element['submit']['#button_type'] = 'primary'; | |
} | |
$count = 0; | |
foreach (Element::children($element) as $action) { | |
$element[$action] += array( | |
'#weight' => ++$count * 5, | |
); | |
} | |
if (!empty($element)) { | |
$element['#type'] = 'actions'; | |
} | |
return $element; | |
} | |
/** | |
* Returns an array of supported actions for the current entity form. | |
* | |
* @todo Consider introducing a 'preview' action here, since it is used by | |
* many entity types. | |
*/ | |
protected function actions(array $form, FormStateInterface $form_state) { | |
// @todo Consider renaming the action key from submit to save. The impacts | |
// are hard to predict. For example, see | |
// \Drupal\language\Element\LanguageConfiguration::processLanguageConfiguration(). | |
$actions['submit'] = array( | |
'#type' => 'submit', | |
'#value' => $this->t('Save'), | |
'#submit' => array('::submitForm', '::save'), | |
); | |
if (!$this->entity->isNew() && $this->entity->hasLinkTemplate('delete-form')) { | |
$route_info = $this->entity->urlInfo('delete-form'); | |
if ($this->getRequest()->query->has('destination')) { | |
$query = $route_info->getOption('query'); | |
$query['destination'] = $this->getRequest()->query->get('destination'); | |
$route_info->setOption('query', $query); | |
} | |
$actions['delete'] = array( | |
'#type' => 'link', | |
'#title' => $this->t('Delete'), | |
'#access' => $this->entity->access('delete'), | |
'#attributes' => array( | |
'class' => array('button', 'button--danger'), | |
), | |
); | |
$actions['delete']['#url'] = $route_info; | |
} | |
return $actions; | |
} | |
/** | |
* {@inheritdoc} | |
* | |
* This is the default entity object builder function. It is called before any | |
* other submit handler to build the new entity object to be used by 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. Generally | |
* this method should not be overridden unless the entity requires the same | |
* preparation for two actions, see \Drupal\comment\CommentForm for an example | |
* with the save and preview actions. | |
* | |
* @param array $form | |
* An associative array containing the structure of the form. | |
* @param \Drupal\Core\Form\FormStateInterface $form_state | |
* The current state of the form. | |
*/ | |
public function submitForm(array &$form, FormStateInterface $form_state) { | |
// Remove button and internal Form API values from submitted values. | |
$form_state->cleanValues(); | |
$this->entity = $this->buildEntity($form, $form_state); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function save(array $form, FormStateInterface $form_state) { | |
return $this->entity->save(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function buildEntity(array $form, FormStateInterface $form_state) { | |
$entity = clone $this->entity; | |
$this->copyFormValuesToEntity($entity, $form, $form_state); | |
// Invoke all specified builders for copying form values to entity | |
// properties. | |
if (isset($form['#entity_builders'])) { | |
foreach ($form['#entity_builders'] as $function) { | |
call_user_func_array($function, array($entity->getEntityTypeId(), $entity, &$form, &$form_state)); | |
} | |
} | |
return $entity; | |
} | |
/** | |
* Copies top-level form values to entity properties | |
* | |
* This should not change existing entity properties that are not being edited | |
* by this form. | |
* | |
* @param \Drupal\Core\Entity\EntityInterface $entity | |
* The entity the current form should operate upon. | |
* @param array $form | |
* A nested array of form elements comprising the form. | |
* @param \Drupal\Core\Form\FormStateInterface $form_state | |
* The current state of the form. | |
*/ | |
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { | |
$values = $form_state->getValues(); | |
if ($this->entity instanceof EntityWithPluginCollectionInterface) { | |
// Do not manually update values represented by plugin collections. | |
$values = array_diff_key($values, $this->entity->getPluginCollections()); | |
} | |
// @todo: This relies on a method that only exists for config and content | |
// entities, in a different way. Consider moving this logic to a config | |
// entity specific implementation. | |
foreach ($values as $key => $value) { | |
$entity->set($key, $value); | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getEntity() { | |
return $this->entity; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setEntity(EntityInterface $entity) { | |
$this->entity = $entity; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) { | |
if ($route_match->getRawParameter($entity_type_id) !== NULL) { | |
$entity = $route_match->getParameter($entity_type_id); | |
} | |
else { | |
$entity = $this->entityManager->getStorage($entity_type_id)->create([]); | |
} | |
return $entity; | |
} | |
/** | |
* Prepares the entity object before the form is built first. | |
*/ | |
protected function prepareEntity() {} | |
/** | |
* Invokes the specified prepare hook variant. | |
* | |
* @param string $hook | |
* The hook variant name. | |
* @param \Drupal\Core\Form\FormStateInterface $form_state | |
* The current state of the form. | |
*/ | |
protected function prepareInvokeAll($hook, FormStateInterface $form_state) { | |
$implementations = $this->moduleHandler->getImplementations($hook); | |
foreach ($implementations as $module) { | |
$function = $module . '_' . $hook; | |
if (function_exists($function)) { | |
// Ensure we pass an updated translation object and form display at | |
// each invocation, since they depend on form state which is alterable. | |
$args = array($this->entity, $this->operation, &$form_state); | |
call_user_func_array($function, $args); | |
} | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getOperation() { | |
return $this->operation; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setModuleHandler(ModuleHandlerInterface $module_handler) { | |
$this->moduleHandler = $module_handler; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setEntityManager(EntityManagerInterface $entity_manager) { | |
$this->entityManager = $entity_manager; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) { | |
$this->entityTypeManager = $entity_type_manager; | |
return $this; | |
} | |
} |