association-1.0.0-alpha2/src/Entity/Form/AssociationTypeForm.php
src/Entity/Form/AssociationTypeForm.php
<?php namespace Drupal\association\Entity\Form; use Drupal\association\Plugin\AssociationPluginFormInterface; use Drupal\association\Plugin\BehaviorPluginManagerInterface; use Drupal\association\Plugin\LandingPagePluginManagerInterface; use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\SubformState; use Drupal\Core\Plugin\PluginFormFactoryInterface; use Drupal\Core\Plugin\PluginFormInterface; use Drupal\Core\Plugin\PluginWithFormsInterface; use Drupal\Core\Utility\Error; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Form for editing and creating new association types. */ class AssociationTypeForm extends EntityForm { /** * Service providing entity bundle information. * * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface */ protected $entityBundleInfo; /** * The form factory for getting form objects for plugins operations. * * @var \Drupal\Core\Plugin\PluginFormFactoryInterface */ protected $pluginFormFactory; /** * Association type behavior plugin manager. * * @var \Drupal\association\Plugin\BehaviorPluginManagerInterface */ protected BehaviorPluginManagerInterface $behaviorManager; /** * The landing page plugin manager. * * @var \Drupal\association\Plugin\LandingPagePluginManagerInterface */ protected LandingPagePluginManagerInterface $landingPageManager; /** * Create a new instance of the AssociationTypeForm object. * * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info * Service providing entity bundle information. * @param \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_factory * The form factory for getting form objects for plugins operations. * @param \Drupal\association\Plugin\BehaviorPluginManagerInterface $behavior_manager * Association type behavior plugin manager. * @param \Drupal\association\Plugin\LandingPagePluginManagerInterface $landing_page_manager * The landing page plugin manager. */ public function __construct(EntityTypeBundleInfoInterface $entity_bundle_info, PluginFormFactoryInterface $plugin_form_factory, BehaviorPluginManagerInterface $behavior_manager, LandingPagePluginManagerInterface $landing_page_manager) { $this->entityBundleInfo = $entity_bundle_info; $this->pluginFormFactory = $plugin_form_factory; $this->behaviorManager = $behavior_manager; $this->landingPageManager = $landing_page_manager; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.bundle.info'), $container->get('plugin_form.factory'), $container->get('plugin.manager.association.behavior'), $container->get('plugin.manager.association.landing_page') ); } /** * Check to see if an ID is already in use for association type bundle. * * @return bool * The boolean to indicate if this ID is already in use. TRUE if it already * exists, and FALSE otherwise. */ public function exists($id) { if ($id === 'add') { return TRUE; } $entityType = $this->getEntity()->getEntityType(); $entityIds = $this->entityTypeManager ->getStorage($entityType->id()) ->getQuery() ->accessCheck(FALSE) ->condition($entityType->getKey('id'), $id) ->execute(); return (bool) $entityIds; } /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); $form += ['#parents' => []]; /** @var \Drupal\association\Entity\AssociationTypeInterface $entity */ $entity = $this->entity; $hasData = $entity->hasData(); if ($hasData) { $form['warning'] = [ '#type' => 'fieldset', '#title' => $this->t('This association type has data!'), '#attributes' => [ 'class' => ['message', 'message--warning'], ], '#markup' => $this->t('This Entity Association type has data (association instances) and will therefore lock configuration options that should not be changed because they can break existing content. You must remove all @label associations to edit these settings.', [ '@label' => $entity->label(), ]), ]; } $form['label'] = [ '#type' => 'textfield', '#title' => $this->t('Label'), '#maxlength' => 255, '#default_value' => $entity->label(), '#description' => $this->t("Label for the Entity Association type."), '#required' => TRUE, ]; $form['id'] = [ '#type' => 'machine_name', '#default_value' => $entity->id(), '#machine_name' => [ 'source' => ['label'], 'exists' => [$this, 'exists'], ], '#disabled' => !$entity->isNew(), ]; // Build the definted association type plugins. foreach ($this->getPluginDefinitions() as $pluginKey => $pluginInfo) { $wrapperId = 'association-type-' . strtr($pluginInfo['id'], '_', '-') . '-configuration'; $form[$pluginKey] = [ '#type' => 'fieldset', '#title' => $pluginInfo['label'], '#tree' => TRUE, 'id' => [ '#type' => 'select', '#title' => $this->t('Type'), '#required' => TRUE, '#disabled' => $hasData, '#options' => $pluginInfo['options'], '#default_value' => 'none', '#ajax' => [ 'wrapper' => $wrapperId, 'callback' => static::class . '::pluginConfigurationAjax', ], ], 'config' => [ '#parents' => [$pluginKey, 'config'], '#array_parents' => [$pluginKey, 'config'], ], ]; if ($plugin = $entity->getPlugin($pluginInfo['id'], TRUE)) { $valueKey = $hasData ? '#value' : '#default_value'; $form[$pluginKey]['id'][$valueKey] = $plugin->getPluginId(); if ($pluginFormInstance = $this->getConfigurePluginForm($plugin)) { $pluginForm = &$form[$pluginKey]['config']; $subformState = SubformState::createForSubform($pluginForm, $form, $form_state); $pluginForm = $pluginFormInstance->buildConfigurationForm($pluginForm, $subformState, $entity); // Apply the default form wrapping components if nothing was defined // internally by the plugin configuration form itself. $pluginForm += [ '#type' => 'container', '#attributes' => [], ]; } else { $form[$pluginKey]['config']['#value'] = []; } } // Add a consistent wrapper around the configuration form elements. $form[$pluginKey]['config']['#prefix'] = '<div id="' . $wrapperId . '">'; $form[$pluginKey]['config']['#suffix'] = '</div>'; } $form['additional_settings'] = [ '#type' => 'vertical_tabs', '#weight' => 50, ]; $form['landingPage']['#group'] = 'additional_settings'; $form['landingPage']['#type'] = 'details'; $form['search_settings'] = [ '#type' => 'details', '#title' => $this->t('Search'), '#group' => 'additional_settings', 'searchable' => [ '#type' => 'checkbox', '#title' => $this->t('Is associated content searchable?'), '#default_value' => $entity->isContentSearchable(), ], ]; return $form; } /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { foreach ($this->getPluginDefinitions() as $pluginKey => $pluginDef) { $pluginId = $form_state->getValue([$pluginKey, 'id']); if (!empty($pluginId)) { try { $config = []; /** @var \Drupal\association\Entity\AssociationTypeInterface $entity */ $entity = $this->getEntity(); /** @var \Drupal\association\Entity\AssociationTypeInterface $current */ $current = $this->entityTypeManager ->getStorage('association_type') ->load($entity->id()); if ($current) { $currentPlugin = $current->get($pluginDef['id']); $config = $currentPlugin['id'] === $pluginId ? $currentPlugin['config'] : []; } $plugin = $pluginDef['manager']->createInstance($pluginId, $config); if ($pluginFormInstance = $this->getConfigurePluginForm($plugin)) { $subformState = SubformState::createForSubform($form[$pluginKey]['config'], $form, $form_state); $pluginFormInstance->validateConfigurationForm($form[$pluginKey]['config'], $subformState); } } catch (\Exception $e) { $form_state->setError($form[$pluginKey], $this->t('Unable to use selected @lablel plugin due to an internal error.', [ '@label' => $pluginDef['label'], ])); Error::logException($this->getLogger('association'), $e); } } } } /** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { /** @var \Drupal\association\Entity\AssociationTypeInterface $entity */ $entity = $this->entity; try { foreach ($this->getPluginDefinitions() as $pluginKey => $pluginDef) { /** @var \Drupal\Component\Plugin\PluginInspectionInterface&\Drupal\Component\Plugin\ConfigurableInterface $plugin */ $plugin = $entity->getPlugin($pluginDef['id']); // If this plugin uses a configuration form, allow the configuration // submit handler to properly handle building the configuration values. if ($pluginFormInstance = $this->getConfigurePluginForm($plugin)) { $subformState = SubformState::createForSubform($form[$pluginKey]['config'], $form, $form_state); $pluginFormInstance->submitConfigurationForm($form[$pluginKey]['config'], $subformState); $entity->set($pluginDef['id'], [ 'id' => $plugin->getPluginId(), 'config' => $plugin->getConfiguration(), ]); } } // Save the bundle entity changes. $status = $entity->save(); $form_state->setRedirectUrl($entity->toUrl('collection')); // Inform the admin that the association type has been saved. $msgParams = ['%label' => $entity->label()]; $this->messenger()->addStatus(SAVED_NEW === $status ? $this->t('Created the %label Entity Association type.', $msgParams) : $this->t('Saved the %label Entity Association type changes.', $msgParams) ); return $status; } catch (\Exception $e) { $this->messenger()->addError($this->t('Unable to have changes to the Entity Association type settings.')); } return 0; } /** * AJAX callback for when the association type behavior is changed. * * @param array $form * Complete form structure and element definitions. * @param \Drupal\Core\Form\FormStateInterface $form_state * Current state, build information and user input for the form. * * @return \Drupal\Core\Ajax\AjaxResponse|array * A set of AJAX commands or a renderable array with the form changes to * perform through AJAX. */ public static function pluginConfigurationAjax(array $form, FormStateInterface $form_state) { $element = $form_state->getTriggeringElement(); $parents = $element['#array_parents']; array_pop($parents); $parents[] = 'config'; return NestedArray::getValue($form, $parents); } /** * Get list of association type plugin types. * * Enumerates the association plugin types and provides basic info about each * in order to create plugin configurations for each. These are plugins that * are managed at the association type, for all associations of that type. * * @return array * The id, label and plugin options information about each of the * association plugin types. */ protected function getPluginDefinitions(): array { return [ 'behavior' => [ 'id' => 'behavior', 'label' => $this->t('Behavior settings'), 'manager' => $this->behaviorManager, 'options' => $this->getPluginOptions($this->behaviorManager), ], 'landingPage' => [ 'id' => 'landing_page', 'label' => $this->t('Landing page settings'), 'manager' => $this->landingPageManager, 'options' => $this->getPluginOptions($this->landingPageManager), ], ]; } /** * Get a list of plugins options for use with association types. * * Association types have different plugin types that are configured at the * assocation type level. The ::getPluginDefinitions() method defines info * about the plugin types that can be configured. * * @return \Drupal\Core\StringTranslation\TranslatableMarkup[]|string[] * An array of behavior plugin labels, keyed by the plugin ID. * * @see self::getPluginDefinitions() */ protected function getPluginOptions(PluginManagerInterface $plugin_manager): array { $options = []; foreach ($plugin_manager->getDefinitions() as $id => $definition) { $options[$id] = $definition['label']; } return $options; } /** * Get an instance of the plugin configuration form if there is one. * * @param \Drupal\Component\Plugin\PluginInspectionInterface $plugin * The association plugin to generate a configuration form for. * * @return \Drupal\association\Plugin\AssociationPluginFormInterface|null * A plugin form object if the plugin has a configuration form. */ protected function getConfigurePluginForm(PluginInspectionInterface $plugin): ?AssociationPluginFormInterface { if ($plugin instanceof PluginFormInterface) { return $plugin; } if ($plugin instanceof PluginWithFormsInterface) { try { return $this->pluginFormFactory->createInstance($plugin, 'configure'); } catch (InvalidPluginDefinitionException $e) { // Plugin does not have this form operation defined. } } return NULL; } }