diff --git a/jsonapi.services.yml b/jsonapi.services.yml index 3147597..89bebe9 100644 --- a/jsonapi.services.yml +++ b/jsonapi.services.yml @@ -69,11 +69,6 @@ services: arguments: ['@entity_type.manager', '@entity_field.manager', '@plugin.manager.field.field_type'] tags: - { name: jsonapi_normalizer_do_not_use_removal_imminent } - serializer.normalizer.entity.label_only.jsonapi: - class: Drupal\jsonapi\Normalizer\LabelOnlyEntityNormalizer - arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository'] - tags: - - { name: jsonapi_normalizer_do_not_use_removal_imminent } serializer.normalizer.config_entity.jsonapi: class: Drupal\jsonapi\Normalizer\ConfigEntityDenormalizer arguments: ['@entity_type.manager', '@entity_field.manager', '@plugin.manager.field.field_type'] diff --git a/src/Controller/EntityResource.php b/src/Controller/EntityResource.php index 217d8ea..12e4690 100644 --- a/src/Controller/EntityResource.php +++ b/src/Controller/EntityResource.php @@ -1042,18 +1042,18 @@ class EntityResource { $entity = $entity_repository->getTranslationFromContext($entity, NULL, ['operation' => 'entity_upcast']); $access = $entity->access('view', NULL, TRUE); $entity->addCacheableDependency($access); + $resource_type = $resource_type_repository->get($entity->getEntityTypeId(), $entity->bundle()); if (!$access->isAllowed()) { $label_access = $entity->access('view label', NULL, TRUE); $entity->addCacheableDependency($label_access); if ($label_access->isAllowed()) { - return new LabelOnlyEntity($entity); + return new LabelOnlyEntity($resource_type, $entity); } else { // Pass an exception to the list of things to normalize. return new EntityAccessDeniedHttpException($entity, $access->orIf($label_access), '/data', 'The current user is not allowed to GET the selected resource.'); } } - $resource_type = $resource_type_repository->get($entity->getEntityTypeId(), $entity->bundle()); return new ResourceObject($resource_type, $entity); } diff --git a/src/IncludeResolver.php b/src/IncludeResolver.php index d290ca4..92f9939 100644 --- a/src/IncludeResolver.php +++ b/src/IncludeResolver.php @@ -41,7 +41,7 @@ class IncludeResolver { * * @param \Drupal\jsonapi\ResourceType\ResourceType $base_resource_type * The base resource type for which includes are to be resolved. - * @param \Drupal\jsonapi\JsonApiResource\ResourceObject|\Drupal\jsonapi\JsonApiResource\EntityCollection $data + * @param \Drupal\jsonapi\JsonApiResource\ResourceIdentifierInterface|\Drupal\jsonapi\JsonApiResource\EntityCollection $data * The resource(s) for which to resolve includes. * @param string $include_parameter * The include query parameter to resolve. @@ -106,7 +106,6 @@ class IncludeResolver { elseif (!$entity instanceof ResourceObject) { continue; } - // @todo: remove this and change IncludeResolver::resolveInternalIncludePath to *not* return internal field names. ResourceObjects make that unnecessary. $public_field_name = $entity->getResourceType()->getPublicName($field_name); // Not all entities in $entity_collection will be of the same bundle and // may not have all of the same fields. Therefore, calling diff --git a/src/JsonApiResource/ResourceIdentifier.php b/src/JsonApiResource/ResourceIdentifier.php index a258000..a4723b9 100644 --- a/src/JsonApiResource/ResourceIdentifier.php +++ b/src/JsonApiResource/ResourceIdentifier.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\EntityReferenceFieldItemListInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\TypedData\DataReferenceDefinitionInterface; +use Drupal\jsonapi\ResourceType\ResourceType; /** * Represents a JSON:API resource identifier object. @@ -23,6 +24,13 @@ class ResourceIdentifier implements ResourceIdentifierInterface { */ protected $resourceTypeName; + /** + * The JSON:API resource type. + * + * @var \Drupal\jsonapi\ResourceType\ResourceType + */ + protected $resourceType; + /** * The resource ID. * @@ -40,18 +48,22 @@ class ResourceIdentifier implements ResourceIdentifierInterface { /** * ResourceIdentifier constructor. * - * @param string $resource_type_name - * The JSON:API resource type name. + * @param \Drupal\jsonapi\ResourceType\ResourceType|string $resource_type + * The JSON:API resource type or a JSON:API resource type name. * @param string $id * The resource ID. * @param array $meta * Any metadata for the ResourceIdentifier. */ - public function __construct($resource_type_name, $id, array $meta = []) { + public function __construct($resource_type, $id, array $meta = []) { + assert(is_string($resource_type) || $resource_type instanceof ResourceType); assert(!isset($meta[static::ARITY_KEY]) || is_int($meta[static::ARITY_KEY]) && $meta[static::ARITY_KEY] >= 0); - $this->resourceTypeName = $resource_type_name; + $this->resourceTypeName = is_string($resource_type) ? $resource_type : $resource_type->getTypeName(); $this->id = $id; $this->meta = $meta; + if (!is_string($resource_type)) { + $this->resourceType = $resource_type; + } } /** @@ -68,9 +80,12 @@ class ResourceIdentifier implements ResourceIdentifierInterface { * {@inheritdoc} */ public function getResourceType() { - /* @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository */ - $resource_type_repository = \Drupal::service('jsonapi.resource_type.repository'); - return $resource_type_repository->getByTypeName($this->getTypeName()); + if (!isset($this->resourceType)) { + /* @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository */ + $resource_type_repository = \Drupal::service('jsonapi.resource_type.repository'); + $this->resourceType = $resource_type_repository->getByTypeName($this->getTypeName()); + } + return $this->resourceType; } /** @@ -277,7 +292,7 @@ class ResourceIdentifier implements ResourceIdentifierInterface { /* @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository */ $resource_type_repository = \Drupal::service('jsonapi.resource_type.repository'); $resource_type = $resource_type_repository->get($entity->getEntityTypeId(), $entity->bundle()); - return new static($resource_type->getTypeName(), $entity->uuid()); + return new static($resource_type, $entity->uuid()); } /** diff --git a/src/JsonApiResource/ResourceIdentifierInterface.php b/src/JsonApiResource/ResourceIdentifierInterface.php index 6c7eae9..a23e0c2 100644 --- a/src/JsonApiResource/ResourceIdentifierInterface.php +++ b/src/JsonApiResource/ResourceIdentifierInterface.php @@ -31,10 +31,10 @@ interface ResourceIdentifierInterface { public function getTypeName(); /** - * Gets the resource identifier's JSON API resource type. + * Gets the resource identifier's JSON:API resource type. * * @return \Drupal\jsonapi\ResourceType\ResourceType - * The JSON API resource type. + * The JSON:API resource type. */ public function getResourceType(); diff --git a/src/JsonApiResource/ResourceIdentifierTrait.php b/src/JsonApiResource/ResourceIdentifierTrait.php index 2c9b796..407f2ad 100644 --- a/src/JsonApiResource/ResourceIdentifierTrait.php +++ b/src/JsonApiResource/ResourceIdentifierTrait.php @@ -19,7 +19,7 @@ trait ResourceIdentifierTrait { protected $resourceIdentifier; /** - * The JSON API resource type of of the identified resource object. + * The JSON:API resource type of of the identified resource object. * * @var \Drupal\jsonapi\ResourceType\ResourceType */ @@ -44,9 +44,7 @@ trait ResourceIdentifierTrait { */ public function getResourceType() { if (!isset($this->resourceType)) { - /* @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository */ - $resource_type_repository = \Drupal::service('jsonapi.resource_type.repository'); - $this->resourceType = $resource_type_repository->getByTypeName($this->getTypeName()); + $this->resourceType = $this->resourceIdentifier->getResourceType(); } return $this->resourceType; } diff --git a/src/JsonApiResource/ResourceObject.php b/src/JsonApiResource/ResourceObject.php index 08a9be2..93dc25d 100644 --- a/src/JsonApiResource/ResourceObject.php +++ b/src/JsonApiResource/ResourceObject.php @@ -11,14 +11,14 @@ use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper; use Drupal\jsonapi\ResourceType\ResourceType; /** - * Represent a JSON API resource object. + * Represent a JSON:API resource object. * - * This value object wraps a Drupal entity so that it can carry a JSON API + * This value object wraps a Drupal entity so that it can carry a JSON:API * resource type object alongside it. * * @internal */ -final class ResourceObject implements CacheableDependencyInterface, ResourceIdentifierInterface { +class ResourceObject implements CacheableDependencyInterface, ResourceIdentifierInterface { use CacheableDependencyTrait; use ResourceIdentifierTrait; @@ -56,20 +56,10 @@ final class ResourceObject implements CacheableDependencyInterface, ResourceIden $this->resourceType = $resource_type; $this->entity = $entity; $this->fields = $this->extractFields($entity); - $this->resourceIdentifier = new ResourceIdentifier($this->resourceType->getTypeName(), $this->entity->uuid()); + $this->resourceIdentifier = new ResourceIdentifier($resource_type, $this->entity->uuid()); $this->setCacheability($entity); } - /** - * Get the resource type of the resource object. - * - * @return \Drupal\jsonapi\ResourceType\ResourceType - * The resource type. - */ - public function getResourceType() { - return $this->resourceType; - } - /** * Whether the resource object has the given field. * diff --git a/src/LabelOnlyEntity.php b/src/LabelOnlyEntity.php index 0d8c73e..e2a1313 100644 --- a/src/LabelOnlyEntity.php +++ b/src/LabelOnlyEntity.php @@ -2,34 +2,16 @@ namespace Drupal\jsonapi; -use Drupal\Core\Cache\CacheableDependencyInterface; -use Drupal\Core\Cache\CacheableDependencyTrait; use Drupal\Core\Entity\EntityInterface; -use Drupal\jsonapi\JsonApiResource\ResourceIdentifier; -use Drupal\jsonapi\JsonApiResource\ResourceIdentifierInterface; -use Drupal\jsonapi\JsonApiResource\ResourceIdentifierTrait; +use Drupal\Core\Entity\FieldableEntityInterface; +use Drupal\jsonapi\JsonApiResource\ResourceObject; /** - * Value object decorating an Entity object; only its label is to be normalized. + * Value object decorating a ResourceObject; only its label is available. * * @internal */ -class LabelOnlyEntity implements CacheableDependencyInterface, ResourceIdentifierInterface { - - use CacheableDependencyTrait; - use ResourceIdentifierTrait; - - /** - * Constructs a LabelOnlyEntity value object. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity for which to only normalize its label. - */ - public function __construct(EntityInterface $entity) { - $this->resourceIdentifier = ResourceIdentifier::fromEntity($entity); - $this->entity = $entity; - $this->setCacheability($entity); - } +final class LabelOnlyEntity extends ResourceObject { /** * Gets the decorated entity. @@ -41,6 +23,19 @@ class LabelOnlyEntity implements CacheableDependencyInterface, ResourceIdentifie return $this->entity; } + /** + * {@inheritdoc} + */ + protected function extractFields(EntityInterface $entity) { + if (!$entity instanceof FieldableEntityInterface) { + return []; + } + $label_field_name = $this->getLabelFieldName(); + return $label_field_name && $this->resourceType->isFieldEnabled($label_field_name) ? + [$this->resourceType->getPublicName($label_field_name) => $entity->get($label_field_name)] + : []; + } + /** * Determines the entity type's (internal) label field name. */ diff --git a/src/Normalizer/ConfigEntityDenormalizer.php b/src/Normalizer/ConfigEntityDenormalizer.php index 4a48185..c54ff87 100644 --- a/src/Normalizer/ConfigEntityDenormalizer.php +++ b/src/Normalizer/ConfigEntityDenormalizer.php @@ -6,7 +6,7 @@ use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\jsonapi\ResourceType\ResourceType; /** - * Converts the Drupal config entity object to a JSON API array structure. + * Converts the Drupal config entity object to a JSON:API array structure. * * @internal */ diff --git a/src/Normalizer/ContentEntityDenormalizer.php b/src/Normalizer/ContentEntityDenormalizer.php index aecb6a2..65209a1 100644 --- a/src/Normalizer/ContentEntityDenormalizer.php +++ b/src/Normalizer/ContentEntityDenormalizer.php @@ -7,7 +7,7 @@ use Drupal\jsonapi\ResourceType\ResourceType; use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; /** - * Converts a JSON API array structure into a Drupal entity object. + * Converts a JSON:API array structure into a Drupal entity object. * * @internal */ diff --git a/src/Normalizer/EntityDenormalizerBase.php b/src/Normalizer/EntityDenormalizerBase.php index 4b48e00..70c000b 100644 --- a/src/Normalizer/EntityDenormalizerBase.php +++ b/src/Normalizer/EntityDenormalizerBase.php @@ -10,7 +10,7 @@ use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; /** - * Converts the Drupal entity object to a JSON API array structure. + * Converts the Drupal entity object to a JSON:API array structure. * * @internal */ @@ -24,7 +24,7 @@ abstract class EntityDenormalizerBase extends NormalizerBase implements Denormal protected $formats = ['api_json']; /** - * The JSON API resource type repository. + * The JSON:API resource type repository. * * @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface */ diff --git a/src/Normalizer/EntityReferenceFieldNormalizer.php b/src/Normalizer/EntityReferenceFieldNormalizer.php index 1e23b4a..b14998c 100644 --- a/src/Normalizer/EntityReferenceFieldNormalizer.php +++ b/src/Normalizer/EntityReferenceFieldNormalizer.php @@ -148,7 +148,7 @@ class EntityReferenceFieldNormalizer extends FieldNormalizer { protected function getTranslatedResourceObject(EntityReferenceItem $item) { // 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 JSON API resource + // we need to know the entity type and bundle to load the JSON:API resource // type for the relationship item and we need to know the UUID, which is not // stored on the entity reference item. We need a better way of finding out // about this. diff --git a/src/Normalizer/LabelOnlyEntityNormalizer.php b/src/Normalizer/LabelOnlyEntityNormalizer.php deleted file mode 100644 index d2d6402..0000000 --- a/src/Normalizer/LabelOnlyEntityNormalizer.php +++ /dev/null @@ -1,94 +0,0 @@ -linkManager = $link_manager; - $this->resourceTypeRepository = $resource_type_repository; - } - - /** - * {@inheritdoc} - */ - public function normalize($label_only_entity, $format = NULL, array $context = []) { - assert($label_only_entity instanceof LabelOnlyEntity); - $entity = $label_only_entity->getEntity(); - $resource_type = $this->resourceTypeRepository->get( - $entity->getEntityTypeId(), - $entity->bundle() - ); - $resource_object = new ResourceObject($resource_type, $entity); - - $context['resource_type'] = $resource_type; - - // Determine the (internal) label field name. - $label_field_name = $label_only_entity->getLabelFieldName(); - - // Determine the public alias for the label field name. - assert($context['resource_type'] instanceof ResourceType); - $resource_type = $context['resource_type']; - $public_field_label_name = $resource_type->getPublicName($label_field_name); - - // Perform the default entity normalization, extract all values from the - // resulting EntityNormalizerValue object. - // @see \Drupal\jsonapi\Normalizer\EntityNormalizer::normalize() - $normalized_object = $this->serializer->normalize($resource_object, $format, $context); - assert($normalized_object instanceof ResourceObjectNormalizerValue); - $all_values = $normalized_object->getValues(); - - // Reconstruct an EntityNormalizerValue object, this time with only the - // label field. - $label_only_values = [$public_field_label_name => $all_values[$public_field_label_name]]; - $link_context = ['link_manager' => $this->linkManager]; - return new ResourceObjectNormalizerValue($label_only_values, $context, $resource_object, $link_context); - } - -} diff --git a/src/Normalizer/ResourceObjectNormalizer.php b/src/Normalizer/ResourceObjectNormalizer.php index 2ede2d4..d89abb7 100644 --- a/src/Normalizer/ResourceObjectNormalizer.php +++ b/src/Normalizer/ResourceObjectNormalizer.php @@ -5,6 +5,7 @@ namespace Drupal\jsonapi\Normalizer; use Drupal\Core\Access\AccessResult; use Drupal\Core\Field\FieldItemListInterface; use Drupal\jsonapi\JsonApiResource\ResourceObject; +use Drupal\jsonapi\LabelOnlyEntity; use Drupal\jsonapi\Normalizer\Value\ConfigFieldItemNormalizerValue; use Drupal\jsonapi\Normalizer\Value\FieldNormalizerValue; use Drupal\jsonapi\Normalizer\Value\ResourceObjectNormalizerValue; @@ -12,7 +13,7 @@ use Drupal\jsonapi\Normalizer\Value\FieldNormalizerValueInterface; use Drupal\jsonapi\LinkManager\LinkManager; /** - * Converts the JSON API module ResourceObject into a JSON API array structure. + * Converts the JSON:API module ResourceObject into a JSON:API array structure. * * @internal */ @@ -23,7 +24,10 @@ class ResourceObjectNormalizer extends NormalizerBase { * * @var string */ - protected $supportedInterfaceOrClass = ResourceObject::class; + protected $supportedInterfaceOrClass = [ + ResourceObject::class, + LabelOnlyEntity::class, + ]; /** * The formats that the Normalizer can handle. @@ -121,7 +125,7 @@ class ResourceObjectNormalizer extends NormalizerBase { protected function serializeField($field, array $context, $format) { if (!$field instanceof FieldItemListInterface) { // Config entities have no concept of "fields", nor any concept of - // "field access". For practical reasons, JSON API uses the same value + // "field access". For practical reasons, JSON:API uses the same value // object that it uses for content entities (FieldNormalizerValue), and // that requires an access result. Therefore we can safely hardcode it. return new FieldNormalizerValue(