diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php index eafb6d88bf..4e81796dc3 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php @@ -27,7 +27,6 @@ * ) */ class EntityAdapter extends TypedData implements \IteratorAggregate, ComplexDataInterface { - /** * The wrapped entity object. * diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php index e53e84b6c8..d1a0539dd5 100644 --- a/core/lib/Drupal/Core/Field/FieldConfigBase.php +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php @@ -6,12 +6,14 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Field\TypedData\FieldItemDataDefinition; +use Drupal\Core\TypedData\ExposableDataDefinitionTrait; /** * Base class for configurable field definitions. */ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface { + use ExposableDataDefinitionTrait; /** * The field ID. * diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php index 52a4394cd7..6e0f936a8f 100644 --- a/core/lib/Drupal/Core/TypedData/DataDefinition.php +++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php @@ -9,6 +9,8 @@ class DataDefinition implements DataDefinitionInterface, \ArrayAccess { use TypedDataTrait; + use ExposableDataDefinitionTrait; + /** * The array holding values for all definition keys. * diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php index 64d779c317..7ea68e47e6 100644 --- a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php +++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php @@ -218,4 +218,23 @@ public function getConstraint($constraint_name); */ public function addConstraint($constraint_name, $options = NULL); + /** + * Sets the whether the data value should be exposed. + * + * @param bool $exposed + * Whether the data value is exposed. + * + * @return static + * The object itself for chaining. + */ + public function setExposed($exposed = TRUE); + + /** + * Determines whether the data value is exposed. + * + * @return bool + * Whether the data value is exposed. + */ + public function isExposed(); + } diff --git a/core/lib/Drupal/Core/TypedData/ExposableDataDefinitionTrait.php b/core/lib/Drupal/Core/TypedData/ExposableDataDefinitionTrait.php new file mode 100644 index 0000000000..ba47fe35c7 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/ExposableDataDefinitionTrait.php @@ -0,0 +1,34 @@ +isComputed() && !isset($this->definition['exposed'])) { + return TRUE; + } + return !empty($this->definition['exposed']); + } + + /** + * {@inheritdoc} + * + * Implements setExposed() for \Drupal\Core\TypedData\DataDefinitionInterface. + */ + public function setExposed($exposed = TRUE) { + $this->definition['exposed'] = $exposed; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/TypedData/TypedDataHelper.php b/core/lib/Drupal/Core/TypedData/TypedDataHelper.php new file mode 100644 index 0000000000..1df10eb252 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/TypedDataHelper.php @@ -0,0 +1,27 @@ +getProperties(TRUE), function (TypedDataInterface $property) { + return $property->getDataDefinition()->isExposed(); + }); + } + +} diff --git a/core/modules/hal/src/Normalizer/FieldItemNormalizer.php b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php index 0c2ee9ed4d..5af3c94ced 100644 --- a/core/modules/hal/src/Normalizer/FieldItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php @@ -3,6 +3,7 @@ namespace Drupal\hal\Normalizer; use Drupal\Core\Field\FieldItemInterface; +use Drupal\serialization\Normalizer\ComplexDataPropertiesNormalizerTrait; use Symfony\Component\Serializer\Exception\InvalidArgumentException; /** @@ -10,6 +11,8 @@ */ class FieldItemNormalizer extends NormalizerBase { + use ComplexDataPropertiesNormalizerTrait; + /** * The interface or class that this Normalizer supports. * @@ -87,13 +90,7 @@ protected function constructValue($data, $context) { * An array of field item values, keyed by property name. */ protected function normalizedFieldValues(FieldItemInterface $field_item, $format, array $context) { - $denormalized = []; - // We normalize each individual property, so each can do their own casting, - // if needed. - /** @var \Drupal\Core\TypedData\TypedDataInterface $property */ - foreach ($field_item as $property_name => $property) { - $denormalized[$property_name] = $this->serializer->normalize($property, $format, $context); - } + $denormalized = $this->normalizeProperties($field_item, $format, $context); if (isset($context['langcode'])) { $denormalized['lang'] = $context['langcode']; diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php index 41063812dd..c77908bac0 100644 --- a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php +++ b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php @@ -186,4 +186,12 @@ protected function ensureLoaded() { } } + /** + * {@inheritdoc} + */ + public function get($property_name) { + $this->ensureLoaded(); + return parent::get($property_name); + } + } diff --git a/core/modules/path/tests/src/Kernel/PathItemTest.php b/core/modules/path/tests/src/Kernel/PathItemTest.php index 9a85a27958..1c7eb28ae0 100644 --- a/core/modules/path/tests/src/Kernel/PathItemTest.php +++ b/core/modules/path/tests/src/Kernel/PathItemTest.php @@ -66,10 +66,18 @@ public function testPathItem() { $node_storage->resetCache(); + // Ensure that isEmpty(), ->alias, and [0]->get('alias')->getValue() can all + // be called individually without the other methods being called. + // @see \Drupal\path\Plugin\Field\FieldType\PathItem::ensureLoaded() /** @var \Drupal\node\NodeInterface $loaded_node */ $loaded_node = $node_storage->load($node->id()); $this->assertFalse($loaded_node->get('path')->isEmpty()); + $node_storage->resetCache(); + $loaded_node = $node_storage->load($node->id()); $this->assertEquals('/foo', $loaded_node->get('path')->alias); + $node_storage->resetCache(); + $loaded_node = $node_storage->load($node->id()); + $this->assertEquals('/foo', $loaded_node->get('path')[0]->get('alias')->getValue()); $node_storage->resetCache(); $loaded_node = $node_storage->load($node->id()); diff --git a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php index df77281f37..7dc6585283 100644 --- a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php +++ b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php @@ -2,6 +2,7 @@ namespace Drupal\rest\EventSubscriber; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheableResponse; use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Render\RenderContext; @@ -126,11 +127,18 @@ public function getResponseFormat(RouteMatchInterface $route_match, Request $req /** * Renders a resource response body. * - * Serialization can invoke rendering (e.g., generating URLs), but the - * serialization API does not provide a mechanism to collect the - * bubbleable metadata associated with that (e.g., language and other - * contexts), so instead, allow those to "leak" and collect them here in - * a render context. + * During serialization, encoders and normalizers are able to explicitly + * bubble cacheability metadata via the 'cacheability' key-value pair in the + * received context. This bubbled cacheability metadata will be applied to the + * the response. + * + * In prior versions of Drupal 8, we allowed implicit bubbling of cacheability + * metadata because there was no explicit cacheability metadata bubbling API. + * To maintain backwards compatibility, we continue to support this, but + * support for this will be dropped in Drupal 9.0.0. This is especially useful + * when interacting with APIs that implicitly invoke rendering (for example: + * generating URLs): this allows those to "leak", and we collect their bubbled + * cacheability metadata automatically in a render context. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. @@ -150,14 +158,25 @@ protected function renderResponseBody(Request $request, ResourceResponseInterfac // If there is data to send, serialize and set it as the response body. if ($data !== NULL) { + $serialization_context = [ + 'request' => $request, + 'cacheability' => new CacheableMetadata(), + ]; + + // @deprecated In Drupal 8.4.0, will be removed before Drupal 9.0.0. Use + // explicit cacheability metadata bubbling instead. (The wrapping call to + // executeInRenderContext() will be removed before Drupal 9.0.0.) $context = new RenderContext(); $output = $this->renderer - ->executeInRenderContext($context, function () use ($serializer, $data, $format) { - return $serializer->serialize($data, $format); + ->executeInRenderContext($context, function() use ($serializer, $data, $format, $serialization_context) { + return $serializer->serialize($data, $format, $serialization_context); }); - - if ($response instanceof CacheableResponseInterface && !$context->isEmpty()) { - $response->addCacheableDependency($context->pop()); + if ($response instanceof CacheableResponseInterface) { + if (!$context->isEmpty()) { + @trigger_error('Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.4.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling.', E_USER_DEPRECATED); + $response->addCacheableDependency($context->pop()); + } + $response->addCacheableDependency($serialization_context['cacheability']); } $response->setContent($output); diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestExposedPropertyNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestExposedPropertyNormalizerTest.php new file mode 100644 index 0000000000..5a7df055e7 --- /dev/null +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestExposedPropertyNormalizerTest.php @@ -0,0 +1,99 @@ + 'value to expose', + 'exposed_value' => 'Exposed! value to expose', + ], + ]; + return $expected; + } + + /** + * {@inheritdoc} + */ + protected function createEntity() { + if (!FieldStorageConfig::loadByName('entity_test', 'field_test_exposed')) { + // Auto-create a field for testing. + FieldStorageConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'field_test_exposed', + 'type' => 'exposed_string_test', + 'cardinality' => 1, + 'translatable' => FALSE, + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'field_test_exposed', + 'bundle' => 'entity_test', + 'label' => 'Test exposed-field', + ])->save(); + } + + $entity = parent::createEntity(); + $entity->field_test_exposed = [ + 'value' => 'value to expose', + ]; + $entity->save(); + return $entity; + } + + /** + * {@inheritdoc} + */ + protected function getNormalizedPostEntity() { + $post_entity = parent::getNormalizedPostEntity(); + $post_entity['field_test_exposed'] = [ + [ + 'value' => 'value to expose', + ], + ]; + return $post_entity; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedCacheContexts() { + return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['request_format']); + } + + /** + * {@inheritdoc} + */ + protected function getExpectedCacheTags() { + return Cache::mergeTags(parent::getExpectedCacheTags(), ['you_are_it', 'no_tag_backs']); + } + +} diff --git a/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php b/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php index 3d2031218f..b3b761c706 100644 --- a/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php +++ b/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php @@ -2,6 +2,8 @@ namespace Drupal\serialization\Normalizer; +use Drupal\Core\TypedData\ComplexDataInterface; + /** * Converts the Drupal entity object structures to a normalized array. * @@ -14,6 +16,7 @@ */ class ComplexDataNormalizer extends NormalizerBase { + use ComplexDataPropertiesNormalizerTrait; /** * The interface or class that this Normalizer supports. * @@ -25,10 +28,19 @@ class ComplexDataNormalizer extends NormalizerBase { * {@inheritdoc} */ public function normalize($object, $format = NULL, array $context = []) { - $attributes = []; - /** @var \Drupal\Core\TypedData\TypedDataInterface $field */ - foreach ($object as $name => $field) { - $attributes[$name] = $this->serializer->normalize($field, $format, $context); + // $object will not always match $supportedInterfaceOrClass. + // @see \Drupal\serialization\Normalizer\EntityNormalizer + // Other normalizer that extend this class may only provide $object that + // implements \Traversable. + if ($object instanceof ComplexDataInterface) { + $attributes = $this->normalizeProperties($object, $format, $context); + } + else { + $attributes = []; + /** @var \Drupal\Core\TypedData\TypedDataInterface $field */ + foreach ($object as $name => $property) { + $attributes[$name] = $this->serializer->normalize($property, $format, $context); + } } return $attributes; } diff --git a/core/modules/serialization/src/Normalizer/ComplexDataPropertiesNormalizerTrait.php b/core/modules/serialization/src/Normalizer/ComplexDataPropertiesNormalizerTrait.php new file mode 100644 index 0000000000..8697d1ff29 --- /dev/null +++ b/core/modules/serialization/src/Normalizer/ComplexDataPropertiesNormalizerTrait.php @@ -0,0 +1,42 @@ + $property) { + $attribute = $this->serializer->normalize($property, $format, $context); + if ($attribute instanceof CacheableDependencyInterface && isset($context['cacheability'])) { + $context['cacheability']->addCacheableDependency($attribute); + } + if (is_object($attribute) && method_exists($attribute, '__toString')) { + $attribute = (string) $attribute; + } + $attributes[$name] = $attribute; + } + return $attributes; + } + +} diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php index dc14d6f428..5ca4726660 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php @@ -7,8 +7,11 @@ namespace Drupal\Tests\serialization\Unit\Normalizer; +use Drupal\Core\Cache\CacheableDependencyInterface; +use Drupal\Core\Cache\RefinableCacheableDependencyInterface; use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\TraversableTypedDataInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; +use Drupal\Core\TypedData\TypedDataInterface; use Drupal\serialization\Normalizer\ComplexDataNormalizer; use Drupal\Tests\UnitTestCase; use Symfony\Component\Serializer\Serializer; @@ -19,6 +22,8 @@ */ class ComplexDataNormalizerTest extends UnitTestCase { + use TypedDataTestTrait; + /** * Test format string. * @@ -44,103 +49,102 @@ protected function setUp() { * @covers ::supportsNormalization */ public function testSupportsNormalization() { - $this->assertTrue($this->normalizer->supportsNormalization(new TestComplexData())); + $complexData = $this->prophesize(ComplexDataInterface::class)->reveal(); + $this->assertTrue($this->normalizer->supportsNormalization($complexData)); // Also test that an object not implementing ComplexDataInterface fails. $this->assertFalse($this->normalizer->supportsNormalization(new \stdClass())); } /** + * Test normalizing complex data. + * * @covers ::normalize */ - public function testNormalize() { - $context = ['test' => 'test']; - + public function testNormalizeComplexData() { $serializer_prophecy = $this->prophesize(Serializer::class); - $serializer_prophecy->normalize('A', static::TEST_FORMAT, $context) - ->shouldBeCalled(); - $serializer_prophecy->normalize('B', static::TEST_FORMAT, $context) + $cacheable = $this->prophesize(TestCacheableDependencyInterface::class); + $cacheable->__toString() + ->willReturn('prop-as-string') ->shouldBeCalled(); + $cacheable = $cacheable->reveal(); - $this->normalizer->setSerializer($serializer_prophecy->reveal()); - - $complex_data = new TestComplexData(['a' => 'A', 'b' => 'B']); - $this->normalizer->normalize($complex_data, static::TEST_FORMAT, $context); - - } - -} - -/** - * Test class implementing ComplexDataInterface and IteratorAggregate. - */ -class TestComplexData implements \IteratorAggregate, ComplexDataInterface { - - private $values; - - public function __construct(array $values = []) { - $this->values = $values; - } - - public function getIterator() { - return new \ArrayIterator($this->values); - } - - public function applyDefaultValue($notify = TRUE) { - } - - public static function createInstance($definition, $name = NULL, TraversableTypedDataInterface $parent = NULL) { - } + $exposed_cacheable_property = $this->getTypedDataProperty(); + $exposed_noncacheable_property = $this->getTypedDataProperty(); - public function get($property_name) { - } - public function getConstraints() { - } - - public function getDataDefinition() { - } - - public function getName() { - } - - public function getParent() { - } + $cacheableMetaData = $this->prophesize(RefinableCacheableDependencyInterface::class); + $cacheableMetaData->addCacheableDependency($cacheable) + ->shouldBeCalled(); + $serialization_context = [ + 'cacheability' => $cacheableMetaData->reveal(), + ]; - public function getProperties($include_computed = FALSE) { - } + $serializer_prophecy->normalize($exposed_noncacheable_property, static::TEST_FORMAT, $serialization_context) + ->willReturn('A-normalized') + ->shouldBeCalled(); + $serializer_prophecy->normalize($exposed_cacheable_property, static::TEST_FORMAT, $serialization_context) + ->willReturn($cacheable) + ->shouldBeCalled(); - public function getPropertyPath() { - } + $this->normalizer->setSerializer($serializer_prophecy->reveal()); - public function getRoot() { - } + $complex_data = $this->prophesize(ComplexDataInterface::class); + $complex_data->getProperties(TRUE) + ->willReturn([ + 'prop:a' => $exposed_noncacheable_property, + 'prop:cacheable' => $exposed_cacheable_property, + 'prop:nonexposed' => $this->getTypedDataProperty(FALSE), + ]) + ->shouldBeCalled(); - public function getString() { + $normalized = $this->normalizer->normalize($complex_data->reveal(), static::TEST_FORMAT, $serialization_context); + $this->assertEquals(['prop:a' => 'A-normalized', 'prop:cacheable' => 'prop-as-string'], $normalized); } - public function getValue() { - } + /** + * Test normalize() where $object does not implement ComplexDataInterface. + * + * Normalizers extending ComplexDataNormalizer may have a different supported + * class. + * + * @covers ::normalize + */ + public function testNormalizeNonComplex() { + $normalizer = new TestExtendedNormalizer(); + $serialization_context = ['test' => 'test']; - public function isEmpty() { - } + $serializer_prophecy = $this->prophesize(Serializer::class); + $serializer_prophecy->normalize('A', static::TEST_FORMAT, $serialization_context) + ->willReturn('A-normalized') + ->shouldBeCalled(); + $serializer_prophecy->normalize('B', static::TEST_FORMAT, $serialization_context) + ->willReturn('B-normalized') + ->shouldBeCalled(); - public function onChange($name) { - } + $normalizer->setSerializer($serializer_prophecy->reveal()); - public function set($property_name, $value, $notify = TRUE) { - } + $stdClass = new \stdClass(); + $stdClass->a = 'A'; + $stdClass->b = 'B'; - public function setContext($name = NULL, TraversableTypedDataInterface $parent = NULL) { - } + $normalized = $normalizer->normalize($stdClass, static::TEST_FORMAT, $serialization_context); + $this->assertEquals(['a' => 'A-normalized', 'b' => 'B-normalized'], $normalized); - public function setValue($value, $notify = TRUE) { } - public function toArray() { - } +} - public function validate() { - } +/** + * Test interface used for mocking. + */ +interface TestCacheableDependencyInterface extends CacheableDependencyInterface { + public function __toString(); +} +/** + * Test normalizer with a different supported class. + */ +class TestExtendedNormalizer extends ComplexDataNormalizer { + protected $supportedInterfaceOrClass = \stdClass::class; } diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php index e0561a1003..b9a4f361ba 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php @@ -4,6 +4,9 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; +use Drupal\Core\TypedData\Plugin\DataType\StringData; +use Drupal\Core\TypedData\Type\StringInterface; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\FieldableEntityInterface; @@ -23,6 +26,8 @@ */ class EntityReferenceFieldItemNormalizerTest extends UnitTestCase { + use TypedDataTestTrait; + /** * The mock serializer. * @@ -122,6 +127,10 @@ public function testNormalize() { ->willReturn($entity_reference) ->shouldBeCalled(); + $this->fieldItem->getProperties(TRUE) + ->willReturn(['target_id' => $this->getTypedDataProperty()]) + ->shouldBeCalled(); + $normalized = $this->normalizer->normalize($this->fieldItem->reveal()); $expected = [ @@ -146,6 +155,10 @@ public function testNormalizeWithNoEntity() { ->willReturn($entity_reference->reveal()) ->shouldBeCalled(); + $this->fieldItem->getProperties(TRUE) + ->willReturn(['target_id' => $this->getTypedDataProperty()]) + ->shouldBeCalled(); + $normalized = $this->normalizer->normalize($this->fieldItem->reveal()); $expected = [ diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php index fd1fc9ce9f..47fead15d2 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php @@ -18,6 +18,8 @@ */ class TimestampItemNormalizerTest extends UnitTestCase { + use TypedDataTestTrait; + /** * @var \Drupal\serialization\Normalizer\TimestampItemNormalizer */ @@ -77,8 +79,18 @@ public function testNormalize() { $timestamp_item->getIterator() ->willReturn(new \ArrayIterator(['value' => 1478422920])); - $serializer = new Serializer(); - $this->normalizer->setSerializer($serializer); + $exposed_property = $this->getTypedDataProperty(); + $timestamp_item->getProperties(TRUE) + ->willReturn(['value' => $exposed_property]) + ->shouldBeCalled(); + + $serializer_prophecy = $this->prophesize(Serializer::class); + + $serializer_prophecy->normalize($exposed_property, NULL, []) + ->willReturn(1478422920) + ->shouldBeCalled(); + + $this->normalizer->setSerializer($serializer_prophecy->reveal()); $normalized = $this->normalizer->normalize($timestamp_item->reveal()); $this->assertSame($expected, $normalized); diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/TypedDataTestTrait.php b/core/modules/serialization/tests/src/Unit/Normalizer/TypedDataTestTrait.php new file mode 100644 index 0000000000..257bf4ca38 --- /dev/null +++ b/core/modules/serialization/tests/src/Unit/Normalizer/TypedDataTestTrait.php @@ -0,0 +1,36 @@ +prophesize(DataDefinitionInterface::class); + $exposed_definition->isExposed() + ->willReturn($exposed) + ->shouldBeCalled(); + $exposed_definition = $exposed_definition->reveal(); + + $exposed_property = $this->prophesize(TypedDataInterface::class); + $exposed_property->getDataDefinition() + ->willReturn($exposed_definition) + ->shouldBeCalled(); + return $exposed_property->reveal(); + } + +} diff --git a/core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml b/core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml new file mode 100644 index 0000000000..bda7a93205 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml @@ -0,0 +1,15 @@ +# Schema for the configuration of the exposed string field type. + +field.storage_settings.exposed_string_test: + type: mapping + label: 'String settings' + mapping: + max_length: + type: integer + label: 'Maximum length' + case_sensitive: + type: boolean + label: 'Case sensitive' + is_ascii: + type: boolean + label: 'Contains US ASCII characters only' diff --git a/core/modules/system/tests/modules/entity_test/src/ComputedString.php b/core/modules/system/tests/modules/entity_test/src/ComputedString.php new file mode 100644 index 0000000000..0c78bdc02d --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/ComputedString.php @@ -0,0 +1,41 @@ +notComputedValue = $not_computed_value; + $this->cacheContexts = ['request_format']; + $this->cacheTags = ['you_are_it', 'no_tag_backs']; + } + + /** + * {@inheritdoc} + */ + public function __toString() { + // Computation is simple concatenation for test. + return "Exposed! " . $this->notComputedValue; + } + +} diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/DataType/ExposedStringData.php b/core/modules/system/tests/modules/entity_test/src/Plugin/DataType/ExposedStringData.php new file mode 100644 index 0000000000..650121754f --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Plugin/DataType/ExposedStringData.php @@ -0,0 +1,37 @@ +getParent(); + $computed = new ComputedString($item->get('value')->getString()); + return $computed; + } + + /** + * {@inheritdoc} + */ + public function getCastedValue() { + return $this->getValue(); + } + +} diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ExposedPropertyTestFieldItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ExposedPropertyTestFieldItem.php new file mode 100644 index 0000000000..fb930826b2 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ExposedPropertyTestFieldItem.php @@ -0,0 +1,39 @@ +setLabel(new TranslatableMarkup('Text value exposed')) + ->setComputed(TRUE) + ->setClass(ExposedStringData::class) + ->setExposed(TRUE); + return $properties; + } + +}