# Normalization

# Usage

The normalizer expects a raw json string and returns a NormalizedDocumentInterface, allowing access to all contained resources.

The following example snippets all refer to this as the json document, e.g. $rawJson:

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON:API paints my bikeshed!",
      "body": "The shortest article. Ever.",
      "created": "2015-05-22T14:56:29.000Z",
      "updated": "2015-05-22T14:56:28.000Z"
    },
    "relationships": {
      "author": {
        "data": {"id": "42", "type": "people"}
      }
    }
  }],
  "included": [
    {
      "type": "people",
      "id": "42",
      "attributes": {
        "name": "John",
        "age": 80,
        "gender": "male"
      }
    }
  ]
}

# Basic normalization example

This shows how to load a raw json string into the normalizer, and what information is available after letting it run. Essentially, the normalizers' job is to provide an object-inspired (not entirely object-oriented) way of traversing the input documents and tries to be somewhat helpful when dealing with unknown contents.

$normalizer = new \EFrane\JsonApiReader\Normalization\DocumentNormalizer();
$normalized = $normalizer->normalize($rawJson);

// get the primary resource type of the document
$primaryType = $normalized->getPrimaryResourceType(); 
// $primaryType = `articles`

// iterate over only the primary resources
foreach ($normalized->getPrimaryResources() as $resourceInfo) {
    // get the article's title
    $title = $resourceInfo->getAttributes()['title'];
    // $title = 'JSON:API paints my bikeshed!'
}

# NormalizedDocumentInterface

The normalized document interface extends upon the \ArrayAccess, \Countable and \Iterator interfaces, allowing for seamless integration with the standard array processing methods.

# ResourceInfoInterface

Normalized documents do not contain raw arrays, instead, these get processed into ResourceInfoInterface conforming objects.

interface ResourceInfoInterface extends InfoInterface
{
    /**
     * The resource attributes.
     *
     * @return array<string, mixed>
     */
    public function getAttributes(): array;

    /**
     * The resource identifier.
     *
     * May be null if the document is from a create request.
     */
    public function getId(): ?string;

    /**
     * The resource link list.
     *
     * Returns an empty array if no links exist.
     *
     * @return array<string, string>
     */
    public function getLinks(): array;

    /**
     * The resource meta information.
     *
     * Returns an empty array if no meta information exists.
     *
     * @return array<mixed, mixed>
     */
    public function getMeta(): array;

    /**
     * List of names of the relationships.
     *
     * Returns an empty array if there are no relationships.
     *
     * @return array<int, string>
     */
    public function getRelationshipNames(): array;

    /**
     * The relationships section.
     *
     * Returns an empty array if there are no relationships.
     * Please consult the specification to get more information
     * about the structure of this block if you need to explicitly
     * interact with it. During normal use of the normalizer, this
     * will be processed internally.
     *
     * @see CompoundResourceInfoInterface
     * @see https://jsonapi.org/format/#document-resource-object-relationships
     *
     * @return array<string, array<string, mixed>>
     */
    public function getRelationships(): array;

    /**
     * The resource type.
     */
    public function getType(): string;

    /**
     * Check if the resource has included relationships.
     */
    public function hasRelationships(): bool;

    /**
     * Check if the resource has attributes.
     */
    public function hasAttributes(): bool;
}

# Relationship Mapping

In addition to the general mapping logic described below, compound documents (opens new window) get their included relationships linked into the primary resources. In effect, the primary resources are upgraded to conform to the CompoundResourceInfoInterface:

interface CompoundResourceInfoInterface extends ResourceInfoInterface
{
    /**
     * Return the `ResourceInfoInterface`s of a relationship.
     *
     * Will return an empty array if no items were included.
     *
     * @return array<int, ResourceInfoInterface>
     */
    public function getRelationshipItems(string $relationshipName): array;

    /**
     * Check whether items for a relationship were included.
     */
    public function hasRelationshipItems(string $relationshipName): bool;
}

# Functional Definition

This library defines normalization of {json:api} documents as flat-mapping the contained resources into a hash map keyed by their resource types. Each entry of that map then carries another hash map of that type's resources keyed by the resource id.

# Mapping Example

Given the above {json:api} document the resulting normalization mapping (converted to JSON for easy comparison) is:

{
  "articles": {
    "1": {
      "type": "articles",
      "id": "1",
      "attributes": {
        "title": "JSON:API paints my bikeshed!",
        "body": "The shortest article. Ever.",
        "created": "2015-05-22T14:56:29.000Z",
        "updated": "2015-05-22T14:56:28.000Z"
      },
      "relationships": {
        "author": {
          "data": {"id": "42", "type": "people"}
        }
      }
    }
  },
  "people": {
    "42": {
      "type": "people",
      "id": "42",
      "attributes": {
        "name": "John",
        "age": 80,
        "gender": "male"
      }
    }
  }
}

# Types of resources

As in the {json:api}-specification, resources in this library refer to typed data objects. Documents may contain multiple objects of different types as well as multiple objects of the same type.

# The primary resource type

The general structure of {json:api} documents is described as in the success case having a data attribute containing either one or multiple resource objects. These objects should all have the same type attribute. As a result, this type is regarded as the primary resource type of the document.

The normalizer allows access to the primary resource type name via the TopLevelDocumentInterface::getPrimaryResourceType() which is returned after normalizing.

You can get all primary objects via TopLevelDocumentInterface::getPrimaryResources().

# Unidentified resources

When making POST requests to create new resources, {json:api} allows to omit the id. However, normalization into the above describe structure requires all elements to have an id to keep the TopLevelDocument data structure synchronous for all use-cases.

Since there can only ever be one unidentified resource in any given document, the normalizer will assign missing-identifier, or more specifically TopLevelDocumentInterface::UNIDENTIFIED_RESOURCE_ID to that resource.