From b7833434246704beaf1bdf169c1cf7895400a251 Mon Sep 17 00:00:00 2001 From: Philipp Melab Date: Tue, 9 Aug 2016 19:26:49 +0200 Subject: [PATCH 1/2] Initial version of FetchEntityByView action. --- src/Plugin/RulesAction/EntityFetchByView.php | 88 +++++++++++++ .../RulesAction/EntityFetchByViewDeriver.php | 122 ++++++++++++++++++ src/Plugin/views/display/Rules.php | 108 ++++++++++++++++ src/Plugin/views/style/Rules.php | 75 +++++++++++ 4 files changed, 393 insertions(+) create mode 100644 src/Plugin/RulesAction/EntityFetchByView.php create mode 100644 src/Plugin/RulesAction/EntityFetchByViewDeriver.php create mode 100644 src/Plugin/views/display/Rules.php create mode 100644 src/Plugin/views/style/Rules.php diff --git a/src/Plugin/RulesAction/EntityFetchByView.php b/src/Plugin/RulesAction/EntityFetchByView.php new file mode 100644 index 00000000..17ada1d3 --- /dev/null +++ b/src/Plugin/RulesAction/EntityFetchByView.php @@ -0,0 +1,88 @@ +entityTypeManager = $entity_type_manager; + $this->viewStorage = $entity_type_manager->getStorage('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_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function execute() { + $view = $this->viewStorage->load($this->pluginDefinition['view_id'])->getExecutable(); + $arguments = []; + + foreach ($this->getContexts() as $name => $context) { + $data = $context->getContextData()->getValue(); + $arguments[$name] = $data instanceof EntityInterface ? $data->id() : $data; + } + + $view->setDisplay($this->pluginDefinition['display_id']); + + $real_args = array_map(function ($key) use ($arguments) { + return $arguments[$key]; + }, array_keys($view->display_handler->getOption('arguments'))); + + $view->setArguments($real_args); + + $entities = $view->render($this->pluginDefinition['display_id']); + $this->setProvidedValue('entity_fetched', $entities ? $entities : []); + } +} \ No newline at end of file diff --git a/src/Plugin/RulesAction/EntityFetchByViewDeriver.php b/src/Plugin/RulesAction/EntityFetchByViewDeriver.php new file mode 100644 index 00000000..d6f7056d --- /dev/null +++ b/src/Plugin/RulesAction/EntityFetchByViewDeriver.php @@ -0,0 +1,122 @@ +get('entity_type.manager'), + $container->get('string_translation') + ); + } + + /** + * EntityFetchByViewDeriver constructor. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation + */ + public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) { + $this->entityTypeManager = $entity_type_manager; + $this->viewsStorage = $entity_type_manager->getStorage('view'); + $this->stringTranslation = $string_translation; + + // Build an array of table names pointing to corresponding entity types. + foreach ($this->entityTypeManager->getDefinitions() as $entity_type) { + + if ($base_table = $entity_type->getBaseTable()) { + $this->entityTables[$base_table] = $entity_type; + } + + if ($data_table = $entity_type->getDataTable()) { + $this->entityTables[$data_table] = $entity_type; + } + + } + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + foreach (Views::getApplicableViews('rules') as $data) { + list($view_id, $display_id) = $data; + $view = $this->viewsStorage->load($view_id); + $table = $view->get('base_table'); + if (isset($this->entityTables[$table])) { + $entity_type = $this->entityTables[$table]; + + $context = []; + + /** @var $views_executable \Drupal\views\ViewExecutable */ + $views_executable = $view->getExecutable(); + $views_executable->setDisplay($display_id); + $display = $views_executable->getDisplay(); + + foreach ($display->getOption('arguments') as $argument_name => $argument) { + $label = $argument['admin_label'] ? $argument['admin_label'] : $argument_name; + $required = !in_array($argument['default_action'], ['ignore', 'default']); + $type = strpos($argument['validate']['type'], 'entity:') !== FALSE ? "entity:" . explode(':', $argument['validate']['type'])[1] : "string"; + + $context[$argument_name] = ContextDefinition::create($type) + ->setLabel($label) + ->setRequired($required); + } + + $this->derivatives[$view_id . ':' . $display_id]= [ + 'label' => $this->t('Fetch entities from @view - @display', [ + '@view' => $view_id, + '@display' => $display->display['display_title'], + ]), + 'view_id' => $view_id, + 'display_id' => $display_id, + 'context' => $context, + 'provides' => [ + 'entity_fetched' => ContextDefinition::create("entity:" . $entity_type->id()) + ->setLabel($entity_type->getLabel()) + ->setMultiple(TRUE) + ], + ] + $base_plugin_definition; + } + } + return $this->derivatives; + } +} diff --git a/src/Plugin/views/display/Rules.php b/src/Plugin/views/display/Rules.php new file mode 100644 index 00000000..441d62ce --- /dev/null +++ b/src/Plugin/views/display/Rules.php @@ -0,0 +1,108 @@ + 'rules'); + $options['defaults']['default']['style'] = FALSE; + + // Set the display title to an empty string (not used in this display type). + $options['title']['default'] = ''; + $options['defaults']['default']['title'] = FALSE; + + return $options; + } + + /** + * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::optionsSummary(). + * + * Disable 'cache' and 'title' so it won't be changed. + */ + public function optionsSummary(&$categories, &$options) { + parent::optionsSummary($categories, $options); + unset($options['title']); + } + + /** + * {@inheritdoc} + */ + public function getType() { + return 'rules'; + } + + /** + * {@inheritdoc} + */ + public function execute() { + return $this->view->render($this->display['id']); + } + + /** + * {@inheritdoc} + */ + public function render() { + if (!empty($this->view->result) && $this->view->style_plugin->evenEmpty()) { + return $this->view->style_plugin->render($this->view->result); + } + return ''; + } + + /** + * {@inheritdoc} + */ + public function usesExposed() { + return FALSE; + } +} \ No newline at end of file diff --git a/src/Plugin/views/style/Rules.php b/src/Plugin/views/style/Rules.php new file mode 100644 index 00000000..4310390c --- /dev/null +++ b/src/Plugin/views/style/Rules.php @@ -0,0 +1,75 @@ +_entity; + }, $this->view->result); + + $entities = array_filter($entities, function ($value) { return (bool) $value; }); + + if (!empty($this->view->live_preview)) { + return [ + '#theme' => 'item_list', + '#items' => array_map(function (EntityInterface $entity) { + return $entity->label(); + }, $entities) + ]; + } + + return $entities; + } + + /** + * {@inheritdoc} + */ + public function evenEmpty() { + return TRUE; + } +} \ No newline at end of file From c340b843f325f0fdca4cae3896bc5b6bbdfbe348 Mon Sep 17 00:00:00 2001 From: Philipp Melab Date: Wed, 10 Aug 2016 11:30:57 +0200 Subject: [PATCH 2/2] Refactoring and comments. --- src/Plugin/RulesAction/EntityFetchByView.php | 46 ++++++++---- .../RulesAction/EntityFetchByViewDeriver.php | 75 +++++++++---------- src/Plugin/views/display/Rules.php | 35 +++++++++ 3 files changed, 104 insertions(+), 52 deletions(-) diff --git a/src/Plugin/RulesAction/EntityFetchByView.php b/src/Plugin/RulesAction/EntityFetchByView.php index 17ada1d3..27eba16d 100644 --- a/src/Plugin/RulesAction/EntityFetchByView.php +++ b/src/Plugin/RulesAction/EntityFetchByView.php @@ -66,23 +66,41 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function execute() { - $view = $this->viewStorage->load($this->pluginDefinition['view_id'])->getExecutable(); - $arguments = []; - foreach ($this->getContexts() as $name => $context) { - $data = $context->getContextData()->getValue(); - $arguments[$name] = $data instanceof EntityInterface ? $data->id() : $data; - } + $view_id = $this->pluginDefinition['view_id']; + $display_id = $this->pluginDefinition['display_id']; + + // Fetch the list of available contexts. + $contexts = $this->getContexts(); + + // Pull values out of contexts. + $contexts = array_map(function ($context) { + return $context->getContextData()->getValue(); + }, $contexts); - $view->setDisplay($this->pluginDefinition['display_id']); + // Convert entities into entity ids. + $contexts = array_map(function ($context) { + return $context instanceof EntityInterface ? $context->id() : $context; + }, $contexts); - $real_args = array_map(function ($key) use ($arguments) { - return $arguments[$key]; - }, array_keys($view->display_handler->getOption('arguments'))); + // Request the views executable for the current display. + $view = $this->viewStorage->load($view_id)->getExecutable(); + $view->setDisplay($display_id); - $view->setArguments($real_args); + $arguments = []; + + // Reverse- loop through the views contextual arguments and skip empty + // arguments until the first defined one. + foreach (array_reverse(array_keys($view->display_handler->getOption('arguments'))) as $arg) { + if ($contexts[$arg] == '' && count($arguments) == 0) { + continue; + } + $arguments[$arg] = $contexts[$arg]; + } - $entities = $view->render($this->pluginDefinition['display_id']); - $this->setProvidedValue('entity_fetched', $entities ? $entities : []); + // Execute the view and pass the result as provided value. + $view->setArguments($arguments); + $entities = $view->render($this->pluginDefinition['display_id']) ?: []; + $this->setProvidedValue('entity_fetched', $entities); } -} \ No newline at end of file +} diff --git a/src/Plugin/RulesAction/EntityFetchByViewDeriver.php b/src/Plugin/RulesAction/EntityFetchByViewDeriver.php index d6f7056d..145cd04c 100644 --- a/src/Plugin/RulesAction/EntityFetchByViewDeriver.php +++ b/src/Plugin/RulesAction/EntityFetchByViewDeriver.php @@ -8,6 +8,7 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\rules\Context\ContextDefinition; +use Drupal\rules\Plugin\views\display\Rules; use Drupal\views\Views; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -58,63 +59,61 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Tra $this->entityTypeManager = $entity_type_manager; $this->viewsStorage = $entity_type_manager->getStorage('view'); $this->stringTranslation = $string_translation; + } - // Build an array of table names pointing to corresponding entity types. - foreach ($this->entityTypeManager->getDefinitions() as $entity_type) { + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + // Build a lookup dictionary of table names pointing to corresponding + // entity types. Used to determine which entity type is the result of a + // given view. + $entity_types = []; + foreach ($this->entityTypeManager->getDefinitions() as $entity_type) { if ($base_table = $entity_type->getBaseTable()) { - $this->entityTables[$base_table] = $entity_type; + $entity_types[$base_table] = $entity_type; } if ($data_table = $entity_type->getDataTable()) { - $this->entityTables[$data_table] = $entity_type; + $entity_types[$data_table] = $entity_type; } - } - } - /** - * {@inheritdoc} - */ - public function getDerivativeDefinitions($base_plugin_definition) { foreach (Views::getApplicableViews('rules') as $data) { list($view_id, $display_id) = $data; + + // Fetch the current view applicable view and get it's base table. + /** @var $view \Drupal\views\Entity\View */ $view = $this->viewsStorage->load($view_id); $table = $view->get('base_table'); - if (isset($this->entityTables[$table])) { - $entity_type = $this->entityTables[$table]; - - $context = []; - /** @var $views_executable \Drupal\views\ViewExecutable */ + /** @var $entity_type \Drupal\Core\Entity\EntityTypeInterface */ + if ($entity_type = $entity_types[$table] ?: FALSE) { + // Proceed only, if the view is based on an entity. + // Prepare views executable and display. $views_executable = $view->getExecutable(); $views_executable->setDisplay($display_id); $display = $views_executable->getDisplay(); - foreach ($display->getOption('arguments') as $argument_name => $argument) { - $label = $argument['admin_label'] ? $argument['admin_label'] : $argument_name; - $required = !in_array($argument['default_action'], ['ignore', 'default']); - $type = strpos($argument['validate']['type'], 'entity:') !== FALSE ? "entity:" . explode(':', $argument['validate']['type'])[1] : "string"; - - $context[$argument_name] = ContextDefinition::create($type) - ->setLabel($label) - ->setRequired($required); - } - - $this->derivatives[$view_id . ':' . $display_id]= [ - 'label' => $this->t('Fetch entities from @view - @display', [ - '@view' => $view_id, - '@display' => $display->display['display_title'], - ]), - 'view_id' => $view_id, - 'display_id' => $display_id, - 'context' => $context, - 'provides' => [ - 'entity_fetched' => ContextDefinition::create("entity:" . $entity_type->id()) - ->setLabel($entity_type->getLabel()) - ->setMultiple(TRUE) - ], + // Build the list of derivative definitions if the display is of type + // "Rules". + if ($display instanceof Rules) { + $this->derivatives[$view_id . ':' . $display_id]= [ + 'label' => $this->t('Fetch entities from @view - @display', [ + '@view' => $view_id, + '@display' => $display->display['display_title'], + ]), + 'view_id' => $view_id, + 'display_id' => $display_id, + 'context' => $display->getRulesContext(), + 'provides' => [ + 'entity_fetched' => ContextDefinition::create("entity:" . $entity_type->id()) + ->setLabel($entity_type->getLabel()) + ->setMultiple(TRUE) + ], ] + $base_plugin_definition; + } } } return $this->derivatives; diff --git a/src/Plugin/views/display/Rules.php b/src/Plugin/views/display/Rules.php index 441d62ce..3542c4d3 100644 --- a/src/Plugin/views/display/Rules.php +++ b/src/Plugin/views/display/Rules.php @@ -1,6 +1,7 @@ getOption('arguments') as $argument_name => $argument) { + // Use the admin title as context label if possible. + $label = $argument['admin_label'] ?: $argument_name; + + // If the view is configured to display all items or has a configured + // default value for this argument, don't mark the context as required. + $required = !in_array($argument['default_action'], ['ignore', 'default']); + + // Default type for arguments is string. + $type = 'string'; + + // Check if views argument validation is configured for a specific entity + // type. Use this type as context type definition. + if (strpos($argument['validate']['type'], 'entity:') !== FALSE) { + $type = $argument['validate']['type']; + } + + $context[$argument_name] = ContextDefinition::create($type) + ->setLabel($label) + ->setRequired($required); + } + + return $context; + } } \ No newline at end of file