diff --git a/src/Plugin/RulesAction/EntityFetchByView.php b/src/Plugin/RulesAction/EntityFetchByView.php new file mode 100644 index 00000000..27eba16d --- /dev/null +++ b/src/Plugin/RulesAction/EntityFetchByView.php @@ -0,0 +1,106 @@ +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_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); + + // Convert entities into entity ids. + $contexts = array_map(function ($context) { + return $context instanceof EntityInterface ? $context->id() : $context; + }, $contexts); + + // Request the views executable for the current display. + $view = $this->viewStorage->load($view_id)->getExecutable(); + $view->setDisplay($display_id); + + $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]; + } + + // 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); + } +} diff --git a/src/Plugin/RulesAction/EntityFetchByViewDeriver.php b/src/Plugin/RulesAction/EntityFetchByViewDeriver.php new file mode 100644 index 00000000..145cd04c --- /dev/null +++ b/src/Plugin/RulesAction/EntityFetchByViewDeriver.php @@ -0,0 +1,121 @@ +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; + } + + /** + * {@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()) { + $entity_types[$base_table] = $entity_type; + } + + if ($data_table = $entity_type->getDataTable()) { + $entity_types[$data_table] = $entity_type; + } + } + + 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'); + + /** @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(); + + // 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 new file mode 100644 index 00000000..3542c4d3 --- /dev/null +++ b/src/Plugin/views/display/Rules.php @@ -0,0 +1,143 @@ + '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; + } + + /** + * Build a list of rules context definitions based on the defined views + * contextual arguments. + * + * @return \Drupal\rules\Context\ContextDefinitionInterface[] + */ + public function getRulesContext() { + $context = []; + + foreach ($this->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 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