pluginreference-2.0.0/src/Plugin/Field/FieldWidget/PluginReferenceConfigurationFormTrait.php
src/Plugin/Field/FieldWidget/PluginReferenceConfigurationFormTrait.php
<?php
namespace Drupal\pluginreference\Plugin\Field\FieldWidget;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\pluginreference\PluginTypeHelperInterface;
/**
* Trait that handles the configuration form on plugin reference field widgets.
*/
trait PluginReferenceConfigurationFormTrait {
/**
* The plugin type helper.
*
* @var \Drupal\pluginreference\PluginTypeHelperInterface
*/
protected $pluginTypeHelper;
/**
* Defines the configuration settings..
*
* @return string[]
* A list of the configuration settings, keyed by the setting name.
*/
public static function configurationDefaultSettings() {
return [
'configuration_form' => 'full',
];
}
/**
* The configuration settings form.
*
* @param array $form
* The form where the configuration settings form is being included in.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form definition for the configuration part.
*/
public function configurationSettingsForm(array $form, FormStateInterface $form_state) {
$elements['configuration_form'] = [
'#type' => 'select',
'#title' => $this->t('Configuration form'),
'#description' => $this->t('How the plugin configuration form will be shown.'),
'#options' => [
'full' => $this->t('Full'),
'hidden' => $this->t('Hidden'),
],
'#default_value' => $this->getSetting('configuration_form'),
'#required' => TRUE,
];
return $elements;
}
/**
* Returns a short summary for the configuration settings.
*
* @return array
* A short summary of the configuration settings.
*/
public function configurationSettingsSummary() {
$summary[] = $this->t('Configuration form: @configuration_form', ['@configuration_form' => $this->getSetting('configuration_form')]);
return $summary;
}
/**
* The configuration form element.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* Array of default values for this field.
* @param int $delta
* The order of this item in the array of sub-elements (0, 1, 2, etc.).
* @param array $element
* A form element array containing basic properties for the widget.
* @param array $form
* The form structure where widgets are being attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form elements for the configuration part.
*/
public function singleConfigurationFormElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
if ($this->getSetting('configuration_form') === 'hidden') {
return $element;
}
/** @var \Drupal\pluginreference\Plugin\Field\FieldType\PluginReferenceItem $item */
$item =& $items[$delta];
$field_name = $this->fieldDefinition->getName();
$configuration_id = implode('-', array_merge(
$element['#field_parents'],
[$field_name, $delta, 'configuration']
));
$values = $form_state->getValues();
$plugin_id = $values[$field_name][$delta]['plugin_id'] ?? $item->getValue()['plugin_id'] ?? NULL;
if (!empty($values[$field_name][$delta]['configuration'])) {
$configuration = $values[$field_name][$delta]['configuration'];
}
else {
$configuration = $item->getValue()['configuration'] ?? [];
}
$item->setValue([
'plugin_id' => $plugin_id,
'configuration' => $configuration,
]);
$element['#target_type'] = $this->fieldDefinition->getSetting('target_type');
$element['plugin_id']['#ajax'] = [
'callback' => [$this, 'configurationForm'],
'wrapper' => $configuration_id,
];
if ($this->pluginId === 'plugin_reference_autocomplete') {
$element['plugin_id']['#ajax']['event'] = 'autocompleteclose';
$element['plugin_id']['#ajax']['progress'] = FALSE;
}
// Build configuration container.
$element['configuration'] = [
'#type' => 'container',
'#attributes' => ['id' => $configuration_id],
'#tree' => TRUE,
'#plugin_id' => $plugin_id,
'#target_type' => $this->fieldDefinition->getSetting('target_type'),
'#element_validate' => [[$this, 'configurationValidate']],
];
$plugin_manager = $this->getPluginTypeHelper()->getPluginManager($element['#target_type']);
if (!$plugin_manager instanceof PluginManagerInterface) {
return $element;
}
if (!$plugin_manager->hasDefinition($plugin_id)) {
return $element;
}
$plugin_definition = $plugin_manager->getDefinition($plugin_id);
if ($this->getPluginTypeHelper()->isPluginConfigurable($plugin_definition)) {
$plugin_instance = $item->referencedPlugin();
if ($plugin_instance instanceof PluginFormInterface) {
// Some blocks like 'system_branding_block rely on block_theme that is
// set in the form_state object.
// @see \Drupal\system\Plugin\Block\SystemBrandingBlock::blockForm().
$form_state->set('block_theme', \Drupal::config('system.theme')
->get('default'));
// Create a separate variable for the plugin configuration form, so
// plugins expecting the form array by reference don't throw an error.
$plugin_configuration_form = [];
$element['configuration'] += $plugin_instance->buildConfigurationForm($plugin_configuration_form, $form_state);
}
}
return $element;
}
/**
* Configuration form element for a widget that contains multiple values.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* Array of default values for this field.
* @param int $delta
* The order of this item in the array of sub-elements (0, 1, 2, etc.).
* @param array $element
* A form element array containing basic properties for the widget.
* @param array $form
* The form structure where widgets are being attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form elements for the configuration part.
*/
public function multipleConfigurationFormElements(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
if ($this->getSetting('configuration_form') === 'hidden') {
return $element;
}
$field_name = $this->fieldDefinition->getName();
$configuration_id = implode('-', array_merge(
$element['#field_parents'],
[$field_name, $delta, 'configuration']
));
$form_state_values = $form_state->getValues();
$values = [];
if (isset($form_state_values[$field_name]['plugin_id'])) {
if (!is_array($form_state_values[$field_name]['plugin_id'])) {
$form_state_values[$field_name]['plugin_id'] = [$form_state_values[$field_name]['plugin_id'] => $form_state_values[$field_name]['plugin_id']];
}
$plugin_ids = array_filter($form_state_values[$field_name]['plugin_id']);
foreach ($plugin_ids as $plugin_id) {
$values[] = [
'plugin_id' => $plugin_id,
'configuration' => $form_state_values[$field_name]['configuration'][$plugin_id] ?? [],
];
}
}
if (!empty($values)) {
$items->setValue($values);
}
$element['plugin_id']['#ajax'] = [
'callback' => [$this, 'configurationForm'],
'wrapper' => $configuration_id,
];
// Build configuration container.
$element['configuration'] = [
'#type' => 'container',
'#attributes' => ['id' => $configuration_id],
'#tree' => TRUE,
];
$plugin_manager = $this->getPluginTypeHelper()->getPluginManager($this->fieldDefinition->getSetting('target_type'));
if (!$plugin_manager instanceof PluginManagerInterface) {
return $element;
}
foreach ($items->getIterator() as $item) {
if (!isset($item->getValue()['plugin_id'])) {
continue;
}
$plugin_id = $item->getValue()['plugin_id'];
if (empty($plugin_id) || !$plugin_manager->hasDefinition($plugin_id)) {
continue;
}
$plugin_definition = $plugin_manager->getDefinition($plugin_id);
if ($this->getPluginTypeHelper()->isPluginConfigurable($plugin_definition)) {
$plugin_instance = $item->referencedPlugin();
if ($plugin_instance instanceof PluginFormInterface) {
// Some blocks like 'system_branding_block rely on block_theme that is
// set in the form_state object.
// @see \Drupal\system\Plugin\Block\SystemBrandingBlock::blockForm().
$form_state->set('block_theme', \Drupal::config('system.theme')
->get('default'));
// Create a separate variable for the plugin configuration form, so
// plugins expecting the form array by reference don't throw an error.
$plugin_configuration_form = [];
$element['configuration'][$plugin_id] = [
'#type' => 'fieldset',
'#title' => $this->pluginTypeHelper->getPluginLabel($plugin_definition),
'#plugin_id' => $plugin_id,
'#target_type' => $this->fieldDefinition->getSetting('target_type'),
'#element_validate' => [[$this, 'configurationValidate']],
];
$element['configuration'][$plugin_id] += $plugin_instance->buildConfigurationForm($plugin_configuration_form, $form_state);
}
}
}
return $element;
}
/**
* The configuration validation checks.
*
* @param array $element
* A form element array containing basic properties for the widget.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $form
* The form where the configuration settings form is being included in.
*/
public function configurationValidate(array $element, FormStateInterface $form_state, array $form): void {
$plugin_manager = $this->getPluginTypeHelper()->getPluginManager($element['#target_type']);
$values = $form_state->getValues();
$plugin_id = $element['#plugin_id'];
if (!$plugin_manager instanceof PluginManagerInterface) {
// Clear all configuration settings.
NestedArray::setValue($values, $element['#parents'], []);
return;
}
$plugin_definition = [];
if ($plugin_manager->hasDefinition($plugin_id)) {
$plugin_definition = $plugin_manager->getDefinition($plugin_id);
}
if (!empty($plugin_id) && $this->pluginTypeHelper->isPluginConfigurable($plugin_definition)) {
// Clean up configuration settings.
$configuration = NestedArray::getValue($values, $element['#parents']) ?? [];
// Execute the plugin validate configuration.
/** @var \Drupal\Core\Plugin\PluginFormInterface $plugin_instance */
$plugin_instance = $plugin_manager->createInstance($plugin_id, $configuration);
$configuration = (new FormState())->setValues($configuration);
$triggering_element = $form_state->getTriggeringElement();
if ($triggering_element['#type'] == 'submit') {
$plugin_instance->validateConfigurationForm($element, $configuration);
// Pass along errors from the plugin validation.
foreach ($configuration->getErrors() as $key => $error) {
$parents = implode('][', $element['#parents']);
// If the plugin form used setError() then the parents will already be
// part of the key since we are passing along the element in the
// context of the whole form. If the plugin form used setErrorByName
// we need to add the parents in.
if (strpos($key, $parents) === FALSE) {
$key = sprintf('%s][%s', $parents, $key);
}
$form_state->setErrorByName($key, $error);
}
}
// Call submitConfiguration, since it's possible that the values are
// altered in the submitConfigurationForm method.
$plugin_instance->submitConfigurationForm($element, $configuration);
/** @var \Drupal\Component\Plugin\ConfigurableInterface $plugin_instance */
NestedArray::setValue($values, $element['#parents'], $plugin_instance->getConfiguration());
$form_state->setValues($values);
}
else {
// Clear all configuration settings.
NestedArray::setValue($values, $element['#parents'], []);
}
}
/**
* Ajax callback that return plugin configuration form.
*
* @param array $form
* The form where the configuration settings form is being included in.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return mixed
* The configuration form element.
*/
public function configurationForm(array $form, FormStateInterface $form_state) {
$triggering_element = (array) $form_state->getTriggeringElement();
$array_parents = $triggering_element['#array_parents'];
array_pop($array_parents);
// Check if the triggering element is a checkbox or radio button. The
// triggering element will be a level deeper, so pop an additional item
// from the parents array.
if (in_array($triggering_element['#type'], ['checkbox', 'radio'])) {
array_pop($array_parents);
}
$array_parents[] = 'configuration';
return NestedArray::getValue($form, $array_parents);
}
/**
* Get the plugin type helper service.
*
* @return \Drupal\pluginreference\PluginTypeHelperInterface
* the plugin type helper.
*/
protected function getPluginTypeHelper(): PluginTypeHelperInterface {
if (!$this->pluginTypeHelper instanceof PluginTypeHelperInterface) {
$this->pluginTypeHelper = \Drupal::service('plugin_reference.plugin_type_helper');
}
return $this->pluginTypeHelper;
}
}
