# 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.