diff options
author | effulgentsia <alex.bronstein@acquia.com> | 2017-01-30 21:53:34 -0800 |
---|---|---|
committer | effulgentsia <alex.bronstein@acquia.com> | 2017-01-30 21:53:34 -0800 |
commit | bfaf6b6e0f2c7057ae78299093870d80a91e1045 (patch) | |
tree | 6ca704fa520da187c98571f91996e99fd12a872c | |
parent | 414574e17330342d94122a2b281a02d18ad1eaf2 (diff) | |
download | drupal-bfaf6b6e0f2c7057ae78299093870d80a91e1045.tar.gz drupal-bfaf6b6e0f2c7057ae78299093870d80a91e1045.zip |
Issue #2758897 by Wim Leers, damiankloip, larowlan: Move rest module's "link manager" services to serialization module
40 files changed, 987 insertions, 607 deletions
diff --git a/core/modules/hal/hal.info.yml b/core/modules/hal/hal.info.yml index 4cf0c7ead66..2d87eff9d29 100644 --- a/core/modules/hal/hal.info.yml +++ b/core/modules/hal/hal.info.yml @@ -5,5 +5,4 @@ package: Web services version: VERSION core: 8.x dependencies: - - rest - serialization diff --git a/core/modules/hal/hal.services.yml b/core/modules/hal/hal.services.yml index 7e40df0d5b8..c8c9a2cd47f 100644 --- a/core/modules/hal/hal.services.yml +++ b/core/modules/hal/hal.services.yml @@ -1,7 +1,7 @@ services: serializer.normalizer.entity_reference_item.hal: class: Drupal\hal\Normalizer\EntityReferenceItemNormalizer - arguments: ['@rest.link_manager', '@serializer.entity_resolver'] + arguments: ['@serialization.link_manager', '@serializer.entity_resolver'] tags: - { name: normalizer, priority: 10 } serializer.normalizer.field_item.hal: @@ -16,10 +16,10 @@ services: class: Drupal\hal\Normalizer\FileEntityNormalizer tags: - { name: normalizer, priority: 20 } - arguments: ['@entity.manager', '@http_client', '@rest.link_manager', '@module_handler'] + arguments: ['@entity.manager', '@http_client', '@serialization.link_manager', '@module_handler'] serializer.normalizer.entity.hal: class: Drupal\hal\Normalizer\ContentEntityNormalizer - arguments: ['@rest.link_manager', '@entity.manager', '@module_handler'] + arguments: ['@serialization.link_manager', '@entity.manager', '@module_handler'] tags: - { name: normalizer, priority: 10 } serializer.encoder.hal: diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index fce3ddca401..88eb36cdcd3 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -6,7 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\rest\LinkManager\LinkManagerInterface; +use Drupal\serialization\LinkManager\LinkManagerInterface; use Drupal\serialization\Normalizer\FieldableEntityNormalizerTrait; use Symfony\Component\Serializer\Exception\UnexpectedValueException; @@ -27,7 +27,7 @@ class ContentEntityNormalizer extends NormalizerBase { /** * The hypermedia link manager. * - * @var \Drupal\rest\LinkManager\LinkManagerInterface + * @var \Drupal\serialization\LinkManager\LinkManagerInterface */ protected $linkManager; @@ -41,7 +41,7 @@ class ContentEntityNormalizer extends NormalizerBase { /** * Constructs an ContentEntityNormalizer object. * - * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager + * @param \Drupal\serialization\LinkManager\LinkManagerInterface $link_manager * The hypermedia link manager. */ public function __construct(LinkManagerInterface $link_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) { diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index 6c73256e147..4dea76c9179 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -3,7 +3,7 @@ namespace Drupal\hal\Normalizer; use Drupal\Core\Entity\FieldableEntityInterface; -use Drupal\rest\LinkManager\LinkManagerInterface; +use Drupal\serialization\LinkManager\LinkManagerInterface; use Drupal\serialization\EntityResolver\EntityResolverInterface; use Drupal\serialization\EntityResolver\UuidReferenceInterface; @@ -22,7 +22,7 @@ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidR /** * The hypermedia link manager. * - * @var \Drupal\rest\LinkManager\LinkManagerInterface + * @var \Drupal\serialization\LinkManager\LinkManagerInterface */ protected $linkManager; @@ -36,7 +36,7 @@ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidR /** * Constructs an EntityReferenceItemNormalizer object. * - * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager + * @param \Drupal\serialization\LinkManager\LinkManagerInterface $link_manager * The hypermedia link manager. * @param \Drupal\serialization\EntityResolver\EntityResolverInterface $entity_Resolver * The entity resolver. diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php index 46c609451df..c7c686fde83 100644 --- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php @@ -4,7 +4,7 @@ namespace Drupal\hal\Normalizer; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\rest\LinkManager\LinkManagerInterface; +use Drupal\serialization\LinkManager\LinkManagerInterface; use GuzzleHttp\ClientInterface; /** @@ -33,7 +33,7 @@ class FileEntityNormalizer extends ContentEntityNormalizer { * The entity manager. * @param \GuzzleHttp\ClientInterface $http_client * The HTTP Client. - * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager + * @param \Drupal\serialization\LinkManager\LinkManagerInterface $link_manager * The hypermedia link manager. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. diff --git a/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php b/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php index 42d7a36a6f5..cacef30fff6 100644 --- a/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php +++ b/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php @@ -7,9 +7,9 @@ use Drupal\file\Entity\File; use Drupal\hal\Encoder\JsonEncoder; use Drupal\hal\Normalizer\FieldItemNormalizer; use Drupal\hal\Normalizer\FileEntityNormalizer; -use Drupal\rest\LinkManager\LinkManager; -use Drupal\rest\LinkManager\RelationLinkManager; -use Drupal\rest\LinkManager\TypeLinkManager; +use Drupal\serialization\LinkManager\LinkManager; +use Drupal\serialization\LinkManager\RelationLinkManager; +use Drupal\serialization\LinkManager\TypeLinkManager; use Symfony\Component\Serializer\Serializer; diff --git a/core/modules/hal/tests/src/Kernel/NormalizerTestBase.php b/core/modules/hal/tests/src/Kernel/NormalizerTestBase.php index 58674763c2c..1fc5e4c0c69 100644 --- a/core/modules/hal/tests/src/Kernel/NormalizerTestBase.php +++ b/core/modules/hal/tests/src/Kernel/NormalizerTestBase.php @@ -10,9 +10,9 @@ use Drupal\hal\Normalizer\EntityReferenceItemNormalizer; use Drupal\hal\Normalizer\FieldItemNormalizer; use Drupal\hal\Normalizer\FieldNormalizer; use Drupal\language\Entity\ConfigurableLanguage; -use Drupal\rest\LinkManager\LinkManager; -use Drupal\rest\LinkManager\RelationLinkManager; -use Drupal\rest\LinkManager\TypeLinkManager; +use Drupal\serialization\LinkManager\LinkManager; +use Drupal\serialization\LinkManager\RelationLinkManager; +use Drupal\serialization\LinkManager\TypeLinkManager; use Drupal\serialization\EntityResolver\ChainEntityResolver; use Drupal\serialization\EntityResolver\TargetIdResolver; use Drupal\serialization\EntityResolver\UuidResolver; @@ -30,7 +30,7 @@ abstract class NormalizerTestBase extends KernelTestBase { * * @var array */ - public static $modules = ['entity_test', 'field', 'hal', 'language', 'rest', 'serialization', 'system', 'text', 'user', 'filter']; + public static $modules = ['entity_test', 'field', 'hal', 'language', 'serialization', 'system', 'text', 'user', 'filter']; /** * The mock serializer. diff --git a/core/modules/rest/config/install/rest.settings.yml b/core/modules/rest/config/install/rest.settings.yml index eb3da2df4ca..8f70c6997d1 100644 --- a/core/modules/rest/config/install/rest.settings.yml +++ b/core/modules/rest/config/install/rest.settings.yml @@ -1,7 +1,3 @@ -# Set the domain for REST type and relation links. -# If left blank, the site's domain will be used. -link_domain: ~ - # Before Drupal 8.2, EntityResource used permissions as well as the entity # access system for access checking. This was confusing, and it only did this # for historical reasons. New Drupal installations opt out from this by default diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml index 2c255ab88cd..49b4b2d9426 100644 --- a/core/modules/rest/config/schema/rest.schema.yml +++ b/core/modules/rest/config/schema/rest.schema.yml @@ -3,6 +3,7 @@ rest.settings: type: config_object label: 'REST settings' mapping: + # @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. link_domain: type: string label: 'Domain of the relation' diff --git a/core/modules/rest/rest.api.php b/core/modules/rest/rest.api.php index fb8993d2680..9bf6921c09b 100644 --- a/core/modules/rest/rest.api.php +++ b/core/modules/rest/rest.api.php @@ -31,6 +31,9 @@ function hook_rest_resource_alter(&$definitions) { /** * Alter the REST type URI. * + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Use + * hook_serialization_type_uri_alter() instead. This exists solely for BC. + * * Modules may wish to alter the type URI generated for a resource based on the * context of the serializer/normalizer operation. * @@ -46,7 +49,7 @@ function hook_rest_resource_alter(&$definitions) { */ function hook_rest_type_uri_alter(&$uri, $context = array()) { if ($context['mymodule'] == TRUE) { - $base = \Drupal::config('rest.settings')->get('link_domain'); + $base = \Drupal::config('serialization.settings')->get('link_domain'); $uri = str_replace($base, 'http://mymodule.domain', $uri); } } @@ -55,6 +58,9 @@ function hook_rest_type_uri_alter(&$uri, $context = array()) { /** * Alter the REST relation URI. * + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. Use + * hook_serialization_relation_uri_alter() instead. This exists solely for BC. + * * Modules may wish to alter the relation URI generated for a resource based on * the context of the serializer/normalizer operation. * @@ -70,7 +76,7 @@ function hook_rest_type_uri_alter(&$uri, $context = array()) { */ function hook_rest_relation_uri_alter(&$uri, $context = array()) { if ($context['mymodule'] == TRUE) { - $base = \Drupal::config('rest.settings')->get('link_domain'); + $base = \Drupal::config('serialization.settings')->get('link_domain'); $uri = str_replace($base, 'http://mymodule.domain', $uri); } } diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index 81eb4510894..2def91e992d 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -11,15 +11,22 @@ services: # @todo Remove this service in Drupal 9.0.0. access_check.rest.csrf: alias: access_check.header.csrf + # Link managers. + # @deprecated in Drupal 8.3.x and will be removed before 9.0.0. Use + # serialization.link_manager instead. rest.link_manager: + parent: serialization.link_manager class: Drupal\rest\LinkManager\LinkManager - arguments: ['@rest.link_manager.type', '@rest.link_manager.relation'] + # @deprecated in Drupal 8.3.x and will be removed before 9.0.0. Use + # serialization.link_manager.type instead. rest.link_manager.type: + parent: serialization.link_manager.type class: Drupal\rest\LinkManager\TypeLinkManager - arguments: ['@cache.default', '@module_handler', '@config.factory', '@request_stack', '@entity_type.bundle.info'] + # @deprecated in Drupal 8.3.x and will be removed before 9.0.0. Use + # serialization.link_manager.relation instead. rest.link_manager.relation: + parent: serialization.link_manager.relation class: Drupal\rest\LinkManager\RelationLinkManager - arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory', '@request_stack'] rest.resource_routes: class: Drupal\rest\Routing\ResourceRoutes arguments: ['@plugin.manager.rest', '@entity_type.manager', '@logger.channel.rest'] diff --git a/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php b/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php index c2eb1f136b1..c73f5f0aef2 100644 --- a/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php @@ -2,19 +2,10 @@ namespace Drupal\rest\LinkManager; +use Drupal\serialization\LinkManager\ConfigurableLinkManagerInterface as MovedConfigurableLinkManagerInterface; + /** - * Defines an interface for a link manager with a configurable domain. + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. */ -interface ConfigurableLinkManagerInterface { - - /** - * Sets the link domain used in constructing link URIs. - * - * @param string $domain - * The link domain to use for constructing link URIs. - * - * @return $this - */ - public function setLinkDomain($domain); - -} +interface ConfigurableLinkManagerInterface extends MovedConfigurableLinkManagerInterface {} diff --git a/core/modules/rest/src/LinkManager/LinkManager.php b/core/modules/rest/src/LinkManager/LinkManager.php index bd94a6580ee..1b450c50a6c 100644 --- a/core/modules/rest/src/LinkManager/LinkManager.php +++ b/core/modules/rest/src/LinkManager/LinkManager.php @@ -2,70 +2,10 @@ namespace Drupal\rest\LinkManager; -class LinkManager implements LinkManagerInterface { +use Drupal\serialization\LinkManager\LinkManager as MovedLinkManager; - /** - * The type link manager. - * - * @var \Drupal\rest\LinkManager\TypeLinkManagerInterface - */ - protected $typeLinkManager; - - /** - * The relation link manager. - * - * @var \Drupal\rest\LinkManager\RelationLinkManagerInterface - */ - protected $relationLinkManager; - - /** - * Constructor. - * - * @param \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_link_manager - * Manager for handling bundle URIs. - * @param \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_link_manager - * Manager for handling bundle URIs. - */ - public function __construct(TypeLinkManagerInterface $type_link_manager, RelationLinkManagerInterface $relation_link_manager) { - $this->typeLinkManager = $type_link_manager; - $this->relationLinkManager = $relation_link_manager; - } - - /** - * {@inheritdoc} - */ - public function getTypeUri($entity_type, $bundle, $context = array()) { - return $this->typeLinkManager->getTypeUri($entity_type, $bundle, $context); - } - - /** - * {@inheritdoc} - */ - public function getTypeInternalIds($type_uri, $context = array()) { - return $this->typeLinkManager->getTypeInternalIds($type_uri, $context); - } - - /** - * {@inheritdoc} - */ - public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { - return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name, $context); - } - - /** - * {@inheritdoc} - */ - public function getRelationInternalIds($relation_uri) { - return $this->relationLinkManager->getRelationInternalIds($relation_uri); - } - - /** - * {@inheritdoc} - */ - public function setLinkDomain($domain) { - $this->relationLinkManager->setLinkDomain($domain); - $this->typeLinkManager->setLinkDomain($domain); - return $this; - } - -} +/** + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. + */ +class LinkManager extends MovedLinkManager implements LinkManagerInterface {} diff --git a/core/modules/rest/src/LinkManager/LinkManagerBase.php b/core/modules/rest/src/LinkManager/LinkManagerBase.php index 0896a80f73d..13f8b53ae3f 100644 --- a/core/modules/rest/src/LinkManager/LinkManagerBase.php +++ b/core/modules/rest/src/LinkManager/LinkManagerBase.php @@ -2,57 +2,10 @@ namespace Drupal\rest\LinkManager; +use Drupal\serialization\LinkManager\LinkManagerBase as MovedLinkManagerBase; + /** - * Defines an abstract base-class for REST link manager objects. + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. */ -abstract class LinkManagerBase { - - /** - * Link domain used for type links URIs. - * - * @var string - */ - protected $linkDomain; - - /** - * Config factory service. - * - * @var \Drupal\Core\Config\ConfigFactoryInterface - */ - protected $configFactory; - - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** - * {@inheritdoc} - */ - public function setLinkDomain($domain) { - $this->linkDomain = rtrim($domain, '/'); - return $this; - } - - /** - * Gets the link domain. - * - * @return string - * The link domain. - */ - protected function getLinkDomain() { - if (empty($this->linkDomain)) { - if ($domain = $this->configFactory->get('rest.settings')->get('link_domain')) { - $this->linkDomain = rtrim($domain, '/'); - } - else { - $request = $this->requestStack->getCurrentRequest(); - $this->linkDomain = $request->getSchemeAndHttpHost() . $request->getBasePath(); - } - } - return $this->linkDomain; - } - -} +abstract class LinkManagerBase extends MovedLinkManagerBase {} diff --git a/core/modules/rest/src/LinkManager/LinkManagerInterface.php b/core/modules/rest/src/LinkManager/LinkManagerInterface.php index 1bd04ff50b9..26f5dc473dd 100644 --- a/core/modules/rest/src/LinkManager/LinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/LinkManagerInterface.php @@ -2,17 +2,10 @@ namespace Drupal\rest\LinkManager; +use Drupal\serialization\LinkManager\LinkManagerInterface as MovedLinkManagerInterface; + /** - * Interface implemented by link managers. - * - * There are no explicit methods on the manager interface. Instead link managers - * broker the interactions of the different components, and therefore must - * implement each component interface, which is enforced by this interface - * extending all of the component ones. - * - * While a link manager may directly implement these interface methods with - * custom logic, it is expected to be more common for plugin managers to proxy - * the method invocations to the respective components. + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. */ -interface LinkManagerInterface extends TypeLinkManagerInterface, RelationLinkManagerInterface { -} +interface LinkManagerInterface extends MovedLinkManagerInterface {} diff --git a/core/modules/rest/src/LinkManager/RelationLinkManager.php b/core/modules/rest/src/LinkManager/RelationLinkManager.php index 297ffd9a0c2..a29eb9a8927 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManager.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManager.php @@ -2,141 +2,10 @@ namespace Drupal\rest\LinkManager; -use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Entity\ContentEntityTypeInterface; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\serialization\LinkManager\RelationLinkManager as MovedLinkRelationManager; -class RelationLinkManager extends LinkManagerBase implements RelationLinkManagerInterface { - - /** - * @var \Drupal\Core\Cache\CacheBackendInterface; - */ - protected $cache; - - /** - * Entity manager. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * Module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * Constructor. - * - * @param \Drupal\Core\Cache\CacheBackendInterface $cache - * The cache of relation URIs and their associated Typed Data IDs. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The config factory service. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. - */ - public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { - $this->cache = $cache; - $this->entityManager = $entity_manager; - $this->configFactory = $config_factory; - $this->moduleHandler = $module_handler; - $this->requestStack = $request_stack; - } - - /** - * {@inheritdoc} - */ - public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { - // Per the interface documention of this method, the returned URI may - // optionally also serve as the URL of a documentation page about this - // field. However, the REST module does not currently implement such - // a documentation page. Therefore, we return a URI assembled relative to - // the site's base URL, which is sufficient to uniquely identify the site's - // entity type + bundle + field for use in hypermedia formats, but we do - // not take into account unclean URLs, language prefixing, or anything else - // that would be required for Drupal to be able to respond with content - // at this URL. If a module is installed that adds such content, but - // requires this URL to be different (e.g., include a language prefix), - // then the module must also override the RelationLinkManager class/service - // to return the desired URL. - $uri = $this->getLinkDomain() . "/rest/relation/$entity_type/$bundle/$field_name"; - $this->moduleHandler->alter('rest_relation_uri', $uri, $context); - return $uri; - } - - /** - * {@inheritdoc} - */ - public function getRelationInternalIds($relation_uri, $context = array()) { - $relations = $this->getRelations($context); - if (isset($relations[$relation_uri])) { - return $relations[$relation_uri]; - } - return FALSE; - } - - /** - * Get the array of relation links. - * - * Any field can be handled as a relation simply by changing how it is - * normalized. Therefore, there is no prior knowledge that can be used here - * to determine which fields to assign relation URIs. Instead, each field, - * even primitives, are given a relation URI. It is up to the caller to - * determine which URIs to use. - * - * @param array $context - * Context from the normalizer/serializer operation. - * - * @return array - * An array of typed data ids (entity_type, bundle, and field name) keyed - * by corresponding relation URI. - */ - protected function getRelations($context = array()) { - $cid = 'rest:links:relations'; - $cache = $this->cache->get($cid); - if (!$cache) { - $this->writeCache($context); - $cache = $this->cache->get($cid); - } - return $cache->data; - } - - /** - * Writes the cache of relation links. - * - * @param array $context - * Context from the normalizer/serializer operation. - */ - protected function writeCache($context = array()) { - $data = array(); - - foreach ($this->entityManager->getDefinitions() as $entity_type) { - if ($entity_type instanceof ContentEntityTypeInterface) { - foreach ($this->entityManager->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) { - foreach ($this->entityManager->getFieldDefinitions($entity_type->id(), $bundle) as $field_definition) { - $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName(), $context); - $data[$relation_uri] = array( - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'field_name' => $field_definition->getName(), - ); - } - } - } - } - // These URIs only change when field info changes, so cache it permanently - // and only clear it when the fields cache is cleared. - $this->cache->set('rest:links:relations', $data, Cache::PERMANENT, array('entity_field_info')); - } - -} +/** + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. + */ +class RelationLinkManager extends MovedLinkRelationManager implements RelationLinkManagerInterface {} diff --git a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php index 53cd03991fb..c171b764eeb 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php @@ -2,38 +2,10 @@ namespace Drupal\rest\LinkManager; -interface RelationLinkManagerInterface extends ConfigurableLinkManagerInterface { +use Drupal\serialization\LinkManager\RelationLinkManagerInterface as MovedRelationLinkManagerInterface; - /** - * Gets the URI that corresponds to a field. - * - * When using hypermedia formats, this URI can be used to indicate which - * field the data represents. Documentation about this field can also be - * provided at this URI. - * - * @param string $entity_type - * The bundle's entity type. - * @param string $bundle - * The bundle name. - * @param string $field_name - * The field name. - * @param array $context - * (optional) Optional serializer/normalizer context. - * - * @return string - * The corresponding URI for the field. - */ - public function getRelationUri($entity_type, $bundle, $field_name, $context = array()); - - /** - * Translates a REST URI into internal IDs. - * - * @param string $relation_uri - * Relation URI to transform into internal IDs - * - * @return array - * Array with keys 'entity_type', 'bundle' and 'field_name'. - */ - public function getRelationInternalIds($relation_uri); - -} +/** + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. + */ +interface RelationLinkManagerInterface extends MovedRelationLinkManagerInterface {} diff --git a/core/modules/rest/src/LinkManager/TypeLinkManager.php b/core/modules/rest/src/LinkManager/TypeLinkManager.php index 3b0c8a23a8f..ac514eb0b0a 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManager.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManager.php @@ -2,148 +2,10 @@ namespace Drupal\rest\LinkManager; -use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Config\Entity\ConfigEntityInterface; -use Drupal\Core\Entity\EntityTypeBundleInfoInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\serialization\LinkManager\TypeLinkManager as MovedTypeLinkManager; -class TypeLinkManager extends LinkManagerBase implements TypeLinkManagerInterface { - - /** - * Injected cache backend. - * - * @var \Drupal\Core\Cache\CacheBackendInterface; - */ - protected $cache; - - /** - * Module handler service. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * The bundle info service. - * - * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface - */ - protected $bundleInfoService; - - /** - * Constructor. - * - * @param \Drupal\Core\Cache\CacheBackendInterface $cache - * The injected cache backend for caching type URIs. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The config factory service. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. - * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info_service - * The bundle info service. - */ - public function __construct(CacheBackendInterface $cache, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack, EntityTypeBundleInfoInterface $bundle_info_service) { - $this->cache = $cache; - $this->configFactory = $config_factory; - $this->moduleHandler = $module_handler; - $this->requestStack = $request_stack; - $this->bundleInfoService = $bundle_info_service; - } - - /** - * {@inheritdoc} - */ - public function getTypeUri($entity_type, $bundle, $context = array()) { - // Per the interface documention of this method, the returned URI may - // optionally also serve as the URL of a documentation page about this - // bundle. However, the REST module does not currently implement such - // a documentation page. Therefore, we return a URI assembled relative to - // the site's base URL, which is sufficient to uniquely identify the site's - // entity type and bundle for use in hypermedia formats, but we do not - // take into account unclean URLs, language prefixing, or anything else - // that would be required for Drupal to be able to respond with content - // at this URL. If a module is installed that adds such content, but - // requires this URL to be different (e.g., include a language prefix), - // then the module must also override the TypeLinkManager class/service to - // return the desired URL. - $uri = $this->getLinkDomain() . "/rest/type/$entity_type/$bundle"; - $this->moduleHandler->alter('rest_type_uri', $uri, $context); - return $uri; - } - - /** - * {@inheritdoc} - */ - public function getTypeInternalIds($type_uri, $context = array()) { - $types = $this->getTypes($context); - if (isset($types[$type_uri])) { - return $types[$type_uri]; - } - return FALSE; - } - - /** - * Get the array of type links. - * - * @param array $context - * Context from the normalizer/serializer operation. - * - * @return array - * An array of typed data ids (entity_type and bundle) keyed by - * corresponding type URI. - */ - protected function getTypes($context = array()) { - $cid = 'rest:links:types'; - $cache = $this->cache->get($cid); - if (!$cache) { - $data = $this->writeCache($context); - } - else { - $data = $cache->data; - } - return $data; - } - - /** - * Writes the cache of type links. - * - * @param array $context - * Context from the normalizer/serializer operation. - * - * @return array - * An array of typed data ids (entity_type and bundle) keyed by - * corresponding type URI. - */ - protected function writeCache($context = array()) { - $data = array(); - - // Type URIs correspond to bundles. Iterate through the bundles to get the - // URI and data for them. - $entity_types = \Drupal::entityManager()->getDefinitions(); - foreach ($this->bundleInfoService->getAllBundleInfo() as $entity_type_id => $bundles) { - // Only content entities are supported currently. - // @todo Consider supporting config entities. - if ($entity_types[$entity_type_id]->entityClassImplements(ConfigEntityInterface::class)) { - continue; - } - foreach ($bundles as $bundle => $bundle_info) { - // Get a type URI for the bundle. - $bundle_uri = $this->getTypeUri($entity_type_id, $bundle, $context); - $data[$bundle_uri] = array( - 'entity_type' => $entity_type_id, - 'bundle' => $bundle, - ); - } - } - // These URIs only change when entity info changes, so cache it permanently - // and only clear it when entity_info is cleared. - $this->cache->set('rest:links:types', $data, Cache::PERMANENT, array('entity_types')); - return $data; - } - -} +/** + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. + */ +class TypeLinkManager extends MovedTypeLinkManager implements TypeLinkManagerInterface {} diff --git a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php index 484ca76f156..e4abf1d2e93 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php @@ -2,39 +2,10 @@ namespace Drupal\rest\LinkManager; -interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface { +use Drupal\serialization\LinkManager\TypeLinkManagerInterface as MovedTypeLinkManagerInterface; - /** - * Gets the URI that corresponds to a bundle. - * - * When using hypermedia formats, this URI can be used to indicate which - * bundle the data represents. Documentation about required and optional - * fields can also be provided at this URI. - * - * @param $entity_type - * The bundle's entity type. - * @param $bundle - * The bundle name. - * @param array $context - * (optional) Optional serializer/normalizer context. - * - * @return string - * The corresponding URI for the bundle. - */ - public function getTypeUri($entity_type, $bundle, $context = array()); - - /** - * Get a bundle's Typed Data IDs based on a URI. - * - * @param string $type_uri - * The type URI. - * @param array $context - * Context from the normalizer/serializer operation. - * - * @return array | boolean - * If the URI matches a bundle, returns an array containing entity_type and - * bundle. Otherwise, returns false. - */ - public function getTypeInternalIds($type_uri, $context = array()); - -} +/** + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This has + * been moved to the serialization module. This exists solely for BC. + */ +interface TypeLinkManagerInterface extends MovedTypeLinkManagerInterface {} diff --git a/core/modules/rest/tests/modules/rest_test/rest_test.module b/core/modules/rest/tests/modules/rest_test/rest_test.module index 01511ea1e0b..98f9b5a129d 100644 --- a/core/modules/rest/tests/modules/rest_test/rest_test.module +++ b/core/modules/rest/tests/modules/rest_test/rest_test.module @@ -12,6 +12,8 @@ use Drupal\Core\Access\AccessResult; /** * Implements hook_rest_type_uri_alter(). + * + * @deprecated Kept only for BC test coverage, see \Drupal\Tests\serialization\Kernel\SerializationLinkManagerTest::testGetTypeUri(). */ function rest_test_rest_type_uri_alter(&$uri, $context = array()) { if (!empty($context['rest_test'])) { @@ -21,6 +23,8 @@ function rest_test_rest_type_uri_alter(&$uri, $context = array()) { /** * Implements hook_rest_relation_uri_alter(). + * + * @deprecated Kept only for BC test coverage, see \Drupal\Tests\serialization\Kernel\SerializationLinkManagerTest::testGetRelationUri(). */ function rest_test_rest_relation_uri_alter(&$uri, $context = array()) { if (!empty($context['rest_test'])) { diff --git a/core/modules/rest/tests/src/Kernel/RestLinkManagerTest.php b/core/modules/rest/tests/src/Kernel/RestLinkManagerTest.php deleted file mode 100644 index ab558e5761c..00000000000 --- a/core/modules/rest/tests/src/Kernel/RestLinkManagerTest.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -namespace Drupal\Tests\rest\Kernel; - -use Drupal\Core\Url; -use Drupal\KernelTests\KernelTestBase; - -/** - * Tests that REST type and relation link managers work as expected - * @group rest - */ -class RestLinkManagerTest extends KernelTestBase { - - /** - * {@inheritdoc} - */ - public static $modules = ['serialization', 'rest', 'rest_test', 'system']; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - \Drupal::service('router.builder')->rebuild(); - } - - /** - * Tests that type hooks work as expected. - */ - public function testRestLinkManagers() { - \Drupal::moduleHandler()->invoke('rest', 'install'); - /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ - $type_manager = \Drupal::service('rest.link_manager.type'); - $base = Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(); - $link = $type_manager->getTypeUri('node', 'page'); - $this->assertEqual($link, $base . 'rest/type/node/page'); - // Now with optional context. - $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_type'); - - /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ - $relation_manager = \Drupal::service('rest.link_manager.relation'); - $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); - $this->assertEqual($link, $base . 'rest/relation/node/page/field_ref'); - // Now with optional context. - $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_relation'); - } - - /** - * Tests that type hooks work as expected even without install hook. - */ - public function testRestLinkManagersNoInstallHook() { - /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ - $type_manager = \Drupal::service('rest.link_manager.type'); - $base = Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(); - $link = $type_manager->getTypeUri('node', 'page'); - $this->assertEqual($link, $base . 'rest/type/node/page'); - // Now with optional context. - $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_type'); - - /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ - $relation_manager = \Drupal::service('rest.link_manager.relation'); - $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); - $this->assertEqual($link, $base . 'rest/relation/node/page/field_ref'); - // Now with optional context. - $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_relation'); - } - - /** - * Tests \Drupal\rest\LinkManager\LinkManager::setLinkDomain(). - */ - public function testRestLinkManagersSetLinkDomain() { - /* @var \Drupal\rest\LinkManager\LinkManager $link_manager */ - $link_manager = \Drupal::service('rest.link_manager'); - $link_manager->setLinkDomain('http://example.com/'); - $link = $link_manager->getTypeUri('node', 'page'); - $this->assertEqual($link, 'http://example.com/rest/type/node/page'); - $link = $link_manager->getRelationUri('node', 'page', 'field_ref'); - $this->assertEqual($link, 'http://example.com/rest/relation/node/page/field_ref'); - } - -} diff --git a/core/modules/serialization/config/install/serialization.settings.yml b/core/modules/serialization/config/install/serialization.settings.yml new file mode 100644 index 00000000000..efe9deb9dda --- /dev/null +++ b/core/modules/serialization/config/install/serialization.settings.yml @@ -0,0 +1,3 @@ +# Set the domain for Serialization type and relation links. +# If left blank, the site's domain will be used. +link_domain: ~ diff --git a/core/modules/serialization/config/schema/serialization.schema.yml b/core/modules/serialization/config/schema/serialization.schema.yml new file mode 100644 index 00000000000..349499aa21f --- /dev/null +++ b/core/modules/serialization/config/schema/serialization.schema.yml @@ -0,0 +1,8 @@ +# Schema for the configuration files of the Serialization module. +serialization.settings: + type: config_object + label: 'Serialization settings' + mapping: + link_domain: + type: string + label: 'Domain of the relation' diff --git a/core/modules/serialization/serialization.api.php b/core/modules/serialization/serialization.api.php new file mode 100644 index 00000000000..a9ced724362 --- /dev/null +++ b/core/modules/serialization/serialization.api.php @@ -0,0 +1,62 @@ +<?php + +/** + * @file + * Describes hooks provided by the Serialization module. + */ + +/** + * @addtogroup hooks + * @{ + */ + +/** + * Alter the serialization type URI. + * + * Modules may wish to alter the type URI generated for a resource based on the + * context of the serializer/normalizer operation. + * + * @param string $uri + * The URI to alter. + * @param array $context + * The context from the serializer/normalizer operation. + * + * @see \Symfony\Component\Serializer\SerializerInterface::serialize() + * @see \Symfony\Component\Serializer\SerializerInterface::deserialize() + * @see \Symfony\Component\Serializer\NormalizerInterface::normalize() + * @see \Symfony\Component\Serializer\DenormalizerInterface::denormalize() + */ +function hook_serialization_type_uri_alter(&$uri, $context = array()) { + if ($context['mymodule'] == TRUE) { + $base = \Drupal::config('serialization.settings')->get('link_domain'); + $uri = str_replace($base, 'http://mymodule.domain', $uri); + } +} + + +/** + * Alter the serialization relation URI. + * + * Modules may wish to alter the relation URI generated for a resource based on + * the context of the serializer/normalizer operation. + * + * @param string $uri + * The URI to alter. + * @param array $context + * The context from the serializer/normalizer operation. + * + * @see \Symfony\Component\Serializer\SerializerInterface::serialize() + * @see \Symfony\Component\Serializer\SerializerInterface::deserialize() + * @see \Symfony\Component\Serializer\NormalizerInterface::normalize() + * @see \Symfony\Component\Serializer\DenormalizerInterface::denormalize() + */ +function hook_serialization_relation_uri_alter(&$uri, $context = array()) { + if ($context['mymodule'] == TRUE) { + $base = \Drupal::config('serialization.settings')->get('link_domain'); + $uri = str_replace($base, 'http://mymodule.domain', $uri); + } +} + +/** + * @} End of "addtogroup hooks". + */ diff --git a/core/modules/serialization/serialization.install b/core/modules/serialization/serialization.install new file mode 100644 index 00000000000..de3004945e4 --- /dev/null +++ b/core/modules/serialization/serialization.install @@ -0,0 +1,43 @@ +<?php + +/** + * @file + * Update functions for the serialization module. + */ + +/** + * @defgroup updates-8.2.x-to-8.3.x + * @{ + * Update functions from 8.2.x to 8.3.x. + */ + +/** + * Move 'link_domain' from 'rest.settings' to 'serialization.settings'. + */ +function serialization_update_8301() { + $config_factory = \Drupal::configFactory(); + + // The default value for the 'link_domain' key is `~`, which is the YAML + // equivalent of PHP's `NULL`. If the REST module is not installed, this is + // the value we will store in 'serialization.settings'. + $link_domain = NULL; + + // But if the REST module was installed, we migrate its 'link_domain' setting, + // because we are actually moving that setting from 'rest.settings' to + // 'serialization.settings'. + $rest_settings = $config_factory->getEditable('rest.settings'); + if ($rest_settings->getRawData() !== []) { + $link_domain = $rest_settings->get('link_domain'); + // Remove the 'link_domain' setting from 'rest.settings'. + $rest_settings->clear('link_domain') + ->save(); + } + + $serialization_settings = $config_factory->getEditable('serialization.settings'); + $serialization_settings->set('link_domain', $link_domain); + $serialization_settings->save(TRUE); +} + +/** + * @} End of "defgroup updates-8.2.x-to-8.3.x". + */ diff --git a/core/modules/serialization/serialization.services.yml b/core/modules/serialization/serialization.services.yml index c8fb3767f7e..98a190bb264 100644 --- a/core/modules/serialization/serialization.services.yml +++ b/core/modules/serialization/serialization.services.yml @@ -89,3 +89,13 @@ services: tags: - { name: event_subscriber } arguments: ['%serializer.formats%'] + # Link managers. + serialization.link_manager: + class: Drupal\serialization\LinkManager\LinkManager + arguments: ['@serialization.link_manager.type', '@serialization.link_manager.relation'] + serialization.link_manager.type: + class: Drupal\serialization\LinkManager\TypeLinkManager + arguments: ['@cache.default', '@module_handler', '@config.factory', '@request_stack', '@entity_type.bundle.info'] + serialization.link_manager.relation: + class: Drupal\serialization\LinkManager\RelationLinkManager + arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory', '@request_stack'] diff --git a/core/modules/serialization/src/LinkManager/ConfigurableLinkManagerInterface.php b/core/modules/serialization/src/LinkManager/ConfigurableLinkManagerInterface.php new file mode 100644 index 00000000000..2e866025e02 --- /dev/null +++ b/core/modules/serialization/src/LinkManager/ConfigurableLinkManagerInterface.php @@ -0,0 +1,20 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +/** + * Defines an interface for a link manager with a configurable domain. + */ +interface ConfigurableLinkManagerInterface { + + /** + * Sets the link domain used in constructing link URIs. + * + * @param string $domain + * The link domain to use for constructing link URIs. + * + * @return $this + */ + public function setLinkDomain($domain); + +} diff --git a/core/modules/serialization/src/LinkManager/LinkManager.php b/core/modules/serialization/src/LinkManager/LinkManager.php new file mode 100644 index 00000000000..19c83e2fefb --- /dev/null +++ b/core/modules/serialization/src/LinkManager/LinkManager.php @@ -0,0 +1,71 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +class LinkManager implements LinkManagerInterface { + + /** + * The type link manager. + * + * @var \Drupal\serialization\LinkManager\TypeLinkManagerInterface + */ + protected $typeLinkManager; + + /** + * The relation link manager. + * + * @var \Drupal\serialization\LinkManager\RelationLinkManagerInterface + */ + protected $relationLinkManager; + + /** + * Constructor. + * + * @param \Drupal\serialization\LinkManager\TypeLinkManagerInterface $type_link_manager + * Manager for handling bundle URIs. + * @param \Drupal\serialization\LinkManager\RelationLinkManagerInterface $relation_link_manager + * Manager for handling bundle URIs. + */ + public function __construct(TypeLinkManagerInterface $type_link_manager, RelationLinkManagerInterface $relation_link_manager) { + $this->typeLinkManager = $type_link_manager; + $this->relationLinkManager = $relation_link_manager; + } + + /** + * {@inheritdoc} + */ + public function getTypeUri($entity_type, $bundle, $context = array()) { + return $this->typeLinkManager->getTypeUri($entity_type, $bundle, $context); + } + + /** + * {@inheritdoc} + */ + public function getTypeInternalIds($type_uri, $context = array()) { + return $this->typeLinkManager->getTypeInternalIds($type_uri, $context); + } + + /** + * {@inheritdoc} + */ + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { + return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name, $context); + } + + /** + * {@inheritdoc} + */ + public function getRelationInternalIds($relation_uri) { + return $this->relationLinkManager->getRelationInternalIds($relation_uri); + } + + /** + * {@inheritdoc} + */ + public function setLinkDomain($domain) { + $this->relationLinkManager->setLinkDomain($domain); + $this->typeLinkManager->setLinkDomain($domain); + return $this; + } + +} diff --git a/core/modules/serialization/src/LinkManager/LinkManagerBase.php b/core/modules/serialization/src/LinkManager/LinkManagerBase.php new file mode 100644 index 00000000000..0cf7e69221d --- /dev/null +++ b/core/modules/serialization/src/LinkManager/LinkManagerBase.php @@ -0,0 +1,58 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +/** + * Defines an abstract base-class for Serialization link manager objects. + */ +abstract class LinkManagerBase { + + /** + * Link domain used for type links URIs. + * + * @var string + */ + protected $linkDomain; + + /** + * Config factory service. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + /** + * {@inheritdoc} + */ + public function setLinkDomain($domain) { + $this->linkDomain = rtrim($domain, '/'); + return $this; + } + + /** + * Gets the link domain. + * + * @return string + * The link domain. + */ + protected function getLinkDomain() { + if (empty($this->linkDomain)) { + if ($domain = $this->configFactory->get('serialization.settings')->get('link_domain')) { + $this->linkDomain = rtrim($domain, '/'); + } + else { + $request = $this->requestStack->getCurrentRequest(); + $this->linkDomain = $request->getSchemeAndHttpHost() . $request->getBasePath(); + } + } + return $this->linkDomain; + } + +} diff --git a/core/modules/serialization/src/LinkManager/LinkManagerInterface.php b/core/modules/serialization/src/LinkManager/LinkManagerInterface.php new file mode 100644 index 00000000000..8986d0faacd --- /dev/null +++ b/core/modules/serialization/src/LinkManager/LinkManagerInterface.php @@ -0,0 +1,18 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +/** + * Interface implemented by link managers. + * + * There are no explicit methods on the manager interface. Instead link managers + * broker the interactions of the different components, and therefore must + * implement each component interface, which is enforced by this interface + * extending all of the component ones. + * + * While a link manager may directly implement these interface methods with + * custom logic, it is expected to be more common for plugin managers to proxy + * the method invocations to the respective components. + */ +interface LinkManagerInterface extends TypeLinkManagerInterface, RelationLinkManagerInterface { +} diff --git a/core/modules/serialization/src/LinkManager/RelationLinkManager.php b/core/modules/serialization/src/LinkManager/RelationLinkManager.php new file mode 100644 index 00000000000..465dda38174 --- /dev/null +++ b/core/modules/serialization/src/LinkManager/RelationLinkManager.php @@ -0,0 +1,144 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Entity\ContentEntityTypeInterface; +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\HttpFoundation\RequestStack; + +class RelationLinkManager extends LinkManagerBase implements RelationLinkManagerInterface { + + /** + * @var \Drupal\Core\Cache\CacheBackendInterface; + */ + protected $cache; + + /** + * Entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * Module handler service. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * Constructor. + * + * @param \Drupal\Core\Cache\CacheBackendInterface $cache + * The cache of relation URIs and their associated Typed Data IDs. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory service. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. + */ + public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { + $this->cache = $cache; + $this->entityManager = $entity_manager; + $this->configFactory = $config_factory; + $this->moduleHandler = $module_handler; + $this->requestStack = $request_stack; + } + + /** + * {@inheritdoc} + */ + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { + // Per the interface documentation of this method, the returned URI may + // optionally also serve as the URL of a documentation page about this + // field. However, Drupal does not currently implement such a documentation + // page. Therefore, we return a URI assembled relative to the site's base + // URL, which is sufficient to uniquely identify the site's entity type + + // bundle + field for use in hypermedia formats, but we do not take into + // account unclean URLs, language prefixing, or anything else that would be + // required for Drupal to be able to respond with content at this URL. If a + // module is installed that adds such content, but requires this URL to be + // different (e.g., include a language prefix), then the module must also + // override the RelationLinkManager class/service to return the desired URL. + $uri = $this->getLinkDomain() . "/rest/relation/$entity_type/$bundle/$field_name"; + $this->moduleHandler->alter('serialization_relation_uri', $uri, $context); + // @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This + // hook is invoked to maintain backwards compatibility + $this->moduleHandler->alter('rest_relation_uri', $uri, $context); + return $uri; + } + + /** + * {@inheritdoc} + */ + public function getRelationInternalIds($relation_uri, $context = array()) { + $relations = $this->getRelations($context); + if (isset($relations[$relation_uri])) { + return $relations[$relation_uri]; + } + return FALSE; + } + + /** + * Get the array of relation links. + * + * Any field can be handled as a relation simply by changing how it is + * normalized. Therefore, there is no prior knowledge that can be used here + * to determine which fields to assign relation URIs. Instead, each field, + * even primitives, are given a relation URI. It is up to the caller to + * determine which URIs to use. + * + * @param array $context + * Context from the normalizer/serializer operation. + * + * @return array + * An array of typed data ids (entity_type, bundle, and field name) keyed + * by corresponding relation URI. + */ + protected function getRelations($context = array()) { + $cid = 'serialization:links:relations'; + $cache = $this->cache->get($cid); + if (!$cache) { + $this->writeCache($context); + $cache = $this->cache->get($cid); + } + return $cache->data; + } + + /** + * Writes the cache of relation links. + * + * @param array $context + * Context from the normalizer/serializer operation. + */ + protected function writeCache($context = array()) { + $data = array(); + + foreach ($this->entityManager->getDefinitions() as $entity_type) { + if ($entity_type instanceof ContentEntityTypeInterface) { + foreach ($this->entityManager->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) { + foreach ($this->entityManager->getFieldDefinitions($entity_type->id(), $bundle) as $field_definition) { + $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName(), $context); + $data[$relation_uri] = array( + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'field_name' => $field_definition->getName(), + ); + } + } + } + } + // These URIs only change when field info changes, so cache it permanently + // and only clear it when the fields cache is cleared. + $this->cache->set('serialization:links:relations', $data, Cache::PERMANENT, array('entity_field_info')); + } + +} diff --git a/core/modules/serialization/src/LinkManager/RelationLinkManagerInterface.php b/core/modules/serialization/src/LinkManager/RelationLinkManagerInterface.php new file mode 100644 index 00000000000..fb0aac8f8f2 --- /dev/null +++ b/core/modules/serialization/src/LinkManager/RelationLinkManagerInterface.php @@ -0,0 +1,39 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +interface RelationLinkManagerInterface extends ConfigurableLinkManagerInterface { + + /** + * Gets the URI that corresponds to a field. + * + * When using hypermedia formats, this URI can be used to indicate which + * field the data represents. Documentation about this field can also be + * provided at this URI. + * + * @param string $entity_type + * The bundle's entity type. + * @param string $bundle + * The bundle name. + * @param string $field_name + * The field name. + * @param array $context + * (optional) Optional serializer/normalizer context. + * + * @return string + * The corresponding URI for the field. + */ + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()); + + /** + * Translates a REST URI into internal IDs. + * + * @param string $relation_uri + * Relation URI to transform into internal IDs + * + * @return array + * Array with keys 'entity_type', 'bundle' and 'field_name'. + */ + public function getRelationInternalIds($relation_uri); + +} diff --git a/core/modules/serialization/src/LinkManager/TypeLinkManager.php b/core/modules/serialization/src/LinkManager/TypeLinkManager.php new file mode 100644 index 00000000000..22801376716 --- /dev/null +++ b/core/modules/serialization/src/LinkManager/TypeLinkManager.php @@ -0,0 +1,150 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\HttpFoundation\RequestStack; + +class TypeLinkManager extends LinkManagerBase implements TypeLinkManagerInterface { + + /** + * Injected cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface; + */ + protected $cache; + + /** + * Module handler service. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * The bundle info service. + * + * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface + */ + protected $bundleInfoService; + + /** + * Constructor. + * + * @param \Drupal\Core\Cache\CacheBackendInterface $cache + * The injected cache backend for caching type URIs. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory service. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. + * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info_service + * The bundle info service. + */ + public function __construct(CacheBackendInterface $cache, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack, EntityTypeBundleInfoInterface $bundle_info_service) { + $this->cache = $cache; + $this->configFactory = $config_factory; + $this->moduleHandler = $module_handler; + $this->requestStack = $request_stack; + $this->bundleInfoService = $bundle_info_service; + } + + /** + * {@inheritdoc} + */ + public function getTypeUri($entity_type, $bundle, $context = array()) { + // Per the interface documentation of this method, the returned URI may + // optionally also serve as the URL of a documentation page about this + // bundle. However, Drupal does not currently implement such a documentation + // page. Therefore, we return a URI assembled relative to the site's base + // URL, which is sufficient to uniquely identify the site's entity type and + // bundle for use in hypermedia formats, but we do not take into account + // unclean URLs, language prefixing, or anything else that would be required + // for Drupal to be able to respond with content at this URL. If a module is + // installed that adds such content, but requires this URL to be different + // (e.g., include a language prefix), then the module must also override the + // TypeLinkManager class/service to return the desired URL. + $uri = $this->getLinkDomain() . "/rest/type/$entity_type/$bundle"; + $this->moduleHandler->alter('serialization_type_uri', $uri, $context); + // @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This + // hook is invoked to maintain backwards compatibility + $this->moduleHandler->alter('rest_type_uri', $uri, $context); + return $uri; + } + + /** + * {@inheritdoc} + */ + public function getTypeInternalIds($type_uri, $context = array()) { + $types = $this->getTypes($context); + if (isset($types[$type_uri])) { + return $types[$type_uri]; + } + return FALSE; + } + + /** + * Get the array of type links. + * + * @param array $context + * Context from the normalizer/serializer operation. + * + * @return array + * An array of typed data ids (entity_type and bundle) keyed by + * corresponding type URI. + */ + protected function getTypes($context = array()) { + $cid = 'serialization:links:types'; + $cache = $this->cache->get($cid); + if (!$cache) { + $data = $this->writeCache($context); + } + else { + $data = $cache->data; + } + return $data; + } + + /** + * Writes the cache of type links. + * + * @param array $context + * Context from the normalizer/serializer operation. + * + * @return array + * An array of typed data ids (entity_type and bundle) keyed by + * corresponding type URI. + */ + protected function writeCache($context = array()) { + $data = array(); + + // Type URIs correspond to bundles. Iterate through the bundles to get the + // URI and data for them. + $entity_types = \Drupal::entityManager()->getDefinitions(); + foreach ($this->bundleInfoService->getAllBundleInfo() as $entity_type_id => $bundles) { + // Only content entities are supported currently. + // @todo Consider supporting config entities. + if ($entity_types[$entity_type_id]->entityClassImplements(ConfigEntityInterface::class)) { + continue; + } + foreach ($bundles as $bundle => $bundle_info) { + // Get a type URI for the bundle. + $bundle_uri = $this->getTypeUri($entity_type_id, $bundle, $context); + $data[$bundle_uri] = array( + 'entity_type' => $entity_type_id, + 'bundle' => $bundle, + ); + } + } + // These URIs only change when entity info changes, so cache it permanently + // and only clear it when entity_info is cleared. + $this->cache->set('serialization:links:types', $data, Cache::PERMANENT, array('entity_types')); + return $data; + } + +} diff --git a/core/modules/serialization/src/LinkManager/TypeLinkManagerInterface.php b/core/modules/serialization/src/LinkManager/TypeLinkManagerInterface.php new file mode 100644 index 00000000000..fed9119cf4b --- /dev/null +++ b/core/modules/serialization/src/LinkManager/TypeLinkManagerInterface.php @@ -0,0 +1,40 @@ +<?php + +namespace Drupal\serialization\LinkManager; + +interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface { + + /** + * Gets the URI that corresponds to a bundle. + * + * When using hypermedia formats, this URI can be used to indicate which + * bundle the data represents. Documentation about required and optional + * fields can also be provided at this URI. + * + * @param $entity_type + * The bundle's entity type. + * @param $bundle + * The bundle name. + * @param array $context + * (optional) Optional serializer/normalizer context. + * + * @return string + * The corresponding URI for the bundle. + */ + public function getTypeUri($entity_type, $bundle, $context = array()); + + /** + * Get a bundle's Typed Data IDs based on a URI. + * + * @param string $type_uri + * The type URI. + * @param array $context + * Context from the normalizer/serializer operation. + * + * @return array | boolean + * If the URI matches a bundle, returns an array containing entity_type and + * bundle. Otherwise, returns false. + */ + public function getTypeInternalIds($type_uri, $context = array()); + +} diff --git a/core/modules/serialization/tests/fixtures/update/drupal-8.rest-serialization_update_8301.php b/core/modules/serialization/tests/fixtures/update/drupal-8.rest-serialization_update_8301.php Binary files differnew file mode 100644 index 00000000000..b83448b5f05 --- /dev/null +++ b/core/modules/serialization/tests/fixtures/update/drupal-8.rest-serialization_update_8301.php diff --git a/core/modules/serialization/tests/fixtures/update/drupal-8.serialization-serialization_update_8301.php b/core/modules/serialization/tests/fixtures/update/drupal-8.serialization-serialization_update_8301.php new file mode 100644 index 00000000000..4c41134d482 --- /dev/null +++ b/core/modules/serialization/tests/fixtures/update/drupal-8.serialization-serialization_update_8301.php @@ -0,0 +1,37 @@ +<?php + +/** + * @file + * Contains database additions to drupal-8.bare.standard.php.gz for testing the + * upgrade path of serialization_update_8301(). + */ + +use Drupal\Core\Database\Database; + +$connection = Database::getConnection(); + +// Set the schema version. +$connection->insert('key_value') + ->fields([ + 'collection' => 'system.schema', + 'name' => 'serialization', + 'value' => 'i:8000;', + ]) + ->execute(); + +// Update core.extension. +$extensions = $connection->select('config') + ->fields('config', ['data']) + ->condition('collection', '') + ->condition('name', 'core.extension') + ->execute() + ->fetchField(); +$extensions = unserialize($extensions); +$extensions['module']['serialization'] = 0; +$connection->update('config') + ->fields([ + 'data' => serialize($extensions), + ]) + ->condition('collection', '') + ->condition('name', 'core.extension') + ->execute(); diff --git a/core/modules/serialization/tests/serialization_test/serialization_test.module b/core/modules/serialization/tests/serialization_test/serialization_test.module new file mode 100644 index 00000000000..c47ca2fe87f --- /dev/null +++ b/core/modules/serialization/tests/serialization_test/serialization_test.module @@ -0,0 +1,24 @@ +<?php + +/** + * @file + * Contains hook implementations for testing Serialization module. + */ + +/** + * Implements hook_serialization_type_uri_alter(). + */ +function serialization_test_serialization_type_uri_alter(&$uri, $context = array()) { + if (!empty($context['serialization_test'])) { + $uri = 'serialization_test_type'; + } +} + +/** + * Implements hook_rest_relation_uri_alter(). + */ +function serialization_test_serialization_relation_uri_alter(&$uri, $context = array()) { + if (!empty($context['serialization_test'])) { + $uri = 'serialization_test_relation'; + } +} diff --git a/core/modules/serialization/tests/src/Functional/Update/CreateSerializationSettingsForLinkDomainUpdateTest.php b/core/modules/serialization/tests/src/Functional/Update/CreateSerializationSettingsForLinkDomainUpdateTest.php new file mode 100644 index 00000000000..a6e55d47153 --- /dev/null +++ b/core/modules/serialization/tests/src/Functional/Update/CreateSerializationSettingsForLinkDomainUpdateTest.php @@ -0,0 +1,47 @@ +<?php + +namespace Drupal\Tests\serialization\Functional\Update; + +use Drupal\system\Tests\Update\UpdatePathTestBase; + +/** + * Tests that 'serialization.settings' is created, to store 'link_domain'. + * + * @see https://www.drupal.org/node/2758897 + * + * @group serialization + */ +class CreateSerializationSettingsForLinkDomainUpdateTest extends UpdatePathTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = ['serialization']; + + /** + * {@inheritdoc} + */ + public function setDatabaseDumpFiles() { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../fixtures/update/drupal-8.serialization-serialization_update_8301.php', + ]; + } + + /** + * Tests serialization_update_8301(). + */ + public function testSerializationSettingsCreated() { + // Make sure we have the expected values before the update. + $serialization_settings = $this->config('serialization.settings'); + $this->assertIdentical([], $serialization_settings->getRawData()); + + $this->runUpdates(); + + // Make sure we have the expected values after the update. + $serialization_settings = \Drupal::configFactory()->get('serialization.settings'); + $this->assertTrue(array_key_exists('link_domain', $serialization_settings->getRawData())); + $this->assertIdentical(NULL, $serialization_settings->getRawData()['link_domain']); + } + +} diff --git a/core/modules/serialization/tests/src/Functional/Update/MigrateLinkDomainSettingFromRestToSerializationUpdateTest.php b/core/modules/serialization/tests/src/Functional/Update/MigrateLinkDomainSettingFromRestToSerializationUpdateTest.php new file mode 100644 index 00000000000..5e490577db8 --- /dev/null +++ b/core/modules/serialization/tests/src/Functional/Update/MigrateLinkDomainSettingFromRestToSerializationUpdateTest.php @@ -0,0 +1,53 @@ +<?php + +namespace Drupal\Tests\serialization\Functional\Update; + +use Drupal\system\Tests\Update\UpdatePathTestBase; + +/** + * 'link_domain' is migrated from 'rest.settings' to 'serialization.settings'. + * + * @see https://www.drupal.org/node/2758897 + * + * @group serialization + */ +class MigrateLinkDomainSettingFromRestToSerializationUpdateTest extends UpdatePathTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = ['rest', 'serialization']; + + /** + * {@inheritdoc} + */ + public function setDatabaseDumpFiles() { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../fixtures/update/drupal-8.serialization-serialization_update_8301.php', + __DIR__ . '/../../../fixtures/update/drupal-8.rest-serialization_update_8301.php', + ]; + } + + /** + * Tests serialization_update_8301(). + */ + public function testLinkDomainMigratedFromRestSettingsToSerializationSettings() { + // Make sure we have the expected values before the update. + $serialization_settings = $this->config('serialization.settings'); + $this->assertIdentical([], $serialization_settings->getRawData()); + $rest_settings = $this->config('rest.settings'); + $this->assertTrue(array_key_exists('link_domain', $rest_settings->getRawData())); + $this->assertIdentical('http://example.com', $rest_settings->getRawData()['link_domain']); + + $this->runUpdates(); + + // Make sure we have the expected values after the update. + $serialization_settings = \Drupal::configFactory()->get('serialization.settings'); + $this->assertTrue(array_key_exists('link_domain', $serialization_settings->getRawData())); + $this->assertIdentical('http://example.com', $serialization_settings->getRawData()['link_domain']); + $rest_settings = $this->config('rest.settings'); + $this->assertFalse(array_key_exists('link_domain', $rest_settings->getRawData())); + } + +} diff --git a/core/modules/serialization/tests/src/Kernel/SerializationLinkManagerTest.php b/core/modules/serialization/tests/src/Kernel/SerializationLinkManagerTest.php new file mode 100644 index 00000000000..e88ef0ab492 --- /dev/null +++ b/core/modules/serialization/tests/src/Kernel/SerializationLinkManagerTest.php @@ -0,0 +1,74 @@ +<?php + +namespace Drupal\Tests\serialization\Kernel; + +use Drupal\Core\Url; +use Drupal\KernelTests\KernelTestBase; + +/** + * @coversDefaultClass \Drupal\serialization\LinkManager\LinkManager + * @group serialization + */ +class SerializationLinkManagerTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = ['serialization', 'serialization_test', 'rest_test', 'system']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + \Drupal::service('router.builder')->rebuild(); + } + + /** + * @covers ::getTypeUri + */ + public function testGetTypeUri() { + /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ + $type_manager = \Drupal::service('serialization.link_manager.type'); + $base = Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(); + $link = $type_manager->getTypeUri('node', 'page'); + $this->assertSame($link, $base . 'rest/type/node/page'); + // Now with optional context. + $link = $type_manager->getTypeUri('node', 'page', ['serialization_test' => TRUE]); + $this->assertSame($link, 'serialization_test_type'); + // Test BC: hook_rest_type_uri_alter(). + $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); + $this->assertSame($link, 'rest_test_type'); + } + + /** + * @covers ::getRelationUri + */ + public function testGetRelationUri() { + /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ + $relation_manager = \Drupal::service('serialization.link_manager.relation'); + $base = Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(); + $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); + $this->assertSame($link, $base . 'rest/relation/node/page/field_ref'); + // Now with optional context. + $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['serialization_test' => TRUE]); + $this->assertSame($link, 'serialization_test_relation'); + // Test BC: hook_rest_relation_uri_alter(). + $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); + $this->assertSame($link, 'rest_test_relation'); + } + + /** + * @covers ::setLinkDomain + */ + public function testSerializationLinkManagersSetLinkDomain() { + /* @var \Drupal\rest\LinkManager\LinkManager $link_manager */ + $link_manager = \Drupal::service('serialization.link_manager'); + $link_manager->setLinkDomain('http://example.com/'); + $link = $link_manager->getTypeUri('node', 'page'); + $this->assertEqual($link, 'http://example.com/rest/type/node/page'); + $link = $link_manager->getRelationUri('node', 'page', 'field_ref'); + $this->assertEqual($link, 'http://example.com/rest/relation/node/page/field_ref'); + } + +} |