layout_paragraphs-1.0.x-dev/src/Form/LayoutParagraphsBuilderForm.php
src/Form/LayoutParagraphsBuilderForm.php
<?php
namespace Drupal\layout_paragraphs\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Component\Utility\Html;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\MessageCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\RevisionLogInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\layout_paragraphs\Ajax\LayoutParagraphsEventCommand;
use Drupal\layout_paragraphs\LayoutParagraphsLayout;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\layout_paragraphs\LayoutParagraphsLayoutTempstoreRepository;
/**
* Class LayoutParagraphsBuilderForm.
*
* Builds a Layout Paragraphs Builder form with save / cancel buttons
* for saving the host entity.
*/
class LayoutParagraphsBuilderForm extends FormBase {
/**
* A layout paragraphs layout object.
*
* @var \Drupal\layout_paragraphs\LayoutParagraphsLayout
*/
protected $layoutParagraphsLayout;
/**
* The layout paragraphs layout tempstore service.
*
* @var \Drupal\layout_paragraphs\LayoutParagraphsLayoutTempstoreRepository
*/
protected $tempstore;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'layout_paragraphs_builder_form';
}
/**
* {@inheritdoc}
*/
public function __construct(LayoutParagraphsLayoutTempstoreRepository $tempstore, EntityTypeManagerInterface $entity_type_manager) {
$this->tempstore = $tempstore;
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('layout_paragraphs.tempstore_repository'),
$container->get('entity_type.manager')
);
}
/**
* Builds the layout paragraphs builder form.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state object.
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The parent entity that contains a layout.
* @param string $field_name
* The name of the layout paragraphs field.
* @param string $view_mode
* The view mode.
*/
public function buildForm(
array $form,
FormStateInterface $form_state,
?ContentEntityInterface $entity = NULL,
?string $field_name = NULL,
?string $view_mode = NULL,
) {
$parents = array_merge($form['#parents'] ?? [], ['layout_paragraphs_storage_key']);
$input = $form_state->getUserInput();
$layout_paragraphs_storage_key = NestedArray::getValue($input, $parents);
// If the form is being rendered for the first time, save the Layout
// Paragraphs Layout instance to tempstore and store the key.
if (empty($layout_paragraphs_storage_key)) {
$render_display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
$renderer = $render_display->getRenderer($field_name);
$layout_paragraphs_settings = $renderer->getSettings() + ['reference_field_view_mode' => $view_mode];
$this->layoutParagraphsLayout = new LayoutParagraphsLayout($entity->{$field_name}, $layout_paragraphs_settings);
$this->tempstore->set($this->layoutParagraphsLayout);
$layout_paragraphs_storage_key = $this->tempstore->getStorageKey($this->layoutParagraphsLayout);
}
// On subsequent form renders, this loads the correct Layout Paragraphs
// Layout from the tempstore using the storage key.
else {
$this->layoutParagraphsLayout = $this->tempstore->getWithStorageKey($layout_paragraphs_storage_key);
}
$form['layout_paragraphs_builder_ui'] = [
'#type' => 'layout_paragraphs_builder',
'#layout_paragraphs_layout' => $this->layoutParagraphsLayout,
];
$form['layout_paragraphs_storage_key'] = [
'#type' => 'hidden',
'#default_value' => $layout_paragraphs_storage_key,
];
$form['#attributes']['data-lpb-form-id'] = Html::getId($this->layoutParagraphsLayout->id());
$form['actions'] = [
'#type' => 'actions',
'submit' => [
'#type' => 'submit',
'#value' => $this->t('Save'),
'#ajax' => [
'callback' => '::save',
],
'#attributes' => [
'class' => ['button--primary'],
],
],
'close' => [
'#type' => 'button',
'#value' => $this->t('Close'),
'#ajax' => [
'callback' => '::close',
],
'#attributes' => [
'class' => ['lpb-btn--cancel'],
],
],
];
$form['actions']['#attributes']['class'][] = 'lpb-form__actions';
return $form;
}
/**
* Ajax callback.
*
* Closes the builder and returns the rendered layout.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An ajax command.
*/
public function close(array $form, FormStateInterface $form_state) {
$this->tempstore->delete($this->layoutParagraphsLayout);
$view_mode = $this->layoutParagraphsLayout->getSetting('reference_field_view_mode', 'default');
$rendered_layout = $this->layoutParagraphsLayout->getParagraphsReferenceField()->view($view_mode);
$response = new AjaxResponse();
$response->addCommand(new LayoutParagraphsEventCommand($this->layoutParagraphsLayout, '', 'builder:close'));
$response->addCommand(new ReplaceCommand('[data-lpb-form-id="' . $form['#attributes']['data-lpb-form-id'] . '"]', $rendered_layout));
return $response;
}
/**
* Ajax callback.
*
* Displays a message when the entity is saved.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An ajax command.
*/
public function save(array $form, FormStateInterface $form_state) {
$entity = $this->layoutParagraphsLayout->getEntity();
$response = new AjaxResponse();
$t_args = [
'@type' => $entity->getEntityType()->getLabel(),
'%title' => $entity->label(),
];
$response->addCommand(new MessageCommand($this->t('@type %title has been updated.', $t_args)));
$response->addCommand(new ReplaceCommand('[data-lpb-form-id="' . $form['#attributes']['data-lpb-form-id'] . '"]', $form));
$response->addCommand(new LayoutParagraphsEventCommand($this->layoutParagraphsLayout, '', 'builder:save'));
return $response;
}
/**
* {@inheritdoc}
*
* Saves the layout to its parent entity.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state object.
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// The entity may have been altered by another process, and needs to be
// loaded from storage to ensure edits to other fields are not overwritten.
// @see https://www.drupal.org/project/layout_paragraphs/issues/3275179
$entity = $this->layoutParagraphsLayout->getEntity();
$entity_id = $entity->id();
$entity_type = $entity->getEntityTypeId();
$field_name = $this->layoutParagraphsLayout->getFieldName();
if ($entity instanceof RevisionableInterface) {
$revision_id = $entity->getRevisionId();
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity_type);
/** @var \Drupal\Core\Entity\RevisionableInterface $entity */
$entity = $storage->loadRevision($revision_id);
$entity->setNewRevision(FALSE);
}
else {
$entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
}
if ($entity instanceof EntityChangedInterface) {
$entity->setChangedTime(time());
}
if ($entity instanceof RevisionLogInterface) {
$entity->setRevisionCreationTime(time());
$entity->setRevisionLogMessage($this->t('Updated with the Layout Paragraphs Frontend Builder.'));
}
$entity->$field_name = $this->layoutParagraphsLayout->getParagraphsReferenceField();
$entity->save();
$this->layoutParagraphsLayout->setParagraphsReferenceField($entity->$field_name);
$this->tempstore->set($this->layoutParagraphsLayout);
}
}
