PageRenderTime 86ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/core/lib/Drupal/Core/Entity/ContentEntityForm.php

https://gitlab.com/reasonat/test8
PHP | 290 lines | 104 code | 33 blank | 153 comment | 7 complexity | 65a85f9ae219b0fb48c3028a12c12ef7 MD5 | raw file
  1. <?php
  2. namespace Drupal\Core\Entity;
  3. use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
  4. use Drupal\Core\Entity\Entity\EntityFormDisplay;
  5. use Drupal\Core\Form\FormStateInterface;
  6. use Symfony\Component\DependencyInjection\ContainerInterface;
  7. /**
  8. * Entity form variant for content entity types.
  9. *
  10. * @see \Drupal\Core\ContentEntityBase
  11. */
  12. class ContentEntityForm extends EntityForm implements ContentEntityFormInterface {
  13. /**
  14. * The entity manager.
  15. *
  16. * @var \Drupal\Core\Entity\EntityManagerInterface
  17. */
  18. protected $entityManager;
  19. /**
  20. * Constructs a ContentEntityForm object.
  21. *
  22. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
  23. * The entity manager.
  24. */
  25. public function __construct(EntityManagerInterface $entity_manager) {
  26. $this->entityManager = $entity_manager;
  27. }
  28. /**
  29. * {@inheritdoc}
  30. */
  31. public static function create(ContainerInterface $container) {
  32. return new static(
  33. $container->get('entity.manager')
  34. );
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function form(array $form, FormStateInterface $form_state) {
  40. $form = parent::form($form, $form_state);
  41. // Content entity forms do not use the parent's #after_build callback
  42. // because they only need to rebuild the entity in the validation and the
  43. // submit handler because Field API uses its own #after_build callback for
  44. // its widgets.
  45. unset($form['#after_build']);
  46. $this->getFormDisplay($form_state)->buildForm($this->entity, $form, $form_state);
  47. // Allow modules to act before and after form language is updated.
  48. $form['#entity_builders']['update_form_langcode'] = [$this, 'updateFormLangcode'];
  49. return $form;
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function submitForm(array &$form, FormStateInterface $form_state) {
  55. parent::submitForm($form, $form_state);
  56. // Update the changed timestamp of the entity.
  57. $this->updateChangedTime($this->entity);
  58. }
  59. /**
  60. * {@inheritdoc}
  61. */
  62. public function buildEntity(array $form, FormStateInterface $form_state) {
  63. /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  64. $entity = parent::buildEntity($form, $form_state);
  65. // Mark the entity as requiring validation.
  66. $entity->setValidationRequired(!$form_state->getTemporaryValue('entity_validated'));
  67. return $entity;
  68. }
  69. /**
  70. * {@inheritdoc}
  71. *
  72. * Button-level validation handlers are highly discouraged for entity forms,
  73. * as they will prevent entity validation from running. If the entity is going
  74. * to be saved during the form submission, this method should be manually
  75. * invoked from the button-level validation handler, otherwise an exception
  76. * will be thrown.
  77. */
  78. public function validateForm(array &$form, FormStateInterface $form_state) {
  79. parent::validateForm($form, $form_state);
  80. /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  81. $entity = $this->buildEntity($form, $form_state);
  82. $violations = $entity->validate();
  83. // Remove violations of inaccessible fields and not edited fields.
  84. $violations
  85. ->filterByFieldAccess($this->currentUser())
  86. ->filterByFields(array_diff(array_keys($entity->getFieldDefinitions()), $this->getEditedFieldNames($form_state)));
  87. $this->flagViolations($violations, $form, $form_state);
  88. // The entity was validated.
  89. $entity->setValidationRequired(FALSE);
  90. $form_state->setTemporaryValue('entity_validated', TRUE);
  91. return $entity;
  92. }
  93. /**
  94. * Gets the names of all fields edited in the form.
  95. *
  96. * If the entity form customly adds some fields to the form (i.e. without
  97. * using the form display), it needs to add its fields here and override
  98. * flagViolations() for displaying the violations.
  99. *
  100. * @param \Drupal\Core\Form\FormStateInterface $form_state
  101. * The current state of the form.
  102. *
  103. * @return string[]
  104. * An array of field names.
  105. */
  106. protected function getEditedFieldNames(FormStateInterface $form_state) {
  107. return array_keys($this->getFormDisplay($form_state)->getComponents());
  108. }
  109. /**
  110. * Flags violations for the current form.
  111. *
  112. * If the entity form customly adds some fields to the form (i.e. without
  113. * using the form display), it needs to add its fields to array returned by
  114. * getEditedFieldNames() and overwrite this method in order to show any
  115. * violations for those fields; e.g.:
  116. * @code
  117. * foreach ($violations->getByField('name') as $violation) {
  118. * $form_state->setErrorByName('name', $violation->getMessage());
  119. * }
  120. * parent::flagViolations($violations, $form, $form_state);
  121. * @endcode
  122. *
  123. * @param \Drupal\Core\Entity\EntityConstraintViolationListInterface $violations
  124. * The violations to flag.
  125. * @param array $form
  126. * A nested array of form elements comprising the form.
  127. * @param \Drupal\Core\Form\FormStateInterface $form_state
  128. * The current state of the form.
  129. */
  130. protected function flagViolations(EntityConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
  131. // Flag entity level violations.
  132. foreach ($violations->getEntityViolations() as $violation) {
  133. /** @var \Symfony\Component\Validator\ConstraintViolationInterface $violation */
  134. $form_state->setErrorByName('', $violation->getMessage());
  135. }
  136. // Let the form display flag violations of its fields.
  137. $this->getFormDisplay($form_state)->flagWidgetsErrorsFromViolations($violations, $form, $form_state);
  138. }
  139. /**
  140. * Initializes the form state and the entity before the first form build.
  141. *
  142. * @param \Drupal\Core\Form\FormStateInterface $form_state
  143. * The current state of the form.
  144. */
  145. protected function init(FormStateInterface $form_state) {
  146. // Ensure we act on the translation object corresponding to the current form
  147. // language.
  148. $this->initFormLangcodes($form_state);
  149. $langcode = $this->getFormLangcode($form_state);
  150. $this->entity = $this->entity->hasTranslation($langcode) ? $this->entity->getTranslation($langcode) : $this->entity->addTranslation($langcode);
  151. $form_display = EntityFormDisplay::collectRenderDisplay($this->entity, $this->getOperation());
  152. $this->setFormDisplay($form_display, $form_state);
  153. parent::init($form_state);
  154. }
  155. /**
  156. * Initializes form language code values.
  157. *
  158. * @param \Drupal\Core\Form\FormStateInterface $form_state
  159. * The current state of the form.
  160. */
  161. protected function initFormLangcodes(FormStateInterface $form_state) {
  162. // Store the entity default language to allow checking whether the form is
  163. // dealing with the original entity or a translation.
  164. if (!$form_state->has('entity_default_langcode')) {
  165. $form_state->set('entity_default_langcode', $this->entity->getUntranslated()->language()->getId());
  166. }
  167. // This value might have been explicitly populated to work with a particular
  168. // entity translation. If not we fall back to the most proper language based
  169. // on contextual information.
  170. if (!$form_state->has('langcode')) {
  171. // Imply a 'view' operation to ensure users edit entities in the same
  172. // language they are displayed. This allows to keep contextual editing
  173. // working also for multilingual entities.
  174. $form_state->set('langcode', $this->entityManager->getTranslationFromContext($this->entity)->language()->getId());
  175. }
  176. }
  177. /**
  178. * {@inheritdoc}
  179. */
  180. public function getFormLangcode(FormStateInterface $form_state) {
  181. $this->initFormLangcodes($form_state);
  182. return $form_state->get('langcode');
  183. }
  184. /**
  185. * {@inheritdoc}
  186. */
  187. public function isDefaultFormLangcode(FormStateInterface $form_state) {
  188. $this->initFormLangcodes($form_state);
  189. return $form_state->get('langcode') == $form_state->get('entity_default_langcode');
  190. }
  191. /**
  192. * {@inheritdoc}
  193. */
  194. protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
  195. // First, extract values from widgets.
  196. $extracted = $this->getFormDisplay($form_state)->extractFormValues($entity, $form, $form_state);
  197. // Then extract the values of fields that are not rendered through widgets,
  198. // by simply copying from top-level form values. This leaves the fields
  199. // that are not being edited within this form untouched.
  200. foreach ($form_state->getValues() as $name => $values) {
  201. if ($entity->hasField($name) && !isset($extracted[$name])) {
  202. $entity->set($name, $values);
  203. }
  204. }
  205. }
  206. /**
  207. * {@inheritdoc}
  208. */
  209. public function getFormDisplay(FormStateInterface $form_state) {
  210. return $form_state->get('form_display');
  211. }
  212. /**
  213. * {@inheritdoc}
  214. */
  215. public function setFormDisplay(EntityFormDisplayInterface $form_display, FormStateInterface $form_state) {
  216. $form_state->set('form_display', $form_display);
  217. return $this;
  218. }
  219. /**
  220. * Updates the form language to reflect any change to the entity language.
  221. *
  222. * There are use cases for modules to act both before and after form language
  223. * being updated, thus the update is performed through an entity builder
  224. * callback, which allows to support both cases.
  225. *
  226. * @param string $entity_type_id
  227. * The entity type identifier.
  228. * @param \Drupal\Core\Entity\EntityInterface $entity
  229. * The entity updated with the submitted values.
  230. * @param array $form
  231. * The complete form array.
  232. * @param \Drupal\Core\Form\FormStateInterface $form_state
  233. * The current state of the form.
  234. *
  235. * @see \Drupal\Core\Entity\ContentEntityForm::form()
  236. */
  237. public function updateFormLangcode($entity_type_id, EntityInterface $entity, array $form, FormStateInterface $form_state) {
  238. // Update the form language as it might have changed.
  239. if ($this->isDefaultFormLangcode($form_state)) {
  240. $langcode = $entity->language()->getId();
  241. $form_state->set('langcode', $langcode);
  242. }
  243. }
  244. /**
  245. * Updates the changed time of the entity.
  246. *
  247. * Applies only if the entity implements the EntityChangedInterface.
  248. *
  249. * @param \Drupal\Core\Entity\EntityInterface $entity
  250. * The entity updated with the submitted values.
  251. */
  252. public function updateChangedTime(EntityInterface $entity) {
  253. if ($entity->getEntityType()->isSubclassOf(EntityChangedInterface::class)) {
  254. $entity->setChangedTime(REQUEST_TIME);
  255. }
  256. }
  257. }