ResourceType.php
Same filename in other branches
Namespace
Drupal\jsonapi\ResourceTypeFile
-
core/
modules/ jsonapi/ src/ ResourceType/ ResourceType.php
View source
<?php
namespace Drupal\jsonapi\ResourceType;
/**
* Value object containing all metadata for a JSON:API resource type.
*
* Used to generate routes (collection, individual, etcetera), generate
* relationship links, and so on.
*
* @internal JSON:API maintains no PHP API since its API is the HTTP API. This
* class may change at any time and this will break any dependencies on it.
*
* @see https://www.drupal.org/project/drupal/issues/3032787
* @see jsonapi.api.php
*
* @see \Drupal\jsonapi\ResourceType\ResourceTypeRepository
*/
class ResourceType {
/**
* A string which is used as path separator in resource type names.
*
* @see \Drupal\jsonapi\ResourceType\ResourceType::getPath()
*/
const TYPE_NAME_URI_PATH_SEPARATOR = '--';
/**
* The entity type ID.
*
* @var string
*/
protected $entityTypeId;
/**
* The bundle ID.
*
* @var string
*/
protected $bundle;
/**
* The type name.
*
* @var string
*/
protected $typeName;
/**
* The class to which a payload converts to.
*
* @var string
*/
protected $deserializationTargetClass;
/**
* Whether this resource type is internal.
*
* @var bool
*/
protected $internal;
/**
* Whether this resource type's resources are locatable.
*
* @var bool
*/
protected $isLocatable;
/**
* Whether this resource type's resources are mutable.
*
* @var bool
*/
protected $isMutable;
/**
* Whether this resource type's resources are versionable.
*
* @var bool
*/
protected $isVersionable;
/**
* The list of fields on the underlying entity type + bundle.
*
* @var string[]
*/
protected $fields;
/**
* An array of arrays of relatable resource types, keyed by public field name.
*
* @var array
*/
protected $relatableResourceTypesByField;
/**
* The mapping for field aliases: keys=public names, values=internal names.
*
* @var string[]
*/
protected $fieldMapping;
/**
* Gets the entity type ID.
*
* @return string
* The entity type ID.
*
* @see \Drupal\Core\Entity\EntityInterface::getEntityTypeId
*/
public function getEntityTypeId() {
return $this->entityTypeId;
}
/**
* Gets the type name.
*
* @return string
* The type name.
*/
public function getTypeName() {
return $this->typeName;
}
/**
* Gets the bundle.
*
* @return string
* The bundle of the entity. Defaults to the entity type ID if the entity
* type does not make use of different bundles.
*
* @see \Drupal\Core\Entity\EntityInterface::bundle
*/
public function getBundle() {
return $this->bundle;
}
/**
* Gets the deserialization target class.
*
* @return string
* The deserialization target class.
*/
public function getDeserializationTargetClass() {
return $this->deserializationTargetClass;
}
/**
* Translates the entity field name to the public field name.
*
* This is only here so we can allow polymorphic implementations to take a
* greater control on the field names.
*
* @return string
* The public field name.
*/
public function getPublicName($field_name) {
// By default the entity field name is the public field name.
return isset($this->fields[$field_name]) ? $this->fields[$field_name]
->getPublicName() : $field_name;
}
/**
* Translates the public field name to the entity field name.
*
* This is only here so we can allow polymorphic implementations to take a
* greater control on the field names.
*
* @return string
* The internal field name as defined in the entity.
*/
public function getInternalName($field_name) {
// By default the entity field name is the public field name.
return $this->fieldMapping[$field_name] ?? $field_name;
}
/**
* Gets the attribute and relationship fields of this resource type.
*
* @return \Drupal\jsonapi\ResourceType\ResourceTypeField[]
* The field objects on this resource type.
*/
public function getFields() {
return $this->fields;
}
/**
* Gets a particular attribute or relationship field by public field name.
*
* @param string $public_field_name
* The public field name of the desired field.
*
* @return \Drupal\jsonapi\ResourceType\ResourceTypeField|null
* A resource type field object or NULL if the field does not exist on this
* resource type.
*/
public function getFieldByPublicName($public_field_name) {
return isset($this->fieldMapping[$public_field_name]) ? $this->getFieldByInternalName($this->fieldMapping[$public_field_name]) : NULL;
}
/**
* Gets a particular attribute or relationship field by internal field name.
*
* @param string $internal_field_name
* The internal field name of the desired field.
*
* @return \Drupal\jsonapi\ResourceType\ResourceTypeField|null
* A resource type field object or NULL if the field does not exist on this
* resource type.
*/
public function getFieldByInternalName($internal_field_name) {
return $this->fields[$internal_field_name] ?? NULL;
}
/**
* Checks if the field exists.
*
* Note: a minority of config entity types which do not define a
* `config_export` in their entity type annotation will not have their fields
* represented here because it is impossible to determine them without an
* instance of config available.
*
* @todo Refactor this in Drupal 9, because thanks to https://www.drupal.org/project/drupal/issues/2949021, `config_export` will be guaranteed to exist, and this won't need an instance anymore.
*
* @param string $field_name
* The internal field name.
*
* @return bool
* TRUE if the field is known to exist on the resource type; FALSE
* otherwise.
*/
public function hasField($field_name) {
return array_key_exists($field_name, $this->fields);
}
/**
* Checks if a field is enabled or not.
*
* This is only here so we can allow polymorphic implementations to take a
* greater control on the data model.
*
* @param string $field_name
* The internal field name.
*
* @return bool
* TRUE if the field exists and is enabled and should be considered as part
* of the data model. FALSE otherwise.
*/
public function isFieldEnabled($field_name) {
return $this->hasField($field_name) && $this->fields[$field_name]
->isFieldEnabled();
}
/**
* Determine whether to include a collection count.
*
* @return bool
* Whether to include a collection count.
*/
public function includeCount() {
// By default, do not return counts in collection queries.
return FALSE;
}
/**
* Whether this resource type is internal.
*
* This must not be used as an access control mechanism.
*
* Internal resource types are not available via the HTTP API. They have no
* routes and cannot be used for filtering or sorting. They cannot be included
* in the response using the `include` query parameter.
*
* However, relationship fields on public resources *will include* a resource
* identifier for the referenced internal resource.
*
* This method exists to remove data that should not logically be exposed by
* the HTTP API. For example, read-only data from an internal resource might
* be embedded in a public resource using computed fields. Therefore,
* including the internal resource as a relationship with distinct routes
* might unnecessarily expose internal implementation details.
*
* @return bool
* TRUE if the resource type is internal. FALSE otherwise.
*/
public function isInternal() {
return $this->internal;
}
/**
* Whether resources of this resource type are locatable.
*
* A resource type may for example not be locatable when it is not stored.
*
* @return bool
* TRUE if the resource type's resources are locatable. FALSE otherwise.
*/
public function isLocatable() {
return $this->isLocatable;
}
/**
* Whether resources of this resource type are mutable.
*
* Indicates that resources of this type may not be created, updated or
* deleted (POST, PATCH or DELETE, respectively).
*
* @return bool
* TRUE if the resource type's resources are mutable. FALSE otherwise.
*/
public function isMutable() {
return $this->isMutable;
}
/**
* Whether resources of this resource type are versionable.
*
* @return bool
* TRUE if the resource type's resources are versionable. FALSE otherwise.
*/
public function isVersionable() {
return $this->isVersionable;
}
/**
* Instantiates a ResourceType object.
*
* @param string $entity_type_id
* An entity type ID.
* @param string $bundle
* A bundle.
* @param string $deserialization_target_class
* The deserialization target class.
* @param bool $internal
* (optional) Whether the resource type should be internal.
* @param bool $is_locatable
* (optional) Whether the resource type is locatable.
* @param bool $is_mutable
* (optional) Whether the resource type is mutable.
* @param bool $is_versionable
* (optional) Whether the resource type is versionable.
* @param \Drupal\jsonapi\ResourceType\ResourceTypeField[] $fields
* (optional) The resource type fields, keyed by internal field name.
* @param null|string $type_name
* The resource type name.
*/
public function __construct($entity_type_id, $bundle, $deserialization_target_class, $internal = FALSE, $is_locatable = TRUE, $is_mutable = TRUE, $is_versionable = FALSE, array $fields = [], $type_name = NULL) {
$this->entityTypeId = $entity_type_id;
$this->bundle = $bundle;
$this->deserializationTargetClass = $deserialization_target_class;
$this->internal = $internal;
$this->isLocatable = $is_locatable;
$this->isMutable = $is_mutable;
$this->isVersionable = $is_versionable;
$this->fields = $fields;
$this->typeName = $type_name;
if ($type_name === NULL) {
$this->typeName = $this->bundle === '?' ? 'unknown' : $this->entityTypeId . self::TYPE_NAME_URI_PATH_SEPARATOR . $this->bundle;
}
$this->fieldMapping = array_flip(array_map(function (ResourceTypeField $field) {
return $field->getPublicName();
}, $this->fields));
}
/**
* Sets the relatable resource types.
*
* @param array $relatable_resource_types
* The resource types with which this resource type may have a relationship.
* The array should be a multi-dimensional array keyed by public field name
* whose values are an array of resource types. There may be duplicate
* across resource types across fields, but not within a field.
*/
public function setRelatableResourceTypes(array $relatable_resource_types) {
$this->fields = array_reduce(array_keys($relatable_resource_types), function ($fields, $public_field_name) use ($relatable_resource_types) {
if (!isset($this->fieldMapping[$public_field_name])) {
throw new \LogicException('A field must exist for relatable resource types to be set on it.');
}
$internal_field_name = $this->fieldMapping[$public_field_name];
$field = $fields[$internal_field_name];
assert($field instanceof ResourceTypeRelationship);
$fields[$internal_field_name] = $field->withRelatableResourceTypes($relatable_resource_types[$public_field_name]);
return $fields;
}, $this->fields);
}
/**
* Get all resource types with which this type may have a relationship.
*
* @return array
* The relatable resource types, keyed by relationship field names.
*
* @see self::setRelatableResourceTypes()
*/
public function getRelatableResourceTypes() {
if (!isset($this->relatableResourceTypesByField)) {
$this->relatableResourceTypesByField = array_reduce(array_map(function (ResourceTypeRelationship $field) {
return [
$field->getPublicName() => $field->getRelatableResourceTypes(),
];
}, array_filter($this->fields, function (ResourceTypeField $field) {
return $field instanceof ResourceTypeRelationship && $field->isFieldEnabled();
})), 'array_merge', []);
}
return $this->relatableResourceTypesByField;
}
/**
* Get all resource types with which the given field may have a relationship.
*
* @param string $field_name
* The public field name.
*
* @return \Drupal\jsonapi\ResourceType\ResourceType[]
* The relatable JSON:API resource types.
*
* @see self::getRelatableResourceTypes()
*/
public function getRelatableResourceTypesByField($field_name) {
return ($field = $this->getFieldByPublicName($field_name)) && $field instanceof ResourceTypeRelationship && $field->isFieldEnabled() ? $field->getRelatableResourceTypes() : [];
}
/**
* Get the resource path.
*
* @return string
* The path to access this resource type. The function
* replaces "--" with "/" in the URI path.
* Example: "node--article" -> "node/article".
*
* @see \Drupal\jsonapi\ResourceType\ResourceType::TYPE_NAME_URI_PATH_SEPARATOR
* @see jsonapi.base_path
*/
public function getPath() {
return '/' . implode('/', explode(self::TYPE_NAME_URI_PATH_SEPARATOR, $this->typeName));
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
ResourceType | Value object containing all metadata for a JSON:API resource type. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.