Same name and namespace in other branches
  1. 8.9.x core/modules/jsonapi/src/ResourceType/ResourceType.php \Drupal\jsonapi\ResourceType\ResourceType
  2. 9 core/modules/jsonapi/src/ResourceType/ResourceType.php \Drupal\jsonapi\ResourceType\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.

Hierarchy

Expanded class hierarchy of ResourceType

See also

https://www.drupal.org/project/drupal/issues/3032787

jsonapi.api.php

\Drupal\jsonapi\ResourceType\ResourceTypeRepository

19 files declare their use of ResourceType
ConfigEntityDenormalizer.php in core/modules/jsonapi/src/Normalizer/ConfigEntityDenormalizer.php
ContentEntityDenormalizer.php in core/modules/jsonapi/src/Normalizer/ContentEntityDenormalizer.php
CountableResourceType.php in core/modules/jsonapi/tests/modules/jsonapi_test_collection_count/src/ResourceType/CountableResourceType.php
EntryPoint.php in core/modules/jsonapi/src/Controller/EntryPoint.php
FieldResolver.php in core/modules/jsonapi/src/Context/FieldResolver.php

... See full list

File

core/modules/jsonapi/src/ResourceType/ResourceType.php, line 19

Namespace

Drupal\jsonapi\ResourceType
View source
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));
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ResourceType::$bundle protected property The bundle ID.
ResourceType::$deserializationTargetClass protected property The class to which a payload converts to.
ResourceType::$entityTypeId protected property The entity type ID.
ResourceType::$fieldMapping protected property The mapping for field aliases: keys=public names, values=internal names.
ResourceType::$fields protected property The list of fields on the underlying entity type + bundle.
ResourceType::$internal protected property Whether this resource type is internal.
ResourceType::$isLocatable protected property Whether this resource type's resources are locatable.
ResourceType::$isMutable protected property Whether this resource type's resources are mutable.
ResourceType::$isVersionable protected property Whether this resource type's resources are versionable.
ResourceType::$relatableResourceTypesByField protected property An array of arrays of relatable resource types, keyed by public field name.
ResourceType::$typeName protected property The type name.
ResourceType::getBundle public function Gets the bundle.
ResourceType::getDeserializationTargetClass public function Gets the deserialization target class.
ResourceType::getEntityTypeId public function Gets the entity type ID.
ResourceType::getFieldByInternalName public function Gets a particular attribute or relationship field by internal field name.
ResourceType::getFieldByPublicName public function Gets a particular attribute or relationship field by public field name.
ResourceType::getFields public function Gets the attribute and relationship fields of this resource type.
ResourceType::getInternalName public function Translates the public field name to the entity field name.
ResourceType::getPath public function Get the resource path.
ResourceType::getPublicName public function Translates the entity field name to the public field name.
ResourceType::getRelatableResourceTypes public function Get all resource types with which this type may have a relationship.
ResourceType::getRelatableResourceTypesByField public function Get all resource types with which the given field may have a relationship.
ResourceType::getTypeName public function Gets the type name.
ResourceType::hasField public function Checks if the field exists.
ResourceType::includeCount public function Determine whether to include a collection count. 1
ResourceType::isFieldEnabled public function Checks if a field is enabled or not.
ResourceType::isInternal public function Whether this resource type is internal.
ResourceType::isLocatable public function Whether resources of this resource type are locatable.
ResourceType::isMutable public function Whether resources of this resource type are mutable.
ResourceType::isVersionable public function Whether resources of this resource type are versionable.
ResourceType::setRelatableResourceTypes public function Sets the relatable resource types.
ResourceType::TYPE_NAME_URI_PATH_SEPARATOR constant A string which is used as path separator in resource type names.
ResourceType::__construct public function Instantiates a ResourceType object.