diff --git a/core/lib/Drupal/Component/Plugin/DependencyRemovalPluginInterface.php b/core/lib/Drupal/Component/Plugin/DependencyRemovalPluginInterface.php new file mode 100644 index 0000000..4bdf9b9 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/DependencyRemovalPluginInterface.php @@ -0,0 +1,30 @@ + $dependencies) { - if ($removed_dependencies[$type]) { - // Config and content entities have the dependency names as keys while - // module and theme dependencies are indexed arrays of dependency names. - // @see \Drupal\Core\Config\ConfigManager::callOnDependencyRemoval() - if (in_array($type, ['config', 'content'])) { - $removed = array_intersect_key($removed_dependencies[$type], array_flip($dependencies)); - } - else { - $removed = array_values(array_intersect($removed_dependencies[$type], $dependencies)); - } - if ($removed) { - $intersect[$type] = $removed; - } - } - } - return $intersect; - } - - /** * {@inheritdoc} */ public function __sleep() { diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php index 1b35d4a..5921b1c 100644 --- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php @@ -76,9 +76,6 @@ public function getSelectionGroups($entity_type_id) { $plugins = array(); $definitions = $this->getDefinitions(); - // Do not display the 'broken' plugin in the UI. - unset($definitions['broken']); - foreach ($definitions as $plugin_id => $plugin) { if (empty($plugin['entity_types']) || in_array($entity_type_id, $plugin['entity_types'])) { $plugins[$plugin['group']][$plugin_id] = $plugin; diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php index d74fb6e..e32bafe 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php +++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php @@ -11,7 +11,8 @@ * * @EntityReferenceSelection( * id = "broken", - * label = @Translation("Broken/Missing") + * label = @Translation("Broken/Missing"), + * group = "broken" * ) */ class Broken implements SelectionInterface { diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php index 9565e77..e8e4023 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php +++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php @@ -5,17 +5,11 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Database\Query\SelectInterface; -use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface; -use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\Core\Plugin\PluginBase; -use Drupal\Core\Session\AccountInterface; use Drupal\user\EntityOwnerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Default plugin implementation of the Entity Reference Selection plugin. @@ -37,65 +31,23 @@ * deriver = "Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver" * ) */ -class DefaultSelection extends PluginBase implements SelectionInterface, SelectionWithAutocreateInterface, ContainerFactoryPluginInterface { - - /** - * The entity manager. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The current user. - * - * @var \Drupal\Core\Session\AccountInterface - */ - protected $currentUser; - - /** - * Constructs a new SelectionBase object. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Session\AccountInterface $current_user - * The current user. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - - $this->entityManager = $entity_manager; - $this->moduleHandler = $module_handler; - $this->currentUser = $current_user; - } +class DefaultSelection extends SelectionPluginBase implements SelectionWithAutocreateInterface { /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('entity.manager'), - $container->get('module_handler'), - $container->get('current_user') - ); + public function defaultHandlerSettings() { + return [ + // For the 'target_bundles' setting, a NULL value is equivalent to "allow + // entities from any bundle to be referenced" and an empty array value is + // equivalent to "no entities from any bundle can be referenced". + 'target_bundles' => NULL, + 'sort' => [ + 'field' => '_none', + ], + 'auto_create' => FALSE, + 'auto_create_bundle' => NULL, + ]; } /** @@ -107,19 +59,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $entity_type = $this->entityManager->getDefinition($entity_type_id); $bundles = $this->entityManager->getBundleInfo($entity_type_id); - // Merge-in default values. - $selection_handler_settings += array( - // For the 'target_bundles' setting, a NULL value is equivalent to "allow - // entities from any bundle to be referenced" and an empty array value is - // equivalent to "no entities from any bundle can be referenced". - 'target_bundles' => NULL, - 'sort' => array( - 'field' => '_none', - ), - 'auto_create' => FALSE, - 'auto_create_bundle' => NULL, - ); - if ($entity_type->hasKey('bundle')) { $bundle_options = array(); foreach ($bundles as $bundle_name => $bundle_info) { @@ -441,4 +380,82 @@ protected function reAlterQuery(AlterableInterface $query, $tag, $base_table) { $query->alterMetaData = $old_metadata; } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + $dependencies = parent::calculateDependencies(); + + $target_entity_type = $this->entityManager->getDefinition($this->configuration['target_type']); + // Depend on target bundle configurations. Dependencies for 'target_bundles' + // also covers the 'auto_create_bundle' setting, if any, because its value + // is included in the 'target_bundles' list. + $handler_settings = $this->configuration['handler_settings']; + if (!empty($handler_settings['target_bundles'])) { + if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) { + if ($storage = $this->entityManager->getStorage($bundle_entity_type_id)) { + foreach ($storage->loadMultiple($handler_settings['target_bundles']) as $bundle) { + $dependencies[$bundle->getConfigDependencyKey()][] = $bundle->getConfigDependencyName(); + } + } + } + } + + return $dependencies; + } + + /** + * {@inheritdoc} + */ + public function onDependencyRemoval(array $dependencies, FieldDefinitionInterface $field_definition = NULL) { + $changed = parent::onDependencyRemoval($dependencies); + + // Update the 'target_bundles' handler setting if a bundle config dependency + // has been removed. + $configuration = $this->getConfiguration(); + $handler_settings = $configuration['handler_settings']; + if (!empty($handler_settings['target_bundles'])) { + $target_entity_type = $this->entityManager->getDefinition($this->configuration['target_type']); + if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) { + if ($storage = $this->entityManager->getStorage($bundle_entity_type_id)) { + foreach ($storage->loadMultiple($handler_settings['target_bundles']) as $bundle) { + if (isset($dependencies[$bundle->getConfigDependencyKey()][$bundle->getConfigDependencyName()])) { + unset($handler_settings['target_bundles'][$bundle->id()]); + + // If this bundle is also used in the 'auto_create_bundle' + // setting, disable the auto-creation feature completely. + $auto_create_bundle = !empty($handler_settings['auto_create_bundle']) ? $handler_settings['auto_create_bundle'] : FALSE; + if ($auto_create_bundle && $auto_create_bundle == $bundle->id()) { + $handler_settings['auto_create'] = FALSE; + $handler_settings['auto_create_bundle'] = NULL; + } + } + } + + if ($handler_settings !== $configuration['handler_settings']) { + // Update the configuration only if handler settings were changed. + $configuration['handler_settings'] = $handler_settings; + $this->setConfiguration($configuration); + $changed = TRUE; + + // In case we deleted the only target bundle allowed by the field we + // have to log a critical message because the field will not + // function correctly anymore. + if (!empty($bundle) && ($handler_settings['target_bundles'] === [])) { + \Drupal::logger('entity_reference')->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [ + '%target_bundle' => $bundle->label(), + '%target_entity_type' => $bundle->getEntityType()->getBundleOf(), + '%field_name' => $field_definition->getName(), + '%entity_type' => $field_definition->getTargetEntityTypeId(), + '%bundle' => $field_definition->getTargetBundle() + ]); + } + } + } + } + } + + return $changed; + } + } diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionPluginBase.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionPluginBase.php new file mode 100644 index 0000000..2617f91 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionPluginBase.php @@ -0,0 +1,134 @@ +setConfiguration($configuration); + $this->entityManager = $entity_manager; + $this->moduleHandler = $module_handler; + $this->currentUser = $current_user; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity.manager'), + $container->get('module_handler'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'target_type' => NULL, + 'handler' => NULL, + 'handler_settings' => $this->defaultHandlerSettings(), + ]; + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = NestedArray::mergeDeep( + $this->defaultConfiguration(), + $configuration + ); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + + /** + * {@inheritdoc} + */ + public function onDependencyRemoval(array $dependencies) { + return FALSE; + } + + /** + * Returns the default handler settings. + * + * @return array + * + * @todo Add this to SelectionInterface in Drupal 9.0.x. + */ + public function defaultHandlerSettings() { + return []; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php index e8832f3..d9fd1b5 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php @@ -2,6 +2,8 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; +use Drupal\Component\Plugin\DependencyRemovalPluginInterface; +use Drupal\Component\Plugin\DependentPluginInterface; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityInterface; @@ -13,6 +15,7 @@ use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\OptGroup; +use Drupal\Core\Plugin\PluginRemovedDependencyTrait; use Drupal\Core\Render\Element; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; @@ -39,6 +42,8 @@ */ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterface, PreconfiguredFieldUiOptionsInterface { + use PluginRemovedDependencyTrait; + /** * {@inheritdoc} */ @@ -300,6 +305,10 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { // Get all selection plugins for this entity type. $selection_plugins = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionGroups($this->getSetting('target_type')); + + // Do not display the 'broken' plugin in the UI. + unset($selection_plugins['broken']); + $handlers_options = array(); foreach (array_keys($selection_plugins) as $selection_group_id) { // We only display base plugins (e.g. 'default', 'views', ...) and not @@ -408,18 +417,11 @@ public static function calculateDependencies(FieldDefinitionInterface $field_def } } - // Depend on target bundle configurations. Dependencies for 'target_bundles' - // also covers the 'auto_create_bundle' setting, if any, because its value - // is included in the 'target_bundles' list. - $handler = $field_definition->getSetting('handler_settings'); - if (!empty($handler['target_bundles'])) { - if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) { - if ($storage = $manager->getStorage($bundle_entity_type_id)) { - foreach ($storage->loadMultiple($handler['target_bundles']) as $bundle) { - $dependencies[$bundle->getConfigDependencyKey()][] = $bundle->getConfigDependencyName(); - } - } - } + $selection_handler = static::getSelectionManager()->getSelectionHandler($field_definition); + + // Let the selection handler plugin add its dependencies. + if ($selection_handler instanceof DependentPluginInterface) { + $dependencies += $selection_handler->calculateDependencies(); } return $dependencies; @@ -460,48 +462,28 @@ public static function onDependencyRemoval(FieldDefinitionInterface $field_defin } } - // Update the 'target_bundles' handler setting if a bundle config dependency - // has been removed. - $bundles_changed = FALSE; - $handler_settings = $field_definition->getSetting('handler_settings'); - if (!empty($handler_settings['target_bundles'])) { - if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) { - if ($storage = $entity_manager->getStorage($bundle_entity_type_id)) { - foreach ($storage->loadMultiple($handler_settings['target_bundles']) as $bundle) { - if (isset($dependencies[$bundle->getConfigDependencyKey()][$bundle->getConfigDependencyName()])) { - unset($handler_settings['target_bundles'][$bundle->id()]); - - // If this bundle is also used in the 'auto_create_bundle' - // setting, disable the auto-creation feature completely. - $auto_create_bundle = !empty($handler_settings['auto_create_bundle']) ? $handler_settings['auto_create_bundle'] : FALSE; - if ($auto_create_bundle && $auto_create_bundle == $bundle->id()) { - $handler_settings['auto_create'] = NULL; - $handler_settings['auto_create_bundle'] = NULL; - } - - $bundles_changed = TRUE; - - // In case we deleted the only target bundle allowed by the field - // we have to log a critical message because the field will not - // function correctly anymore. - if ($handler_settings['target_bundles'] === []) { - \Drupal::logger('entity_reference')->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [ - '%target_bundle' => $bundle->label(), - '%target_entity_type' => $bundle->getEntityType()->getBundleOf(), - '%field_name' => $field_definition->getName(), - '%entity_type' => $field_definition->getTargetEntityTypeId(), - '%bundle' => $field_definition->getTargetBundle() - ]); - } - } - } + // Let the selection handler plugin react on dependency removal. + $handler = static::getSelectionManager()->getSelectionHandler($field_definition); + if ($handler instanceof DependencyRemovalPluginInterface) { + $handler_removed_dependencies = static::getPluginRemovedDependencies($handler->calculateDependencies(), $dependencies); + if ($handler_removed_dependencies) { + if ($handler->onDependencyRemoval($handler_removed_dependencies, $field_definition)) { + $field_definition->setSetting('handler_settings', $handler->getConfiguration()['handler_settings']); + $changed = TRUE; + } + if (static::getPluginRemovedDependencies($handler->calculateDependencies(), $dependencies)) { + /** @var \Drupal\field\Entity\FieldConfig $field_definition */ + $field_definition + ->setSetting('handler', 'broken') + ->setSetting('handler_settings', []); + $changed = TRUE; + $arguments = [ + '@field' => "{$field_definition->getTargetEntityTypeId()}.{$field_definition->getTargetBundle()}.{$field_definition->getName()}", + ]; + \Drupal::logger('entity_reference')->critical("Field @field selection handler settings have dependencies that were removed. The field is not working correctly anymore and has to be adjusted.", $arguments); } } } - if ($bundles_changed) { - $field_definition->setSetting('handler_settings', $handler_settings); - } - $changed |= $bundles_changed; return $changed; } @@ -645,4 +627,15 @@ public static function getPreconfiguredOptions() { return $options; } + /** + * Returns the selection plugin manager service. + * + * @return \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface + * + * @todo Properly inject this in Drupal 9.0.x. + */ + protected static function getSelectionManager() { + return \Drupal::service('plugin.manager.entity_reference_selection'); + } + } diff --git a/core/lib/Drupal/Core/Plugin/PluginRemovedDependencyTrait.php b/core/lib/Drupal/Core/Plugin/PluginRemovedDependencyTrait.php new file mode 100644 index 0000000..77c95c9 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/PluginRemovedDependencyTrait.php @@ -0,0 +1,52 @@ + $dependencies) { + if ($removed_dependencies[$type]) { + // Config and content entities have the dependency names as keys while + // module and theme dependencies are indexed arrays of dependency names. + // @see \Drupal\Core\Config\ConfigManager::callOnDependencyRemoval() + if (in_array($type, ['config', 'content'])) { + $removed = array_intersect_key($removed_dependencies[$type], array_flip($dependencies)); + } + else { + $removed = array_values(array_intersect($removed_dependencies[$type], $dependencies)); + } + if ($removed) { + $intersect[$type] = $removed; + } + } + } + return $intersect; + } + +} diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceDependencyTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceDependencyTest.php new file mode 100644 index 0000000..6337bb9 --- /dev/null +++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceDependencyTest.php @@ -0,0 +1,62 @@ +drupalLogin($this->drupalCreateUser(['access content'])); + + // Create an entity reference field using ViewsSelection handler. + $handler_settings = [ + 'view' => [ + 'view_name' => 'test_entity_reference', + 'display_name' => 'entity_reference_1', + 'arguments' => [], + ] + ]; + $this->createEntityReferenceField('entity_test', 'entity_test', 'reference', 'Referenced content', 'node', 'views', $handler_settings); + + /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager */ + $selection_manager = $this->container->get('plugin.manager.entity_reference_selection'); + $field_config = FieldConfig::loadByName('entity_test', 'entity_test', 'reference'); + $entities = $selection_manager->getSelectionHandler($field_config)->getReferenceableEntities(); + + // Check that the selection handler returns an empty list of entities. + $this->assertSame([], $entities); + + // Delete the view that provides options for the entity reference field. + View::load('test_entity_reference')->delete(); + + // Re-load the field config. + $field_config = FieldConfig::loadByName('entity_test', 'entity_test', 'reference'); + + // Check that the entity reference field was not deleted. + $this->assertNotNull($field_config); + + // Check that the entity reference has 'broken' as handler. + $this->assertSame('broken', $field_config->getSetting('handler')); + $this->assertSame([], $field_config->getSetting('handler_settings')); + } + +} diff --git a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php index db19052..d6f9ded 100644 --- a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php +++ b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php @@ -5,7 +5,9 @@ use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface; +use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionPluginBase; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Field\FieldException; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginBase; @@ -24,80 +26,33 @@ * weight = 0 * ) */ -class ViewsSelection extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface { +class ViewsSelection extends SelectionPluginBase { /** - * The entity manager. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * The module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The current user. - * - * @var \Drupal\Core\Session\AccountInterface - */ - protected $currentUser; - - /** - * Constructs a new SelectionBase object. + * The loaded View object. * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Session\AccountInterface $current_user - * The current user. + * @var \Drupal\views\ViewExecutable; */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - - $this->entityManager = $entity_manager; - $this->moduleHandler = $module_handler; - $this->currentUser = $current_user; - } + protected $view; /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('entity.manager'), - $container->get('module_handler'), - $container->get('current_user') - ); + public function defaultHandlerSettings() { + return [ + 'view' => [ + 'view_name' => NULL, + 'display_name' => NULL, + 'arguments' => [], + ], + ] + parent::defaultHandlerSettings(); } /** - * The loaded View object. - * - * @var \Drupal\views\ViewExecutable; - */ - protected $view; - - /** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $selection_handler_settings = $this->configuration['handler_settings']; - $view_settings = !empty($selection_handler_settings['view']) ? $selection_handler_settings['view'] : array(); + $view_settings = $this->getConfiguration()['handler_settings']['view']; $displays = Views::getApplicableViews('entity_reference_display'); // Filter views that list the entity type we want, and group the separate // displays by view. @@ -182,6 +137,10 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s * * @return bool * Return TRUE if the view was initialized, FALSE otherwise. + * + * @throws \Drupal\Core\Field\FieldException + * When the view or view display stored in entity reference field handler + * settings are missed or inaccessible. */ protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $limit = 0, $ids = NULL) { $handler_settings = $this->configuration['handler_settings']; @@ -189,10 +148,11 @@ protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $ $display_name = $handler_settings['view']['display_name']; // Check that the view is valid and the display still exists. - $this->view = Views::getView($view_name); - if (!$this->view || !$this->view->access($display_name)) { - drupal_set_message(t('The reference view %view_name cannot be found.', array('%view_name' => $view_name)), 'warning'); - return FALSE; + if (!$view_name || !($this->view = Views::getView($view_name))) { + throw new FieldException("The reference view $view_name cannot be found."); + } + elseif (!$display_name || !$this->view->access($display_name)) { + throw new FieldException("The reference display $view_name:$display_name doesn't exist or is inaccessible."); } $this->view->setDisplay($display_name); @@ -211,14 +171,14 @@ protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $ * {@inheritdoc} */ public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { + $this->initializeView($match, $match_operator, $limit); + $handler_settings = $this->configuration['handler_settings']; $display_name = $handler_settings['view']['display_name']; $arguments = $handler_settings['view']['arguments']; - $result = array(); - if ($this->initializeView($match, $match_operator, $limit)) { - // Get the results. - $result = $this->view->executeDisplay($display_name, $arguments); - } + + // Get the results. + $result = $this->view->executeDisplay($display_name, $arguments); $return = array(); if ($result) { @@ -242,16 +202,15 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON * {@inheritdoc} */ public function validateReferenceableEntities(array $ids) { + $this->initializeView(NULL, 'CONTAINS', 0, $ids); + $handler_settings = $this->configuration['handler_settings']; $display_name = $handler_settings['view']['display_name']; $arguments = $handler_settings['view']['arguments']; - $result = array(); - if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) { - // Get the results. - $entities = $this->view->executeDisplay($display_name, $arguments); - $result = array_keys($entities); - } - return $result; + + // Get the results. + $entities = $this->view->executeDisplay($display_name, $arguments); + return array_keys($entities); } /** @@ -288,4 +247,23 @@ public static function settingsFormValidate($element, FormStateInterface $form_s */ public function entityQueryAlter(SelectInterface $query) { } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + $dependencies = parent::calculateDependencies(); + + try { + $this->initializeView(NULL, 'CONTAINS', 0); + $view = $this->view->storage; + // Add the view as dependency. + $dependencies[$view->getConfigDependencyKey()][] = $view->getConfigDependencyName(); + } + catch (\Exception $e) { + // Nothing to do. + } + + return $dependencies; + } + }