layout_builder_paragraphs-1.0.x-dev/src/Form/AddParagraphForm.php
src/Form/AddParagraphForm.php
<?php
namespace Drupal\layout_builder_paragraphs\Form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Form\SubformStateInterface;
use Drupal\layout_builder\LayoutBuilderHighlightTrait;
use Drupal\layout_builder\SectionComponent;
use Drupal\layout_builder\SectionStorageInterface;
use Drupal\layout_builder\Form\ConfigureBlockFormBase;
use Drupal\layout_builder_paragraphs\Controller\ModalLayoutRebuildTrait;
/**
* Class AddParagraphForm.
*/
class AddParagraphForm extends ConfigureBlockFormBase {
use LayoutBuilderHighlightTrait;
use ModalLayoutRebuildTrait; // Override LayoutRebuildTrait.
/**
* Drupal\Core\Entity\EntityTypeManagerInterface definition.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
$instance->entityTypeManager = $container->get('entity_type.manager');
return $instance;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'layout_builder_paragraphs_add_paragraph_form';
}
/**
* {@inheritdoc}
*/
protected function submitLabel() {
return $this->t('Add paragraph and return to layout builder');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL, $delta = NULL, $region = NULL, $plugin_id = NULL, $paragraph_type = NULL) {
// Alter plugin id to match that of paragraph_blocks format, e.g.
// paragraph_field:node:field_contents:0:landing_page
$plugin_id = 'paragraph_field:' . $plugin_id;
$form_state->set('plugin_id', $plugin_id);
// The following is copied from AddBlockForm.
// Only generate a new component once per form submission.
if (!$component = $form_state->get('layout_builder__component')) {
$component = new SectionComponent($this->uuidGenerator->generate(), $region, ['id' => $plugin_id]);
$section_storage->getSection($delta)->appendComponent($component);
$form_state->set('layout_builder__component', $component);
}
$form['#attributes']['data-layout-builder-target-highlight-id'] = $this->blockAddHighlightId($delta, $region);
// The following is copied from ConfigureBlockFormBase.
$this->sectionStorage = $section_storage;
$this->delta = $delta;
$this->uuid = $component->getUuid();
$this->block = $component->getPlugin();
$form_state->setTemporaryValue('gathered_contexts', $this->getPopulatedContexts($section_storage));
$contexts = $this->getPopulatedContexts($section_storage);
// @todo Remove once https://www.drupal.org/node/2268787 is resolved.
$form_state->set('block_theme', $this->config('system.theme')->get('default'));
$form['#tree'] = TRUE;
$form['settings'] = [];
$subform_state = SubformState::createForSubform($form['settings'], $form, $form_state);
$form['settings'] = $this->getPluginForm($this->block)->buildConfigurationForm($form['settings'], $subform_state);
// Hide the settings fields when suppressed in paragraph_blocks.
// @see \Drupal\paragraph_blocks\Plugin\Block\ParagraphBlock::buildConfigurationForm()
if ($this->config('paragraph_blocks.settings')->get('suppress_label')) {
if (isset($form['settings']['admin_label'])) unset($form['settings']['admin_label']);
}
// Paragraph form creation. Partially copied from:
// https://www.webomelette.com/how-render-entity-field-widgets-inside-custom-form-drupal-8
// Set the entity and bundle.
$entity = $this->entityTypeManager->getStorage('paragraph')->create([
'type' => $paragraph_type,
]);
// Initiate the form state.
$form_state->set('entity', $entity);
// Retrieve and assign the parent entity.
$parent_entity = $section_storage->getContextValue('entity');
$field_name = PARAGRAPH_FIELD;
$entity->setParentEntity($parent_entity, $field_name);
// Set langcode to parent langcode.
$parent_langcode = $parent_entity->language()->getId();
$entity->set('langcode', $parent_langcode);
// Build and assign the form display.
$form_display = EntityFormDisplay::collectRenderDisplay($entity, 'default');
$form_display->buildForm($entity, $form, $form_state);
$form_state->set('form_display', $form_display);
// Assign form parents.
// @TODO: find out if this is necessary?
$form['#parents'] = [];
// The following is copied from ConfigureBlockFormBase.
// Add submit button.
$form['actions'] = [
'#type' => 'actions',
'submit' => [
'#type' => 'submit',
'#value' => $this->submitLabel(),
'#button_type' => 'primary',
],
'#weight' => 100,
];
if ($this->isAjax()) {
// @todo Fix ajax fields (e.g. media) breaking form submit in full-page form.
// This occurs when ajaxified elements cause form to be rebuilt in ajax
// mode, which in turn causes form to assume it is rendering in a modal.
// When the form is reloaded without rebuilding (e.g. when there is a
// validation error), unnecessary ajax handlers remain on the submit button.
// $form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit';
// @todo static::ajaxSubmit() requires data-drupal-selector to be the same
// between the various Ajax requests. A bug in
// \Drupal\Core\Form\FormBuilder prevents that from happening unless
// $form['#id'] is also the same. Normally, #id is set to a unique HTML
// ID via Html::getUniqueId(), but here we bypass that in order to work
// around the data-drupal-selector bug. This is okay so long as we
// assume that this form only ever occurs once on a page. Remove this
// workaround in https://www.drupal.org/node/2897377.
$form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']);
}
// Mark this as an administrative page for JavaScript ("Back to site" link).
$form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE;
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state->getValues() as $key => $value) {
// @TODO: Validate fields.
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Retrieve the form display and entity.
$form_display = $form_state->get('form_display');
$entity = $form_state->get('entity');
$plugin_id = $form_state->get('plugin_id');
// Make sure that we have the complete form state.
// @see: docroot/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php:184
$complete_form_state = $form_state instanceof SubformStateInterface ? $form_state->getCompleteFormState() : $form_state;
// Extract the form values and populate it to the entity.
$form_display->extractFormValues($entity, $form, $complete_form_state);
$entity->save();
// Add the reference to the newly created paragraph in the parent entity.
// @TODO: don't hardcode the field as field_contents.
$parent_entity = $entity->getParentEntity();
$original_para_count = $parent_entity->{PARAGRAPH_FIELD}->count();
$parent_entity->{PARAGRAPH_FIELD}->appendItem([
'target_id' => $entity->id(),
'target_revision_id' => $entity->getRevisionId(),
'langcode' => $entity->get('langcode'),
]);
$parent_entity->save();
// Update the entity object stored in the section storage with new paragraph.
$context_entity = $this->sectionStorage->getContextValue('entity');
$context_entity->{PARAGRAPH_FIELD}->setValue($parent_entity->{PARAGRAPH_FIELD}->getValue());
$this->sectionStorage->setContextValue('entity', $context_entity);
// Hardcoded configuration for paragraph_blocks.
$configuration = [
'id' => $plugin_id,
'label' => 'Paragraph item ' . $original_para_count,
'provider' => 'paragraph_blocks',
'label_display' => '',
'context_mapping' => [
'entity' => 'layout_builder.entity',
],
];
// The following is copied from ConfigureBlockFormBase.
$section = $this->sectionStorage->getSection($this->delta);
$section->getComponent($this->uuid)->setConfiguration($configuration);
$this->layoutTempstoreRepository->set($this->sectionStorage);
$form_state->setRedirectUrl($this->sectionStorage->getLayoutBuilderUrl());
}
}
