diff --git a/plugin.module b/plugin.module index b12b44e..1a626a0 100644 --- a/plugin.module +++ b/plugin.module @@ -42,7 +42,7 @@ function plugin_field_views_data_alter(array &$data, FieldStorageConfigInterface $table_name = $field_storage->getTargetEntityTypeId() . '__' . $field_storage->getName(); $original_field_name = $field_storage->getName() . '_plugin_id'; - // Skip if there is no data for this field. + // Skip if there is no Views data for this field. if (!isset($data[$table_name][$original_field_name])) { return; } diff --git a/src/Plugin/views/filter/PluginId.php b/src/Plugin/views/filter/PluginId.php index 7ad2f70..25cfefd 100644 --- a/src/Plugin/views/filter/PluginId.php +++ b/src/Plugin/views/filter/PluginId.php @@ -2,6 +2,8 @@ namespace Drupal\plugin\Plugin\views\filter; +use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\plugin\PluginDefinition\PluginLabelDefinitionInterface; use Drupal\plugin\PluginType\PluginTypeInterface; @@ -59,17 +61,61 @@ final class PluginId extends InOperator implements ContainerFactoryPluginInterfa return $this->valueOptions; } - $this->valueOptions = []; - $this->valueOptions = array_map(function($plugin_definition) { + $this->valueOptions = array_reduce($this->pluginType->getPluginManager()->getDefinitions(), function(array $value_options, $plugin_definition) { $plugin_definition = $this->pluginType->ensureTypedPluginDefinition($plugin_definition); - if ($plugin_definition instanceof PluginLabelDefinitionInterface) { - return $plugin_definition->getLabel(); - } - return $plugin_definition->getId(); - }, $this->pluginType->getPluginManager()->getDefinitions()); + $value_options[$plugin_definition->getId()] = $plugin_definition instanceof PluginLabelDefinitionInterface ? $plugin_definition->getLabel() : $plugin_definition->getId(); + return $value_options; + }, []); natcasesort($this->valueOptions); return $this->valueOptions; } + /** + * {@inheritdoc} + */ + protected function valueForm(&$form, FormStateInterface $form_state) { + parent::valueForm($form, $form_state); + // Apply cacheability metadata, because the parent class does not. + $this->getCacheableMetadata()->applyTo($form); + + return $form; + } + + /** + * Gets this instance's cacheable metadata. + * + * @return \Drupal\Core\Cache\CacheableMetadata + */ + protected function getCacheableMetadata() { + $cacheable_metadata = new CacheableMetadata(); + $cacheable_metadata->addCacheableDependency($this->pluginType->getPluginManager()); + $cacheable_metadata->addCacheTags(parent::getCacheTags()); + $cacheable_metadata->addCacheContexts(parent::getCacheContexts()); + $cacheable_metadata->mergeCacheMaxAge(parent::getCacheMaxAge()); + + return $cacheable_metadata; + } + + /** + * {@inheritdoc} + */ + public function getCacheTags() { + return $this->getCacheableMetadata()->getCacheTags(); + } + + /** + * {@inheritdoc} + */ + public function getCacheContexts() { + return $this->getCacheableMetadata()->getCacheContexts(); + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + return $this->getCacheableMetadata()->getCacheMaxAge(); + } + } diff --git a/tests/src/Unit/Plugin/views/filter/PluginIdTest.php b/tests/src/Unit/Plugin/views/filter/PluginIdTest.php new file mode 100644 index 0000000..316e4c5 --- /dev/null +++ b/tests/src/Unit/Plugin/views/filter/PluginIdTest.php @@ -0,0 +1,206 @@ + $plugin_id, + ]; + $configuration = [ + 'plugin_type_id' => $plugin_type_id, + ]; + + $this->pluginType = $this->prophesize(PluginTypeInterface::class); + + $this->sut = new PluginId($configuration, $plugin_id, $plugin_definition, $this->pluginType->reveal()); + } + + /** + * @covers ::create + * @covers ::__construct + */ + public function testCreate() { + $plugin_type_id = 'foo_bar'; + $plugin_id = ''; + $plugin_definition = [ + 'id' => $plugin_id, + ]; + $configuration = [ + 'plugin_type_id' => $plugin_type_id, + ]; + + $plugin_type_manager = $this->prophesize(PluginTypeManagerInterface::class); + $plugin_type_manager->getPluginType($plugin_type_id)->wilLReturn($this->pluginType->reveal()); + + $container = $this->prophesize(ContainerInterface::class); + $container->get('plugin.plugin_type_manager')->willReturn($plugin_type_manager->reveal()); + + $this->sut = PluginId::create($container->reveal(), $configuration, $plugin_id, $plugin_definition); + $this->assertInstanceOf(PluginId::class, $this->sut); + } + + /** + * @covers ::getCacheContexts + * @covers ::getCacheableMetadata + */ + public function testCacheContexts() { + $plugin_manager_cache_contexts = ['dog', 'ball']; + + $plugin_manager = $this->prophesize(CacheableDependencyPluginManagerInterface::class); + $plugin_manager->getCacheContexts()->willReturn($plugin_manager_cache_contexts); + $plugin_manager->getCacheTags()->willReturn([]); + $plugin_manager->getCacheMaxAge()->willReturn(0); + + $this->pluginType->getPluginManager()->willReturn($plugin_manager->reveal()); + + // Temporarily disable asserts, because Cache::mergeContexts() calls + // \Drupal::service(). See https://www.drupal.org/node/2720947. + assert_options(ASSERT_ACTIVE, FALSE); + $cache_contexts = $this->sut->getCacheContexts(); + $this->assertInternalType('array', $cache_contexts); + foreach ($plugin_manager_cache_contexts as $plugin_manager_cache_context) { + $this->assertTrue(in_array($plugin_manager_cache_context, $cache_contexts)); + } + assert_options(ASSERT_ACTIVE, TRUE); + } + + /** + * @covers ::getCacheTags + * @covers ::getCacheableMetadata + */ + public function testCacheTags() { + $plugin_manager_cache_tags = ['bar', 'foo']; + + $plugin_manager = $this->prophesize(CacheableDependencyPluginManagerInterface::class); + $plugin_manager->getCacheContexts()->willReturn([]); + $plugin_manager->getCacheTags()->willReturn($plugin_manager_cache_tags); + $plugin_manager->getCacheMaxAge()->willReturn(0); + + $this->pluginType->getPluginManager()->willReturn($plugin_manager->reveal()); + + $this->assertArraySubset($plugin_manager_cache_tags, $this->sut->getCacheTags()); + } + + /** + * @covers ::getCacheMaxAge + * @covers ::getCacheableMetadata + * + * @dataProvider provideCacheMaxAge + */ + public function testCacheMaxAge($expected, $plugin_manager_max_age) { + $plugin_manager = $this->prophesize(CacheableDependencyPluginManagerInterface::class); + $plugin_manager->getCacheContexts()->willReturn([]); + $plugin_manager->getCacheTags()->willReturn([]); + $plugin_manager->getCacheMaxAge()->willReturn($plugin_manager_max_age); + + $this->pluginType->getPluginManager()->willReturn($plugin_manager->reveal()); + + $this->assertSame($expected, $this->sut->getCacheMaxAge()); + } + + /** + * Provides data to self::testCacheMaxAge(). + */ + public function provideCacheMaxAge() { + $data = []; + + $data['plugin-manager-permanent'] = [Cache::PERMANENT, Cache::PERMANENT]; + $data['plugin-manager-never'] = [0, 0]; + $data['plugin-manager-limited'] = [7, 7]; + + return $data; + } + + /** + * @covers ::getValueOptions + * @covers ::getCacheableMetadata + */ + public function testGetValueOptions() { + $plugin_label_1 = 'Foo'; + $plugin_id_1 = 'aaa_foo'; + $plugin_id_2 = 'baz'; + $plugin_id_3 = 'qux'; + $plugin_label_4 = 'Bar'; + $plugin_id_4 = 'zzz_bar'; + + // Values must be sorted naturally. + $expected = [ + $plugin_id_4 => $plugin_label_4, + $plugin_id_2 => $plugin_id_2, + $plugin_id_1 => $plugin_label_1, + $plugin_id_3 => $plugin_id_3, + ]; + + $plugin_definition_1 = $this->prophesize(PluginLabelDefinitionInterface::class); + $plugin_definition_1->getId()->willReturn($plugin_id_1); + $plugin_definition_1->getLabel()->willReturn($plugin_label_1); + $plugin_definition_2 = $this->prophesize(PluginDefinitionInterface::class); + $plugin_definition_2->getId()->willReturn($plugin_id_2); + $plugin_definition_3 = $this->prophesize(PluginDefinitionInterface::class); + $plugin_definition_3->getId()->willReturn($plugin_id_3); + $plugin_definition_4 = $this->prophesize(PluginLabelDefinitionInterface::class); + $plugin_definition_4->getId()->willReturn($plugin_id_4); + $plugin_definition_4->getLabel()->willReturn($plugin_label_4); + + $plugin_definitions = [ + $plugin_id_1 => $plugin_definition_1, + $plugin_id_2 => $plugin_definition_2, + $plugin_id_3 => $plugin_definition_3, + $plugin_id_4 => $plugin_definition_4, + ]; + + $plugin_manager = $this->prophesize(PluginManagerInterface::class); + $plugin_manager->getDefinitions()->willReturn($plugin_definitions); + + $this->pluginType->ensureTypedPluginDefinition(Argument::any())->willReturnArgument(); + $this->pluginType->getPluginManager()->willReturn($plugin_manager); + + $this->assertSame($expected, $this->sut->getValueOptions()); + } + + +} + +/** + * Defines a plugin manager which is also a cacheable dependency. + */ +interface CacheableDependencyPluginManagerInterface extends PluginManagerInterface, CacheableDependencyInterface { +}