diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index 6a24454..4b10c33 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -71,7 +71,7 @@ public function normalize($entity, $format = NULL, array $context = array()) { 'href' => $this->getEntityUri($entity), ), 'type' => array( - 'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle()), + 'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle(), $context), ), ), ); @@ -124,7 +124,7 @@ public function denormalize($data, $class, $format = NULL, array $context = arra } // Create the entity. - $typed_data_ids = $this->getTypedDataIds($data['_links']['type']); + $typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context); $values = array(); // Figure out the language to use. if (isset($data['langcode'])) { @@ -211,13 +211,14 @@ protected function getEntityUri($entity) { * * @param array $types * The type array(s) (value of the 'type' attribute of the incoming data). + * @param array $context + * Context from the normalizer/serializer operation. * * @return array * The typed data IDs. * - * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException */ - protected function getTypedDataIds($types) { + protected function getTypedDataIds($types, $context = array()) { // The 'type' can potentially contain an array of type objects. By default, // Drupal only uses a single type in serializing, but allows for multiple // types when deserializing. @@ -232,7 +233,7 @@ protected function getTypedDataIds($types) { $type_uri = $type['href']; // Check whether the URI corresponds to a known type on this site. Break // once one does. - if ($typed_data_ids = $this->linkManager->getTypeInternalIds($type['href'])) { + if ($typed_data_ids = $this->linkManager->getTypeInternalIds($type['href'], $context)) { break; } } diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index 44ba171..027d904 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -85,7 +85,7 @@ public function normalize($field_item, $format = NULL, array $context = array()) // objects. $field_name = $field_item->getParent()->getName(); $entity = $field_item->getEntity(); - $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name); + $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name, $context); return array( '_links' => array( $field_uri => array($link), diff --git a/core/modules/rest/rest.api.php b/core/modules/rest/rest.api.php index 2f3f6ee..36e0860 100644 --- a/core/modules/rest/rest.api.php +++ b/core/modules/rest/rest.api.php @@ -29,5 +29,42 @@ function hook_rest_resource_alter(&$definitions) { } /** + * Alter the rest 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. + */ +function hook_rest_type_uri_alter(&$uri, $context = array()) { + if ($context['mymodule'] == TRUE) { + $base = \Drupal::config('rest.settings')->get('relation_domain'); + $uri = str_replace($base, 'http://mymodule.domain', $uri); + } +} + + +/** + * Alter the rest 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. + */ +function hook_rest_relation_uri_alter(&$uri, $context = array()) { + if ($context['mymodule'] == TRUE) { + $base = \Drupal::config('rest.settings')->get('relation_domain'); + $uri = str_replace($base, 'http://mymodule.domain', $uri); + } +} + +/** * @} End of "addtogroup hooks". */ diff --git a/core/modules/rest/rest.install b/core/modules/rest/rest.install index e69de29..b72bfb3 100644 --- a/core/modules/rest/rest.install +++ b/core/modules/rest/rest.install @@ -0,0 +1,17 @@ +set('relation_domain', Url::fromRoute('', [], ['absolute' => TRUE])->toString()) + ->save(); +} diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index c25d692..8c846d2 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -18,10 +18,10 @@ services: arguments: ['@rest.link_manager.type', '@rest.link_manager.relation'] rest.link_manager.type: class: Drupal\rest\LinkManager\TypeLinkManager - arguments: ['@cache.default'] + arguments: ['@cache.default', '@module_handler', '@config.factory'] rest.link_manager.relation: class: Drupal\rest\LinkManager\RelationLinkManager - arguments: ['@cache.default', '@entity.manager'] + arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory'] rest.resource_routes: class: Drupal\rest\Routing\ResourceRoutes arguments: ['@plugin.manager.rest', '@config.factory', '@logger.channel.rest'] diff --git a/core/modules/rest/src/LinkManager/LinkManager.php b/core/modules/rest/src/LinkManager/LinkManager.php index 742fcfd..1a6b7e0 100644 --- a/core/modules/rest/src/LinkManager/LinkManager.php +++ b/core/modules/rest/src/LinkManager/LinkManager.php @@ -38,22 +38,22 @@ public function __construct(TypeLinkManagerInterface $type_link_manager, Relatio /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeUri(). */ - public function getTypeUri($entity_type, $bundle) { - return $this->typeLinkManager->getTypeUri($entity_type, $bundle); + public function getTypeUri($entity_type, $bundle, $context = array()) { + return $this->typeLinkManager->getTypeUri($entity_type, $bundle, $context); } /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeInternalIds(). */ - public function getTypeInternalIds($type_uri) { - return $this->typeLinkManager->getTypeInternalIds($type_uri); + public function getTypeInternalIds($type_uri, $context = array()) { + return $this->typeLinkManager->getTypeInternalIds($type_uri, $context); } /** * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationUri(). */ - public function getRelationUri($entity_type, $bundle, $field_name) { - return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name); + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { + return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name, $context); } /** diff --git a/core/modules/rest/src/LinkManager/RelationLinkManager.php b/core/modules/rest/src/LinkManager/RelationLinkManager.php index fbe0b3d..d4c364d 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManager.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManager.php @@ -9,8 +9,10 @@ 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; class RelationLinkManager implements RelationLinkManagerInterface{ @@ -32,40 +34,45 @@ class RelationLinkManager implements RelationLinkManagerInterface{ protected $relationDomain; /** + * 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. */ - public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager) { + public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) { $this->cache = $cache; $this->entityManager = $entity_manager; - $relationDomain = \Drupal::config('rest.settings')->get('relation_domain'); - $this->relationDomain = $relationDomain ? rtrim($relationDomain, '/') : null; + $this->relationDomain = rtrim($config_factory->get('rest.settings')->get('relation_domain'), '/'); + $this->moduleHandler = $module_handler; } /** * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationUri(). */ - public function getRelationUri($entity_type, $bundle, $field_name) { - $base = "/rest/relation/$entity_type/$bundle/$field_name"; - if($this->relationDomain) - { - return $this->relationDomain . $base; - } - else - { - return \Drupal::service('unrouted_url_assembler')->assemble('base://' . ltrim($base, '/'), array('absolute' => TRUE)); - } + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { + $uri = $this->relationDomain . "/rest/relation/$entity_type/$bundle/$field_name"; + $this->moduleHandler->alter('rest_relation_uri', $uri, $context); + return $uri; } /** * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationInternalIds(). */ - public function getRelationInternalIds($relation_uri) { - $relations = $this->getRelations(); + public function getRelationInternalIds($relation_uri, $context = array()) { + $relations = $this->getRelations($context); if (isset($relations[$relation_uri])) { return $relations[$relation_uri]; } @@ -81,15 +88,18 @@ public function getRelationInternalIds($relation_uri) { * 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. */ - public function getRelations() { + public function getRelations($context = array()) { $cid = 'rest:links:relations'; $cache = $this->cache->get($cid); if (!$cache) { - $this->writeCache(); + $this->writeCache($context); $cache = $this->cache->get($cid); } return $cache->data; @@ -97,15 +107,18 @@ public function getRelations() { /** * Writes the cache of relation links. + * + * @param array $context + * Context from the normalizer/serializer operation. */ - protected function writeCache() { + 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()); + $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName(), $context); $data[$relation_uri] = array( 'entity_type' => $entity_type, 'bundle' => $bundle, diff --git a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php index bd28432..0ce4924 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php @@ -18,9 +18,11 @@ * 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); + public function getRelationUri($entity_type, $bundle, $field_name, $context = array()); } diff --git a/core/modules/rest/src/LinkManager/TypeLinkManager.php b/core/modules/rest/src/LinkManager/TypeLinkManager.php index c559266..fb95ed2 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManager.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManager.php @@ -9,6 +9,9 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; class TypeLinkManager implements TypeLinkManagerInterface { @@ -25,15 +28,26 @@ class TypeLinkManager implements TypeLinkManagerInterface { protected $relationDomain; /** + * Module handler service. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** * 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. */ - public function __construct(CacheBackendInterface $cache) { + public function __construct(CacheBackendInterface $cache, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) { $this->cache = $cache; - $relationDomain = \Drupal::config('rest.settings')->get('relation_domain'); - $this->relationDomain = $relationDomain ? rtrim($relationDomain, '/') : null; + $this->relationDomain = rtrim($config_factory->get('rest.settings')->get('relation_domain'), '/'); + $this->moduleHandler = $module_handler; } /** @@ -43,27 +57,23 @@ public function __construct(CacheBackendInterface $cache) { * The bundle's entity type. * @param string $bundle * The name of the bundle. + * @param array $context + * Context of normalizer/serializer. * * @return array * The URI that identifies this bundle. */ - public function getTypeUri($entity_type, $bundle) { - $base = "/rest/type/$entity_type/$bundle"; - if($this->relationDomain) - { - return $this->relationDomain . $base; - } - else - { - return \Drupal::service('unrouted_url_assembler')->assemble('base://' . ltrim($base, '/'), array('absolute' => TRUE)); - } + public function getTypeUri($entity_type, $bundle, $context = array()) { + $uri = $this->relationDomain . "/rest/type/$entity_type/$bundle"; + $this->moduleHandler->alter('rest_type_uri', $uri, $context); + return $uri; } /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeInternalIds(). */ - public function getTypeInternalIds($type_uri) { - $types = $this->getTypes(); + public function getTypeInternalIds($type_uri, $context) { + $types = $this->getTypes($context); if (isset($types[$type_uri])) { return $types[$type_uri]; } @@ -73,15 +83,18 @@ public function getTypeInternalIds($type_uri) { /** * 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. */ - public function getTypes() { + public function getTypes($context = array()) { $cid = 'rest:links:types'; $cache = $this->cache->get($cid); if (!$cache) { - $this->writeCache(); + $this->writeCache($context); $cache = $this->cache->get($cid); } return $cache->data; @@ -89,8 +102,11 @@ public function getTypes() { /** * Writes the cache of type links. + * + * @param array $context + * Context from the normalizer/serializer operation. */ - protected function writeCache() { + protected function writeCache($context = array()) { $data = array(); // Type URIs correspond to bundles. Iterate through the bundles to get the @@ -104,7 +120,7 @@ protected function writeCache() { } foreach ($bundles as $bundle => $bundle_info) { // Get a type URI for the bundle. - $bundle_uri = $this->getTypeUri($entity_type_id, $bundle); + $bundle_uri = $this->getTypeUri($entity_type_id, $bundle, $context); $data[$bundle_uri] = array( 'entity_type' => $entity_type_id, 'bundle' => $bundle, diff --git a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php index 9c2d942..185f707 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php @@ -20,21 +20,25 @@ * 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); + 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); + public function getTypeInternalIds($type_uri, $context = array()); } diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php index e1aae7f..cc70309 100644 --- a/core/modules/rest/src/Tests/ReadTest.php +++ b/core/modules/rest/src/Tests/ReadTest.php @@ -8,6 +8,7 @@ namespace Drupal\rest\Tests; use Drupal\Component\Serialization\Json; +use Drupal\Core\Url; use Drupal\rest\Tests\RESTTestBase; /** @@ -28,6 +29,8 @@ class ReadTest extends RESTTestBase { * Tests several valid and invalid read requests on all entity types. */ public function testRead() { + // Test that relation domain is correctly set. + $this->assertEqual(\Drupal::config('rest.settings')->get('relation_domain'), Url::fromRoute('', [], ['absolute' => TRUE])->toString()); // @todo Expand this at least to users. // Define the entity types we want to test. $entity_types = array('entity_test', 'node'); diff --git a/core/modules/rest/src/Tests/RestLinkManagerTest.php b/core/modules/rest/src/Tests/RestLinkManagerTest.php index e69de29..821752e 100644 --- a/core/modules/rest/src/Tests/RestLinkManagerTest.php +++ b/core/modules/rest/src/Tests/RestLinkManagerTest.php @@ -0,0 +1,55 @@ +installSchema('system', ['router']); + \Drupal::service('router.builder')->rebuild(); + \Drupal::moduleHandler()->invoke('rest', 'install'); + } + + /** + * Tests that type hooks work as expected. + */ + public function testRestLinkManagers() { + /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ + $type_manager = \Drupal::service('rest.link_manager.type'); + $base = Url::fromRoute('', [], ['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'); + } + +} diff --git a/core/modules/rest/tests/modules/rest_test/rest_test.info.yml b/core/modules/rest/tests/modules/rest_test/rest_test.info.yml index e69de29..b5f4966 100644 --- a/core/modules/rest/tests/modules/rest_test/rest_test.info.yml +++ b/core/modules/rest/tests/modules/rest_test/rest_test.info.yml @@ -0,0 +1,8 @@ +name: 'REST test' +type: module +description: 'Provides test hooks for REST module.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - rest 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 e69de29..272603d 100644 --- a/core/modules/rest/tests/modules/rest_test/rest_test.module +++ b/core/modules/rest/tests/modules/rest_test/rest_test.module @@ -0,0 +1,24 @@ +