Update core 8.3.0

This commit is contained in:
Rob Davies 2017-04-13 15:53:35 +01:00
parent da7a7918f8
commit cd7a898e66
6144 changed files with 132297 additions and 87747 deletions

View file

@ -0,0 +1,4 @@
# Before Drupal 8.3, typed data primitive values were normalized as strings, as
# this was usually returned from database storage. A primitive data normalizer
# has been introduced to get the casted value instead.
bc_primitives_as_strings: false

View file

@ -0,0 +1,7 @@
serialization.settings:
type: config_object
label: 'Serialization settings'
mapping:
bc_primitives_as_strings:
type: boolean
label: 'Whether to retain pre Drupal 8.3 behavior of serializing all primitive items as strings.'

View file

@ -0,0 +1,48 @@
<?php
/**
* @file
* Update functions for the Serialization module.
*/
/**
* Implements hook_requirements().
*/
function serialization_requirements($phase) {
$requirements = [];
if ($phase == 'runtime') {
$requirements['serialization_as_strings'] = [
'title' => t('Serialized data types'),
'severity' => REQUIREMENT_INFO,
];
if (\Drupal::config('serialization.settings')->get('bc_primitives_as_strings')) {
$requirements['serialization_as_strings']['value'] = t('Enabled');
$requirements['serialization_as_strings']['description'] = t('The Serialization API is configured to output only string values for REST and other applications (instead of integers or Booleans when appropriate). <a href="https://www.drupal.org/node/2837696">Disabling this backwards compatibility mode</a> is recommended unless your sites or applications require string output.');
}
else {
$requirements['serialization_as_strings']['value'] = t('Not enabled');
$requirements['serialization_as_strings']['description'] = t('The Serialization API is configured with the recommended default and outputs typed values (integers, Booleans, or strings as appropriate) for REST and other applications. If your site or applications require string output, you can <a href="https://www.drupal.org/node/2837696">enable backwards compatibility mode</a>.');
}
}
return $requirements;
}
/**
* @see hal_update_8301()
*/
function serialization_update_8301() {}
/**
* Add serialization.settings::bc_primitives_as_strings configuration.
*/
function serialization_update_8302() {
$config_factory = \Drupal::configFactory();
$config_factory->getEditable('serialization.settings')
->set('bc_primitives_as_strings', FALSE)
->save(TRUE);
return t('The REST API will no longer output all values as strings. Integers/booleans will be used where appropriate. If your site depends on these value being strings, <a href="https://www.drupal.org/node/2837696">read the change record to learn how to enable the BC mode.</a>');
}

View file

@ -18,8 +18,8 @@ function serialization_help($route_name, RouteMatchInterface $route_match) {
$output .= '<p>' . t('The Serialization module provides a service for serializing and deserializing data to and from formats such as JSON and XML.') . '</p>';
$output .= '<p>' . t('Serialization is the process of converting data structures like arrays and objects into a string. This allows the data to be represented in a way that is easy to exchange and store (for example, for transmission over the Internet or for storage in a local file system). These representations can then be deserialized to get back to the original data structures.') . '</p>';
$output .= '<p>' . t('The serializer splits this process into two parts. Normalization converts an object to a normalized array structure. Encoding takes that array and converts it to a string.') . '</p>';
$output .= '<p>' . t('This module does not have a user interface. It is used by other modules which need to serialize data, such as <a href=":rest">REST</a>.', array(':rest' => (\Drupal::moduleHandler()->moduleExists('rest')) ? \Drupal::url('help.page', array('name' => 'rest')) : '#')) . '</p>';
$output .= '<p>' . t('For more information, see the <a href=":doc_url">online documentation for the Serialization module</a>.', array(':doc_url' => 'https://www.drupal.org/documentation/modules/serialization')) . '</p>';
$output .= '<p>' . t('This module does not have a user interface. It is used by other modules which need to serialize data, such as <a href=":rest">REST</a>.', [':rest' => (\Drupal::moduleHandler()->moduleExists('rest')) ? \Drupal::url('help.page', ['name' => 'rest']) : '#']) . '</p>';
$output .= '<p>' . t('For more information, see the <a href=":doc_url">online documentation for the Serialization module</a>.', [':doc_url' => 'https://www.drupal.org/documentation/modules/serialization']) . '</p>';
return $output;
}
}

View file

@ -17,6 +17,10 @@ services:
tags:
- { name: normalizer }
arguments: ['@entity.manager']
serializer.normalizer.primitive_data:
class: Drupal\serialization\Normalizer\PrimitiveDataNormalizer
tags:
- { name: normalizer, priority: 5, bc: bc_primitives_as_strings, bc_config_name: 'serialization.settings' }
serializer.normalizer.complex_data:
class: Drupal\serialization\Normalizer\ComplexDataNormalizer
tags:
@ -25,13 +29,28 @@ services:
class: Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer
tags:
# Set the priority lower than the hal entity reference field item
# normalizer, so that we do not replace that for hal_json.
# normalizer, so that we do not replace that for hal_json but higher than
# this modules generic field item normalizer.
# @todo Find a better way for this in https://www.drupal.org/node/2575761.
- { name: normalizer, priority: 5 }
- { name: normalizer, priority: 8 }
serialization.normalizer.field_item:
class: Drupal\serialization\Normalizer\FieldItemNormalizer
tags:
# Priority must be lower than serializer.normalizer.field_item.hal and any
# field type specific normalizer such as
# serializer.normalizer.entity_reference_field_item.
- { name: normalizer, priority: 6 }
serialization.normalizer.field:
class: Drupal\serialization\Normalizer\FieldNormalizer
tags:
# Priority must be lower than serializer.normalizer.field.hal.
- { name: normalizer, priority: 6 }
serializer.normalizer.list:
class: Drupal\serialization\Normalizer\ListNormalizer
tags:
- { name: normalizer }
# Priority must be higher than serialization.normalizer.field but less
# than hal field normalizer.
- { name: normalizer, priority: 9 }
serializer.normalizer.password_field_item:
class: Drupal\serialization\Normalizer\NullNormalizer
arguments: ['Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem']
@ -73,4 +92,9 @@ services:
class: Drupal\serialization\EventSubscriber\UserRouteAlterSubscriber
tags:
- { name: event_subscriber }
arguments: ['@serializer', '%serializer.formats%']
arguments: ['%serializer.formats%']
serialization.bc_config_subscriber:
class: Drupal\serialization\EventSubscriber\BcConfigSubscriber
tags:
- { name: event_subscriber }
arguments: ['@kernel']

View file

@ -4,6 +4,8 @@ namespace Drupal\serialization\Encoder;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\JsonDecode;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Encoder\JsonEncoder as BaseJsonEncoder;
/**
@ -16,7 +18,19 @@ class JsonEncoder extends BaseJsonEncoder implements EncoderInterface, DecoderIn
*
* @var array
*/
protected static $format = array('json', 'ajax');
protected static $format = ['json', 'ajax'];
/**
* {@inheritdoc}
*/
public function __construct(JsonEncode $encodingImpl = NULL, JsonDecode $decodingImpl = NULL) {
// Encode <, >, ', &, and " for RFC4627-compliant JSON, which may also be
// embedded into HTML.
// @see \Symfony\Component\HttpFoundation\JsonResponse
$json_encoding_options = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT;
$this->encodingImpl = $encodingImpl ?: new JsonEncode($json_encoding_options);
$this->decodingImpl = $decodingImpl ?: new JsonDecode(TRUE);
}
/**
* {@inheritdoc}

View file

@ -19,7 +19,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface {
*
* @var array
*/
static protected $format = array('xml');
static protected $format = ['xml'];
/**
* An instance of the Symfony XmlEncoder to perform the actual encoding.
@ -54,7 +54,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface {
/**
* {@inheritdoc}
*/
public function encode($data, $format, array $context = array()){
public function encode($data, $format, array $context = []){
return $this->getBaseEncoder()->encode($data, $format, $context);
}
@ -68,7 +68,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface {
/**
* {@inheritdoc}
*/
public function decode($data, $format, array $context = array()){
public function decode($data, $format, array $context = []){
return $this->getBaseEncoder()->decode($data, $format, $context);
}

View file

@ -14,7 +14,7 @@ class ChainEntityResolver implements ChainEntityResolverInterface {
*
* @var \Drupal\serialization\EntityResolver\EntityResolverInterface[]
*/
protected $resolvers = array();
protected $resolvers = [];
/**
* Constructs a ChainEntityResolver object.
@ -22,7 +22,7 @@ class ChainEntityResolver implements ChainEntityResolverInterface {
* @param \Drupal\serialization\EntityResolver\EntityResolverInterface[] $resolvers
* The array of concrete resolvers.
*/
public function __construct(array $resolvers = array()) {
public function __construct(array $resolvers = []) {
$this->resolvers = $resolvers;
}

View file

@ -0,0 +1,55 @@
<?php
namespace Drupal\serialization\EventSubscriber;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Drupal\Core\DrupalKernelInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Config event subscriber to rebuild the container when BC config is saved.
*/
class BcConfigSubscriber implements EventSubscriberInterface {
/**
* The Drupal Kernel.
*
* @var \Drupal\Core\DrupalKernelInterface
*/
protected $kernel;
/**
* BcConfigSubscriber constructor.
*
* @param \Drupal\Core\DrupalKernelInterface $kernel
* The Drupal Kernel.
*/
public function __construct(DrupalKernelInterface $kernel) {
$this->kernel = $kernel;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[ConfigEvents::SAVE][] = 'onConfigSave';
return $events;
}
/**
* Invalidates the service container if serialization BC config gets updated.
*
* @param \Drupal\Core\Config\ConfigCrudEvent $event
*/
public function onConfigSave(ConfigCrudEvent $event) {
$saved_config = $event->getConfig();
if ($saved_config->getName() === 'serialization.settings') {
if ($event->isChanged('bc_primitives_as_strings')) {
$this->kernel->invalidateContainer();
}
}
}
}

View file

@ -56,88 +56,25 @@ class DefaultExceptionSubscriber extends HttpExceptionSubscriberBase {
}
/**
* Handles a 400 error for HTTP.
* Handles all 4xx errors for all serialization failures.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on400(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_BAD_REQUEST);
}
public function on4xx(GetResponseForExceptionEvent $event) {
/** @var \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $exception */
$exception = $event->getException();
$request = $event->getRequest();
/**
* Handles a 403 error for HTTP.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on403(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_FORBIDDEN);
}
/**
* Handles a 404 error for HTTP.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on404(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_NOT_FOUND);
}
/**
* Handles a 405 error for HTTP.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on405(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_METHOD_NOT_ALLOWED);
}
/**
* Handles a 406 error for HTTP.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on406(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_NOT_ACCEPTABLE);
}
/**
* Handles a 422 error for HTTP.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on422(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_UNPROCESSABLE_ENTITY);
}
/**
* Handles a 429 error for HTTP.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function on429(GetResponseForExceptionEvent $event) {
$this->setEventResponse($event, Response::HTTP_TOO_MANY_REQUESTS);
}
/**
* Sets the Response for the exception event.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The current exception event.
* @param int $status
* The HTTP status code to set for the response.
*/
protected function setEventResponse(GetResponseForExceptionEvent $event, $status) {
$format = $event->getRequest()->getRequestFormat();
$format = $request->getRequestFormat();
$content = ['message' => $event->getException()->getMessage()];
$encoded_content = $this->serializer->serialize($content, $format);
$response = new Response($encoded_content, $status);
$headers = $exception->getHeaders();
// Add the MIME type from the request to send back in the header.
$headers['Content-Type'] = $request->getMimeType($format);
$response = new Response($encoded_content, $exception->getStatusCode(), $headers);
$event->setResponse($response);
}

View file

@ -5,20 +5,12 @@ namespace Drupal\serialization\EventSubscriber;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Alters user authentication routes to support additional serialization formats.
*/
class UserRouteAlterSubscriber implements EventSubscriberInterface {
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* The available serialization formats.
*
@ -29,13 +21,10 @@ class UserRouteAlterSubscriber implements EventSubscriberInterface {
/**
* UserRouteAlterSubscriber constructor.
*
* @param \Symfony\Component\Serializer\SerializerInterface $serializer
* The serializer service.
* @param array $serializer_formats
* The available serializer formats.
*/
public function __construct(SerializerInterface $serializer, array $serializer_formats) {
$this->serializer = $serializer;
public function __construct(array $serializer_formats) {
$this->serializerFormats = $serializer_formats;
}

View file

@ -24,8 +24,9 @@ class ComplexDataNormalizer extends NormalizerBase {
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
$attributes = array();
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);
}

View file

@ -12,12 +12,12 @@ class ConfigEntityNormalizer extends EntityNormalizer {
*
* @var array
*/
protected $supportedInterfaceOrClass = array('Drupal\Core\Config\Entity\ConfigEntityInterface');
protected $supportedInterfaceOrClass = ['Drupal\Core\Config\Entity\ConfigEntityInterface'];
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
public function normalize($object, $format = NULL, array $context = []) {
return $object->toArray();
}

View file

@ -8,19 +8,17 @@ namespace Drupal\serialization\Normalizer;
class ContentEntityNormalizer extends EntityNormalizer {
/**
* The interface or class that this Normalizer supports.
*
* @var array
* {@inheritdoc}
*/
protected $supportedInterfaceOrClass = ['Drupal\Core\Entity\ContentEntityInterface'];
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
$context += array(
public function normalize($object, $format = NULL, array $context = []) {
$context += [
'account' => NULL,
);
];
$attributes = [];
foreach ($object as $name => $field) {

View file

@ -2,8 +2,9 @@
namespace Drupal\serialization\Normalizer;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Drupal\Core\Entity\FieldableEntityInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
@ -11,19 +12,14 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
*/
class EntityNormalizer extends ComplexDataNormalizer implements DenormalizerInterface {
use FieldableEntityNormalizerTrait;
/**
* The interface or class that this Normalizer supports.
*
* @var array
*/
protected $supportedInterfaceOrClass = array('Drupal\Core\Entity\EntityInterface');
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
protected $supportedInterfaceOrClass = [EntityInterface::class];
/**
* Constructs an EntityNormalizer object.
@ -39,49 +35,26 @@ class EntityNormalizer extends ComplexDataNormalizer implements DenormalizerInte
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = NULL, array $context = []) {
// Get the entity type ID while letting context override the $class param.
$entity_type_id = !empty($context['entity_type']) ? $context['entity_type'] : $this->entityManager->getEntityTypeFromClass($class);
$entity_type_id = $this->determineEntityTypeId($class, $context);
$entity_type_definition = $this->getEntityTypeDefinition($entity_type_id);
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition */
// Get the entity type definition.
$entity_type_definition = $this->entityManager->getDefinition($entity_type_id, FALSE);
// The bundle property will be required to denormalize a bundleable
// fieldable entity.
if ($entity_type_definition->hasKey('bundle') && $entity_type_definition->isSubclassOf(FieldableEntityInterface::class)) {
// Get an array containing the bundle only. This also remove the bundle
// key from the $data array.
$bundle_data = $this->extractBundleData($data, $entity_type_definition);
// Don't try to create an entity without an entity type id.
if (!$entity_type_definition) {
throw new UnexpectedValueException(sprintf('The specified entity type "%s" does not exist. A valid etnity type is required for denormalization', $entity_type_id));
// Create the entity from bundle data only, then apply field values after.
$entity = $this->entityManager->getStorage($entity_type_id)->create($bundle_data);
$this->denormalizeFieldData($data, $entity, $format, $context);
}
// The bundle property will be required to denormalize a bundleable entity.
if ($entity_type_definition->hasKey('bundle')) {
$bundle_key = $entity_type_definition->getKey('bundle');
// Get the base field definitions for this entity type.
$base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
// Get the ID key from the base field definition for the bundle key or
// default to 'value'.
$key_id = isset($base_field_definitions[$bundle_key]) ? $base_field_definitions[$bundle_key]->getFieldStorageDefinition()->getMainPropertyName() : 'value';
// Normalize the bundle if it is not explicitly set.
$data[$bundle_key] = isset($data[$bundle_key][0][$key_id]) ? $data[$bundle_key][0][$key_id] : (isset($data[$bundle_key]) ? $data[$bundle_key] : NULL);
// Get the bundle entity type from the entity type definition.
$bundle_type_id = $entity_type_definition->getBundleEntityType();
$bundle_types = $bundle_type_id ? $this->entityManager->getStorage($bundle_type_id)->getQuery()->execute() : [];
// Make sure a bundle has been provided.
if (!is_string($data[$bundle_key])) {
throw new UnexpectedValueException('A string must be provided as a bundle value.');
}
// Make sure the submitted bundle is a valid bundle for the entity type.
if ($bundle_types && !in_array($data[$bundle_key], $bundle_types)) {
throw new UnexpectedValueException(sprintf('"%s" is not a valid bundle type for denormalization.', $data[$bundle_key]));
}
else {
// Create the entity from all data.
$entity = $this->entityManager->getStorage($entity_type_id)->create($data);
}
// Create the entity from data.
$entity = $this->entityManager->getStorage($entity_type_id)->create($data);
// Pass the names of the fields whose values can be merged.
// @todo https://www.drupal.org/node/2456257 remove this.
$entity->_restSubmittedFields = array_keys($data);

View file

@ -0,0 +1,57 @@
<?php
namespace Drupal\serialization\Normalizer;
use Drupal\Core\Field\FieldItemInterface;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* Denormalizes field item object structure by updating the entity field values.
*/
class FieldItemNormalizer extends ComplexDataNormalizer implements DenormalizerInterface {
/**
* {@inheritdoc}
*/
protected $supportedInterfaceOrClass = FieldItemInterface::class;
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = NULL, array $context = []) {
if (!isset($context['target_instance'])) {
throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldItemNormalizer');
}
if ($context['target_instance']->getParent() == NULL) {
throw new InvalidArgumentException('The field item passed in via $context[\'target_instance\'] must have a parent set.');
}
/** @var \Drupal\Core\Field\FieldItemInterface $field_item */
$field_item = $context['target_instance'];
$field_item->setValue($this->constructValue($data, $context));
return $field_item;
}
/**
* Build the field item value using the incoming data.
*
* Most normalizers that extend this class can simply use this method to
* construct the denormalized value without having to override denormalize()
* and reimplementing its validation logic or its call to set the field value.
*
* @param mixed $data
* The incoming data for this field item.
* @param array $context
* The context passed into the Normalizer.
*
* @return mixed
* The value to use in Entity::setValue().
*/
protected function constructValue($data, $context) {
return $data;
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Drupal\serialization\Normalizer;
use Drupal\Core\Field\FieldItemListInterface;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* Denormalizes data to Drupal field values.
*
* This class simply calls denormalize() on the individual FieldItems. The
* FieldItem normalizers are responsible for setting the field values for each
* item.
*
* @see \Drupal\serialization\Normalizer\FieldItemNormalizer.
*/
class FieldNormalizer extends ListNormalizer implements DenormalizerInterface {
/**
* {@inheritdoc}
*/
protected $supportedInterfaceOrClass = FieldItemListInterface::class;
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = NULL, array $context = []) {
if (!isset($context['target_instance'])) {
throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldNormalizer');
}
if ($context['target_instance']->getParent() == NULL) {
throw new InvalidArgumentException('The field passed in via $context[\'target_instance\'] must have a parent set.');
}
/** @var FieldItemListInterface $items */
$items = $context['target_instance'];
$item_class = $items->getItemDefinition()->getClass();
if (!is_array($data)) {
throw new UnexpectedValueException(sprintf('Field values for "%s" must use an array structure', $items->getName()));
}
foreach ($data as $item_data) {
// Create a new item and pass it as the target for the unserialization of
// $item_data. All items in field should have removed before this method
// was called.
// @see \Drupal\serialization\Normalizer\ContentEntityNormalizer::denormalize().
$context['target_instance'] = $items->appendItem();
$this->serializer->denormalize($item_data, $item_class, $format, $context);
}
return $items;
}
}

View file

@ -0,0 +1,140 @@
<?php
namespace Drupal\serialization\Normalizer;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
* A trait for providing fieldable entity normalization/denormalization methods.
*
* @todo Move this into a FieldableEntityNormalizer in Drupal 9. This is a trait
* used in \Drupal\serialization\Normalizer\EntityNormalizer to maintain BC.
* @see https://www.drupal.org/node/2834734
*/
trait FieldableEntityNormalizerTrait {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Determines the entity type ID to denormalize as.
*
* @param string $class
* The entity type class to be denormalized to.
* @param array $context
* The serialization context data.
*
* @return string
* The entity type ID.
*/
protected function determineEntityTypeId($class, $context) {
// Get the entity type ID while letting context override the $class param.
return !empty($context['entity_type']) ? $context['entity_type'] : $this->entityManager->getEntityTypeFromClass($class);
}
/**
* Gets the entity type definition.
*
* @param string $entity_type_id
* The entity type ID to load the definition for.
*
* @return \Drupal\Core\Entity\EntityTypeInterface
* The loaded entity type definition.
*
* @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
*/
protected function getEntityTypeDefinition($entity_type_id) {
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition */
// Get the entity type definition.
$entity_type_definition = $this->entityManager->getDefinition($entity_type_id, FALSE);
// Don't try to create an entity without an entity type id.
if (!$entity_type_definition) {
throw new UnexpectedValueException(sprintf('The specified entity type "%s" does not exist. A valid entity type is required for denormalization', $entity_type_id));
}
return $entity_type_definition;
}
/**
* Denormalizes the bundle property so entity creation can use it.
*
* @param array $data
* The data being denormalized.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition
* The entity type definition.
*
* @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
*
* @return string
* The valid bundle name.
*/
protected function extractBundleData(array &$data, EntityTypeInterface $entity_type_definition) {
$bundle_key = $entity_type_definition->getKey('bundle');
// Get the base field definitions for this entity type.
$base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_definition->id());
// Get the ID key from the base field definition for the bundle key or
// default to 'value'.
$key_id = isset($base_field_definitions[$bundle_key]) ? $base_field_definitions[$bundle_key]->getFieldStorageDefinition()->getMainPropertyName() : 'value';
// Normalize the bundle if it is not explicitly set.
$bundle_value = isset($data[$bundle_key][0][$key_id]) ? $data[$bundle_key][0][$key_id] : (isset($data[$bundle_key]) ? $data[$bundle_key] : NULL);
// Unset the bundle from the data.
unset($data[$bundle_key]);
// Get the bundle entity type from the entity type definition.
$bundle_type_id = $entity_type_definition->getBundleEntityType();
$bundle_types = $bundle_type_id ? $this->entityManager->getStorage($bundle_type_id)->getQuery()->execute() : [];
// Make sure a bundle has been provided.
if (!is_string($bundle_value)) {
throw new UnexpectedValueException(sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_key));
}
// Make sure the submitted bundle is a valid bundle for the entity type.
if ($bundle_types && !in_array($bundle_value, $bundle_types)) {
throw new UnexpectedValueException(sprintf('"%s" is not a valid bundle type for denormalization.', $bundle_value));
}
return [$bundle_key => $bundle_value];
}
/**
* Denormalizes entity data by denormalizing each field individually.
*
* @param array $data
* The data to denormalize.
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The fieldable entity to set field values for.
* @param string $format
* The serialization format.
* @param array $context
* The context data.
*/
protected function denormalizeFieldData(array $data, FieldableEntityInterface $entity, $format, array $context) {
foreach ($data as $field_name => $field_data) {
$field_item_list = $entity->get($field_name);
// Remove any values that were set as a part of entity creation (e.g
// uuid). If the incoming field data is set to an empty array, this will
// also have the effect of emptying the field in REST module.
$field_item_list->setValue([]);
$field_item_list_class = get_class($field_item_list);
if ($field_data) {
// The field instance must be passed in the context so that the field
// denormalizer can update field values for the parent entity.
$context['target_instance'] = $field_item_list;
$this->serializer->denormalize($field_data, $field_item_list_class, $format, $context);
}
}
}
}

View file

@ -23,8 +23,8 @@ class ListNormalizer extends NormalizerBase {
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
$attributes = array();
public function normalize($object, $format = NULL, array $context = []) {
$attributes = [];
foreach ($object as $fieldItem) {
$attributes[] = $this->serializer->normalize($fieldItem, $format, $context);
}

View file

@ -12,12 +12,12 @@ class MarkupNormalizer extends NormalizerBase {
*
* @var array
*/
protected $supportedInterfaceOrClass = array('Drupal\Component\Render\MarkupInterface');
protected $supportedInterfaceOrClass = ['Drupal\Component\Render\MarkupInterface'];
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
public function normalize($object, $format = NULL, array $context = []) {
return (string) $object;
}

View file

@ -20,7 +20,7 @@ class NullNormalizer extends NormalizerBase {
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
public function normalize($object, $format = NULL, array $context = []) {
return NULL;
}

View file

@ -0,0 +1,32 @@
<?php
namespace Drupal\serialization\Normalizer;
use Drupal\Core\TypedData\PrimitiveInterface;
/**
* Converts primitive data objects to their casted values.
*/
class PrimitiveDataNormalizer extends NormalizerBase {
/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass = PrimitiveInterface::class;
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []) {
// Typed data casts NULL objects to their empty variants, so for example
// the empty string ('') for string type data, or 0 for integer typed data.
// In a better world with typed data implementing algebraic data types,
// getCastedValue would return NULL, but as typed data is not aware of real
// optional values on the primitive level, we implement our own optional
// value normalization here.
return $object->getValue() === NULL ? NULL : $object->getCastedValue();
}
}

View file

@ -17,7 +17,7 @@ class TypedDataNormalizer extends NormalizerBase {
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = array()) {
public function normalize($object, $format = NULL, array $context = []) {
return $object->getValue();
}

View file

@ -19,7 +19,7 @@ class RegisterEntityResolversCompilerPass implements CompilerPassInterface {
*/
public function process(ContainerBuilder $container) {
$definition = $container->getDefinition('serializer.entity_resolver');
$resolvers = array();
$resolvers = [];
// Retrieve registered Normalizers and Encoders from the container.
foreach ($container->findTaggedServiceIds('entity_resolver') as $id => $attributes) {
@ -29,7 +29,7 @@ class RegisterEntityResolversCompilerPass implements CompilerPassInterface {
// Add the registered concrete EntityResolvers to the ChainEntityResolver.
foreach ($this->sort($resolvers) as $resolver) {
$definition->addMethodCall('addResolver', array($resolver));
$definition->addMethodCall('addResolver', [$resolver]);
}
}
@ -48,7 +48,7 @@ class RegisterEntityResolversCompilerPass implements CompilerPassInterface {
* to low priority.
*/
protected function sort($services) {
$sorted = array();
$sorted = [];
krsort($services);
// Flatten the array.

View file

@ -2,6 +2,7 @@
namespace Drupal\serialization;
use Drupal\Core\Config\BootstrapConfigStorageFactory;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@ -22,6 +23,12 @@ class RegisterSerializationClassesCompilerPass implements CompilerPassInterface
// Retrieve registered Normalizers and Encoders from the container.
foreach ($container->findTaggedServiceIds('normalizer') as $id => $attributes) {
// If there is a BC key present, pass this to determine if the normalizer
// should be skipped.
if (isset($attributes[0]['bc']) && $this->normalizerBcSettingIsEnabled($attributes[0]['bc'], $attributes[0]['bc_config_name'])) {
continue;
}
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$normalizers[$priority][] = new Reference($id);
}
@ -39,7 +46,7 @@ class RegisterSerializationClassesCompilerPass implements CompilerPassInterface
}
// Find all serialization formats known.
$formats = array();
$formats = [];
$format_providers = [];
foreach ($container->findTaggedServiceIds('encoder') as $service_id => $attributes) {
$format = $attributes[0]['format'];
@ -53,6 +60,18 @@ class RegisterSerializationClassesCompilerPass implements CompilerPassInterface
$container->setParameter('serializer.format_providers', $format_providers);
}
/**
* Returns whether a normalizer BC setting is disabled or not.
*
* @param string $key
*
* @return bool
*/
protected function normalizerBcSettingIsEnabled($key, $config_name) {
$settings = BootstrapConfigStorageFactory::get()->read($config_name);
return !empty($settings[$key]);
}
/**
* Sorts by priority.
*
@ -68,7 +87,7 @@ class RegisterSerializationClassesCompilerPass implements CompilerPassInterface
* to low priority.
*/
protected function sort($services) {
$sorted = array();
$sorted = [];
krsort($services);
// Flatten the array.

View file

@ -0,0 +1,6 @@
name: 'FieldItem normalization test support'
type: module
description: 'Provides test support for fieldItem normalization test support.'
package: Testing
version: VERSION
core: 8.x

View file

@ -0,0 +1,6 @@
services:
serializer.normalizer.silly_fielditem:
class: Drupal\field_normalization_test\Normalization\TextItemSillyNormalizer
tags:
# The priority must be higher than serialization.normalizer.field_item.
- { name: normalizer , priority: 9 }

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\field_normalization_test\Normalization;
use Drupal\serialization\Normalizer\FieldItemNormalizer;
use Drupal\text\Plugin\Field\FieldType\TextItemBase;
/**
* A test TextItem normalizer to test denormalization.
*/
class TextItemSillyNormalizer extends FieldItemNormalizer {
/**
* {@inheritdoc}
*/
protected $supportedInterfaceOrClass = TextItemBase::class;
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []) {
$data = parent::normalize($object, $format, $context);
$data['value'] .= '::silly_suffix';
return $data;
}
/**
* {@inheritdoc}
*/
protected function constructValue($data, $context) {
$value = parent::constructValue($data, $context);
$value['value'] = str_replace('::silly_suffix', '', $value['value']);
return $value;
}
}

View file

@ -16,7 +16,7 @@ class SerializationTestEncoder implements EncoderInterface {
/**
* {@inheritdoc}
*/
public function encode($data, $format, array $context = array()) {
public function encode($data, $format, array $context = []) {
// @see \Drupal\serialization_test\SerializationTestNormalizer::normalize().
return 'Normalized by ' . $data['normalized_by'] . ', Encoded by SerializationTestEncoder';
}

View file

@ -25,7 +25,7 @@ class SerializationTestNormalizer implements NormalizerInterface {
* An array containing a normalized representation of $object, appropriate
* for encoding to the requested format.
*/
public function normalize($object, $format = NULL, array $context = array()) {
public function normalize($object, $format = NULL, array $context = []) {
$normalized = (array) $object;
// Add identifying value that can be used to verify that the expected
// normalizer was invoked.

View file

@ -34,14 +34,14 @@ class EntityResolverTest extends NormalizerTestBase {
\Drupal::service('router.builder')->rebuild();
// Create the test field storage.
FieldStorageConfig::create(array(
FieldStorageConfig::create([
'entity_type' => 'entity_test_mulrev',
'field_name' => 'field_test_entity_reference',
'type' => 'entity_reference',
'settings' => array(
'settings' => [
'target_type' => 'entity_test_mulrev',
),
))->save();
],
])->save();
// Create the test field.
FieldConfig::create([
@ -54,41 +54,41 @@ class EntityResolverTest extends NormalizerTestBase {
/**
* Test that fields referencing UUIDs can be denormalized.
*/
function testUuidEntityResolver() {
public function testUuidEntityResolver() {
// Create an entity to get the UUID from.
$entity = EntityTestMulRev::create(array('type' => 'entity_test_mulrev'));
$entity = EntityTestMulRev::create(['type' => 'entity_test_mulrev']);
$entity->set('name', 'foobar');
$entity->set('field_test_entity_reference', array(array('target_id' => 1)));
$entity->set('field_test_entity_reference', [['target_id' => 1]]);
$entity->save();
$field_uri = Url::fromUri('base:rest/relation/entity_test_mulrev/entity_test_mulrev/field_test_entity_reference', array('absolute' => TRUE))->toString();
$field_uri = Url::fromUri('base:rest/relation/entity_test_mulrev/entity_test_mulrev/field_test_entity_reference', ['absolute' => TRUE])->toString();
$data = array(
'_links' => array(
'type' => array(
'href' => Url::fromUri('base:rest/type/entity_test_mulrev/entity_test_mulrev', array('absolute' => TRUE))->toString(),
),
$field_uri => array(
array(
$data = [
'_links' => [
'type' => [
'href' => Url::fromUri('base:rest/type/entity_test_mulrev/entity_test_mulrev', ['absolute' => TRUE])->toString(),
],
$field_uri => [
[
'href' => $entity->url(),
),
),
),
'_embedded' => array(
$field_uri => array(
array(
'_links' => array(
],
],
],
'_embedded' => [
$field_uri => [
[
'_links' => [
'self' => $entity->url(),
),
'uuid' => array(
array(
],
'uuid' => [
[
'value' => $entity->uuid(),
),
),
),
),
),
);
],
],
],
],
],
];
$denormalized = $this->container->get('serializer')->denormalize($data, 'Drupal\entity_test\Entity\EntityTestMulRev', $this->format);
$field_value = $denormalized->get('field_test_entity_reference')->getValue();

View file

@ -17,7 +17,7 @@ class EntitySerializationTest extends NormalizerTestBase {
*
* @var array
*/
public static $modules = array('serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user', 'entity_serialization_test');
public static $modules = ['serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user', 'entity_serialization_test'];
/**
* The test values.
@ -29,7 +29,7 @@ class EntitySerializationTest extends NormalizerTestBase {
/**
* The test entity.
*
* @var \Drupal\Core\Entity\ContentEntityBase
* @var \Drupal\Core\Entity\ContentEntityInterface
*/
protected $entity;
@ -58,7 +58,7 @@ class EntitySerializationTest extends NormalizerTestBase {
parent::setUp();
// User create needs sequence table.
$this->installSchema('system', array('sequences'));
$this->installSchema('system', ['sequences']);
// Create a test user to use as the entity owner.
$this->user = \Drupal::entityManager()->getStorage('user')->create([
@ -69,74 +69,75 @@ class EntitySerializationTest extends NormalizerTestBase {
$this->user->save();
// Create a test entity to serialize.
$this->values = array(
$this->values = [
'name' => $this->randomMachineName(),
'user_id' => $this->user->id(),
'field_test_text' => array(
'field_test_text' => [
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
);
],
];
$this->entity = EntityTestMulRev::create($this->values);
$this->entity->save();
$this->serializer = $this->container->get('serializer');
$this->installConfig(array('field'));
$this->installConfig(['field']);
}
/**
* Test the normalize function.
*/
public function testNormalize() {
$expected = array(
'id' => array(
array('value' => 1),
),
'uuid' => array(
array('value' => $this->entity->uuid()),
),
'langcode' => array(
array('value' => 'en'),
),
'name' => array(
array('value' => $this->values['name']),
),
'type' => array(
array('value' => 'entity_test_mulrev'),
),
'created' => array(
array('value' => $this->entity->created->value),
),
'user_id' => array(
array(
'target_id' => $this->user->id(),
$expected = [
'id' => [
['value' => 1],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'langcode' => [
['value' => 'en'],
],
'name' => [
['value' => $this->values['name']],
],
'type' => [
['value' => 'entity_test_mulrev'],
],
'created' => [
['value' => $this->entity->created->value],
],
'user_id' => [
[
// id() will return the string value as it comes from the database.
'target_id' => (int) $this->user->id(),
'target_type' => $this->user->getEntityTypeId(),
'target_uuid' => $this->user->uuid(),
'url' => $this->user->url(),
),
),
'revision_id' => array(
array('value' => 1),
),
'default_langcode' => array(
array('value' => TRUE),
),
'non_rev_field' => array(),
'field_test_text' => array(
array(
],
],
'revision_id' => [
['value' => 1],
],
'default_langcode' => [
['value' => TRUE],
],
'non_rev_field' => [],
'field_test_text' => [
[
'value' => $this->values['field_test_text']['value'],
'format' => $this->values['field_test_text']['format'],
),
),
);
],
],
];
$normalized = $this->serializer->normalize($this->entity);
foreach (array_keys($expected) as $fieldName) {
$this->assertEqual($expected[$fieldName], $normalized[$fieldName], "ComplexDataNormalizer produces expected array for $fieldName.");
$this->assertSame($expected[$fieldName], $normalized[$fieldName], "Normalization produces expected array for $fieldName.");
}
$this->assertEqual(array_diff_key($normalized, $expected), array(), 'No unexpected data is added to the normalized array.');
$this->assertEqual(array_diff_key($normalized, $expected), [], 'No unexpected data is added to the normalized array.');
}
/**
@ -181,7 +182,7 @@ class EntitySerializationTest extends NormalizerTestBase {
// Generate the expected xml in a way that allows changes to entity property
// order.
$expected = array(
$expected = [
'id' => '<id><value>' . $this->entity->id() . '</value></id>',
'uuid' => '<uuid><value>' . $this->entity->uuid() . '</value></uuid>',
'langcode' => '<langcode><value>en</value></langcode>',
@ -193,7 +194,7 @@ class EntitySerializationTest extends NormalizerTestBase {
'default_langcode' => '<default_langcode><value>1</value></default_langcode>',
'non_rev_field' => '<non_rev_field/>',
'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format></field_test_text>',
);
];
// Sort it in the same order as normalised.
$expected = array_merge($normalized, $expected);
// Add header and footer.
@ -214,9 +215,9 @@ class EntitySerializationTest extends NormalizerTestBase {
public function testDenormalize() {
$normalized = $this->serializer->normalize($this->entity);
foreach (array('json', 'xml') as $type) {
$denormalized = $this->serializer->denormalize($normalized, $this->entityClass, $type, array('entity_type' => 'entity_test_mulrev'));
$this->assertTrue($denormalized instanceof $this->entityClass, SafeMarkup::format('Denormalized entity is an instance of @class', array('@class' => $this->entityClass)));
foreach (['json', 'xml'] as $type) {
$denormalized = $this->serializer->denormalize($normalized, $this->entityClass, $type, ['entity_type' => 'entity_test_mulrev']);
$this->assertTrue($denormalized instanceof $this->entityClass, SafeMarkup::format('Denormalized entity is an instance of @class', ['@class' => $this->entityClass]));
$this->assertIdentical($denormalized->getEntityTypeId(), $this->entity->getEntityTypeId(), 'Expected entity type found.');
$this->assertIdentical($denormalized->bundle(), $this->entity->bundle(), 'Expected entity bundle found.');
$this->assertIdentical($denormalized->uuid(), $this->entity->uuid(), 'Expected entity UUID found.');

View file

@ -0,0 +1,135 @@
<?php
namespace Drupal\Tests\serialization\Kernel;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
* Test field level normalization process.
*
* @group serialization
*/
class FieldItemSerializationTest extends NormalizerTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user', 'field_normalization_test'];
/**
* The class name of the test class.
*
* @var string
*/
protected $entityClass = 'Drupal\entity_test\Entity\EntityTestMulRev';
/**
* The test values.
*
* @var array
*/
protected $values;
/**
* The test entity.
*
* @var \Drupal\Core\Entity\ContentEntityBase
*/
protected $entity;
/**
* The serializer service.
*
* @var \Symfony\Component\Serializer\Serializer.
*/
protected $serializer;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Auto-create a field for testing default field values.
FieldStorageConfig::create([
'entity_type' => 'entity_test_mulrev',
'field_name' => 'field_test_text_default',
'type' => 'text',
'cardinality' => 1,
'translatable' => FALSE,
])->save();
FieldConfig::create([
'entity_type' => 'entity_test_mulrev',
'field_name' => 'field_test_text_default',
'bundle' => 'entity_test_mulrev',
'label' => 'Test text-field with default',
'default_value' => [
[
'value' => 'This is the default',
'format' => 'full_html',
],
],
'widget' => [
'type' => 'text_textfield',
'weight' => 0,
],
])->save();
// Create a test entity to serialize.
$this->values = [
'name' => $this->randomMachineName(),
'field_test_text' => [
'value' => $this->randomMachineName(),
'format' => 'full_html',
],
];
$this->entity = EntityTestMulRev::create($this->values);
$this->entity->save();
$this->serializer = $this->container->get('serializer');
$this->installConfig(['field']);
}
/**
* Tests normalizing and denormalizing an entity with field item normalizer.
*/
public function testFieldNormalizeDenormalize() {
$normalized = $this->serializer->normalize($this->entity, 'json');
$expected_field_value = $this->entity->field_test_text[0]->getValue()['value'] . '::silly_suffix';
$this->assertEquals($expected_field_value, $normalized['field_test_text'][0]['value'], 'Text field item normalized');
$denormalized = $this->serializer->denormalize($normalized, $this->entityClass, 'json');
$this->assertEquals($denormalized->field_test_text[0]->getValue(), $this->entity->field_test_text[0]->getValue(), 'Text field item denormalized.');
$this->assertEquals($denormalized->field_test_text_default[0]->getValue(), $this->entity->field_test_text_default[0]->getValue(), 'Text field item with default denormalized.');
// Unset the values for text field that has a default value.
unset($normalized['field_test_text_default']);
$denormalized_without_all_fields = $this->serializer->denormalize($normalized, $this->entityClass, 'json');
// Check that denormalized entity is still the same even if not all fields
// are not provided.
$this->assertEquals($denormalized_without_all_fields->field_test_text[0]->getValue(), $this->entity->field_test_text[0]->getValue(), 'Text field item denormalized.');
// Even though field_test_text_default value was unset before
// denormalization it should still have the default values for the field.
$this->assertEquals($denormalized_without_all_fields->field_test_text_default[0]->getValue(), $this->entity->field_test_text_default[0]->getValue(), 'Text field item with default denormalized.');
}
/**
* Tests denormalizing using a scalar field value.
*/
public function testFieldDenormalizeWithScalarValue() {
$this->setExpectedException(UnexpectedValueException::class, 'Field values for "uuid" must use an array structure');
$normalized = $this->serializer->normalize($this->entity, 'json');
// Change the UUID value to use the UUID directly. No array structure.
$normalized['uuid'] = $normalized['uuid'][0]['value'];
$this->serializer->denormalize($normalized, $this->entityClass, 'json');
}
}

View file

@ -16,35 +16,35 @@ abstract class NormalizerTestBase extends KernelTestBase {
*
* @var array
*/
public static $modules = array('serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user');
public static $modules = ['serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user'];
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_mulrev');
$this->installEntitySchema('user');
$this->installConfig(array('field'));
$this->installConfig(['field']);
\Drupal::service('router.builder')->rebuild();
\Drupal::moduleHandler()->invoke('rest', 'install');
// Auto-create a field for testing.
FieldStorageConfig::create(array(
FieldStorageConfig::create([
'entity_type' => 'entity_test_mulrev',
'field_name' => 'field_test_text',
'type' => 'text',
'cardinality' => 1,
'translatable' => FALSE,
))->save();
FieldConfig::create(array(
])->save();
FieldConfig::create([
'entity_type' => 'entity_test_mulrev',
'field_name' => 'field_test_text',
'bundle' => 'entity_test_mulrev',
'label' => 'Test text-field',
'widget' => array(
'widget' => [
'type' => 'text_textfield',
'weight' => 0,
),
))->save();
],
])->save();
}
}

View file

@ -17,7 +17,7 @@ class SerializationTest extends KernelTestBase {
*
* @var array
*/
public static $modules = array('serialization', 'serialization_test');
public static $modules = ['serialization', 'serialization_test'];
/**
* The serializer service to test.

View file

@ -1,6 +1,6 @@
<?php
namespace Drupal\serialization\Tests;
namespace Drupal\Tests\serialization\Unit\CompilerPass;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\serialization\RegisterSerializationClassesCompilerPass;

View file

@ -28,7 +28,7 @@ class XmlEncoderTest extends UnitTestCase {
*
* @var array
*/
protected $testArray = array('test' => 'test');
protected $testArray = ['test' => 'test'];
protected function setUp() {
$this->baseEncoder = $this->getMock('Symfony\Component\Serializer\Encoder\XmlEncoder');
@ -58,7 +58,7 @@ class XmlEncoderTest extends UnitTestCase {
public function testEncode() {
$this->baseEncoder->expects($this->once())
->method('encode')
->with($this->testArray, 'test', array())
->with($this->testArray, 'test', [])
->will($this->returnValue('test'));
$this->assertEquals('test', $this->encoder->encode($this->testArray, 'test'));
@ -70,7 +70,7 @@ class XmlEncoderTest extends UnitTestCase {
public function testDecode() {
$this->baseEncoder->expects($this->once())
->method('decode')
->with('test', 'test', array())
->with('test', 'test', [])
->will($this->returnValue($this->testArray));
$this->assertEquals($this->testArray, $this->encoder->decode('test', 'test'));

View file

@ -47,10 +47,10 @@ class ChainEntityResolverTest extends UnitTestCase {
* @covers ::resolve
*/
public function testResolverWithNoneResolved() {
$resolvers = array(
$resolvers = [
$this->createEntityResolverMock(),
$this->createEntityResolverMock(),
);
];
$resolver = new ChainEntityResolver($resolvers);
@ -78,10 +78,10 @@ class ChainEntityResolverTest extends UnitTestCase {
* @covers ::resolve
*/
public function testResolverWithFirstResolved() {
$resolvers = array(
$resolvers = [
$this->createEntityResolverMock(10),
$this->createEntityResolverMock(NULL, FALSE),
);
];
$resolver = new ChainEntityResolver($resolvers);
@ -95,10 +95,10 @@ class ChainEntityResolverTest extends UnitTestCase {
* @covers ::resolve
*/
public function testResolverWithLastResolved() {
$resolvers = array(
$resolvers = [
$this->createEntityResolverMock(),
$this->createEntityResolverMock(10),
);
];
$resolver = new ChainEntityResolver($resolvers);
@ -112,10 +112,10 @@ class ChainEntityResolverTest extends UnitTestCase {
* @covers ::resolve
*/
public function testResolverWithResolvedToZero() {
$resolvers = array(
$resolvers = [
$this->createEntityResolverMock(0),
$this->createEntityResolverMock(NULL, FALSE),
);
];
$resolver = new ChainEntityResolver($resolvers);

View file

@ -44,7 +44,7 @@ class UuidResolverTest extends UnitTestCase {
->method('loadEntityByUuid');
$normalizer = $this->getMock('Symfony\Component\Serializer\Normalizer\NormalizerInterface');
$this->assertNull($this->resolver->resolve($normalizer, array(), 'test_type'));
$this->assertNull($this->resolver->resolve($normalizer, [], 'test_type'));
}
/**
@ -57,9 +57,9 @@ class UuidResolverTest extends UnitTestCase {
$normalizer = $this->getMock('Drupal\serialization\EntityResolver\UuidReferenceInterface');
$normalizer->expects($this->once())
->method('getUuid')
->with(array())
->with([])
->will($this->returnValue(NULL));
$this->assertNull($this->resolver->resolve($normalizer, array(), 'test_type'));
$this->assertNull($this->resolver->resolve($normalizer, [], 'test_type'));
}
/**
@ -76,10 +76,10 @@ class UuidResolverTest extends UnitTestCase {
$normalizer = $this->getMock('Drupal\serialization\EntityResolver\UuidReferenceInterface');
$normalizer->expects($this->once())
->method('getUuid')
->with(array())
->with([])
->will($this->returnValue($uuid));
$this->assertNull($this->resolver->resolve($normalizer, array(), 'test_type'));
$this->assertNull($this->resolver->resolve($normalizer, [], 'test_type'));
}
/**
@ -101,9 +101,9 @@ class UuidResolverTest extends UnitTestCase {
$normalizer = $this->getMock('Drupal\serialization\EntityResolver\UuidReferenceInterface');
$normalizer->expects($this->once())
->method('getUuid')
->with(array())
->with([])
->will($this->returnValue($uuid));
$this->assertSame(1, $this->resolver->resolve($normalizer, array(), 'test_type'));
$this->assertSame(1, $this->resolver->resolve($normalizer, [], 'test_type'));
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\Tests\serialization\Unit\EventSubscriber;
use Drupal\serialization\Encoder\JsonEncoder;
use Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Serializer\Serializer;
/**
* @coversDefaultClass \Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber
* @group serialization
*/
class DefaultExceptionSubscriberTest extends UnitTestCase {
/**
* @covers ::on4xx
*/
public function testOn4xx() {
$kernel = $this->prophesize(HttpKernelInterface::class);
$request = Request::create('/test');
$request->setRequestFormat('json');
$e = new MethodNotAllowedHttpException(['POST', 'PUT'], 'test message');
$event = new GetResponseForExceptionEvent($kernel->reveal(), $request, 'GET', $e);
$subscriber = new DefaultExceptionSubscriber(new Serializer([], [new JsonEncoder()]), []);
$subscriber->on4xx($event);
$response = $event->getResponse();
$this->assertInstanceOf(Response::class, $response);
$this->assertEquals('{"message":"test message"}', $response->getContent());
$this->assertEquals(405, $response->getStatusCode());
$this->assertEquals('POST, PUT', $response->headers->get('Allow'));
$this->assertEquals('application/json', $response->headers->get('Content-Type'));
}
}

View file

@ -17,7 +17,7 @@ class ConfigEntityNormalizerTest extends UnitTestCase {
* @covers ::normalize
*/
public function testNormalize() {
$test_export_properties = array('test' => 'test');
$test_export_properties = ['test' => 'test'];
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$normalizer = new ConfigEntityNormalizer($entity_manager);

View file

@ -40,7 +40,7 @@ class ContentEntityNormalizerTest extends UnitTestCase {
$this->contentEntityNormalizer = new ContentEntityNormalizer($this->entityManager);
$this->serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
->disableOriginalConstructor()
->setMethods(array('normalize'))
->setMethods(['normalize'])
->getMock();
$this->contentEntityNormalizer->setSerializer($this->serializer);
}
@ -66,10 +66,10 @@ class ContentEntityNormalizerTest extends UnitTestCase {
->with($this->containsOnlyInstancesOf('Drupal\Core\Field\FieldItemListInterface'), 'test_format', ['account' => NULL])
->will($this->returnValue('test'));
$definitions = array(
$definitions = [
'field_1' => $this->createMockFieldListItem(),
'field_2' => $this->createMockFieldListItem(FALSE),
);
];
$content_entity_mock = $this->createMockForContentEntity($definitions);
$normalized = $this->contentEntityNormalizer->normalize($content_entity_mock, 'test_format');
@ -98,10 +98,10 @@ class ContentEntityNormalizerTest extends UnitTestCase {
// The mock account should get passed directly into the access() method on
// field items from $context['account'].
$definitions = array(
$definitions = [
'field_1' => $this->createMockFieldListItem(TRUE, $mock_account),
'field_2' => $this->createMockFieldListItem(FALSE, $mock_account),
);
];
$content_entity_mock = $this->createMockForContentEntity($definitions);
$normalized = $this->contentEntityNormalizer->normalize($content_entity_mock, 'test_format', $context);
@ -121,7 +121,7 @@ class ContentEntityNormalizerTest extends UnitTestCase {
public function createMockForContentEntity($definitions) {
$content_entity_mock = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
->disableOriginalConstructor()
->setMethods(array('getFields'))
->setMethods(['getFields'])
->getMockForAbstractClass();
$content_entity_mock->expects($this->once())
->method('getFields')

View file

@ -2,8 +2,11 @@
namespace Drupal\Tests\serialization\Unit\Normalizer;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\serialization\Normalizer\EntityNormalizer;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
* @coversDefaultClass \Drupal\serialization\Normalizer\EntityNormalizer
@ -49,14 +52,14 @@ class EntityNormalizerTest extends UnitTestCase {
$list_item_1 = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
$list_item_2 = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
$definitions = array(
$definitions = [
'field_1' => $list_item_1,
'field_2' => $list_item_2,
);
];
$content_entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
->disableOriginalConstructor()
->setMethods(array('getFields'))
->setMethods(['getFields'])
->getMockForAbstractClass();
$content_entity->expects($this->once())
->method('getFields')
@ -64,7 +67,7 @@ class EntityNormalizerTest extends UnitTestCase {
$serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
->disableOriginalConstructor()
->setMethods(array('normalize'))
->setMethods(['normalize'])
->getMock();
$serializer->expects($this->at(0))
->method('normalize')
@ -82,11 +85,10 @@ class EntityNormalizerTest extends UnitTestCase {
* Tests the denormalize() method with no entity type provided in context.
*
* @covers ::denormalize
*
* @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
*/
public function testDenormalizeWithNoEntityType() {
$this->entityNormalizer->denormalize(array(), 'Drupal\Core\Entity\ContentEntityBase');
$this->setExpectedException(UnexpectedValueException::class);
$this->entityNormalizer->denormalize([], 'Drupal\Core\Entity\ContentEntityBase');
}
/**
@ -104,6 +106,10 @@ class EntityNormalizerTest extends UnitTestCase {
];
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->once())
->method('id')
->willReturn('test');
$entity_type->expects($this->once())
->method('hasKey')
->with('bundle')
@ -112,6 +118,11 @@ class EntityNormalizerTest extends UnitTestCase {
->method('getKey')
->with('bundle')
->will($this->returnValue('test_type'));
$entity_type->expects($this->once())
->method('isSubClassOf')
->with(FieldableEntityInterface::class)
->willReturn(TRUE);
$entity_type->expects($this->once())
->method('getBundleEntityType')
->will($this->returnValue('test_bundle'));
@ -154,32 +165,56 @@ class EntityNormalizerTest extends UnitTestCase {
->with('test_bundle')
->will($this->returnValue($entity_type_storage));
// The expected test data should have a modified test_type property.
$expected_test_data = array(
'key_1' => 'value_1',
'key_2' => 'value_2',
'test_type' => 'test_bundle',
);
$key_1 = $this->getMock(FieldItemListInterface::class);
$key_2 = $this->getMock(FieldItemListInterface::class);
$entity = $this->getMock(FieldableEntityInterface::class);
$entity->expects($this->at(0))
->method('get')
->with('key_1')
->willReturn($key_1);
$entity->expects($this->at(1))
->method('get')
->with('key_2')
->willReturn($key_2);
$storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
// Create should only be called with the bundle property at first.
$expected_test_data = [
'test_type' => 'test_bundle',
];
$storage->expects($this->once())
->method('create')
->with($expected_test_data)
->will($this->returnValue($this->getMock('Drupal\Core\Entity\EntityInterface')));
->will($this->returnValue($entity));
$this->entityManager->expects($this->at(3))
->method('getStorage')
->with('test')
->will($this->returnValue($storage));
// Setup expectations for the serializer. This will be called for each field
// item.
$serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
->disableOriginalConstructor()
->setMethods(['denormalize'])
->getMock();
$serializer->expects($this->at(0))
->method('denormalize')
->with('value_1', get_class($key_1), NULL, ['target_instance' => $key_1, 'entity_type' => 'test']);
$serializer->expects($this->at(1))
->method('denormalize')
->with('value_2', get_class($key_2), NULL, ['target_instance' => $key_2, 'entity_type' => 'test']);
$this->entityNormalizer->setSerializer($serializer);
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
}
/**
* Tests the denormalize method with a bundle property.
*
* @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
*
* @covers ::denormalize
*/
public function testDenormalizeWithInvalidBundle() {
@ -192,6 +227,10 @@ class EntityNormalizerTest extends UnitTestCase {
];
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->once())
->method('id')
->willReturn('test');
$entity_type->expects($this->once())
->method('hasKey')
->with('bundle')
@ -200,6 +239,11 @@ class EntityNormalizerTest extends UnitTestCase {
->method('getKey')
->with('bundle')
->will($this->returnValue('test_type'));
$entity_type->expects($this->once())
->method('isSubClassOf')
->with(FieldableEntityInterface::class)
->willReturn(TRUE);
$entity_type->expects($this->once())
->method('getBundleEntityType')
->will($this->returnValue('test_bundle'));
@ -242,7 +286,7 @@ class EntityNormalizerTest extends UnitTestCase {
->with('test_bundle')
->will($this->returnValue($entity_type_storage));
$this->setExpectedException(UnexpectedValueException::class);
$this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']);
}
@ -252,10 +296,10 @@ class EntityNormalizerTest extends UnitTestCase {
* @covers ::denormalize
*/
public function testDenormalizeWithNoBundle() {
$test_data = array(
$test_data = [
'key_1' => 'value_1',
'key_2' => 'value_2',
);
];
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->once())
@ -284,7 +328,7 @@ class EntityNormalizerTest extends UnitTestCase {
$this->entityManager->expects($this->never())
->method('getBaseFieldDefinitions');
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, array('entity_type' => 'test')));
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
}
}

View file

@ -34,7 +34,7 @@ class ListNormalizerTest extends UnitTestCase {
*
* @var array
*/
protected $expectedListValues = array('test', 'test', 'test');
protected $expectedListValues = ['test', 'test', 'test'];
/**
* The mocked typed data.
@ -54,7 +54,7 @@ class ListNormalizerTest extends UnitTestCase {
// Set up a mock container as ItemList() will call for the 'typed_data_manager'
// service.
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')
->setMethods(array('get'))
->setMethods(['get'])
->getMock();
$container->expects($this->any())
->method('get')

View file

@ -45,20 +45,20 @@ class NormalizerBaseTest extends UnitTestCase {
* An array of provider data for testSupportsNormalization.
*/
public function providerTestSupportsNormalization() {
return array(
return [
// Something that is not an object should return FALSE immediately.
array(FALSE, array()),
[FALSE, []],
// An object with no class set should return FALSE.
array(FALSE, new \stdClass()),
[FALSE, new \stdClass()],
// Set a supported Class.
array(TRUE, new \stdClass(), 'stdClass'),
[TRUE, new \stdClass(), 'stdClass'],
// Set a supported interface.
array(TRUE, new \RecursiveArrayIterator(), 'RecursiveIterator'),
[TRUE, new \RecursiveArrayIterator(), 'RecursiveIterator'],
// Set a different class.
array(FALSE, new \stdClass(), 'ArrayIterator'),
[FALSE, new \stdClass(), 'ArrayIterator'],
// Set a different interface.
array(FALSE, new \stdClass(), 'RecursiveIterator'),
);
[FALSE, new \stdClass(), 'RecursiveIterator'],
];
}
}

View file

@ -0,0 +1,101 @@
<?php
namespace Drupal\Tests\serialization\Unit\Normalizer;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\Plugin\DataType\BooleanData;
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
use Drupal\Core\TypedData\Plugin\DataType\StringData;
use Drupal\Tests\UnitTestCase;
use Drupal\serialization\Normalizer\PrimitiveDataNormalizer;
/**
* @coversDefaultClass \Drupal\serialization\Normalizer\PrimitiveDataNormalizer
* @group serialization
*/
class PrimitiveDataNormalizerTest extends UnitTestCase {
/**
* The TypedDataNormalizer instance.
*
* @var \Drupal\serialization\Normalizer\TypedDataNormalizer
*/
protected $normalizer;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->normalizer = new PrimitiveDataNormalizer();
}
/**
* @covers ::supportsNormalization
* @dataProvider dataProviderPrimitiveData
*/
public function testSupportsNormalization($primitive_data, $expected) {
$this->assertTrue($this->normalizer->supportsNormalization($primitive_data));
}
/**
* @covers ::supportsNormalization
*/
public function testSupportsNormalizationFail() {
// Test that an object not implementing PrimitiveInterface fails.
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
}
/**
* @covers ::normalize
* @dataProvider dataProviderPrimitiveData
*/
public function testNormalize($primitive_data, $expected) {
$this->assertSame($expected, $this->normalizer->normalize($primitive_data));
}
/**
* Data provider for testNormalize().
*/
public function dataProviderPrimitiveData() {
$data = [];
$definition = DataDefinition::createFromDataType('string');
$string = new StringData($definition, 'string');
$string->setValue('test');
$data['string'] = [$string, 'test'];
$definition = DataDefinition::createFromDataType('string');
$string = new StringData($definition, 'string');
$string->setValue(NULL);
$data['string-null'] = [$string, NULL];
$definition = DataDefinition::createFromDataType('integer');
$integer = new IntegerData($definition, 'integer');
$integer->setValue(5);
$data['integer'] = [$integer, 5];
$definition = DataDefinition::createFromDataType('integer');
$integer = new IntegerData($definition, 'integer');
$integer->setValue(NULL);
$data['integer-null'] = [$integer, NULL];
$definition = DataDefinition::createFromDataType('boolean');
$boolean = new BooleanData($definition, 'boolean');
$boolean->setValue(TRUE);
$data['boolean'] = [$boolean, TRUE];
$definition = DataDefinition::createFromDataType('boolean');
$boolean = new BooleanData($definition, 'boolean');
$boolean->setValue(NULL);
$data['boolean-null'] = [$boolean, NULL];
return $data;
}
}