layout_paragraphs-1.0.x-dev/src/Plugin/paragraphs/Behavior/LayoutParagraphsBehavior.php
src/Plugin/paragraphs/Behavior/LayoutParagraphsBehavior.php
<?php
namespace Drupal\layout_paragraphs\Plugin\paragraphs\Behavior;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\paragraphs\ParagraphsBehaviorBase;
use Drupal\Core\Plugin\PluginWithFormsInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\PluginFormFactoryInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Layout\LayoutPluginManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\layout_paragraphs\LayoutParagraphsSection;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\layout_paragraphs\LayoutParagraphsRendererService;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a way to define grid based layouts.
*
* @ParagraphsBehavior(
* id = "layout_paragraphs",
* label = @Translation("Layout Paragraphs"),
* description = @Translation("Integrates paragraphs with layout discovery and layout API."),
* weight = 0
* )
*/
class LayoutParagraphsBehavior extends ParagraphsBehaviorBase {
/**
* The layout plugin manager service.
*
* @var \Drupal\Core\Layout\LayoutPluginManagerInterface
*/
protected $layoutPluginManager;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManager
* The entity type manager service.
*/
protected $entityTypeManager;
/**
* The layout paragraphs service.
*
* @var \Drupal\layout_paragraphs\LayoutParagraphsRendererService
*/
protected $layoutParagraphsRendererService;
/**
* A reference to the paragraph instance.
*
* @var [type]
*/
protected $paragraph;
/**
* The plugin form factory.
*
* @var \Drupal\Core\Plugin\PluginFormFactoryInterface
*/
protected $pluginFormFactory;
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* ParagraphsLayoutPlugin constructor.
*
* @param array $configuration
* The configuration array.
* @param string $plugin_id
* This plugin id.
* @param mixed $plugin_definition
* Plugin definition.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* Entity field manager service.
* @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_plugin_manager
* The grid discovery service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The grid discovery service.
* @param \Drupal\layout_paragraphs\LayoutParagraphsRendererService $layout_paragraphs_renderer_service
* The layout paragraphs service.
* @param \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_factory
* The plugin form factory.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
EntityFieldManagerInterface $entity_field_manager,
LayoutPluginManagerInterface $layout_plugin_manager,
EntityTypeManagerInterface $entity_type_manager,
LayoutParagraphsRendererService $layout_paragraphs_renderer_service,
PluginFormFactoryInterface $plugin_form_factory,
LoggerChannelFactoryInterface $logger_factory,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_field_manager);
$this->layoutPluginManager = $layout_plugin_manager;
$this->entityTypeManager = $entity_type_manager;
$this->layoutParagraphsRendererService = $layout_paragraphs_renderer_service;
$this->pluginFormFactory = $plugin_form_factory;
$this->loggerFactory = $logger_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_field.manager'),
$container->get('plugin.manager.core.layout'),
$container->get('entity_type.manager'),
$container->get('layout_paragraphs.renderer'),
$container->get('plugin_form.factory'),
$container->get('logger.factory')
);
}
/**
* {@inheritdoc}
*/
public function buildBehaviorForm(
ParagraphInterface $paragraph,
array &$form,
FormStateInterface $form_state,
) {
$layout_paragraphs_section = new LayoutParagraphsSection($paragraph);
$layout_settings = $layout_paragraphs_section->getSetting('config');
$available_layouts = $this->configuration['available_layouts'];
$path = array_merge($form['#parents'], ['layout']);
$input_layout_id = NestedArray::getValue($form_state->getUserInput(), $path);
$layout_id = $input_layout_id ?? $layout_paragraphs_section->getLayoutId();
$layout_id = Html::escape($layout_id);
$default_value = !empty($layout_id) ? $layout_id : key($available_layouts);
// @todo Throw an error if plugin instance cannot be loaded.
$plugin_instance = $this->layoutPluginManager->createInstance($default_value, $layout_settings ?? []);
$plugin_form = $this->getLayoutPluginForm($plugin_instance);
$wrapper_id = Html::getId(implode('-', array_merge($form['#parents'], ['layout-options'])));
$form['layout'] = [
'#title' => $this->t('Choose a layout:'),
'#type' => 'layout_select',
'#options' => $available_layouts,
'#default_value' => $default_value,
'#ajax' => [
'wrapper' => $wrapper_id,
'callback' => [$this, 'ajaxUpdateOptions'],
'progress' => [
'type' => 'throbber',
],
],
'#weight' => 0,
];
if ($plugin_form) {
$form['config'] = [
'#type' => 'details',
'#id' => $wrapper_id,
'#title' => $this->t('Layout Options'),
'#weight' => 10,
];
$form['config'] += $plugin_form->buildConfigurationForm([], $form_state);
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateBehaviorForm(ParagraphInterface $paragraph, array &$form, FormStateInterface $form_state) {
$plugin_instance = $this->layoutPluginManager->createInstance($form_state->getValue('layout'), $form_state->getValue('config') ?? []);
if ($plugin_form = $this->getLayoutPluginForm($plugin_instance)) {
$plugin_form->validateConfigurationForm($form, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function submitBehaviorForm(ParagraphInterface $paragraph, array &$form, FormStateInterface $form_state) {
$filtered_values = $this->filterBehaviorFormSubmitValues($paragraph, $form, $form_state);
$plugin_instance = $this->layoutPluginManager->createInstance($form_state->getValue('layout'), $form_state->getValue('config') ?? []);
if ($plugin_form = $this->getLayoutPluginForm($plugin_instance)) {
// Add default #parents array to prevent form errors.
// @see https://www.drupal.org/project/layout_paragraphs/issues/3291180
$form['config'] += ['#parents' => []];
$form += ['#parents' => []];
$subform_state = SubformState::createForSubform($form['config'], $form, $form_state);
$plugin_form->submitConfigurationForm($form['config'], $subform_state);
$filtered_values['config'] = $plugin_form->getConfiguration();
}
// Merge existing behavior settings.
$behavior_settings = $paragraph->getAllBehaviorSettings();
$existing_settings = $behavior_settings[$this->getPluginId()] ?? [];
$filtered_values = $filtered_values + $existing_settings;
// Set the updated behavior settings.
$paragraph->setBehaviorSettings($this->getPluginId(), $filtered_values);
}
/**
* Ajax callback - returns the updated layout options form.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The layout options form.
*/
public function ajaxUpdateOptions(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state->getTriggeringElement();
$parents = $triggering_element['#parents'];
array_splice($parents, -1, 1, ['config']);
$config_form = NestedArray::getValue($form, $parents);
if (isset($config_form)) {
return $config_form;
}
return [];
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'available_layouts' => [],
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$options = $this->layoutPluginManager->getLayoutOptions();
$available_layouts = $this->configuration['available_layouts'];
$form['available_layouts'] = [
'#title' => $this->t('Available Layouts'),
'#type' => 'select',
'#multiple' => TRUE,
'#options' => $options,
'#default_value' => array_keys($available_layouts),
'#size' => count($options) < 8 ? count($options) * 2 : 10,
'#required' => FALSE,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
if (empty($form_state->getValue('available_layouts'))) {
$form_state->setErrorByName('available_layouts', $this->t('You must select at least one layout.'));
}
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$available_layouts = array_filter($form_state->getValue('available_layouts'));
foreach ($available_layouts as $layout_name) {
$layout = $this->layoutPluginManager->getDefinition($layout_name);
$this->configuration['available_layouts'][$layout_name] = $layout->getLabel();
}
}
/**
* {@inheritdoc}
*/
public function settingsSummary(ParagraphInterface $paragraph) {
$summary = [];
return $summary;
}
/**
* {@inheritdoc}
*/
public function view(array &$build, ParagraphInterface $paragraph, EntityViewDisplayInterface $display, $view_mode) {
if (empty($build['regions']) && LayoutParagraphsSection::isLayoutComponent($paragraph)) {
$build['regions'] = $this->layoutParagraphsRendererService->renderLayoutSection($build, $paragraph, $view_mode);
}
}
/**
* Retrieves the plugin form for a given layout.
*
* @param \Drupal\Core\Layout\LayoutInterface $layout
* The layout plugin.
*
* @return \Drupal\Core\Plugin\PluginFormInterface|null
* The plugin form for the layout.
*/
protected function getLayoutPluginForm(LayoutInterface $layout) {
if ($layout instanceof PluginWithFormsInterface) {
try {
return $this->pluginFormFactory->createInstance($layout, 'configure');
}
catch (\Exception $e) {
$this->loggerFactory->get('layout_paragraphs')->error('Erl, Layout Configuration', [$e]);
}
}
if ($layout instanceof PluginFormInterface) {
return $layout;
}
return NULL;
}
}
