/** * Maps variables from rules state into the plugin context. * * @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin * The plugin that is populated with context values. * @param \Drupal\rules\Engine\ExecutionStateInterface $state * The Rules state containing available variables. * * @throws \Drupal\rules\Exception\RulesEvaluationException * In case a required context is missing for the plugin. */ protected function mapContext(CoreContextAwarePluginInterface $plugin, ExecutionStateInterface $state) { $context_definitions = $plugin->getContextDefinitions(); foreach ($context_definitions as $name => $definition) { // Check if a data selector is configured that maps to the state. if (isset($this->configuration['context_mapping'][$name])) { $typed_data = $state->fetchDataByPropertyPath($this->configuration['context_mapping'][$name]); if ($typed_data->getValue() === NULL && !$definition->isAllowedNull()) { throw new RulesEvaluationException('The value of data selector ' . $this->configuration['context_mapping'][$name] . " is NULL, but the context {$name} in " . $plugin->getPluginId() . ' requires a value.'); } $context = $plugin->getContext($name); $new_context = Context::createFromContext($context, $typed_data); $plugin->setContext($name, $new_context); } elseif (isset($this->configuration['context_values']) && array_key_exists($name, $this->configuration['context_values'])) { if ($this->configuration['context_values'][$name] === NULL && !$definition->isAllowedNull()) { throw new RulesEvaluationException("The context value for {$name} is NULL, but the context {$name} in " . $plugin->getPluginId() . ' requires a value.'); } $context = $plugin->getContext($name); $new_context = Context::createFromContext($context, $this->configuration['context_values'][$name]); $plugin->setContext($name, $new_context); } elseif ($definition->isRequired()) { throw new RulesEvaluationException("Required context {$name} is missing for plugin " . $plugin->getPluginId() . '.'); } } }
/** * {@inheritdoc} */ public function executeWithState(ExecutionStateInterface $state) { $list_data = $state->fetchDataByPropertyPath($this->configuration['list']); // Use a configured list item variable name, otherwise fall back to just // 'list_item' as variable name. $list_item_name = isset($this->configuration['list_item']) ? $this->configuration['list_item'] : 'list_item'; foreach ($list_data as $item) { $state->setVariableData($list_item_name, $item); foreach ($this->actions as $action) { $action->executeWithState($state); } } // After the loop the list item is out of scope and cannot be used by any // following actions. $state->removeVariable($list_item_name); }
/** * Prepares plugin context based upon the set context configuration. * * The plugin is prepared for execution by mapping the variables from the * execution state into the plugin context and applying data processors. * In addition, it is ensured that all required context is basically * available as defined. This include the following checks: * - Required context must have a value set. * - Context may not have NULL values unless the plugin allows it. * * @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin * The plugin that is populated with context values. * @param \Drupal\rules\Engine\ExecutionStateInterface $state * The execution state containing available variables. * * @throws \Drupal\rules\Exception\RulesEvaluationException * Thrown if some context is not satisfied; e.g. a required context is * missing. * * @see ::prepareContextWithMetadata() */ protected function prepareContext(CoreContextAwarePluginInterface $plugin, ExecutionStateInterface $state) { if (isset($this->configuration['context_values'])) { foreach ($this->configuration['context_values'] as $name => $value) { $plugin->setContextValue($name, $value); } } $selected_data = []; // Map context by applying data selectors and collected the definitions of // selected data for refining context definitions later. Note, that we must // refine context definitions on execution time also, such that provided // context gets the right metadata attached. if (isset($this->configuration['context_mapping'])) { foreach ($this->configuration['context_mapping'] as $name => $selector) { $typed_data = $state->fetchDataByPropertyPath($selector); $plugin->setContextValue($name, $typed_data); $selected_data[$name] = $typed_data->getDataDefinition(); } } if ($plugin instanceof ContextAwarePluginInterface) { // Getting context values may lead to undocumented exceptions if context // is not set right now. So catch those exceptions. // @todo: Remove ones https://www.drupal.org/node/2677162 got fixed. try { $plugin->refineContextDefinitions($selected_data); } catch (ContextException $e) { if (strpos($e->getMessage(), 'context is required') === FALSE) { throw new RulesEvaluationException($e->getMessage()); } } } // Apply data processors. $this->processData($plugin, $state); // Finally, ensure all contexts are set as expected now. foreach ($plugin->getContextDefinitions() as $name => $definition) { if ($plugin->getContextValue($name) === NULL && $definition->isRequired()) { // If a context mapping has been specified, the value might end up NULL // but valid (e.g. a reference on an empty property). In that case // isAllowedNull determines whether the context is conform. if (!isset($this->configuration['context_mapping'][$name])) { throw new RulesEvaluationException("Required context {$name} is missing for plugin " . $plugin->getPluginId() . '.'); } elseif (!$definition->isAllowedNull()) { throw new RulesEvaluationException("The context for {$name} is NULL, but the context {$name} in " . $plugin->getPluginId() . ' requires a value.'); } } } }