diff --git a/jsonapi.services.yml b/jsonapi.services.yml index 946ea6c..0a969aa 100644 --- a/jsonapi.services.yml +++ b/jsonapi.services.yml @@ -1,9 +1,7 @@ services: serializer.normalizer.entity_reference_item.jsonapi: - class: Drupal\jsonapi\Normalizer\EntityReferenceItemNormalizer - arguments: ['@jsonapi.resource.manager'] - calls: - - [setContainer, ['@service_container']] + class: Drupal\jsonapi\Normalizer\RelationshipItemNormalizer + arguments: ['@jsonapi.resource.manager', '@serializer.normalizer.document_root.jsonapi',] tags: - { name: normalizer, priority: 21 } serializer.normalizer.field_item.jsonapi: @@ -20,9 +18,14 @@ services: - { name: normalizer, priority: 21 } serializer.normalizer.entity_reference_field.jsonapi: class: Drupal\jsonapi\Normalizer\EntityReferenceFieldNormalizer + arguments: ['@jsonapi.link_manager', '@entity_field.manager', '@plugin.manager.field.field_type', '@jsonapi.resource.manager'] tags: - { name: normalizer, priority: 31 } - arguments: ['@jsonapi.link_manager', '@entity_field.manager', '@plugin.manager.field.field_type', '@jsonapi.resource.manager'] + serializer.normalizer.relationship.jsonapi: + class: Drupal\jsonapi\Normalizer\RelationshipNormalizer + arguments: ['@jsonapi.resource.manager', '@serializer.normalizer.document_root.jsonapi', '@jsonapi.link_manager'] + tags: + - { name: normalizer, priority: 21 } serializer.normalizer.entity.jsonapi: class: Drupal\jsonapi\Normalizer\ContentEntityNormalizer arguments: ['@jsonapi.link_manager', '@jsonapi.current_context'] diff --git a/src/Normalizer/ConfigEntityNormalizer.php b/src/Normalizer/ConfigEntityNormalizer.php index be8494f..4b96654 100644 --- a/src/Normalizer/ConfigEntityNormalizer.php +++ b/src/Normalizer/ConfigEntityNormalizer.php @@ -3,6 +3,9 @@ namespace Drupal\jsonapi\Normalizer; use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\jsonapi\EntityCollection; +use Drupal\jsonapi\Relationship; +use Drupal\jsonapi\RelationshipInterface; use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** @@ -22,35 +25,30 @@ class ConfigEntityNormalizer extends ContentEntityNormalizer { protected $supportedInterfaceOrClass = ConfigEntityInterface::class; /** - * Gets the field names for the given entity. - * - * @param mixed $entity - * The entity. - * - * @return array - * The fields. + * {@inheritdoc} */ - protected function getFields($entity) { + protected function getFields($entity, $bundle_id) { /* @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */ - return $entity->toArray(); + $fields = $entity->toArray(); + + if (!$bundle_id) { + // Now create a relationship to the bundle level resource so we can + // include the rest of the fields. + $field_name = 'bundle-resource'; + $entity_collection = new EntityCollection([$entity]); + $fields[$field_name] = new Relationship($this->resourceManager, $field_name, 1, $entity_collection, $entity); + } + return $fields; } /** - * Serializes a given field. - * - * @param mixed $field - * The field to serialize. - * @param array $context - * The normalization context. - * @param string $format - * The serialization format. - * - * @return Value\FieldNormalizerValueInterface - * The normalized value. + * {@inheritdoc} */ protected function serializeField($field, $context, $format) { $output = $this->serializer->normalize($field, $format, $context); - $output->setPropertyType('attributes'); + $field instanceof RelationshipInterface ? + $output->setPropertyType('relationships') : + $output->setPropertyType('attributes'); return $output; } diff --git a/src/Normalizer/ContentEntityNormalizer.php b/src/Normalizer/ContentEntityNormalizer.php index 1c0b7c1..21d68d1 100644 --- a/src/Normalizer/ContentEntityNormalizer.php +++ b/src/Normalizer/ContentEntityNormalizer.php @@ -2,12 +2,17 @@ namespace Drupal\jsonapi\Normalizer; +use Drupal\Core\Access\AccessibleInterface; use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\EntityReferenceFieldItemList; use Drupal\Core\Field\FieldItemListInterface; use Drupal\jsonapi\Configuration\ResourceConfigInterface; use Drupal\jsonapi\Context\CurrentContextInterface; +use Drupal\jsonapi\EntityCollection; use Drupal\jsonapi\LinkManager\LinkManagerInterface; +use Drupal\jsonapi\Relationship; +use Drupal\jsonapi\RelationshipInterface; use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -71,15 +76,18 @@ class ContentEntityNormalizer extends NormalizerBase implements DenormalizerInte public function normalize($entity, $format = NULL, array $context = array()) { // If the fields to use were specified, only output those field values. $resource_type = $context['resource_config']->getTypeName(); + // Get the bundle ID of the requested resource. This is used to determine if + // this is a bundle level resource or an entity level resource. + $bundle_id = $context['resource_config']->getBundleId(); if (!empty($context['sparse_fieldset'][$resource_type])) { $field_names = $context['sparse_fieldset'][$resource_type]; } else { - $field_names = $this->getFieldNames($entity); + $field_names = $this->getFieldNames($entity, $bundle_id); } /* @var Value\FieldNormalizerValueInterface[] $normalizer_values */ $normalizer_values = []; - foreach ($this->getFields($entity) as $field_name => $field) { + foreach ($this->getFields($entity, $bundle_id) as $field_name => $field) { // Relationships cannot be excluded by using sparse fieldsets. $is_relationship = $this->isRelationship($field); if (!$is_relationship && !in_array($field_name, $field_names)) { @@ -104,7 +112,7 @@ class ContentEntityNormalizer extends NormalizerBase implements DenormalizerInte * TRUE if it's a JSON API relationship. */ protected function isRelationship($field) { - return $field instanceof EntityReferenceFieldItemList; + return $field instanceof EntityReferenceFieldItemList || $field instanceof RelationshipInterface; } /** @@ -137,9 +145,9 @@ class ContentEntityNormalizer extends NormalizerBase implements DenormalizerInte * @return string[] * The field names. */ - protected function getFieldNames($entity) { + protected function getFieldNames($entity, $bundle_id) { /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - return array_keys($this->getFields($entity)); + return array_keys($this->getFields($entity, $bundle_id)); } /** @@ -147,26 +155,20 @@ class ContentEntityNormalizer extends NormalizerBase implements DenormalizerInte * * @param mixed $entity * The entity. + * @param string $bundle_id + * The bundle id. * * @return array * The fields. */ - protected function getFields($entity) { + protected function getFields($entity, $bundle_id) { /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $fields = $entity->getFields(); - if (!$this->currentContext->getResourceConfig()->getBundleId()) { - // If this request if for the base entity, then strip all fields attached to a specific bundles. That way all the - // entity requests will look the same regardless of their bundle. If you need the information about the fields - // attached to the bundle, you can request the whole entity by using the bundle resource. As a shortcut, all - // entity resources contain a fake relationship to the bundle specific resource. - $fields = array_filter($fields, function (FieldItemListInterface $field) { - return (bool) !$field->getDataDefinition()->getTargetBundle(); - }); - } - return $fields; + return $bundle_id ? + $fields : + $this->getResourceBundleRelationshipFields($entity, $fields); } - /** * Serializes a given field. * @@ -181,9 +183,9 @@ class ContentEntityNormalizer extends NormalizerBase implements DenormalizerInte * The normalized value. */ protected function serializeField($field, $context, $format) { - /* @var \Drupal\Core\Field\FieldItemListInterface $field */ + /* @var \Drupal\Core\Field\FieldItemListInterface|\Drupal\jsonapi\RelationshipInterface $field */ // Continue if the current user does not have access to view this field. - if (!$field->access('view', $context['account'])) { + if ($field instanceof AccessibleInterface && !$field->access('view', $context['account'])) { return NULL; } $output = $this->serializer->normalize($field, $format, $context); @@ -193,4 +195,32 @@ class ContentEntityNormalizer extends NormalizerBase implements DenormalizerInte return $output; } + /** + * Checks if the fields pertain to the bundle and removes them from the list. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The host entity + * @param array $fields + * The fields to check. + * + * @return array + * The list of fields keyed by field name. + */ + protected function getResourceBundleRelationshipFields(EntityInterface $entity, array $fields) { + // If this request if for the base entity, then strip all fields attached to a specific bundles. That way all the + // entity requests will look the same regardless of their bundle. If you need the information about the fields + // attached to the bundle, you can request the whole entity by using the bundle resource. As a shortcut, all + // entity resources contain a fake relationship to the bundle specific resource. + $fields = array_filter($fields, function (FieldItemListInterface $field) { + return (bool) !$field->getDataDefinition()->getTargetBundle(); + }); + // Now create a relationship to the bundle level resource so we can + // include the rest of the fields. + $field_name = 'bundle-resource'; + $entity_collection = new EntityCollection([$entity]); + $fields[$field_name] = new Relationship($this->resourceManager, $field_name, 1, $entity_collection, $entity); + + return $fields; + } + } diff --git a/src/Normalizer/DocumentRootNormalizer.php b/src/Normalizer/DocumentRootNormalizer.php index e378931..d87cd78 100644 --- a/src/Normalizer/DocumentRootNormalizer.php +++ b/src/Normalizer/DocumentRootNormalizer.php @@ -9,13 +9,14 @@ use Drupal\jsonapi\Resource\DocumentWrapperInterface; use Drupal\jsonapi\LinkManager\LinkManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; /** * Class DocumentRootNormalizer. * * @package Drupal\jsonapi\Normalizer */ -class DocumentRootNormalizer extends NormalizerBase implements DenormalizerInterface, DocumentRootNormalizerInterface { +class DocumentRootNormalizer extends NormalizerBase implements DenormalizerInterface, NormalizerInterface, DocumentRootNormalizerInterface { /** * The interface or class that this Normalizer supports. diff --git a/src/Normalizer/DocumentRootNormalizerInterface.php b/src/Normalizer/DocumentRootNormalizerInterface.php index c0221fe..1efd612 100644 --- a/src/Normalizer/DocumentRootNormalizerInterface.php +++ b/src/Normalizer/DocumentRootNormalizerInterface.php @@ -2,10 +2,20 @@ namespace Drupal\jsonapi\Normalizer; +use Drupal\Core\Field\EntityReferenceFieldItemListInterface; +use Drupal\jsonapi\EntityCollectionInterface; /** * Class DocumentRootNormalizerInterface. * * @package Drupal\jsonapi\Normalizer */ -interface DocumentRootNormalizerInterface {} +interface DocumentRootNormalizerInterface { + /** + * Build the normalizer value. + * + * @return \Drupal\jsonapi\Normalizer\Value\ContentEntityNormalizerValueInterface + * The normalizer value. + */ + public function buildNormalizerValue($data, $format = NULL, array $context = array()); +} diff --git a/src/Normalizer/EntityReferenceFieldNormalizer.php b/src/Normalizer/EntityReferenceFieldNormalizer.php index 757cb90..0300d8d 100644 --- a/src/Normalizer/EntityReferenceFieldNormalizer.php +++ b/src/Normalizer/EntityReferenceFieldNormalizer.php @@ -6,9 +6,12 @@ use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Field\EntityReferenceFieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; +use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Field\TypedData\FieldItemDataDefinition; use Drupal\jsonapi\Configuration\ResourceManagerInterface; +use Drupal\jsonapi\EntityCollection; use Drupal\jsonapi\LinkManager\LinkManagerInterface; +use Drupal\jsonapi\Relationship; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -65,36 +68,22 @@ class EntityReferenceFieldNormalizer extends FieldNormalizer implements Denormal } /** - * Helper function to normalize field items. - * - * @param \Drupal\Core\Field\FieldItemListInterface $field - * The field object. - * @param string $format - * The format. - * @param array $context - * The context array. - * - * @return array - * The array of normalized field items. + * {@inheritdoc} */ - protected function normalizeFieldItems(FieldItemListInterface $field, $format, $context) { - $normalizer_items = array(); - if (!$field->isEmpty()) { - foreach ($field as $field_item) { - $normalizer_items[] = $this->serializer->normalize($field_item, $format, $context); - } - } + public function normalize($field, $format = NULL, array $context = array()) { + /* @var $field \Drupal\Core\Field\FieldItemListInterface */ + // Build the relationship object based on the Entity Reference and normalize + // that object instead. + $main_property = $field->getItemDefinition()->getMainPropertyName(); $definition = $field->getFieldDefinition(); $cardinality = $definition ->getFieldStorageDefinition() ->getCardinality(); - $link_context = [ - 'host_entity_id' => $field->getEntity()->id(), - 'field_name' => $definition->getName(), - 'link_manager' => $this->linkManager, - 'resource_config' => $context['resource_config'], - ]; - return new Value\EntityReferenceNormalizerValue($normalizer_items, $cardinality, $link_context); + $entity_collection = new EntityCollection(array_map(function ($item) { + return $item->get('entity')->getValue(); + }, (array) $field->getIterator())); + $relationship = new Relationship($this->resourceManager, $field->getName(), $cardinality, $entity_collection, $field->getEntity(), $main_property); + return $this->serializer->normalize($relationship, $format, $context); } /** diff --git a/src/Normalizer/EntityReferenceItemNormalizer.php b/src/Normalizer/EntityReferenceItemNormalizer.php deleted file mode 100644 index 36cac3a..0000000 --- a/src/Normalizer/EntityReferenceItemNormalizer.php +++ /dev/null @@ -1,116 +0,0 @@ -resourceManager = $resource_manager; - } - - /** - * {@inheritdoc} - */ - public function normalize($field_item, $format = NULL, array $context = array()) { - /* @var $field_item \Drupal\Core\Field\FieldItemInterface */ - $target_entity = $field_item->get('entity')->getValue(); - $values = $field_item->toArray(); - $main_property = $field_item->mainPropertyName(); - $values = [$main_property => $values[$main_property]]; - if (isset($context['langcode'])) { - $values['lang'] = $context['langcode']; - } - $normalizer_value = new Value\EntityReferenceItemNormalizerValue( - $values, - $this->resourceManager - ->get($target_entity->getEntityTypeId(), $target_entity->bundle()) - ->getTypeName() - ); - - // TODO Only include if the target entity type has the resource enabled. - if (!empty($context['include']) && in_array($field_item->getParent() - ->getName(), $context['include']) - ) { - $context = $this->buildSubContext($context, $target_entity, $field_item->getParent() - ->getName()); - $entity_normalizer = $this->container->get('serializer.normalizer.document_root.jsonapi'); - $normalizer_value->setInclude($entity_normalizer->buildNormalizerValue($target_entity, $format, $context)); - } - return $normalizer_value; - } - - /** - * Builds the sub-context for the relationship include. - * - * @param array $context - * The serialization context. - * @param \Drupal\Core\Entity\EntityInterface $entity - * The related entity. - * @param string $host_field_name - * The name of the field reference. - * - * @return array - * The modified new context. - */ - protected function buildSubContext($context, EntityInterface $entity, $host_field_name) { - // Swap out the context for the context of the referenced resource. - $context['resource_config'] = $this->resourceManager - ->get($entity->getEntityTypeId(), $entity->bundle()); - // Since we're going one level down the only includes we need are the ones - // that apply to this level as well. - $include_candidates = array_filter($context['include'], function ($include) use ($host_field_name) { - return strpos($include, $host_field_name . '.') === 0; - }); - $context['include'] = array_map(function ($include) use ($host_field_name) { - return str_replace($host_field_name . '.', '', $include); - }, $include_candidates); - return $context; - } - - /** - * {@inheritdoc} - */ - public function getUuid($data) { - if (isset($data['uuid'])) { - return NULL; - } - $uuid = $data['uuid']; - // The value may be a nested array like $uuid[0]['value']. - if (is_array($uuid) && isset($uuid[0]['value'])) { - $uuid = $uuid[0]['value']; - } - return $uuid; - } - -} diff --git a/src/Normalizer/FieldItemNormalizer.php b/src/Normalizer/FieldItemNormalizer.php index 0a504a4..d4ac599 100644 --- a/src/Normalizer/FieldItemNormalizer.php +++ b/src/Normalizer/FieldItemNormalizer.php @@ -27,8 +27,8 @@ class FieldItemNormalizer extends NormalizerBase { /** * {@inheritdoc} */ - public function normalize($field_item, $format = NULL, array $context = array()) { - $values = $field_item->toArray(); + public function normalize($relationship_item, $format = NULL, array $context = array()) { + $values = $relationship_item->toArray(); if (isset($context['langcode'])) { $values['lang'] = $context['langcode']; } diff --git a/src/Normalizer/FieldNormalizer.php b/src/Normalizer/FieldNormalizer.php index 4b1c292..6bb59db 100644 --- a/src/Normalizer/FieldNormalizer.php +++ b/src/Normalizer/FieldNormalizer.php @@ -54,7 +54,6 @@ class FieldNormalizer extends NormalizerBase { */ protected function normalizeFieldItems(FieldItemListInterface $field, $format, $context) { $normalizer_items = array(); - $includes = []; if (!$field->isEmpty()) { foreach ($field as $field_item) { $normalizer_items[] = $this->serializer->normalize($field_item, $format, $context); diff --git a/src/Normalizer/RelationshipItemNormalizer.php b/src/Normalizer/RelationshipItemNormalizer.php new file mode 100644 index 0000000..1127699 --- /dev/null +++ b/src/Normalizer/RelationshipItemNormalizer.php @@ -0,0 +1,125 @@ +resourceManager = $resource_manager; + $this->documentRootNormalizer = $document_root_normalizer; + } + + /** + * {@inheritdoc} + */ + public function normalize($relationship_item, $format = NULL, array $context = array()) { + /* @var $relationship_item \Drupal\jsonapi\RelationshipItemInterface */ + // TODO: We are always loading the referenced entity. Even if it is not + // going to be included. That may be a performance issue. We do it because + // we need to know the entity type and bundle to load the resource config to + // get the type for the relationship item. We need a better way of finding + // about this. + $target_entity = $relationship_item->getTargetEntity(); + $values = $relationship_item->getValue(); + if (isset($context['langcode'])) { + $values['lang'] = $context['langcode']; + } + $normalizer_value = new Value\RelationshipItemNormalizerValue( + $values, + $relationship_item->getTargetResourceConfig() + ); + + $host_field_name = $relationship_item->getParent()->getPropertyName(); + // TODO Only include if the target entity type has the resource enabled. + if (!empty($context['include']) && in_array($host_field_name, $context['include'])) { + $context = $this->buildSubContext($context, $target_entity, $host_field_name); + $included_normalizer_value = $this->documentRootNormalizer->buildNormalizerValue($target_entity, $format, $context); + $normalizer_value->setInclude($included_normalizer_value); + } + return $normalizer_value; + } + + /** + * Builds the sub-context for the relationship include. + * + * @param array $context + * The serialization context. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The related entity. + * @param string $host_field_name + * The name of the field reference. + * + * @return array + * The modified new context. + */ + protected function buildSubContext($context, EntityInterface $entity, $host_field_name) { + // Swap out the context for the context of the referenced resource. + $context['resource_config'] = $this->resourceManager + ->get($entity->getEntityTypeId(), $entity->bundle()); + // Since we're going one level down the only includes we need are the ones + // that apply to this level as well. + $include_candidates = array_filter($context['include'], function ($include) use ($host_field_name) { + return strpos($include, $host_field_name . '.') === 0; + }); + $context['include'] = array_map(function ($include) use ($host_field_name) { + return str_replace($host_field_name . '.', '', $include); + }, $include_candidates); + return $context; + } + + /** + * {@inheritdoc} + */ + public function getUuid($data) { + if (isset($data['uuid'])) { + return NULL; + } + $uuid = $data['uuid']; + // The value may be a nested array like $uuid[0]['value']. + if (is_array($uuid) && isset($uuid[0]['value'])) { + $uuid = $uuid[0]['value']; + } + return $uuid; + } + +} diff --git a/src/Normalizer/RelationshipNormalizer.php b/src/Normalizer/RelationshipNormalizer.php new file mode 100644 index 0000000..a09e869 --- /dev/null +++ b/src/Normalizer/RelationshipNormalizer.php @@ -0,0 +1,160 @@ +resourceManager = $resource_manager; + $this->documentRootNormalizer = $document_root_normalizer; + $this->linkManager = $link_manager; + } + +// /** +// * {@inheritdoc} +// */ +// public function normalize($relationship, $format = NULL, array $context = array()) { +// /* @var \Drupal\jsonapi\Relationship $relationship */ +// $values = $relationship->getValues(); +// $resource_config = $relationship->getTargetResourceConfig(); +// $type = $resource_config->getTypeName(); +// $output = array_map(function ($item) use ($type, $relationship, $context, $format) { +// $normalizer_value = new Value\RelationshipItemNormalizerValue($item, $type); +// if (!empty($context['include']) && in_array($relationship->getPropertyName(), $context['include'])) { +// $target_entity = $relationship->getHostEntity(); +// $context = $this->buildSubContext($context, $target_entity, $relationship->getPropertyName()); +// $included_normalizer_value = $this->documentRootNormalizer->buildNormalizerValue($target_entity, $format, $context); +// $normalizer_value->setInclude($included_normalizer_value); +// return $normalizer_value; +// } +// return $normalizer_value; +// }, $values); +// return new Value\RelationshipNormalizerValue($output, $relationship->getCardinality(), [ +// 'host_entity_id' => $relationship->getHostEntity()->id(), +// 'resource_config' => $context['resource_config'], +// 'field_name' => 'resource-bundle', +// 'link_manager' => $this->linkManager, +// ]); +// } + + /** + * {@inheritdoc} + */ + public function denormalize($data, $class, $format = NULL, array $context = array()) { + throw new UnexpectedValueException('Denormalization not implemented for JSON API'); + } + + /** + * Helper function to normalize field items. + * + * @param \Drupal\jsonapi\RelationshipInterface $relationship + * The field object. + * @param string $format + * The format. + * @param array $context + * The context array. + * + * @return array + * The array of normalized field items. + */ + public function normalize($relationship, $format = NULL, array $context = array()) { + /* @var \Drupal\jsonapi\RelationshipInterface $relationship */ + $normalizer_items = array(); + foreach ($relationship->getItems() as $relationship_item) { + $normalizer_items[] = $this->serializer->normalize($relationship_item, $format, $context); + } + $cardinality = $relationship->getCardinality(); + $link_context = [ + 'host_entity_id' => $relationship->getHostEntity()->id(), + 'field_name' => $relationship->getPropertyName(), + 'link_manager' => $this->linkManager, + 'resource_config' => $context['resource_config'], + ]; + return new Value\RelationshipNormalizerValue($normalizer_items, $cardinality, $link_context); + } + + /** + * Builds the sub-context for the relationship include. + * + * @param array $context + * The serialization context. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The related entity. + * @param string $host_field_name + * The name of the field reference. + * + * @return array + * The modified new context. + * + * @see EntityReferenceItemNormalizer::buildSubContext() + * @todo This is duplicated code from the reference item. Reuse code instead. + */ + protected function buildSubContext($context, EntityInterface $entity, $host_field_name) { + // Swap out the context for the context of the referenced resource. + $context['resource_config'] = $this->resourceManager + ->get($entity->getEntityTypeId(), $entity->bundle()); + // Since we're going one level down the only includes we need are the ones + // that apply to this level as well. + $include_candidates = array_filter($context['include'], function ($include) use ($host_field_name) { + return strpos($include, $host_field_name . '.') === 0; + }); + $context['include'] = array_map(function ($include) use ($host_field_name) { + return str_replace($host_field_name . '.', '', $include); + }, $include_candidates); + return $context; + } + +} \ No newline at end of file diff --git a/src/Normalizer/Value/EntityReferenceItemNormalizerValue.php b/src/Normalizer/Value/EntityReferenceItemNormalizerValue.php deleted file mode 100644 index 45e06a6..0000000 --- a/src/Normalizer/Value/EntityReferenceItemNormalizerValue.php +++ /dev/null @@ -1,52 +0,0 @@ -resource = $resource; - } - - /** - * {@inheritdoc} - */ - public function rasterizeValue() { - if (!$value = parent::rasterizeValue()) { - return $value; - } - return [ - 'type' => $this->resource, - 'id' => $value, - ]; - } - - /** - * {@inheritdoc} - */ - public function setResource($resource) { - $this->resource = $resource; - } - -} diff --git a/src/Normalizer/Value/EntityReferenceItemNormalizerValueInterface.php b/src/Normalizer/Value/EntityReferenceItemNormalizerValueInterface.php deleted file mode 100644 index 1961c83..0000000 --- a/src/Normalizer/Value/EntityReferenceItemNormalizerValueInterface.php +++ /dev/null @@ -1,20 +0,0 @@ -hostEntityId = $link_context['host_entity_id']; - $this->fieldName = $link_context['field_name']; - $this->linkManager = $link_context['link_manager']; - $this->resourceConfig = $link_context['resource_config']; - array_walk($values, function ($field_item_value) { - if (!$field_item_value instanceof EntityReferenceItemNormalizerValueInterface) { - throw new \RuntimeException(sprintf('Unexpected normalizer item value for this %s.', get_called_class())); - } - }); - parent::__construct($values, $cardinality); - } - - /** - * {@inheritdoc} - */ - public function rasterizeValue() { - if (!$value = parent::rasterizeValue()) { - // According to the JSON API specs empty relationships are either NULL or - // an empty array. - return $this->cardinality == 1 ? ['data' => NULL] : ['data' => []]; - } - // Generate the links for the relationship. - $route_parameters = ['related' => $this->fieldName]; - return [ - 'data' => $value, - 'links' => [ - 'self' => $this->linkManager->getEntityLink( - $this->hostEntityId, - $this->resourceConfig, - $route_parameters, - 'relationship' - ), - 'related' => $this->linkManager->getEntityLink( - $this->hostEntityId, - $this->resourceConfig, - $route_parameters, - 'related' - ), - ], - ]; - } - -} diff --git a/src/Normalizer/Value/EntityReferenceNormalizerValueInterface.php b/src/Normalizer/Value/EntityReferenceNormalizerValueInterface.php deleted file mode 100644 index cb68951..0000000 --- a/src/Normalizer/Value/EntityReferenceNormalizerValueInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -include = $include; } diff --git a/src/Normalizer/Value/FieldItemNormalizerValueInterface.php b/src/Normalizer/Value/FieldItemNormalizerValueInterface.php index 32f33cd..efe1966 100644 --- a/src/Normalizer/Value/FieldItemNormalizerValueInterface.php +++ b/src/Normalizer/Value/FieldItemNormalizerValueInterface.php @@ -12,10 +12,10 @@ interface FieldItemNormalizerValueInterface extends ValueExtractorInterface { /** * Add an include. * - * @param DocumentRootNormalizerValueInterface $include + * @param ValueExtractorInterface $include * The included entity. */ - public function setInclude(DocumentRootNormalizerValueInterface $include); + public function setInclude(ValueExtractorInterface $include); /** * Gets the include. diff --git a/src/Normalizer/Value/RelationshipItemNormalizerValue.php b/src/Normalizer/Value/RelationshipItemNormalizerValue.php new file mode 100644 index 0000000..a437ff7 --- /dev/null +++ b/src/Normalizer/Value/RelationshipItemNormalizerValue.php @@ -0,0 +1,52 @@ +resource = $resource; + } + + /** + * {@inheritdoc} + */ + public function rasterizeValue() { + if (!$value = parent::rasterizeValue()) { + return $value; + } + return [ + 'type' => $this->resource->getTypeName(), + 'id' => $value, + ]; + } + + /** + * {@inheritdoc} + */ + public function setResource($resource) { + $this->resource = $resource; + } + +} diff --git a/src/Normalizer/Value/RelationshipItemNormalizerValueInterface.php b/src/Normalizer/Value/RelationshipItemNormalizerValueInterface.php new file mode 100644 index 0000000..1655e66 --- /dev/null +++ b/src/Normalizer/Value/RelationshipItemNormalizerValueInterface.php @@ -0,0 +1,20 @@ +hostEntityId = $link_context['host_entity_id']; + $this->fieldName = $link_context['field_name']; + $this->linkManager = $link_context['link_manager']; + $this->resourceConfig = $link_context['resource_config']; + array_walk($values, function ($field_item_value) { + if (!$field_item_value instanceof RelationshipItemNormalizerValueInterface) { + throw new \RuntimeException(sprintf('Unexpected normalizer item value for this %s.', get_called_class())); + } + }); + parent::__construct($values, $cardinality); + } + + /** + * {@inheritdoc} + */ + public function rasterizeValue() { + if (!$value = parent::rasterizeValue()) { + // According to the JSON API specs empty relationships are either NULL or + // an empty array. + return $this->cardinality == 1 ? ['data' => NULL] : ['data' => []]; + } + // Generate the links for the relationship. + $route_parameters = ['related' => $this->fieldName]; + return [ + 'data' => $value, + 'links' => [ + 'self' => $this->linkManager->getEntityLink( + $this->hostEntityId, + $this->resourceConfig, + $route_parameters, + 'relationship' + ), + 'related' => $this->linkManager->getEntityLink( + $this->hostEntityId, + $this->resourceConfig, + $route_parameters, + 'related' + ), + ], + ]; + } + +} diff --git a/src/Normalizer/Value/RelationshipNormalizerValueInterface.php b/src/Normalizer/Value/RelationshipNormalizerValueInterface.php new file mode 100644 index 0000000..da0908a --- /dev/null +++ b/src/Normalizer/Value/RelationshipNormalizerValueInterface.php @@ -0,0 +1,10 @@ +resourceManager = $resource_manager; + $this->propertyName = $field_name; + $this->cardinality = $cardinality; + $this->hostEntity = $host_entity; + $this->items = []; + foreach ($entities as $entity) { + $this->items[] = new RelationshipItem( + $resource_manager, + $entity, + $this, + $target_key + ); + } + } + + /** + * {@inheritdoc} + */ + public function getCardinality() { + return $this->cardinality; + } + + /** + * {@inheritdoc} + */ + public function getHostEntity() { + return $this->hostEntity; + } + + /** + * {@inheritdoc} + */ + public function setHostEntity(EntityInterface $hostEntity) { + $this->hostEntity = $hostEntity; + } + + /** + * {@inheritdoc} + */ + public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) { + // Hard coded to TRUE. Revisit this if we need more control over this. + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function getPropertyName() { + return $this->propertyName; + } + + /** + * {@inheritdoc} + */ + public function getItems() { + return $this->items; + } + +} diff --git a/src/RelationshipInterface.php b/src/RelationshipInterface.php new file mode 100644 index 0000000..8789b2c --- /dev/null +++ b/src/RelationshipInterface.php @@ -0,0 +1,49 @@ +targetResourceConfig = $resource_manager->get( + $target_entity->getEntityTypeId(), + $target_entity->bundle() + ); + $this->targetKey = $target_key; + $this->targetEntity = $target_entity; + $this->parent = $parent; + } + + /** + * {@inheritdoc} + */ + public function getTargetEntity() { + return $this->targetEntity; + } + + /** + * {@inheritdoc} + */ + public function getTargetResourceConfig() { + return $this->targetResourceConfig; + } + + /** + * {@inheritdoc} + */ + public function getValue() { + return [$this->targetKey => $this->getTargetEntity()->id()]; + } + + /** + * {@inheritdoc} + */ + public function getParent() { + return $this->parent; + } + +} \ No newline at end of file diff --git a/src/RelationshipItemInterface.php b/src/RelationshipItemInterface.php new file mode 100644 index 0000000..e910a70 --- /dev/null +++ b/src/RelationshipItemInterface.php @@ -0,0 +1,37 @@ +get('include')->willReturn('uid'); $query->getIterator()->willReturn(new \ArrayIterator()); $request->query = $query->reveal(); - $route = $this->prophesize(Route::class); - $route->getPath()->willReturn('/node/article/{node}'); - $route->getRequirement('_entity_type')->willReturn('node'); - $route->getRequirement('_bundle')->willReturn('article'); - $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route->reveal()); + $route = new Route('/node/article/{node}', [], [ + '_entity_type' => 'node', + '_bundle' => 'article', + ]); + $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route); $document_wrapper = $this->prophesize(DocumentWrapper::class); $document_wrapper->getData()->willReturn($this->node); $resource_config = $this->prophesize(ResourceConfigInterface::CLASS); $resource_config->getTypeName()->willReturn('node--article'); + $resource_config->getBundleId()->willReturn('article'); // Make sure the route contains the entity type and bundle. $current_context = $this->container->get('jsonapi.current_context'); - $current_context->setCurrentRoute($route->reveal()); + $current_context->setCurrentRoute($route); $this->container->set('jsonapi.current_context', $current_context); $this->container->get('serializer'); @@ -185,22 +186,20 @@ class DocumentRootNormalizerTest extends KernelTestBase { $request = $this->prophesize(Request::class); $query = $this->prophesize(ParameterBag::class); $query->get('fields')->willReturn([]); - $query->get('include')->willReturn(NULL); + $query->get('include')->willReturn('bundle-resource'); $query->getIterator()->willReturn(new \ArrayIterator()); $request->query = $query->reveal(); - $route = $this->prophesize(Route::class); - $route->getPath()->willReturn('/node/{node}'); - $route->getRequirement('_entity_type')->willReturn('node'); - $route->getRequirement('_bundle')->willReturn(NULL); - $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route->reveal()); + $route = new Route('/node/{node}', [], ['_entity_type' => 'node']); + $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route); $document_wrapper = $this->prophesize(DocumentWrapper::class); $document_wrapper->getData()->willReturn($this->node); $resource_config = $this->prophesize(ResourceConfigInterface::CLASS); $resource_config->getTypeName()->willReturn('node'); + $resource_config->getBundleId()->willReturn(NULL); // Make sure the route contains the entity type. $current_context = $this->container->get('jsonapi.current_context'); - $current_context->setCurrentRoute($route->reveal()); + $current_context->setCurrentRoute($route); $this->container->set('jsonapi.current_context', $current_context); $this->container->get('serializer'); @@ -232,6 +231,18 @@ class DocumentRootNormalizerTest extends KernelTestBase { $this->assertTrue(!isset($normalized['data']['attributes']['body'])); $this->assertTrue(!isset($normalized['data']['attributes']['field_tags'])); $this->assertSame('node', $normalized['data']['type']); + $this->assertSame([ + 'data' => [ + 'type' => 'node--article', + 'id' => '1', + ], + 'links' => [ + 'self' => 'dummy_entity_link', + 'related' => 'dummy_entity_link', + ], + ], $normalized['data']['relationships']['bundle-resource']); + $this->assertEquals('node--article', $normalized['included'][0]['data']['type']); + $this->assertEquals('1', $normalized['included'][0]['data']['id']); } /** @@ -246,19 +257,20 @@ class DocumentRootNormalizerTest extends KernelTestBase { $query->get('include')->willReturn(NULL); $query->getIterator()->willReturn(new \ArrayIterator()); $request->query = $query->reveal(); - $route = $this->prophesize(Route::class); - $route->getPath()->willReturn('/node_type/node_type/{node_type}'); - $route->getRequirement('_entity_type')->willReturn('node'); - $route->getRequirement('_bundle')->willReturn('article'); - $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route->reveal()); + $route = new Route('/node_type/node_type/{node_type}', [], [ + '_entity_type' => 'node_type', + '_bundle' => 'node_type', + ]); + $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route); $document_wrapper = $this->prophesize(DocumentWrapper::class); $document_wrapper->getData()->willReturn($this->nodeType); $resource_config = $this->prophesize(ResourceConfigInterface::CLASS); $resource_config->getTypeName()->willReturn('node_type--node_type'); + $resource_config->getBundleId()->willReturn('node_type'); // Make sure the route contains the entity type and bundle. $current_context = $this->container->get('jsonapi.current_context'); - $current_context->setCurrentRoute($route->reveal()); + $current_context->setCurrentRoute($route); $this->container->set('jsonapi.current_context', $current_context); $this->container->get('serializer'); @@ -277,6 +289,58 @@ class DocumentRootNormalizerTest extends KernelTestBase { } /** + * @covers ::normalize + */ + public function testNormalizeConfigNoBundle() { + $request = $this->prophesize(Request::class); + $query = $this->prophesize(ParameterBag::class); + $query->get('fields')->willReturn([ + 'node_type' => 'uuid,display_submitted', + ]); + $query->get('include')->willReturn('bundle-resource'); + $query->getIterator()->willReturn(new \ArrayIterator()); + $request->query = $query->reveal(); + $route = new Route('/node_type/{node_type}', [], ['_entity_type' => 'node']); + $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route); + $document_wrapper = $this->prophesize(DocumentWrapper::class); + $document_wrapper->getData()->willReturn($this->nodeType); + $resource_config = $this->prophesize(ResourceConfigInterface::CLASS); + $resource_config->getTypeName()->willReturn('node_type'); + $resource_config->getBundleId()->willReturn(NULL); + + // Make sure the route contains the entity type and bundle. + $current_context = $this->container->get('jsonapi.current_context'); + $current_context->setCurrentRoute($route); + + $this->container->set('jsonapi.current_context', $current_context); + $this->container->get('serializer'); + $normalized = $this + ->container + ->get('serializer.normalizer.document_root.jsonapi') + ->normalize($document_wrapper->reveal(), 'api_json', [ + 'request' => $request->reveal(), + 'resource_config' => $resource_config->reveal(), + ]); + $this->assertTrue(empty($normalized['data']['attributes']['type'])); + $this->assertTrue(!empty($normalized['data']['attributes']['uuid'])); + $this->assertSame($normalized['data']['attributes']['display_submitted'], TRUE); + $this->assertSame($normalized['data']['id'], 'article'); + $this->assertSame($normalized['data']['type'], 'node_type'); + $this->assertSame([ + 'data' => [ + 'type' => 'node_type--node_type', + 'id' => 'article', + ], + 'links' => [ + 'self' => 'dummy_entity_link', + 'related' => 'dummy_entity_link', + ], + ], $normalized['data']['relationships']['bundle-resource']); + $this->assertEquals('node_type--node_type', $normalized['included'][0]['data']['type']); + $this->assertEquals('article', $normalized['included'][0]['data']['id']); + } + + /** * Try to POST a node and check if it exists afterwards. * * @covers ::denormalize @@ -284,12 +348,11 @@ class DocumentRootNormalizerTest extends KernelTestBase { public function testDenormalize() { $payload = '{"type":"article", "data":{"attributes":{"title":"Testing article"}}}'; $request = $this->prophesize(Request::class); - $route = $this->prophesize(Route::class); - $route->getPath()->willReturn('/node/article'); - $route->getRequirement('_entity_type')->willReturn('node'); - $route->getRequirement('_bundle')->willReturn('article'); - $route->getDefault('_on_relationship')->willReturn(NULL); - $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route->reveal()); + $route = new Route('/node/article', ['_on_relationship' => NULL], [ + '_entity_type' => 'node', + '_bundle' => 'article', + ]); + $request->get(RouteObjectInterface::ROUTE_OBJECT)->willReturn($route); $resource_config = $this->prophesize(ResourceConfigInterface::CLASS); $resource_config->getTypeName()->willReturn('node--article'); $resource_config->getEntityTypeId()->willReturn('node'); diff --git a/tests/src/Unit/Normalizer/Value/ContentEntityNormalizerValueTest.php b/tests/src/Unit/Normalizer/Value/ContentEntityNormalizerValueTest.php index b61836e..88d9ceb 100644 --- a/tests/src/Unit/Normalizer/Value/ContentEntityNormalizerValueTest.php +++ b/tests/src/Unit/Normalizer/Value/ContentEntityNormalizerValueTest.php @@ -10,7 +10,7 @@ use Drupal\jsonapi\LinkManager\LinkManagerInterface; use Drupal\jsonapi\Normalizer\Value\ContentEntityNormalizerValue; use Drupal\jsonapi\Normalizer\Value\ContentEntityNormalizerValueInterface; use Drupal\jsonapi\Normalizer\Value\DocumentRootNormalizerValueInterface; -use Drupal\jsonapi\Normalizer\Value\EntityReferenceNormalizerValueInterface; +use Drupal\jsonapi\Normalizer\Value\RelationshipNormalizerValueInterface; use Drupal\jsonapi\Normalizer\Value\FieldNormalizerValueInterface; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; @@ -42,7 +42,7 @@ class ContentEntityNormalizerValueTest extends UnitTestCase { $field1->getIncludes()->willReturn([]); $field1->getPropertyType()->willReturn('attributes'); $field1->rasterizeValue()->willReturn('dummy_title'); - $field2 = $this->prophesize(EntityReferenceNormalizerValueInterface::class); + $field2 = $this->prophesize(RelationshipNormalizerValueInterface::class); $field2->getPropertyType()->willReturn('relationships'); $field2->rasterizeValue()->willReturn(['data' => ['type' => 'node', 'id' => 2]]); $included[] = $this->prophesize(DocumentRootNormalizerValueInterface::class); diff --git a/tests/src/Unit/Normalizer/Value/DocumentRootNormalizerValueTest.php b/tests/src/Unit/Normalizer/Value/DocumentRootNormalizerValueTest.php index 687f0a4..bb29ae7 100644 --- a/tests/src/Unit/Normalizer/Value/DocumentRootNormalizerValueTest.php +++ b/tests/src/Unit/Normalizer/Value/DocumentRootNormalizerValueTest.php @@ -8,7 +8,7 @@ use Drupal\jsonapi\Configuration\ResourceConfigInterface; use Drupal\jsonapi\LinkManager\LinkManagerInterface; use Drupal\jsonapi\Normalizer\Value\DocumentRootNormalizerValue; use Drupal\jsonapi\Normalizer\Value\DocumentRootNormalizerValueInterface; -use Drupal\jsonapi\Normalizer\Value\EntityReferenceNormalizerValueInterface; +use Drupal\jsonapi\Normalizer\Value\RelationshipNormalizerValueInterface; use Drupal\jsonapi\Normalizer\Value\FieldNormalizerValueInterface; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; @@ -40,7 +40,7 @@ class DocumentRootNormalizerValueTest extends UnitTestCase{ $field1->getIncludes()->willReturn([]); $field1->getPropertyType()->willReturn('attributes'); $field1->rasterizeValue()->willReturn('dummy_title'); - $field2 = $this->prophesize(EntityReferenceNormalizerValueInterface::class); + $field2 = $this->prophesize(RelationshipNormalizerValueInterface::class); $field2->getPropertyType()->willReturn('relationships'); $field2->rasterizeValue()->willReturn(['data' => ['type' => 'node', 'id' => 2]]); $included[] = $this->prophesize(DocumentRootNormalizerValue::class); diff --git a/tests/src/Unit/Normalizer/Value/EntityReferenceItemNormalizerValueTest.php b/tests/src/Unit/Normalizer/Value/EntityReferenceItemNormalizerValueTest.php deleted file mode 100644 index 843adcd..0000000 --- a/tests/src/Unit/Normalizer/Value/EntityReferenceItemNormalizerValueTest.php +++ /dev/null @@ -1,39 +0,0 @@ -assertEquals($expected, $object->rasterizeValue()); - } - - /** - * Data provider for testRasterizeValue. - */ - public function rasterizeValueProvider() { - return [ - [['target_id' => 1], 'node', ['type' => 'node', 'id' => 1]], - [['value' => 1], 'node', ['type' => 'node', 'id' => 1]], - [[1], 'node', ['type' => 'node', 'id' => 1]], - [[], 'node', []], - [[NULL], 'node', NULL], - ]; - } -} diff --git a/tests/src/Unit/Normalizer/Value/EntityReferenceNormalizerValueTest.php b/tests/src/Unit/Normalizer/Value/EntityReferenceNormalizerValueTest.php deleted file mode 100644 index 2a9b163..0000000 --- a/tests/src/Unit/Normalizer/Value/EntityReferenceNormalizerValueTest.php +++ /dev/null @@ -1,97 +0,0 @@ -prophesize(LinkManagerInterface::class); - $link_manager - ->getEntityLink(Argument::any(), Argument::any(), Argument::type('array'), Argument::type('string')) - ->willReturn('dummy_entity_link'); - $object = new EntityReferenceNormalizerValue($values, $cardinality, [ - 'link_manager' => $link_manager->reveal(), - 'host_entity_id' => 'lorem', - 'resource_config' => $this->prophesize(ResourceConfigInterface::class)->reveal(), - 'field_name' => 'ipsum', - ]); - $this->assertEquals($expected, $object->rasterizeValue()); - } - - /** - * Data provider fortestRasterizeValue. - */ - public function rasterizeValueProvider() { - $uid_raw = 1; - $uid1 = $this->prophesize(EntityReferenceItemNormalizerValue::class); - $uid1->rasterizeValue()->willReturn(['type' => 'user', 'id' => $uid_raw++]); - $uid1->getInclude()->willReturn(NULL); - $uid2 = $this->prophesize(EntityReferenceItemNormalizerValue::class); - $uid2->rasterizeValue()->willReturn(['type' => 'user', 'id' => $uid_raw]); - $uid2->getInclude()->willReturn(NULL); - $links = [ - 'self' => 'dummy_entity_link', - 'related' => 'dummy_entity_link', - ]; - return [ - [[$uid1->reveal()], 1, [ - 'data' => ['type' => 'user', 'id' => 1], - 'links' => $links, - ]], - [ - [$uid1->reveal(), $uid2->reveal()], 2, [ - 'data' => [ - ['type' => 'user', 'id' => 1], - ['type' => 'user', 'id' => 2], - ], - 'links' => $links, - ], - ], - ]; - } - - /** - * @covers ::rasterizeValue - * - * @expectedException \RuntimeException - */ - public function testRasterizeValueFails() { - $uid1 = $this->prophesize(FieldItemNormalizerValue::class); - $uid1->rasterizeValue()->willReturn(1); - $uid1->getInclude()->willReturn(NULL); - $link_manager = $this->prophesize(LinkManagerInterface::class); - $link_manager - ->getEntityLink(Argument::any(), Argument::any(), Argument::type('array'), Argument::type('string')) - ->willReturn('dummy_entity_link'); - $object = new EntityReferenceNormalizerValue([$uid1->reveal()], 1, [ - 'link_manager' => $link_manager->reveal(), - 'host_entity_id' => 'lorem', - 'resource_config' => $this->prophesize(ResourceConfigInterface::class)->reveal(), - 'field_name' => 'ipsum', - ]); - $object->rasterizeValue(); - // If the exception was not thrown, then the following fails. - $this->assertTrue(FALSE); - } - -} diff --git a/tests/src/Unit/Normalizer/Value/RelationshipItemNormalizerValueTest.php b/tests/src/Unit/Normalizer/Value/RelationshipItemNormalizerValueTest.php new file mode 100644 index 0000000..5cb808c --- /dev/null +++ b/tests/src/Unit/Normalizer/Value/RelationshipItemNormalizerValueTest.php @@ -0,0 +1,42 @@ +prophesize(ResourceConfigInterface::class); + $resource->getTypeName()->willReturn($resource_type); + $object = new RelationshipItemNormalizerValue($values, $resource->reveal()); + $this->assertEquals($expected, $object->rasterizeValue()); + } + + /** + * Data provider for testRasterizeValue. + */ + public function rasterizeValueProvider() { + return [ + [['target_id' => 1], 'node', ['type' => 'node', 'id' => 1]], + [['value' => 1], 'node', ['type' => 'node', 'id' => 1]], + [[1], 'node', ['type' => 'node', 'id' => 1]], + [[], 'node', []], + [[NULL], 'node', NULL], + ]; + } +} diff --git a/tests/src/Unit/Normalizer/Value/RelationshipNormalizerValueTest.php b/tests/src/Unit/Normalizer/Value/RelationshipNormalizerValueTest.php new file mode 100644 index 0000000..506b5b6 --- /dev/null +++ b/tests/src/Unit/Normalizer/Value/RelationshipNormalizerValueTest.php @@ -0,0 +1,97 @@ +prophesize(LinkManagerInterface::class); + $link_manager + ->getEntityLink(Argument::any(), Argument::any(), Argument::type('array'), Argument::type('string')) + ->willReturn('dummy_entity_link'); + $object = new RelationshipNormalizerValue($values, $cardinality, [ + 'link_manager' => $link_manager->reveal(), + 'host_entity_id' => 'lorem', + 'resource_config' => $this->prophesize(ResourceConfigInterface::class)->reveal(), + 'field_name' => 'ipsum', + ]); + $this->assertEquals($expected, $object->rasterizeValue()); + } + + /** + * Data provider fortestRasterizeValue. + */ + public function rasterizeValueProvider() { + $uid_raw = 1; + $uid1 = $this->prophesize(RelationshipItemNormalizerValue::class); + $uid1->rasterizeValue()->willReturn(['type' => 'user', 'id' => $uid_raw++]); + $uid1->getInclude()->willReturn(NULL); + $uid2 = $this->prophesize(RelationshipItemNormalizerValue::class); + $uid2->rasterizeValue()->willReturn(['type' => 'user', 'id' => $uid_raw]); + $uid2->getInclude()->willReturn(NULL); + $links = [ + 'self' => 'dummy_entity_link', + 'related' => 'dummy_entity_link', + ]; + return [ + [[$uid1->reveal()], 1, [ + 'data' => ['type' => 'user', 'id' => 1], + 'links' => $links, + ]], + [ + [$uid1->reveal(), $uid2->reveal()], 2, [ + 'data' => [ + ['type' => 'user', 'id' => 1], + ['type' => 'user', 'id' => 2], + ], + 'links' => $links, + ], + ], + ]; + } + + /** + * @covers ::rasterizeValue + * + * @expectedException \RuntimeException + */ + public function testRasterizeValueFails() { + $uid1 = $this->prophesize(FieldItemNormalizerValue::class); + $uid1->rasterizeValue()->willReturn(1); + $uid1->getInclude()->willReturn(NULL); + $link_manager = $this->prophesize(LinkManagerInterface::class); + $link_manager + ->getEntityLink(Argument::any(), Argument::any(), Argument::type('array'), Argument::type('string')) + ->willReturn('dummy_entity_link'); + $object = new RelationshipNormalizerValue([$uid1->reveal()], 1, [ + 'link_manager' => $link_manager->reveal(), + 'host_entity_id' => 'lorem', + 'resource_config' => $this->prophesize(ResourceConfigInterface::class)->reveal(), + 'field_name' => 'ipsum', + ]); + $object->rasterizeValue(); + // If the exception was not thrown, then the following fails. + $this->assertTrue(FALSE); + } + +}