diff --git a/core/modules/hal/lib/Drupal/hal/HalBundle.php b/core/modules/hal/lib/Drupal/hal/HalBundle.php index d782512..bb1e2dc 100644 --- a/core/modules/hal/lib/Drupal/hal/HalBundle.php +++ b/core/modules/hal/lib/Drupal/hal/HalBundle.php @@ -23,10 +23,13 @@ public function build(ContainerBuilder $container) { $priority = 10; $container->register('serializer.normalizer.entity_reference_item.hal', 'Drupal\hal\Normalizer\EntityReferenceItemNormalizer') + ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager'))) ->addTag('normalizer', array('priority' => $priority)); $container->register('serializer.normalizer.field_item.hal', 'Drupal\hal\Normalizer\FieldItemNormalizer') + ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager'))) ->addTag('normalizer', array('priority' => $priority)); $container->register('serializer.normalizer.field.hal', 'Drupal\hal\Normalizer\FieldNormalizer') + ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager'))) ->addTag('normalizer', array('priority' => $priority)); $container->register('serializer.normalizer.entity.hal', 'Drupal\hal\Normalizer\EntityNormalizer') ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager'))) diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php index 6e4da47..e06c2b9 100644 --- a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php +++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php @@ -23,13 +23,6 @@ class EntityNormalizer extends NormalizerBase { protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\EntityInterface'; /** - * The hypermedia link manager. - * - * @var \Drupal\rest\LinkManager\LinkManager - */ - protected $linkManager; - - /** * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize() */ public function normalize($entity, $format = NULL, array $context = array()) { @@ -65,23 +58,6 @@ public function normalize($entity, $format = NULL, array $context = array()) { $normalized = NestedArray::mergeDeep($normalized, $normalized_property); } - // Only add the curies array if there are curies in the link relations. - $link_relations = array_keys($normalized['_links']); - if (isset($normalized['_embedded'])) { - $link_relations = array_merge($link_relations, array_keys($normalized['_embedded'])); - } - foreach ($link_relations as $link_relation) { - if (strpos($link_relation, ':') == TRUE) { - $normalized['_links']['curies'][] = array( - // @todo Make this configurable. - 'href' => url('relations') . '/{rel}', - 'name' => 'site', - 'templated' => TRUE, - ); - break; - } - } - return $normalized; } @@ -98,7 +74,7 @@ protected function getEntityUri($entity) { // @todo Remove this conditional once entities are converted to EntityNG. if ($entity instanceof EntityNG) { $uri_info = $entity->uri(); - return url($uri_info['path']); + return url($uri_info['path'], array('absolute' => TRUE)); } } diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php index 46170b5..b56c2bb 100644 --- a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php @@ -45,14 +45,14 @@ public function normalize($field_item, $format = NULL, array $context = array()) // entity so that the items are properly added to the _links and _embedded // objects. $field_name = $field_item->getParent()->getName(); - // @todo Introduce a RelationLinkManager to get the CURIE. - $field_curie = "site:$field_name"; + $entity = $field_item->getRoot(); + $field_uri = $this->linkManager->getRelationUri($entity->entityType(), $entity->bundle(), $field_name); return array( '_links' => array( - $field_curie => array($link), + $field_uri => array($link), ), '_embedded' => array( - $field_curie => array($embedded), + $field_uri => array($embedded), ), ); } diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php b/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php index 4be97f7..d159f1f 100644 --- a/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php +++ b/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php @@ -22,6 +22,13 @@ protected $formats = array('hal_json'); /** + * The hypermedia link manager. + * + * @var \Drupal\rest\LinkManager\LinkManager + */ + protected $linkManager; + + /** * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization(). */ public function supportsNormalization($data, $format = NULL) { diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php index c87941e..848b19a 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php @@ -57,6 +57,9 @@ public function testNormalize() { $entity->getTranslation('en')->set('field_test_entity_reference', array(0 => $translation_values['field_test_entity_reference'])); $entity->save(); + $type_uri = url('rest/type/entity_test/entity_test', array('absolute' => TRUE)); + $relation_uri = url('rest/relation/entity_test/entity_test/field_test_entity_reference', array('absolute' => TRUE)); + $expected_array = array( '_links' => array( 'curies' => array( @@ -67,43 +70,31 @@ public function testNormalize() { ), ), 'self' => array( - 'href' => $this->getUri($entity), + 'href' => $this->getEntityUri($entity), ), 'type' => array( - 'href' => url('rest/types/entity_test/entity_test', array('absolute' => TRUE)), - ), - 'site:user_id' => array( - array( - 'href' => NULL, - 'lang' => 'de', - ), + 'href' => $type_uri, ), - 'site:field_test_entity_reference' => array( + $relation_uri => array( array( - 'href' => $this->getUri($target_entity_de), + 'href' => $this->getEntityUri($target_entity_de), 'lang' => 'de', ), array( - 'href' => $this->getUri($target_entity_en), + 'href' => $this->getEntityUri($target_entity_en), 'lang' => 'en', ), ), ), '_embedded' => array( - 'site:user_id' => array( - array( - 'href' => NULL, - 'lang' => 'de', - ), - ), - 'site:field_test_entity_reference' => array( + $relation_uri => array( array( '_links' => array( 'self' => array( - 'href' => $this->getUri($target_entity_de), + 'href' => $this->getEntityUri($target_entity_de), ), 'type' => array( - 'href' =>url('rest/types/entity_test/entity_test', array('absolute' => TRUE)), + 'href' => $type_uri, ), ), 'uuid' => array( @@ -116,10 +107,10 @@ public function testNormalize() { array( '_links' => array( 'self' => array( - 'href' => $this->getUri($target_entity_en), + 'href' => $this->getEntityUri($target_entity_en), ), 'type' => array( - 'href' => url('rest/types/entity_test/entity_test', array('absolute' => TRUE)), + 'href' => $type_uri, ), ), 'uuid' => array( @@ -167,13 +158,22 @@ public function testNormalize() { $this->assertEqual($normalized['uuid'], $expected_array['uuid'], 'Non-translatable fields is normalized.'); $this->assertEqual($normalized['name'], $expected_array['name'], 'Translatable field with multiple language values is normalized.'); $this->assertEqual($normalized['field_test_text'], $expected_array['field_test_text'], 'Field with properties is normalized.'); - $this->assertEqual($normalized['_embedded']['site:field_test_entity_reference'], $expected_array['_embedded']['site:field_test_entity_reference'], 'Entity reference field is normalized.'); - $this->assertEqual($normalized['_links']['site:field_test_entity_reference'], $expected_array['_links']['site:field_test_entity_reference'], 'Links are added for entity reference field.'); + $this->assertEqual($normalized['_embedded'][$relation_uri], $expected_array['_embedded'][$relation_uri], 'Entity reference field is normalized.'); + $this->assertEqual($normalized['_links'][$relation_uri], $expected_array['_links'][$relation_uri], 'Links are added for entity reference field.'); } - protected function getUri($entity) { + /** + * Constructs the entity URI. + * + * @param $entity + * The entity. + * + * @return string + * The entity URI. + */ + protected function getEntityUri($entity) { $entity_uri_info = $entity->uri(); - return url($entity_uri_info['path']); + return url($entity_uri_info['path'], array('absolute' => TRUE)); } } diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php index 7f739c6..3f5b947 100644 --- a/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php +++ b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php @@ -16,12 +16,23 @@ class LinkManager implements LinkManagerInterface { 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) { + public function __construct(TypeLinkManagerInterface $type_link_manager, RelationLinkManagerInterface $relation_link_manager) { $this->typeLinkManager = $type_link_manager; + $this->relationLinkManager = $relation_link_manager; } /** @@ -30,4 +41,11 @@ public function __construct(TypeLinkManagerInterface $type_link_manager) { public function getTypeUri($entity_type, $bundle) { return $this->typeLinkManager->getTypeUri($entity_type, $bundle); } + + /** + * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationUri(). + */ + public function getRelationUri($entity_type, $bundle, $field_name) { + return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name); + } } diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php index 30f5d25..60e5629 100644 --- a/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php +++ b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php @@ -19,5 +19,5 @@ * 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 { +interface LinkManagerInterface extends TypeLinkManagerInterface, RelationLinkManagerInterface { } diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php new file mode 100644 index 0000000..5836de1 --- /dev/null +++ b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php @@ -0,0 +1,92 @@ +cache = $cache; + } + + /** + * Get a relation link for the field. + * + * @param string $entity_type + * The bundle's entity type. + * @param string $bundle + * The name of the bundle. + * @param string $field_name + * The name of the field. + * + * @return array + * The URI that identifies this field. + */ + public function getRelationUri($entity_type, $bundle, $field_name) { + // @todo Make the base path configurable. + return url("rest/relation/$entity_type/$bundle/$field_name", array('absolute' => TRUE)); + } + + /** + * 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. + * + * @return array + * An array of typed data ids (entity_type, bundle, and field name) keyed + * by corresponding relation URI. + */ + public function getRelations() { + $cid = 'rest:links:relations'; + $cache = $this->cache->get($cid); + if (!$cache) { + $this->writeCache(); + $cache = $this->cache->get($cid); + } + return $cache->data; + } + + /** + * Writes the cache of relation links. + */ + protected function writeCache() { + $data = array(); + + foreach (field_info_fields() as $field_info) { + foreach ($field_info['bundles'] as $entity_type => $bundles) { + foreach ($bundles as $bundle) { + $relation_uri = $this->getRelationUri($entity_type, $bundle, $field_info['field_name']); + $data[$relation_uri] = array( + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'field_name' => $field_info['field_name'], + ); + } + } + } + // These URIs only change when field info changes, so cache it permanently + // and only clear it when field_info is cleared. + $this->cache->set('rest:links:relations', $data, CacheBackendInterface::CACHE_PERMANENT, array('field_info' => TRUE)); + } +} diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManagerInterface.php b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManagerInterface.php new file mode 100644 index 0000000..bd28432 --- /dev/null +++ b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManagerInterface.php @@ -0,0 +1,26 @@ + TRUE)); + return url("rest/type/$entity_type/$bundle", array('absolute' => TRUE)); } /** diff --git a/core/modules/rest/lib/Drupal/rest/RestBundle.php b/core/modules/rest/lib/Drupal/rest/RestBundle.php index 36e2524..294cbc6 100644 --- a/core/modules/rest/lib/Drupal/rest/RestBundle.php +++ b/core/modules/rest/lib/Drupal/rest/RestBundle.php @@ -34,8 +34,11 @@ public function build(ContainerBuilder $container) { ->addTag('access_check'); $container->register('rest.link_manager', 'Drupal\rest\LinkManager\LinkManager') - ->addArgument(new Reference('rest.link_manager.type')); + ->addArgument(new Reference('rest.link_manager.type')) + ->addArgument(new Reference('rest.link_manager.relation')); $container->register('rest.link_manager.type', 'Drupal\rest\LinkManager\TypeLinkManager') ->addArgument(new Reference('cache.cache')); + $container->register('rest.link_manager.relation', 'Drupal\rest\LinkManager\RelationLinkManager') + ->addArgument(new Reference('cache.cache')); } }