{"id":13828176,"url":"https://github.com/woohoolabs/yang","last_synced_at":"2026-01-06T04:56:31.185Z","repository":{"id":1632832,"uuid":"43612938","full_name":"woohoolabs/yang","owner":"woohoolabs","description":"The efficient and elegant, PSR-7 compliant JSON:API 1.1 client library for PHP","archived":false,"fork":false,"pushed_at":"2023-09-06T09:46:08.000Z","size":586,"stargazers_count":167,"open_issues_count":3,"forks_count":13,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-17T07:02:19.860Z","etag":null,"topics":["client","json-api","php","psr-7"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/woohoolabs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-10-03T20:03:55.000Z","updated_at":"2024-06-18T12:38:19.024Z","dependencies_parsed_at":"2024-06-18T12:38:15.671Z","dependency_job_id":"5fbc2964-1c2f-41d5-9e64-fa0b3b48e674","html_url":"https://github.com/woohoolabs/yang","commit_stats":{"total_commits":335,"total_committers":9,"mean_commits":37.22222222222222,"dds":0.04179104477611939,"last_synced_commit":"5cbd05d5e6f94d40c4bef4a7b9a0fdb42503d8dc"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woohoolabs%2Fyang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woohoolabs%2Fyang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woohoolabs%2Fyang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woohoolabs%2Fyang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/woohoolabs","download_url":"https://codeload.github.com/woohoolabs/yang/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":213898239,"owners_count":15654240,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","json-api","php","psr-7"],"created_at":"2024-08-04T09:02:35.312Z","updated_at":"2026-01-06T04:56:31.179Z","avatar_url":"https://github.com/woohoolabs.png","language":"PHP","readme":"# Woohoo Labs. Yang\n\n[![Latest Version on Packagist][ico-version]][link-version]\n[![Software License][ico-license]](LICENSE)\n[![Build Status][ico-build]][link-build]\n[![Coverage Status][ico-coverage]][link-coverage]\n[![Quality Score][ico-code-quality]][link-code-quality]\n[![Total Downloads][ico-downloads]][link-downloads]\n[![Gitter][ico-support]][link-support]\n\n**Woohoo Labs. Yang is a PHP framework which helps you to communicate with JSON:API servers more easily.**\n\n## Table of Contents\n\n* [Introduction](#introduction)\n    * [Features](#features)\n* [Install](#install)\n* [Basic Usage](#basic-usage)\n    * [Request builder](#request-builder)\n    * [HTTP clients](#http-clients)\n    * [Response](#response)\n    * [Hydration](#hydration)\n* [Advanced Usage](#advanced-usage)\n    * [Custom serialization](#custom-serialization)\n    * [Custom deserialization](#custom-deserialization)\n* [Versioning](#versioning)\n* [Change Log](#change-log)\n* [Contributing](#contributing)\n* [Support](#support)\n* [Credits](#credits)\n* [License](#license)\n\n## Introduction\n\n[JSON:API](https://jsonapi.org) specification\n[reached 1.0 on 29th May 2015](https://www.programmableweb.com/news/new-json-api-specification-aims-to-speed-api-development/2015/06/10)\nand we also believe it is a big day for RESTful APIs as this specification makes APIs more robust and future-proof\nthan they have ever been. Woohoo Labs. Yang (named after Yin-Yang) was born to bring efficiency and elegance to your\nJSON:API clients, while [Woohoo Labs. Yin](https://github.com/woohoolabs/yin) is its server-side counterpart.\n\n### Features\n\n- 100% [PSR-7](https://www.php-fig.org/psr/psr-7/) compatibility\n- 99% [JSON:API 1.1](https://jsonapi.org/) conformance (approximately)\n- Provides a Request Builder to setup JSON:API request more easily\n- Provides easy-to-use HTTP clients via [PSR-18](https://www.php-fig.org/psr/psr-18/) and [HTTPlug](https://github.com/php-http/httplug)\n- Supports [hydrators](#hydration) out-of-the-box in order to easily convert API responses to objects\n\n## Install\n\nThe only thing you need before getting started is [Composer](https://getcomposer.org).\n\n### Install an HTTP client and message implementations:\n\nBecause Yang requires a HTTP client implementation, you must install one first. You may use [Guzzle 7 Adapter](https://github.com/php-http/guzzle7-adapter)\nor any other library of your preference:\n\n```bash\n$ composer require php-http/guzzle7-adapter\n```\n\n### Install Yang:\n\nTo install the latest version of this library, run the command below:\n\n```bash\n$ composer require woohoolabs/yang\n```\n\n\u003e Note: The tests and examples won't be downloaded by default. You have to use `composer require woohoolabs/yang --prefer-source`\nor clone the repository if you need them.\n\nYang requires PHP 7.4 at least. You may use Yang 2.3 for PHP 7.2.\n\n## Basic Usage\n\nYang can help you in three ways to communicate with JSON:API servers. The following subsections will cover these topics.\n\n### Request builder\n\nYang comes with a powerful request builder with which you are able to setup PSR-7 `Request` objects in a JSON:API compliant way.\nFor this purpose, you may use the `JsonApiRequestBuilder` class as it can be seen in the following example.\n\n```php\nuse GuzzleHttp\\Psr7\\Request;\nuse WoohooLabs\\Yang\\JsonApi\\Request\\JsonApiRequestBuilder;\n\n// Instantiate an empty PSR-7 request, note that the default HTTP method must be provided\n$request = new Request('GET', '');\n\n// Instantiate the request builder\n$requestBuilder = new JsonApiRequestBuilder($request);\n\n// Setup the request with general properties\n$requestBuilder\n    -\u003esetProtocolVersion(\"1.1\")\n    -\u003esetMethod(\"GET\")\n    -\u003esetUri(\"https://www.example.com/api/users\")\n    -\u003esetHeader(\"Accept-Charset\", \"utf-8\");\n\n// Setup the request with JSON:API specific properties\n$requestBuilder\n    -\u003esetJsonApiFields(                                      // To define sparse fieldset\n        [\n            \"users\" =\u003e [\"first_name\", \"last_name\"],\n            \"address\" =\u003e [\"country\", \"city\", \"postal_code\"]\n        ]\n    )\n    -\u003esetJsonApiIncludes(                                    // To include related resources\n        [\"address\", \"friends\"]\n    )\n    -\u003esetJsonApiIncludes(                                    // Or you can pass a string instead\n        \"address,friends\"\n    )\n    -\u003esetJsonApiSort(                                        // To sort resource collections\n        [\"last_name\", \"first_name\"]\n    )\n    -\u003esetJsonApiPage(                                        // To paginate the primary data\n        [\"number\" =\u003e 1, \"size\" =\u003e 100]\n    )\n    -\u003esetJsonApiFilter(                                      // To filter the primary data\n        [\"first_name\" =\u003e \"John\"]\n    )\n    -\u003eaddJsonApiAppliedProfile(                              // To add a profile to the request (JSON:API 1.1 feature)\n        [\"https://example.com/profiles/last-modified\"]\n    )\n    -\u003eaddJsonApiRequestedProfile(                            // To request the server to apply a profile (JSON:API 1.1 feature)\n        [\"https://example.com/profiles/last-modified\"]\n    )\n    -\u003eaddJsonApiRequiredProfile(                             // To require the server to apply a profile (JSON:API 1.1 feature)\n        [\"https://example.com/profiles/last-modified\"]\n    );\n\n// Setup the request body\n$requestBuilder\n    -\u003esetJsonApiBody(                                        // You can pass the content as a JSON string\n        '{\n           \"data\": [\n             { \"type\": \"user\", \"id\": \"1\" },\n             { \"type\": \"user\", \"id\": \"2\" }\n           ]\n         }'\n    )\n    -\u003esetJsonApiBody(                                        // or you can pass it as an array\n        [\n            \"data\" =\u003e [\n                [\"type\" =\u003e \"user\", \"id\" =\u003e 1],\n                [\"type\" =\u003e \"user\", \"id\" =\u003e 2],\n            ],\n        ]\n    )\n    -\u003esetJsonApiBody(                                        // or as a ResourceObject instance\n        new ResourceObject(\"user\", 1)\n    );\n\n// Get the composed request\n$request = $requestBuilder-\u003egetRequest();\n```\n\nIf you do not want to use the built-in Request Builder, you can freely setup any PSR-7 `RequestInterface` instances\nin order to proceed with the next steps:\n\n```php\n$request = new Request('GET', '');\n$request = $request\n    -\u003ewithProtocolVersion(\"1.1\")\n    -\u003ewithUri(new Uri(\"https://example.com/api/users?fields[users]=first_name,last_name\"))\n    -\u003ewithHeader(\"Accept\", \"application/vnd.api+json\")\n    -\u003ewithHeader(\"Content-Type\", \"application/vnd.api+json\");\n```\n\n### HTTP clients\n\nThe library comes with support for [PSR-18](https://www.php-fig.org/psr/psr-18/) and [HTTPlug](https://github.com/php-http/httplug),\nso you can choose how you want to send your requests. If you installed the `php-http/guzzle6-adapter` package, then you\nwill be able to use Guzzle to do so:\n\n```php\nuse Http\\Adapter\\Guzzle6\\Client;\n\n// Instantiate the Guzzle HTTP Client\n$guzzleClient = Client::createWithConfig([]);\n\n// Instantiate the syncronous JSON:API Client\n$client = new JsonApiClient($guzzleClient);\n\n// Send the request syncronously to retrieve the response\n$response = $client-\u003esendRequest($request);\n\n// Instantiate the asyncronous JSON:API Client\n$client = new JsonApiAsyncClient($guzzleClient);\n\n// Send the request asyncronously to retrieve a promise\n$promise = $client-\u003esendAsyncRequest($request);\n\n// Send multiple request asyncronously to retrieve an array of promises\n$promises = $client-\u003esendConcurrentAsyncRequests([$request, $request]);\n```\n\nOf course, you can use any available HTTP Clients or create a custom HTTP Client thanks to PSR-18 and HTTPlug.\n\n### Response\n\nAs soon as you have retrieved the server response, you can start querying it. Yang uses the PSR-7 compatible\n`JsonApiResponse` class for this purpose. If you used a HTTP client introduced above, you will automatically\nget an object of this type, otherwise you have to take care of instantiating it with the right dependencies:\n\n```php\n// Instantiate a JSON:API response object from a PSR-7 response object with the default deserializer\n$response = new JsonApiResponse($psr7Response);\n```\n\nThe `JsonApiResponse` class - above the ones defined by PSR-7 - has some methods to make the handling of JSON:API\nresponses easier:\n\n```php\n// Checks if the response doesn't contain any errors\n$isSuccessful = $response-\u003eisSuccessful();\n\n// Checks if the response doesn't contain any errors, and has the status codes listed below\n$isSuccessful = $response-\u003eisSuccessful([200, 202]);\n\n// The same as the isSuccessful() method, but also ensures the response contains a document\n$isSuccessfulDocument = $response-\u003eisSuccessfulDocument();\n\n// Checks if the response contains a JSON:API document\n$hasDocument = $response-\u003ehasDocument();\n\n// Retrieves and deserializes the JSON:API document in the response body\n$document = $response-\u003edocument();\n```\n\nThe `Document` class has various methods too:\n\n```php\n// Retrieves the \"jsonapi\" member as a JsonApiObject instance\n$jsonApi = $document-\u003ejsonApi();\n\n$jsonApiVersion = $jsonApi-\u003eversion();\n$jsonApiMeta = $jsonApi-\u003emeta();\n\n// Checks if the document has the \"meta\" member\n$hasMeta = $document-\u003ehasMeta();\n\n// Retrieves the \"meta\" member as an array\n$meta = $document-\u003emeta();\n\n// Checks if the document has any links\n$hasLinks = $document-\u003ehasLinks();\n\n// Retrieves the \"links\" member as a DocumentLinks object\n$links = $document-\u003elinks();\n\n// Checks if the document has any errors\n$hasErrors = $document-\u003ehasErrors();\n\n// Counts the number of errors in the document\n$errorCount = $document-\u003eerrorCount();\n\n// Retrieves the \"errors\" member as an array of Error objects\n$errors = $document-\u003eerrors();\n\n// Retrieves the first error as an Error object or throws an exception if it is missing\n$firstError = $document-\u003eerror(0);\n\n// Checks if the document contains a single resource as its primary data\n$isSingleResourceDocument = $document-\u003eisSingleResourceDocument();\n\n// Checks if the document contains a collection of resources as its primary data\n$isResourceCollectionDocument = $document-\u003eisResourceCollectionDocument();\n\n// Checks if the document contains any primary data\n$hasPrimaryData = $document-\u003ehasAnyPrimaryResources();\n\n// Returns the primary resource as a ResourceObject instance if the document is a single-resource document\n// or throws an exception otherwise or when the document is empty\n$primaryResource = $document-\u003eprimaryResource();\n\n// Returns the primary resources as an array of ResourceObject instances if the document is a collection document\n// or throws an exception otherwise\n$primaryResources = $document-\u003eprimaryResources();\n\n// Checks if there are any included resources in the document\n$hasIncludedResources = $document-\u003ehasAnyIncludedResources();\n\n// Checks if there is a specific included resource in the document\n$isUserIncluded = $document-\u003ehasIncludedResource(\"user\", \"1234\");\n\n// Retrieves all the included resources as an array of ResourceObject instances\n$includedResources = $document-\u003eincludedResources();\n```\n\nThe `DocumentLinks` class features the following methods:\n\n```php\n// Checks if the \"self\" link is present\n$hasSelf = $links-\u003ehasSelf();\n\n// Returns the \"self\" link as a Link object or throws an exception if it is missing\n$selfLink = $links-\u003eself();\n\n// Checks if the \"related\" link is present\n$hasRelated = $links-\u003ehasRelated();\n\n// Returns the \"related\" link as a Link object or throws an exception if it is missing\n$relatedLink = $links-\u003erelated();\n\n// Checks if the \"first\" link is present\n$hasFirst = $links-\u003ehasFirst();\n\n// Returns the \"first\" link as a Link object or throws an exception if it is missing\n$firstLink = $links-\u003efirst();\n\n// Checks if the \"last\" link is present\n$hasLast = $links-\u003ehasLast();\n\n// Returns the \"last\" link as a Link object or throws an exception if it is missing\n$lastLink = $links-\u003elast();\n\n// Checks if the \"prev\" link is present\n$hasPrev = $links-\u003ehasPrev();\n\n// Returns the \"prev\" link as a Link object or throws an exception if it is missing\n$prevLink = $links-\u003eprev();\n\n// Checks if the \"next\" link is present\n$hasNext = $links-\u003ehasNext();\n\n// Returns the \"next\" link as a Link object or throws an exception if it is missing\n$nextLink = $links-\u003enext();\n\n// Checks if a specific link is present\n$hasLink = $links-\u003ehasLink(\"next\");\n\n// Returns a specific link as a Link object or throws an exception if it is missing\n$link = $links-\u003elink(\"next\");\n\n// Checks if the there is any profile defined\n$hasProfiles = $links-\u003ehasAnyProfiles();\n\n// Retrieves the profiles as an array of ProfileLink objects\n$profiles = $links-\u003eprofiles();\n\n// Checks if there is a specific profile defined\n$hasProfile = $links-\u003ehasProfile(\"https://example.com/profiles/last-modified\");\n\n// Retrieves a specific profile as a ProfileLink object\n$profile = $links-\u003eprofile(\"https://example.com/profiles/last-modified\");\n```\n\nThe `Error` class has the following methods:\n\n```php\n// Returns the \"id\" member of the error\n$id = $firstError-\u003eid();\n\n// Checks if the error has the \"meta\" member\n$hasMeta = $firstError-\u003ehasMeta();\n\n// Retrieves the \"meta\" member as an array\n$meta = $firstError-\u003emeta();\n\n// Checks if the error has any links\n$hasLinks = $firstError-\u003ehasLinks();\n\n// Retrieves the \"links\" member as an ErrorLinks object\n$links = $firstError-\u003elinks();\n\n// Returns the \"status\" member\n$status = $firstError-\u003estatus();\n\n// Returns the \"code\" member\n$code = $firstError-\u003ecode();\n\n// Returns the \"title\" member\n$title = $firstError-\u003etitle();\n\n// Returns the \"detail\" member\n$detail = $firstError-\u003edetail();\n\n// Checks if the error has the \"source\" member\n$hasSource = $firstError-\u003ehasSource();\n\n// Returns the \"source\" member as an ErrorSource object\n$source = $firstError-\u003esource();\n```\n\nThe `ResourceObject` class has the following methods:\n\n```php\n// Returns the type of the resource\n$type = $primaryResource-\u003etype();\n\n// Returns the id of the resource\n$id = $primaryResource-\u003eid();\n\n// Checks if the resource has the \"meta\" member\n$hasMeta = $primaryResource-\u003ehasMeta();\n\n// Returns the \"meta\" member as an array\n$meta = $primaryResource-\u003emeta();\n\n// Checks if the resource has any links\n$hasLinks = $primaryResource-\u003ehasLinks();\n\n// Returns the \"links\" member as a ResourceLinks object\n$links = $primaryResource-\u003elinks();\n\n// Returns the attributes of the resource as an array\n$attributes = $primaryResource-\u003eattributes();\n\n// Returns the ID and attributes of the resource as an array\n$idAndAttributes = $primaryResource-\u003eidAndAttributes();\n\n// Checks if the resource has a specific attribute\n$hasFirstName = $primaryResource-\u003ehasAttribute(\"first_name\");\n\n// Returns an attribute of the resource or null if it is missing\n$firstName = $primaryResource-\u003eattribute(\"first_name\");\n\n// Returns an attribute of the resource or the default value if it is missing\n$lastName = $primaryResource-\u003eattribute(\"last_name\", \"\");\n\n// Returns all relationships of the resource as an array of Relationship objects\n$relationships = $primaryResource-\u003erelationships();\n\n// Checks if the resource has a specific relationship\n$hasAddress = $primaryResource-\u003ehasRelationship(\"address\");\n\n// Returns a relationship of the resource as a Relationship object or throws an exception if it is missing\n$relationship = $primaryResource-\u003erelationship(\"address\");\n```\nThe `Relationship` object supports the following methods:\n\n```php\n// Checks if it is a to-one relationship\n$isToOneRelationship = $relationship-\u003eisToOneRelationship();\n\n// Checks if it is a to-many relationship\n$isToManyRelationship = $relationship-\u003eisToManyRelationship();\n\n// Returns the name of the relationship\n$name = $relationship-\u003ename();\n\n// Checks if the relationship has the \"meta\" member\n$hasMeta = $relationship-\u003ehasMeta();\n\n// Returns the \"meta\" member of the relationship as an array\n$meta = $relationship-\u003emeta();\n\n// Returns the \"links\" member of the relationship as a RelationshipLinks object\n$links = $relationship-\u003elinks();\n\n// Returns the first resource linkage of the relationship as an array (e.g.: [\"type\" =\u003e \"address\", \"id\" =\u003e \"123\"])\n// or null if there isn't any related data\n$resourceLinkage = $relationship-\u003efirstResourceLink();\n\n// Returns the resource linkage as an array of array (e.g.: [[\"type\" =\u003e \"address\", \"id\" =\u003e \"123\"]])\n$resourceLinkage = $relationship-\u003eresourceLinks();\n\n// Checks if a specific resource object is included\n$isIncluded = $relationship-\u003ehasIncludedResource(\"address\", \"abcd\");\n\n// Returns the resource object of a to-one relationship as a `ResourceObject` instance\n// or throws an exception otherwise or when the relationship is empty\n$resource = $relationship-\u003eresource();\n\n// Returns the resource objects of a to-many relationship as an array of `ResourceObject` instances\n// or throws an exception otherwise\n$resources = $relationship-\u003eresources();\n```\n\n### Hydration\n\nJSON:API responses with many related resources are not easily to process with the above approach. For example, if you want\nto retrieve the value of an attribute of a related resource, you need the following code:\n\n```php\n$dogResource = $response-\u003edocument()-\u003eprimaryResource();\n\n$breedName = $dogResource-\u003erelationship(\"breed\")-\u003eresource()-\u003eattribute(\"name\");\n```\n\nThis is a bit too much code to write, and it gets a lot worse when you want to map complex response documents with many\nrelationships to objects:\n\n```php\n$dogResource = $response-\u003edocument()-\u003eprimaryResource();\n\n$dog = new stdClass();\n$dog-\u003ename = $dogResource-\u003eattribute(\"name\");\n$dog-\u003eage = $dogResource-\u003eattribute(\"age\");\n$dog-\u003ebreed = $dogResource-\u003erelationship(\"breed\")-\u003eresource()-\u003eattribute(\"name\");\nforeach ($dogResource-\u003erelationship(\"owners\")-\u003eresources() as $ownerResource) {\n    $owner = new stdClass();\n    $owner-\u003ename = $ownerResource-\u003eattribute(\"name\");\n\n    $addressResource = $ownerResource-\u003erelationship(\"address\")-\u003eresource();\n    $owner-\u003eaddress = new stdClass();\n    $owner-\u003eaddress-\u003ecity = $addressResource-\u003eattribute(\"city\");\n    $owner-\u003eaddress-\u003eaddressLine = $addressResource-\u003eattribute(\"city\");\n\n    $dog-\u003eowners[] = $owner;\n}\n```\n\nThis is the situation when using a hydrator can help you. Currently, Yang has only one hydrator, the `ClassDocumentHydrator` which - if the\nresponse was successful - maps the specified document to an `stdClass` along with all the resource attributes and relationships.\nIt means that errors, links, meta data won't be present in the returned object. However, relationships are very easy to\naccess now.\n\nLet's use the document from the last example for demonstrating the power of hydrators:\n\n```php\n// Check if hydration is possible\nif ($document-\u003ehasAnyPrimaryResources() === false) {\n    return;\n}\n\n// Hydrate the document to an stdClass\n$hydrator = new ClassDocumentHydrator();\n$dog = $hydrator-\u003ehydrateSingleResource($response-\u003edocument());\n```\n\nThat's all you need to do in order to create the same `$dog` object as in the first example! Now, you can display its properties:\n\n```php\necho \"Dog:\\n\";\necho \"Name : \" . $dog-\u003ename . \"\\n\";\necho \"Breed: \" . $dog-\u003ebreed-\u003ename . \"\\n\\n\";\n\necho \"Owners:\\n\";\nforeach ($dog-\u003eowners as $owner) {\n    echo \"Name   : \" . $dog-\u003eowner-\u003ename . \"\\n\";\n    echo \"Address: \" . $dog-\u003eowner-\u003eaddress-\u003ecity . \", \" . $dog-\u003eowner-\u003eaddress-\u003eaddressLine . \"\\n\";\n    echo \"------------------\\n\";\n}\n```\n\n\u003e Note: The method `ClassDocumentHydrator::hydrateSingleResource()` throws `DocumentException` when the document doesn't\nhave any primary data or if the primary data is a collection. Otherwise - when the primary data is a single resource -\nan `stdObject` along with all the attributes and relationships is returned.\n\nAdditionally, you may use the `ClassHydrator::hydrateCollection()` method for retrieving many dogs:\n\n```php\n// Check if hydration is possible\nif ($document-\u003eisSingleResourceDocument()) {\n    return;\n}\n\n// Hydrate the document to an array of stdClass\n$hydrator = new ClassDocumentHydrator();\n$dogs = $hydrator-\u003ehydrateCollection($response-\u003edocument());\n```\n\n\u003e Note: The method `ClassHydrator::hydrateCollection()` throws `DocumentException` when the primary data is a single resource.\nOtherwise - when the primary data is a collection of resources - an array of `stdObject`s along with all the attributes and\nrelationship is returned.\n\nFurthermore, there is a `hydrate()` method available for you when you don't care if the primary data is a single resource\nor a collection of resources.\n\n\u003e Note: The method `ClassDocumentHydrator::hydrate()` returns an empty array when the document doesn't have any primary data.\nIt returns an array containing a single `stdClass` if the primary data is a single resource. Otherwise - when the primary data\nis a collection of resources - an array of `stdObject`s is returned.\n\n## Advanced Usage\n\n### Custom serialization\n\nSometimes you might need to be tricky to serialize the request body in a custom way. For example, if you\ndispatch a server request internally (within the original request), then you can send the request body as an array thanks\nto this feature - so you don't need to serialize at client-side and then deserialize at server-size. If you use\nWoohoo Labs. Yin and a [custom deserializer](https://github.com/woohoolabs/yin/#custom-deserialization) at server-side,\nthen this is an easy task to do.\n\nAt client-side, if you use Yang with the [Request Builder](#request-builder), then you only have to pass a second\nconstructor argument to it like below to take advantage of custom serialization:\n\n```php\n// Instantiate a PSR-7 request\n$request = new Request();\n\n// Instantiate your custom serializer\n$mySerializer = new MyCustomSerializer();\n\n// Instantiate the request builder with a custom serializer\n$requestBuilder = new JsonApiRequestBuilder($request, $mySerializer);\n```\n\nYou only have to make sure that your custom serializer implements the `SerializerInterface`.\n\n### Custom deserialization\n\nSometimes you might need to be tricky to deserialize a server response in a custom way. For example, if you\ndispatch a server request internally (within the original request), then you can receive the response body as an array thanks\nto this feature - so you don't need to serialize at server-side and then deserialize at client-size. If you use\nWoohoo Labs. Yin and a [custom serializer](https://github.com/woohoolabs/yin/#custom-serialization) at server-side,\nthen this is an easy task to do.\n\nAt client-side, if you use Yang with the default [HTTP Clients](#http-clients) then you only have to pass a second\nconstructor argument to them like below to take advantage of custom deserialization:\n\n```php\nuse Http\\Adapter\\Guzzle7\\Client;\n\n// Instantiate the Guzzle HTTP Client\n$guzzleClient = Client::createWithConfig([]);\n\n// Instantiate your custom deserializer\n$myDeserializer = new MyCustomDeserializer();\n\n// Instantiate the syncronous JSON:API Client with a custom deserializer\n$syncClient = new JsonApiClient($guzzleClient, $myDeserializer);\n\n// Instantiate the asyncronous JSON:API Client with a custom deserializer\n$asyncClient = new JsonApiAsyncClient($guzzleClient, $myDeserializer);\n```\n\nOtherwise pass your deserializer to the `JsonApiResponse` as its second argument like below:\n\n```php\n// Instantiate a JSON:API response from a PSR-7 response with a custom deserializer\n$response = new JsonApiResponse($psr7Response, new MyCustomDeserializer());\n```\n\nYou only have to make sure that your custom deserializer implements the `DeserializerInterface`.\n\n## Examples\n\nHave a look at the [examples directory](https://github.com/woohoolabs/yang/blob/master/examples/) for a really basic\nexample.\n\n## Versioning\n\nThis library follows [SemVer v2.0.0](https://semver.org/).\n\n## Change Log\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.\n\n## Testing\n\nWoohoo Labs. Yang has a PHPUnit test suite. To run the tests, run the following command from the project folder:\n\n``` bash\n$ phpunit\n```\n\nAdditionally, you may run `docker-compose up` or `make test` in order to execute the tests.\n\n## Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) for details.\n\n## Support\n\nPlease see [SUPPORT](SUPPORT.md) for details.\n\n## Credits\n\n- [Máté Kocsis][link-author]\n- [All Contributors][link-contributors]\n\n## License\n\nThe MIT License (MIT). Please see the [License File](LICENSE) for more information.\n\n[ico-version]: https://img.shields.io/packagist/v/woohoolabs/yang.svg\n[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg\n[ico-build]: https://img.shields.io/github/workflow/status/woohoolabs/yang/Continuous%20Integration\n[ico-coverage]: https://img.shields.io/codecov/c/github/woohoolabs/yang\n[ico-code-quality]: https://img.shields.io/scrutinizer/g/woohoolabs/yang.svg\n[ico-downloads]: https://img.shields.io/packagist/dt/woohoolabs/yang.svg\n[ico-support]: https://badges.gitter.im/woohoolabs/yang.svg\n\n[link-version]: https://packagist.org/packages/woohoolabs/yang\n[link-build]: https://github.com/woohoolabs/yang/actions\n[link-coverage]: https://codecov.io/gh/woohoolabs/yang\n[link-code-quality]: https://scrutinizer-ci.com/g/woohoolabs/yang\n[link-downloads]: https://packagist.org/packages/woohoolabs/yang\n[link-author]: https://github.com/kocsismate\n[link-contributors]: ../../contributors\n[link-support]: https://gitter.im/woohoolabs/yang?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\n","funding_links":[],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoohoolabs%2Fyang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwoohoolabs%2Fyang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoohoolabs%2Fyang/lists"}