Unverified Commit 1b66e536 authored by larowlan's avatar larowlan

Issue #2961822 by phenaproxima, tim.plunkett: Support object-based plugin...

Issue #2961822 by phenaproxima, tim.plunkett: Support object-based plugin definitions in ContextHandler
parent 41c3a88e
<?php
namespace Drupal\Component\Plugin\Definition;
use Drupal\Component\Plugin\Context\ContextDefinitionInterface;
/**
* Provides an interface for plugin definitions which use contexts.
*
* @ingroup Plugin
*/
interface ContextAwarePluginDefinitionInterface extends PluginDefinitionInterface {
/**
* Checks if the plugin defines a particular context.
*
* @param string $name
* The context name.
*
* @return bool
* TRUE if the plugin defines the given context, otherwise FALSE.
*/
public function hasContextDefinition($name);
/**
* Returns all context definitions for this plugin.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]
* The context definitions.
*/
public function getContextDefinitions();
/**
* Returns a particular context definition for this plugin.
*
* @param string $name
* The context name.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface
* The context definition.
*
* @throws \Drupal\Component\Plugin\Exception\ContextException
* Thrown if the plugin does not define the given context.
*/
public function getContextDefinition($name);
/**
* Adds a context to this plugin definition.
*
* @param string $name
* The context name.
* @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $definition
* The context definition.
*
* @return $this
* The called object.
*/
public function addContextDefinition($name, ContextDefinitionInterface $definition);
/**
* Removes a context definition from this plugin.
*
* @param string $name
* The context name.
*
* @return $this
* The called object.
*/
public function removeContextDefinition($name);
}
<?php
namespace Drupal\Component\Plugin\Definition;
use Drupal\Component\Plugin\Context\ContextDefinitionInterface;
use Drupal\Component\Plugin\Exception\ContextException;
/**
* Provides a trait for context-aware object-based plugin definitions.
*/
trait ContextAwarePluginDefinitionTrait {
/**
* The context definitions for this plugin definition.
*
* @var \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]
*/
protected $contextDefinitions = [];
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::hasContextDefinition().
*/
public function hasContextDefinition($name) {
return array_key_exists($name, $this->contextDefinitions);
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::getContextDefinitions().
*/
public function getContextDefinitions() {
return $this->contextDefinitions;
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::getContextDefinition().
*/
public function getContextDefinition($name) {
if ($this->hasContextDefinition($name)) {
return $this->contextDefinitions[$name];
}
throw new ContextException($this->id() . " does not define a '$name' context");
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::addContextDefinition().
*/
public function addContextDefinition($name, ContextDefinitionInterface $definition) {
$this->contextDefinitions[$name] = $definition;
return $this;
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::removeContextDefinition().
*/
public function removeContextDefinition($name) {
unset($this->contextDefinitions[$name]);
return $this;
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\Core\Plugin\Context;
use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Component\Plugin\Exception\MissingValueContextException;
use Drupal\Core\Cache\CacheableDependencyInterface;
......@@ -17,18 +18,37 @@ class ContextHandler implements ContextHandlerInterface {
*/
public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
return array_filter($definitions, function ($plugin_definition) use ($contexts) {
// If this plugin doesn't need any context, it is available to use.
// @todo Support object-based plugin definitions in
// https://www.drupal.org/project/drupal/issues/2961822.
if (!is_array($plugin_definition) || !isset($plugin_definition['context'])) {
return TRUE;
}
$context_definitions = $this->getContextDefinitions($plugin_definition);
// Check the set of contexts against the requirements.
return $this->checkRequirements($contexts, $plugin_definition['context']);
if ($context_definitions) {
// Check the set of contexts against the requirements.
return $this->checkRequirements($contexts, $context_definitions);
}
// If this plugin doesn't need any context, it is available to use.
return TRUE;
});
}
/**
* Returns the context definitions associated with a plugin definition.
*
* @param array|\Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface $plugin_definition
* The plugin definition.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]|null
* The context definitions, or NULL if the plugin definition does not
* support contexts.
*/
protected function getContextDefinitions($plugin_definition) {
if ($plugin_definition instanceof ContextAwarePluginDefinitionInterface) {
return $plugin_definition->getContextDefinitions();
}
if (is_array($plugin_definition) && isset($plugin_definition['context'])) {
return $plugin_definition['context'];
}
return NULL;
}
/**
* {@inheritdoc}
*/
......
......@@ -8,6 +8,9 @@
namespace Drupal\Tests\Core\Plugin;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionTrait;
use Drupal\Component\Plugin\Definition\PluginDefinition;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Component\Plugin\Exception\MissingValueContextException;
use Drupal\Core\Cache\NullBackend;
......@@ -209,49 +212,100 @@ public function providerTestFilterPluginDefinitionsByContexts() {
// No context and no plugins, no plugins available.
$data[] = [FALSE, $plugins, []];
$plugins = ['expected_plugin' => []];
$plugins = [
'expected_array_plugin' => [],
'expected_object_plugin' => new ContextAwarePluginDefinition(),
];
// No context, all plugins available.
$data[] = [FALSE, $plugins, $plugins];
$plugins = ['expected_plugin' => ['context' => []]];
$plugins = [
'expected_array_plugin' => ['context' => []],
'expected_object_plugin' => new ContextAwarePluginDefinition(),
];
// No context, all plugins available.
$data[] = [FALSE, $plugins, $plugins];
$plugins = ['expected_plugin' => ['context' => ['context1' => new ContextDefinition('string')]]];
$plugins = [
'expected_array_plugin' => [
'context' => ['context1' => new ContextDefinition('string')],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', new ContextDefinition('string')),
];
// Missing context, no plugins available.
$data[] = [FALSE, $plugins, []];
// Satisfied context, all plugins available.
$data[] = [TRUE, $plugins, $plugins];
$mismatched_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']);
$plugins = ['expected_plugin' => ['context' => ['context1' => $mismatched_context_definition]]];
$plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $mismatched_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $mismatched_context_definition),
];
// Mismatched constraints, no plugins available.
$data[] = [TRUE, $plugins, []];
$optional_mismatched_context_definition = clone $mismatched_context_definition;
$optional_mismatched_context_definition->setRequired(FALSE);
$plugins = ['expected_plugin' => ['context' => ['context1' => $optional_mismatched_context_definition]]];
$plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $optional_mismatched_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $optional_mismatched_context_definition),
];
// Optional mismatched constraint, all plugins available.
$data[] = [FALSE, $plugins, $plugins];
$expected_context_definition = (new ContextDefinition('string'))->setConstraints(['Blank' => []]);
$plugins = ['expected_plugin' => ['context' => ['context1' => $expected_context_definition]]];
$plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $expected_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $expected_context_definition),
];
// Satisfied context with constraint, all plugins available.
$data[] = [TRUE, $plugins, $plugins];
$optional_expected_context_definition = clone $expected_context_definition;
$optional_expected_context_definition->setRequired(FALSE);
$plugins = ['expected_plugin' => ['context' => ['context1' => $optional_expected_context_definition]]];
$plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $optional_expected_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $optional_expected_context_definition),
];
// Optional unsatisfied context, all plugins available.
$data[] = [FALSE, $plugins, $plugins];
$unexpected_context_definition = (new ContextDefinition('unexpected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']);
$plugins = [
'unexpected_plugin' => ['context' => ['context1' => $unexpected_context_definition]],
'expected_plugin' => ['context' => ['context2' => new ContextDefinition('string')]],
'unexpected_array_plugin' => [
'context' => ['context1' => $unexpected_context_definition],
],
'expected_array_plugin' => [
'context' => ['context2' => new ContextDefinition('string')],
],
'unexpected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $unexpected_context_definition),
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context2', new ContextDefinition('string')),
];
// Context only satisfies two plugins.
$data[] = [
TRUE,
$plugins,
[
'expected_array_plugin' => $plugins['expected_array_plugin'],
'expected_object_plugin' => $plugins['expected_object_plugin'],
],
];
// Context only satisfies one plugin.
$data[] = [TRUE, $plugins, ['expected_plugin' => $plugins['expected_plugin']]];
return $data;
}
......@@ -517,4 +571,10 @@ public function testApplyContextMappingConfigurableAssignedMiss() {
}
interface TestConfigurableContextAwarePluginInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface {
}
class ContextAwarePluginDefinition extends PluginDefinition implements ContextAwarePluginDefinitionInterface {
use ContextAwarePluginDefinitionTrait;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment