diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php index 8c68948924..3d5f7fea33 100644 --- a/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php +++ b/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php @@ -15,6 +15,8 @@ use Drupal\field_ui\FieldUI; use Drupal\layout_builder\DefaultsSectionStorageInterface; use Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator; +use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface; +use Drupal\layout_builder\SectionListInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\RouteCollection; @@ -89,6 +91,19 @@ public static function create(ContainerInterface $container, array $configuratio ); } + /** + * {@inheritdoc} + */ + public function setSectionList(SectionListInterface $section_list) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::setSectionList() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + if (!$section_list instanceof LayoutEntityDisplayInterface) { + throw new \InvalidArgumentException('Defaults expect a display-based section list'); + } + + $this->setContext('display', EntityContext::fromEntity($section_list)); + return $this; + } + /** * {@inheritdoc} */ @@ -232,6 +247,63 @@ protected function getEntityTypes() { }); } + /** + * {@inheritdoc} + */ + public function extractIdFromRoute($value, $definition, $name, array $defaults) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::extractIdFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() should be used instead. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + if (is_string($value) && strpos($value, '.') !== FALSE) { + return $value; + } + + // If a bundle is not provided but a value corresponding to the bundle key + // is, use that for the bundle value. + if (empty($defaults['bundle']) && isset($defaults['bundle_key']) && !empty($defaults[$defaults['bundle_key']])) { + $defaults['bundle'] = $defaults[$defaults['bundle_key']]; + } + + if (!empty($defaults['entity_type_id']) && !empty($defaults['bundle']) && !empty($defaults['view_mode_name'])) { + return $defaults['entity_type_id'] . '.' . $defaults['bundle'] . '.' . $defaults['view_mode_name']; + } + } + + /** + * {@inheritdoc} + */ + public function getSectionListFromId($id) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::getSectionListFromId() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + if (strpos($id, '.') === FALSE) { + throw new \InvalidArgumentException(sprintf('The "%s" ID for the "%s" section storage type is invalid', $id, $this->getStorageType())); + } + + $storage = $this->entityTypeManager->getStorage('entity_view_display'); + // If the display does not exist, create a new one. + if (!$display = $storage->load($id)) { + list($entity_type_id, $bundle, $view_mode) = explode('.', $id, 3); + $display = $storage->create([ + 'targetEntityType' => $entity_type_id, + 'bundle' => $bundle, + 'mode' => $view_mode, + 'status' => TRUE, + ]); + } + return $display; + } + + /** + * {@inheritdoc} + */ + public function getContextsDuringPreview() { + $contexts = parent::getContextsDuringPreview(); + + // During preview add a sample entity for the target entity type and bundle. + $display = $this->getDisplay(); + $entity = $this->sampleEntityGenerator->get($display->getTargetEntityTypeId(), $display->getTargetBundle()); + + $contexts['entity'] = EntityContext::fromEntity($entity); + return $contexts; + } + /** * {@inheritdoc} */ @@ -288,20 +360,6 @@ protected function extractEntityFromRoute($value, array $defaults) { return $display; } - /** - * {@inheritdoc} - */ - public function getContextsDuringPreview() { - $contexts = parent::getContextsDuringPreview(); - - // During preview add a sample entity for the target entity type and bundle. - $display = $this->getDisplay(); - $entity = $this->sampleEntityGenerator->get($display->getTargetEntityTypeId(), $display->getTargetBundle()); - - $contexts['entity'] = EntityContext::fromEntity($entity); - return $contexts; - } - /** * {@inheritdoc} */ diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php index 1085a0fac6..db7607a9dc 100644 --- a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php +++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php @@ -7,12 +7,14 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; +use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\Context\EntityContext; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay; use Drupal\layout_builder\OverridesSectionStorageInterface; +use Drupal\layout_builder\SectionListInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\RouteCollection; @@ -85,6 +87,19 @@ public static function create(ContainerInterface $container, array $configuratio ); } + /** + * {@inheritdoc} + */ + public function setSectionList(SectionListInterface $section_list) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::setSectionList() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + if (!$section_list instanceof FieldItemListInterface) { + throw new \InvalidArgumentException('Overrides expect a field-based section list'); + } + + $this->setContext('entity', EntityContext::fromEntity($section_list->getEntity())); + return $this; + } + /** * {@inheritdoc} */ @@ -110,6 +125,37 @@ public function getStorageId() { return $entity->getEntityTypeId() . '.' . $entity->id(); } + /** + * {@inheritdoc} + */ + public function extractIdFromRoute($value, $definition, $name, array $defaults) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::extractIdFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() should be used instead. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + if (strpos($value, '.') !== FALSE) { + return $value; + } + + if (isset($defaults['entity_type_id']) && !empty($defaults[$defaults['entity_type_id']])) { + $entity_type_id = $defaults['entity_type_id']; + $entity_id = $defaults[$entity_type_id]; + return $entity_type_id . '.' . $entity_id; + } + } + + /** + * {@inheritdoc} + */ + public function getSectionListFromId($id) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::getSectionListFromId() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + if (strpos($id, '.') !== FALSE) { + list($entity_type_id, $entity_id) = explode('.', $id, 2); + $entity = $this->entityTypeManager->getStorage($entity_type_id)->load($entity_id); + if ($entity instanceof FieldableEntityInterface && $entity->hasField(static::FIELD_NAME)) { + return $entity->get(static::FIELD_NAME); + } + } + throw new \InvalidArgumentException(sprintf('The "%s" ID for the "%s" section storage type is invalid', $id, $this->getStorageType())); + } + /** * {@inheritdoc} */ diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php b/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php index fd8c019757..6f3d8e2dec 100644 --- a/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php +++ b/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php @@ -5,6 +5,7 @@ use Drupal\Core\Plugin\ContextAwarePluginBase; use Drupal\layout_builder\Routing\LayoutBuilderRoutesTrait; use Drupal\layout_builder\Section; +use Drupal\layout_builder\SectionListInterface; use Drupal\layout_builder\SectionStorageInterface; /** @@ -19,6 +20,14 @@ abstract class SectionStorageBase extends ContextAwarePluginBase implements Sect use LayoutBuilderRoutesTrait; + /** + * {@inheritdoc} + */ + public function setSectionList(SectionListInterface $section_list) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::setSectionList() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + throw new \Exception('A base implementation of \Drupal\layout_builder\SectionStorageInterface::setSectionList() can no longer be provided. The section list should be derived from context. See https://www.drupal.org/node/3016262.'); + } + /** * Gets the section list. * diff --git a/core/modules/layout_builder/src/Routing/LayoutTempstoreParamConverter.php b/core/modules/layout_builder/src/Routing/LayoutTempstoreParamConverter.php index 89ffe45c39..a04e08fade 100644 --- a/core/modules/layout_builder/src/Routing/LayoutTempstoreParamConverter.php +++ b/core/modules/layout_builder/src/Routing/LayoutTempstoreParamConverter.php @@ -46,16 +46,14 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore */ public function convert($value, $definition, $name, array $defaults) { // If no section storage type is specified or if it is invalid, return. - if (!isset($defaults['section_storage_type']) || !$this->sectionStorageManager->hasDefinition($defaults['section_storage_type'])) { - return NULL; - } - - // Load an empty instance and derive the available contexts. - $contexts = $this->sectionStorageManager->loadEmpty($defaults['section_storage_type'])->deriveContextsFromRoute($value, $definition, $name, $defaults); - // Attempt to load a full instance based on the context. - if ($section_storage = $this->sectionStorageManager->load($defaults['section_storage_type'], $contexts)) { - // Pass the plugin through the tempstore repository. - return $this->layoutTempstoreRepository->get($section_storage); + if (isset($defaults['section_storage_type']) && $this->sectionStorageManager->hasDefinition($defaults['section_storage_type'])) { + // Load an empty instance and derive the available contexts. + $contexts = $this->sectionStorageManager->loadEmpty($defaults['section_storage_type'])->deriveContextsFromRoute($value, $definition, $name, $defaults); + // Attempt to load a full instance based on the context. + if ($section_storage = $this->sectionStorageManager->load($defaults['section_storage_type'], $contexts)) { + // Pass the plugin through the tempstore repository. + return $this->layoutTempstoreRepository->get($section_storage); + } } } diff --git a/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php b/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php index c65a79ba72..6c1d6df290 100644 --- a/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php +++ b/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php @@ -107,4 +107,22 @@ public function loadEmpty($type) { return $this->createInstance($type); } + /** + * {@inheritdoc} + */ + public function loadFromStorageId($type, $id) { + @trigger_error('\Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::loadFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() should be used instead. See https://www.drupal.org/node/3012353.', E_USER_DEPRECATED); + $contexts = $this->loadEmpty($type)->deriveContextsFromRoute($id, [], '', []); + return $this->load($type, $contexts); + } + + /** + * {@inheritdoc} + */ + public function loadFromRoute($type, $value, $definition, $name, array $defaults) { + @trigger_error('\Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::loadFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() and \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() should be used instead. See https://www.drupal.org/node/3012353.', E_USER_DEPRECATED); + $contexts = $this->loadEmpty($type)->deriveContextsFromRoute($value, $definition, $name, $defaults); + return $this->load($type, $contexts); + } + } diff --git a/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php b/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php index ca87d7568c..51e1d7aca4 100644 --- a/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php +++ b/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php @@ -14,6 +14,23 @@ */ interface SectionStorageManagerInterface extends DiscoveryInterface { + /** + * Loads a section storage with no associated section list. + * + * @param string $type + * The type of section storage being instantiated. + * + * @return \Drupal\layout_builder\SectionStorageInterface + * The section storage. + * + * @internal + * Section storage relies on context to load section lists. Use ::load() + * when that context is available. This method is intended for use by + * collaborators of the plugins in build-time situations when section + * storage type must be consulted. + */ + public function loadEmpty($type); + /** * Loads a section storage with the provided contexts applied. * @@ -41,20 +58,49 @@ public function load($type, array $contexts = []); public function findByContext($operation, array $contexts); /** - * Loads an empty section storage with no associated section list. + * Loads a section storage populated with an existing section list. * * @param string $type - * The type of section storage being instantiated. + * The section storage type. + * @param string $id + * The section list ID. * * @return \Drupal\layout_builder\SectionStorageInterface * The section storage. * - * @internal - * Section storage relies on context to load section lists. Use ::load() - * when that context is available. This method is intended for use by - * collaborators of the plugins in build-time situations when section - * storage type must be consulted. + * @throws \InvalidArgumentException + * Thrown if the ID is invalid. + * + * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. + * \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() + * should be used instead. See https://www.drupal.org/node/3012353. */ - public function loadEmpty($type); + public function loadFromStorageId($type, $id); + + /** + * Loads a section storage populated with a section list derived from a route. + * + * @param string $type + * The section storage type. + * @param string $value + * The raw value. + * @param mixed $definition + * The parameter definition provided in the route options. + * @param string $name + * The name of the parameter. + * @param array $defaults + * The route defaults array. + * + * @return \Drupal\layout_builder\SectionStorageInterface|null + * The section storage if it could be loaded, or NULL otherwise. + * + * @see \Drupal\Core\ParamConverter\ParamConverterInterface::convert() + * + * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. + * \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() + * and \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() + * should be used instead. See https://www.drupal.org/node/3012353. + */ + public function loadFromRoute($type, $value, $definition, $name, array $defaults); } diff --git a/core/modules/layout_builder/src/SectionStorageInterface.php b/core/modules/layout_builder/src/SectionStorageInterface.php index e7455aecd6..b79a702de3 100644 --- a/core/modules/layout_builder/src/SectionStorageInterface.php +++ b/core/modules/layout_builder/src/SectionStorageInterface.php @@ -35,6 +35,44 @@ public function getStorageId(); */ public function getStorageType(); + /** + * Sets the section list on the storage. + * + * @param \Drupal\layout_builder\SectionListInterface $section_list + * The section list. + * + * @return $this + * + * @internal + * This should only be called during section storage instantiation. + * + * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The + * section list should be derived from context. See + * https://www.drupal.org/node/3016262. + */ + public function setSectionList(SectionListInterface $section_list); + + /** + * Derives the section list from the storage ID. + * + * @param string $id + * The storage ID, see ::getStorageId(). + * + * @return \Drupal\layout_builder\SectionListInterface + * The section list. + * + * @throws \InvalidArgumentException + * Thrown if the ID is invalid. + * + * @internal + * This should only be called during section storage instantiation. + * + * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The + * section list should be derived from context. See + * https://www.drupal.org/node/3016262. + */ + public function getSectionListFromId($id); + /** * Provides the routes needed for Layout Builder UI. * @@ -69,6 +107,30 @@ public function getRedirectUrl(); */ public function getLayoutBuilderUrl($rel = 'view'); + /** + * Configures the plugin based on route values. + * + * @param mixed $value + * The raw value. + * @param mixed $definition + * The parameter definition provided in the route options. + * @param string $name + * The name of the parameter. + * @param array $defaults + * The route defaults array. + * + * @return string|null + * The section storage ID if it could be extracted, NULL otherwise. + * + * @internal + * This should only be called during section storage instantiation. + * + * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. + * \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() + * should be used instead. See https://www.drupal.org/node/3016262. + */ + public function extractIdFromRoute($value, $definition, $name, array $defaults); + /** * Derives the available plugin contexts from route values. * diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php index 38150b2185..a7b3e58e65 100644 --- a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php @@ -13,6 +13,7 @@ use Drupal\layout_builder\Plugin\SectionStorage\SectionStorageLocalTaskProviderInterface; use Drupal\layout_builder\Routing\LayoutBuilderRoutesTrait; use Drupal\layout_builder\Section; +use Drupal\layout_builder\SectionListInterface; use Drupal\layout_builder\SectionStorage\SectionStorageTrait; use Drupal\layout_builder\SectionStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -197,4 +198,28 @@ public function getContextsDuringPreview() { return $this->getContexts(); } + /** + * {@inheritdoc} + */ + public function setSectionList(SectionListInterface $section_list) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::setSectionList() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSectionListFromId($id) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::getSectionListFromId() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + return $this; + } + + /** + * {@inheritdoc} + */ + public function extractIdFromRoute($value, $definition, $name, array $defaults) { + @trigger_error('\Drupal\layout_builder\SectionStorageInterface::extractIdFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() should be used instead. See https://www.drupal.org/node/3016262.', E_USER_DEPRECATED); + return $value ?: $defaults['id']; + } + } diff --git a/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php b/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php index 125f19cf33..542014773e 100644 --- a/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php +++ b/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php @@ -90,6 +90,134 @@ public function testThirdPartySettings() { $this->assertSame('value 2', $this->plugin->getThirdPartySetting('the_module', 'the_key')); } + /** + * @covers ::extractIdFromRoute + * + * @dataProvider providerTestExtractIdFromRoute + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::extractIdFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() should be used instead. See https://www.drupal.org/node/3016262. + * + * @group legacy + */ + public function testExtractIdFromRoute($expected, $value, array $defaults) { + $result = $this->plugin->extractIdFromRoute($value, [], 'the_parameter_name', $defaults); + $this->assertSame($expected, $result); + } + + /** + * Provides data for ::testExtractIdFromRoute(). + */ + public function providerTestExtractIdFromRoute() { + $data = []; + $data['with value'] = [ + 'foo.bar.baz', + 'foo.bar.baz', + [], + ]; + $data['empty value, without bundle'] = [ + 'my_entity_type.bundle_name.default', + '', + [ + 'entity_type_id' => 'my_entity_type', + 'view_mode_name' => 'default', + 'bundle_key' => 'my_bundle', + 'my_bundle' => 'bundle_name', + ], + ]; + $data['empty value, with bundle'] = [ + 'my_entity_type.bundle_name.default', + '', + [ + 'entity_type_id' => 'my_entity_type', + 'view_mode_name' => 'default', + 'bundle' => 'bundle_name', + ], + ]; + $data['without value, empty defaults'] = [ + NULL, + '', + [], + ]; + return $data; + } + + /** + * @covers ::getSectionListFromId + * + * @dataProvider providerTestGetSectionListFromId + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::getSectionListFromId() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262. + * + * @group legacy + */ + public function testGetSectionListFromId($success, $expected_entity_id, $value) { + if ($expected_entity_id) { + $entity_storage = $this->prophesize(EntityStorageInterface::class); + $entity_storage->load($expected_entity_id)->willReturn('the_return_value'); + + $this->entityTypeManager->getDefinition('entity_view_display')->willReturn(new EntityType(['id' => 'entity_view_display'])); + $this->entityTypeManager->getStorage('entity_view_display')->willReturn($entity_storage->reveal()); + } + else { + $this->entityTypeManager->getDefinition('entity_view_display')->shouldNotBeCalled(); + $this->entityTypeManager->getStorage('entity_view_display')->shouldNotBeCalled(); + } + + if (!$success) { + $this->setExpectedException(\InvalidArgumentException::class); + } + + $result = $this->plugin->getSectionListFromId($value); + if ($success) { + $this->assertEquals('the_return_value', $result); + } + } + + /** + * Provides data for ::testGetSectionListFromId(). + */ + public function providerTestGetSectionListFromId() { + $data = []; + $data['with value'] = [ + TRUE, + 'foo.bar.baz', + 'foo.bar.baz', + ]; + $data['without value, empty defaults'] = [ + FALSE, + NULL, + '', + ]; + return $data; + } + + /** + * @covers ::getSectionListFromId + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::getSectionListFromId() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262. + * + * @group legacy + */ + public function testGetSectionListFromIdCreate() { + $expected = 'the_return_value'; + $value = 'foo.bar.baz'; + $expected_create_values = [ + 'targetEntityType' => 'foo', + 'bundle' => 'bar', + 'mode' => 'baz', + 'status' => TRUE, + ]; + $entity_storage = $this->prophesize(EntityStorageInterface::class); + $entity_storage->load($value)->willReturn(NULL); + $entity_storage->create($expected_create_values)->willReturn($expected); + + $this->entityTypeManager->getDefinition('entity_view_display')->willReturn(new EntityType(['id' => 'entity_view_display'])); + $this->entityTypeManager->getStorage('entity_view_display')->willReturn($entity_storage->reveal()); + + $result = $this->plugin->getSectionListFromId($value); + $this->assertSame($expected, $result); + } + /** * @covers ::extractEntityFromRoute * diff --git a/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreParamConverterTest.php b/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreParamConverterTest.php index 1bd28e70ff..40e202f52b 100644 --- a/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreParamConverterTest.php +++ b/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreParamConverterTest.php @@ -36,7 +36,7 @@ public function testConvert() { $section_storage->deriveContextsFromRoute($value, $definition, $name, $defaults)->willReturn([]); $section_storage_manager->load('my_type', [])->willReturn($section_storage->reveal()); - $layout_tempstore_repository->get($section_storage)->willReturn($expected); + $layout_tempstore_repository->get($section_storage->reveal())->willReturn($expected); $result = $converter->convert($value, $definition, $name, $defaults); $this->assertEquals($expected, $result); @@ -56,6 +56,7 @@ public function testConvertNoType() { $defaults = ['section_storage_type' => NULL]; $section_storage_manager->hasDefinition()->shouldNotBeCalled(); + $section_storage_manager->loadFromRoute()->shouldNotBeCalled(); $section_storage_manager->load()->shouldNotBeCalled(); $layout_tempstore_repository->get()->shouldNotBeCalled(); @@ -77,6 +78,7 @@ public function testConvertInvalidConverter() { $defaults = ['section_storage_type' => 'invalid']; $section_storage_manager->hasDefinition('invalid')->willReturn(FALSE); + $section_storage_manager->loadFromRoute()->shouldNotBeCalled(); $section_storage_manager->load()->shouldNotBeCalled(); $layout_tempstore_repository->get()->shouldNotBeCalled(); diff --git a/core/modules/layout_builder/tests/src/Unit/OverridesSectionStorageTest.php b/core/modules/layout_builder/tests/src/Unit/OverridesSectionStorageTest.php index 07ecda97ae..922e7a817b 100644 --- a/core/modules/layout_builder/tests/src/Unit/OverridesSectionStorageTest.php +++ b/core/modules/layout_builder/tests/src/Unit/OverridesSectionStorageTest.php @@ -59,6 +59,115 @@ protected function setUp() { $this->plugin = new OverridesSectionStorage([], 'overrides', $definition, $this->entityTypeManager->reveal(), $this->entityFieldManager->reveal()); } + /** + * @covers ::extractIdFromRoute + * + * @dataProvider providerTestExtractIdFromRoute + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::extractIdFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() should be used instead. See https://www.drupal.org/node/3016262. + * + * @group legacy + */ + public function testExtractIdFromRoute($expected, $value, array $defaults) { + $result = $this->plugin->extractIdFromRoute($value, [], 'the_parameter_name', $defaults); + $this->assertSame($expected, $result); + } + + /** + * Provides data for ::testExtractIdFromRoute(). + */ + public function providerTestExtractIdFromRoute() { + $data = []; + $data['with value, with layout'] = [ + 'my_entity_type.entity_with_layout', + 'my_entity_type.entity_with_layout', + [], + ]; + $data['with value, without layout'] = [ + NULL, + 'my_entity_type', + [], + ]; + $data['empty value, populated defaults'] = [ + 'my_entity_type.entity_with_layout', + '', + [ + 'entity_type_id' => 'my_entity_type', + 'my_entity_type' => 'entity_with_layout', + ], + ]; + $data['empty value, empty defaults'] = [ + NULL, + '', + [], + ]; + return $data; + } + + /** + * @covers ::getSectionListFromId + * + * @dataProvider providerTestGetSectionListFromId + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::getSectionListFromId() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. The section list should be derived from context. See https://www.drupal.org/node/3016262. + * + * @group legacy + */ + public function testGetSectionListFromId($success, $expected_entity_type_id, $id) { + $defaults['the_parameter_name'] = $id; + + if ($expected_entity_type_id) { + $entity_storage = $this->prophesize(EntityStorageInterface::class); + + $entity_without_layout = $this->prophesize(FieldableEntityInterface::class); + $entity_without_layout->hasField(OverridesSectionStorage::FIELD_NAME)->willReturn(FALSE); + $entity_without_layout->get(OverridesSectionStorage::FIELD_NAME)->shouldNotBeCalled(); + $entity_storage->load('entity_without_layout')->willReturn($entity_without_layout->reveal()); + + $entity_with_layout = $this->prophesize(FieldableEntityInterface::class); + $entity_with_layout->hasField(OverridesSectionStorage::FIELD_NAME)->willReturn(TRUE); + $entity_with_layout->get(OverridesSectionStorage::FIELD_NAME)->willReturn('the_return_value'); + $entity_storage->load('entity_with_layout')->willReturn($entity_with_layout->reveal()); + + $this->entityTypeManager->getStorage($expected_entity_type_id)->willReturn($entity_storage->reveal()); + } + else { + $this->entityTypeManager->getStorage(Argument::any())->shouldNotBeCalled(); + } + + if (!$success) { + $this->setExpectedException(\InvalidArgumentException::class); + } + + $result = $this->plugin->getSectionListFromId($id); + if ($success) { + $this->assertEquals('the_return_value', $result); + } + } + + /** + * Provides data for ::testGetSectionListFromId(). + */ + public function providerTestGetSectionListFromId() { + $data = []; + $data['with value, with layout'] = [ + TRUE, + 'my_entity_type', + 'my_entity_type.entity_with_layout', + ]; + $data['with value, without layout'] = [ + FALSE, + 'my_entity_type', + 'my_entity_type.entity_without_layout', + ]; + $data['empty value, empty defaults'] = [ + FALSE, + NULL, + '', + ]; + return $data; + } + /** * @covers ::extractEntityFromRoute * diff --git a/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php b/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php index 3a33ede44f..82c9914e96 100644 --- a/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php +++ b/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php @@ -26,10 +26,17 @@ class SectionStorageManagerTest extends UnitTestCase { /** * The section storage manager. * - * @var \Drupal\Tests\layout_builder\Unit\TestSectionStorageManager + * @var \Drupal\layout_builder\SectionStorage\SectionStorageManager */ protected $manager; + /** + * The plugin. + * + * @var \Drupal\layout_builder\SectionStorageInterface + */ + protected $plugin; + /** * The plugin discovery. * @@ -57,54 +64,96 @@ class SectionStorageManagerTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $this->discovery = $this->prophesize(DiscoveryInterface::class); - $this->factory = $this->prophesize(FactoryInterface::class); $cache = $this->prophesize(CacheBackendInterface::class); $module_handler = $this->prophesize(ModuleHandlerInterface::class); $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); - $this->manager = new TestSectionStorageManager($this->discovery->reveal(), $this->factory->reveal(), $cache->reveal(), $module_handler->reveal(), $this->contextHandler->reveal()); + $this->manager = new SectionStorageManager(new \ArrayObject(), $cache->reveal(), $module_handler->reveal(), $this->contextHandler->reveal()); + + $this->discovery = $this->prophesize(DiscoveryInterface::class); + $reflection_property = new \ReflectionProperty($this->manager, 'discovery'); + $reflection_property->setAccessible(TRUE); + $reflection_property->setValue($this->manager, $this->discovery->reveal()); + + $this->plugin = $this->prophesize(SectionStorageInterface::class); + $this->factory = $this->prophesize(FactoryInterface::class); + $this->factory->createInstance('the_plugin_id', [])->willReturn($this->plugin->reveal()); + $reflection_property = new \ReflectionProperty($this->manager, 'factory'); + $reflection_property->setAccessible(TRUE); + $reflection_property->setValue($this->manager, $this->factory->reveal()); } /** * @covers ::loadEmpty */ public function testLoadEmpty() { - $plugin = $this->prophesize(SectionStorageInterface::class); - $this->factory->createInstance('the_plugin_id', [])->willReturn($plugin->reveal()); - $result = $this->manager->loadEmpty('the_plugin_id'); - $this->assertSame($plugin->reveal(), $result); + $this->assertInstanceOf(SectionStorageInterface::class, $result); + $this->assertSame($this->plugin->reveal(), $result); + } + + /** + * @covers ::loadFromStorageId + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::loadFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() should be used instead. See https://www.drupal.org/node/3012353. + * + * @group legacy + */ + public function testLoadFromStorageId() { + $this->plugin->deriveContextsFromRoute('the_storage_id', [], '', [])->willReturn([]); + + $result = $this->manager->loadFromStorageId('the_plugin_id', 'the_storage_id'); + $this->assertInstanceOf(SectionStorageInterface::class, $result); + } + + /** + * @covers ::loadFromRoute + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::loadFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() and \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() should be used instead. See https://www.drupal.org/node/3012353. + * + * @group legacy + */ + public function testLoadFromRoute() { + $this->plugin->deriveContextsFromRoute('the_value', [], 'the_parameter_name', [])->willReturn([]); + $result = $this->manager->loadFromRoute('the_plugin_id', 'the_value', [], 'the_parameter_name', []); + $this->assertInstanceOf(SectionStorageInterface::class, $result); + } + + /** + * @covers ::loadFromRoute + * + * @expectedDeprecation \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::loadFromRoute() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute() and \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::load() should be used instead. See https://www.drupal.org/node/3012353. + * + * @group legacy + */ + public function testLoadFromRouteNull() { + $this->plugin->deriveContextsFromRoute('the_value', [], 'the_parameter_name', ['_route' => 'the_route_name'])->willReturn([]); + $result = $this->manager->loadFromRoute('the_plugin_id', 'the_value', [], 'the_parameter_name', ['_route' => 'the_route_name']); + $this->assertInstanceOf(SectionStorageInterface::class, $result); } /** * @covers ::load */ public function testLoad() { - $plugin = $this->prophesize(SectionStorageInterface::class); - $this->factory->createInstance('the_plugin_id', [])->willReturn($plugin->reveal()); - $contexts = [ 'the_context' => $this->prophesize(ContextInterface::class)->reveal(), ]; - $this->contextHandler->applyContextMapping($plugin, $contexts)->shouldBeCalled(); + $this->contextHandler->applyContextMapping($this->plugin, $contexts)->shouldBeCalled(); $result = $this->manager->load('the_plugin_id', $contexts); - $this->assertSame($plugin->reveal(), $result); + $this->assertSame($this->plugin->reveal(), $result); } /** * @covers ::load */ public function testLoadNull() { - $plugin = $this->prophesize(SectionStorageInterface::class); - $this->factory->createInstance('the_plugin_id', [])->willReturn($plugin->reveal()); - $contexts = [ 'the_context' => $this->prophesize(ContextInterface::class)->reveal(), ]; - $this->contextHandler->applyContextMapping($plugin, $contexts)->willThrow(new ContextException()); + $this->contextHandler->applyContextMapping($this->plugin, $contexts)->willThrow(new ContextException()); $result = $this->manager->load('the_plugin_id', $contexts); $this->assertNull($result); @@ -185,19 +234,3 @@ public function providerTestFindByContext() { } } - -/** - * Provides a test manager. - */ -class TestSectionStorageManager extends SectionStorageManager { - - /** - * {@inheritdoc} - */ - public function __construct(DiscoveryInterface $discovery, FactoryInterface $factory, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ContextHandlerInterface $context_handler) { - parent::__construct(new \ArrayObject(), $cache_backend, $module_handler, $context_handler); - $this->discovery = $discovery; - $this->factory = $factory; - } - -}