core/modules/file/src/Entity/File.php | 31 ++++++++++++++++++++++ .../hal/src/Normalizer/ContentEntityNormalizer.php | 12 +++++++-- .../EntityResource/EntityResourceTestBase.php | 15 ++++++++++- .../EntityReferenceFieldItemNormalizer.php | 5 ++-- .../EntityReferenceFieldItemNormalizerTest.php | 14 ++++++++-- .../Tests/Listeners/DeprecationListenerTrait.php | 1 - 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php index 33f6d74..6829402 100644 --- a/core/modules/file/src/Entity/File.php +++ b/core/modules/file/src/Entity/File.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Url; use Drupal\file\FileInterface; use Drupal\user\UserInterface; @@ -78,12 +79,42 @@ public function setFileUri($uri) { * @see file_url_transform_relative() */ public function url($rel = 'canonical', $options = []) { + // @todo Remove this work-around before Drupal 9.0.0. This was introduced as + // a temporary work-around in https://www.drupal.org/node/2277705 to provide + // REST support for file entities. https://www.drupal.org/node/2825487 makes + // this obsolete. return file_create_url($this->getFileUri()); } /** * {@inheritdoc} */ + public function toUrl($rel = 'canonical', array $options = []) { + // Lie because we must provide BC. + // @see ::url() + // @todo Remove in https://www.drupal.org/node/2825487. + if ($rel === 'canonical') { + return Url::fromUri($this->url()); + } + return parent::toUrl($rel, $options); + } + + /** + * {@inheritdoc} + */ + public function hasLinkTemplate($rel) { + // Lie because we must provide BC. + // @see ::url() + // @todo Remove in https://www.drupal.org/node/2825487. + if ($rel === 'canonical') { + return TRUE; + } + return parent::hasLinkTemplate($rel); + } + + /** + * {@inheritdoc} + */ public function getMimeType() { return $this->get('filemime')->value; } diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index 4a9f7c4..c41029b 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -179,17 +179,25 @@ public function denormalize($data, $class, $format = NULL, array $context = []) * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. + * @param array $context + * Normalization/serialization context. + * * @return string * The entity URI. */ - protected function getEntityUri(EntityInterface $entity) { + protected function getEntityUri(EntityInterface $entity, array $context = []) { // Some entity types don't provide a canonical link template, at least call // out to ->url(). if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) { return $entity->url('canonical', []); } $url = $entity->urlInfo('canonical', ['absolute' => TRUE]); - return $url->setRouteParameter('_format', 'hal_json')->toString(); + if (!$url->isExternal()) { + $url->setRouteParameter('_format', 'hal_json'); + } + $generated_url = $url->toString(TRUE); + $this->addCacheableDependency($context, $generated_url); + return $generated_url->getGeneratedUrl(); } /** diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index b5d7fbb..9b0d911 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -416,6 +416,8 @@ protected function getExpectedCacheContexts() { public function testGet() { $this->initAuthentication(); $has_canonical_url = $this->entity->hasLinkTemplate('canonical'); + // @todo Remove in https://www.drupal.org/node/2825487. + $has_canonical_url = $has_canonical_url && static::$entityTypeId !== 'file'; // The URL and Guzzle request options that will be used in this test. The // request options will be modified/expanded throughout this test: @@ -1013,6 +1015,8 @@ public function testPatch() { $this->initAuthentication(); $has_canonical_url = $this->entity->hasLinkTemplate('canonical'); + // @todo Remove in https://www.drupal.org/node/2825487. + $has_canonical_url = $has_canonical_url && static::$entityTypeId !== 'file'; // Try with all of the following request bodies. $unparseable_request_body = '!{>}<'; @@ -1241,6 +1245,8 @@ public function testDelete() { $this->initAuthentication(); $has_canonical_url = $this->entity->hasLinkTemplate('canonical'); + // @todo Remove in https://www.drupal.org/node/2825487. + $has_canonical_url = $has_canonical_url && static::$entityTypeId !== 'file'; // The URL and Guzzle request options that will be used in this test. The // request options will be modified/expanded throughout this test: @@ -1356,6 +1362,8 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques */ protected function getEntityResourceUrl() { $has_canonical_url = $this->entity->hasLinkTemplate('canonical'); + // @todo Remove in https://www.drupal.org/node/2825487. + $has_canonical_url = $has_canonical_url && static::$entityTypeId !== 'file'; // Note that the 'canonical' link relation type must be specified explicitly // in the call to ::toUrl(). 'canonical' is the default for // \Drupal\Core\Entity\Entity::toUrl(), but ConfigEntityBase overrides this. @@ -1471,7 +1479,10 @@ protected function makeNormalizationInvalid(array $normalization, $entity_key) { * The response to assert. */ protected function assert406Response(ResponseInterface $response) { - if ($this->entity->hasLinkTemplate('canonical') && ($this->account && static::$auth !== 'cookie')) { + $has_canonical_url = $this->entity->hasLinkTemplate('canonical'); + // @todo Remove in https://www.drupal.org/node/2825487. + $has_canonical_url = $has_canonical_url && static::$entityTypeId !== 'file'; + if ($has_canonical_url && ($this->account && static::$auth !== 'cookie')) { $this->assertSame(403, $response->getStatusCode()); } else { @@ -1490,6 +1501,8 @@ protected function assert406Response(ResponseInterface $response) { */ protected function assertResourceNotAvailable(Url $url, array $request_options) { $has_canonical_url = $this->entity->hasLinkTemplate('canonical'); + // @todo Remove in https://www.drupal.org/node/2825487. + $has_canonical_url = $has_canonical_url && static::$entityTypeId !== 'file'; $response = $this->request('GET', $url, $request_options); if (!$has_canonical_url) { $this->assertSame(404, $response->getStatusCode()); diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php index 2d3c6f0..8e3d7d5 100644 --- a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php +++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php @@ -55,8 +55,9 @@ public function normalize($field_item, $format = NULL, array $context = []) { // Add a 'url' value if there is a reference and a canonical URL. Hard // code 'canonical' here as config entities override the default $rel // parameter value to 'edit-form. - if ($url = $entity->url('canonical')) { - $values['url'] = $url; + if ($entity->hasLinkTemplate('canonical') && $url = $entity->toUrl('canonical')->toString(TRUE)) { + $this->addCacheableDependency($context, $url); + $values['url'] = $url->getGeneratedUrl(); } } diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php index 9320d17..d5a3b45 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php @@ -4,12 +4,14 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\GeneratedUrl; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; +use Drupal\Core\Url; use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; @@ -104,9 +106,17 @@ public function testSupportsDenormalization() { public function testNormalize() { $test_url = '/test/100'; + $generated_url = (new GeneratedUrl())->setGeneratedUrl($test_url); + + $url = $this->prophesize(Url::class); + $url->toString(TRUE) + ->willReturn($generated_url); + $entity = $this->prophesize(EntityInterface::class); - $entity->url('canonical') - ->willReturn($test_url) + $entity->hasLinkTemplate('canonical') + ->willReturn(TRUE); + $entity->toUrl('canonical') + ->willReturn($url) ->shouldBeCalled(); $entity->uuid() ->willReturn('080e3add-f9d5-41ac-9821-eea55b7b42fb') diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index 99cb949..144e600 100644 --- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -157,7 +157,6 @@ public static function getSkippedDeprecations() { 'CommentVariablePerCommentType is deprecated in Drupal 8.4.x and will be removed before Drupal 9.0.x. Use \Drupal\node\Plugin\migrate\source\d6\NodeType instead.', 'The Drupal\config_translation\Plugin\migrate\source\d6\I18nProfileField is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use Drupal\config_translation\Plugin\migrate\source\d6\ProfileFieldTranslation', 'The Drupal\migrate_drupal\Plugin\migrate\source\d6\i18nVariable is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use Drupal\migrate_drupal\Plugin\migrate\source\d6\VariableTranslation', - 'Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.5.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling. See https://www.drupal.org/node/2918937', 'Automatically creating the first item for computed fields is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\TypedData\ComputedItemListTrait instead.', '"\Drupal\Core\Entity\ContentEntityStorageBase::doLoadRevisionFieldItems()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. "\Drupal\Core\Entity\ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems()" should be implemented instead. See https://www.drupal.org/node/2924915.', 'Passing a single revision ID to "\Drupal\Core\Entity\Sql\SqlContentEntityStorage::buildQuery()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. An array of revision IDs should be given instead. See https://www.drupal.org/node/2924915.',