The efficient and elegant, PSR-7 compliant JSON:API 1.1 client library for PHP

Overview

Woohoo Labs. Yang

Latest Version on Packagist Software License Build Status Coverage Status Quality Score Total Downloads Gitter

Woohoo Labs. Yang is a PHP framework which helps you to communicate with JSON:API servers more easily.

Table of Contents

Introduction

JSON:API specification reached 1.0 on 29th May 2015 and we also believe it is a big day for RESTful APIs as this specification makes APIs more robust and future-proof than they have ever been. Woohoo Labs. Yang (named after Yin-Yang) was born to bring efficiency and elegance to your JSON:API clients, while Woohoo Labs. Yin is its server-side counterpart.

Features

  • 100% PSR-7 compatibility
  • 99% JSON:API 1.1 conformance (approximately)
  • Provides a Request Builder to setup JSON:API request more easily
  • Provides easy-to-use HTTP clients via PSR-18 and HTTPlug
  • Supports hydrators out-of-the-box in order to easily convert API responses to objects

Install

The only thing you need before getting started is Composer.

Install an HTTP client and message implementations:

Because Yang requires a HTTP client implementation, you must install one first. You may use Guzzle 6 Adapter or any other library of your preference:

$ composer require php-http/guzzle6-adapter

Install Yang:

To install the latest version of this library, run the command below:

$ composer require woohoolabs/yang

Note: The tests and examples won't be downloaded by default. You have to use composer require woohoolabs/yang --prefer-source or clone the repository if you need them.

Yang requires PHP 7.2 at least. You may use Yang 2.1 for PHP 7.1.

Basic Usage

Yang can help you in three ways to communicate with JSON:API servers. The following subsections will cover these topics.

Request builder

Yang comes with a powerful request builder with which you are able to setup PSR-7 Request objects in a JSON:API compliant way. For this purpose, you may use the JsonApiRequestBuilder class as it can be seen in the following example.

use GuzzleHttp\Psr7\Request;
use WoohooLabs\Yang\JsonApi\Request\JsonApiRequestBuilder;

// Instantiate an empty PSR-7 request, note that the default HTTP method must be provided
$request = new Request('GET', '');

// Instantiate the request builder
$requestBuilder = new JsonApiRequestBuilder($request);

// Setup the request with general properties
$requestBuilder
    ->setProtocolVersion("1.1")
    ->setMethod("GET")
    ->setUri("https://www.example.com/api/users")
    ->setHeader("Accept-Charset", "utf-8");

// Setup the request with JSON:API specific properties
$requestBuilder
    ->setJsonApiFields(                                      // To define sparse fieldset
        [
            "users" => ["first_name", "last_name"],
            "address" => ["country", "city", "postal_code"]
        ]
    )
    ->setJsonApiIncludes(                                    // To include related resources
        ["address", "friends"]
    )
    ->setJsonApiIncludes(                                    // Or you can pass a string instead
        "address,friends"
    )
    ->setJsonApiSort(                                        // To sort resource collections
        ["last_name", "first_name"]
    )
    ->setJsonApiPage(                                        // To paginate the primary data
        ["number" => 1, "size" => 100]
    )
    ->setJsonApiFilter(                                      // To filter the primary data
        ["first_name" => "John"]
    )
    ->addJsonApiAppliedProfile(                              // To add a profile to the request (JSON:API 1.1 feature)
        ["https://example.com/profiles/last-modified"]
    )
    ->addJsonApiRequestedProfile(                            // To request the server to apply a profile (JSON:API 1.1 feature)
        ["https://example.com/profiles/last-modified"]
    )
    ->addJsonApiRequiredProfile(                             // To require the server to apply a profile (JSON:API 1.1 feature)
        ["https://example.com/profiles/last-modified"]
    );

// Setup the request body
$requestBuilder
    ->setJsonApiBody(                                        // You can pass the content as a JSON string
        '{
           "data": [
             { "type": "user", "id": "1" },
             { "type": "user", "id": "2" }
           ]
         }'
    )
    ->setJsonApiBody(                                        // or you can pass it as an array
        [
            "data" => [
                ["type" => "user", "id" => 1],
                ["type" => "user", "id" => 2],
            ],
        ]
    )
    ->setJsonApiBody(                                        // or as a ResourceObject instance
        new ResourceObject("user", 1)
    );

// Get the composed request
$request = $requestBuilder->getRequest();

If you do not want to use the built-in Request Builder, you can freely setup any PSR-7 RequestInterface instances in order to proceed with the next steps:

$request = new Request('GET', '');
$request = $request
    ->withProtocolVersion("1.1")
    ->withUri(new Uri("https://example.com/api/users?fields[users]=first_name,last_name"))
    ->withHeader("Accept", "application/vnd.api+json")
    ->withHeader("Content-Type", "application/vnd.api+json");

HTTP clients

The library comes with support for PSR-18 and HTTPlug, so you can choose how you want to send your requests. If you installed the php-http/guzzle6-adapter package, then you will be able to use Guzzle to do so:

use Http\Adapter\Guzzle6\Client;

// Instantiate the Guzzle HTTP Client
$guzzleClient = Client::createWithConfig([]);

// Instantiate the syncronous JSON:API Client
$client = new JsonApiClient($guzzleClient);

// Send the request syncronously to retrieve the response
$response = $client->sendRequest($request);

// Instantiate the asyncronous JSON:API Client
$client = new JsonApiAsyncClient($guzzleClient);

// Send the request asyncronously to retrieve a promise
$promise = $client->sendAsyncRequest($request);

// Send multiple request asyncronously to retrieve an array of promises
$promises = $client->sendConcurrentAsyncRequests([$request, $request]);

Of course, you can use any available HTTP Clients or create a custom HTTP Client thanks to PSR-18 and HTTPlug.

Response

As soon as you have retrieved the server response, you can start querying it. Yang uses the PSR-7 compatible JsonApiResponse class for this purpose. If you used a HTTP client introduced above, you will automatically get an object of this type, otherwise you have to take care of instantiating it with the right dependencies:

// Instantiate a JSON:API response object from a PSR-7 response object with the default deserializer
$response = new JsonApiResponse($psr7Response);

The JsonApiResponse class - above the ones defined by PSR-7 - has some methods to make the handling of JSON:API responses easier:

// Checks if the response doesn't contain any errors
$isSuccessful = $response->isSuccessful();

// Checks if the response doesn't contain any errors, and has the status codes listed below
$isSuccessful = $response->isSuccessful([200, 202]);

// The same as the isSuccessful() method, but also ensures the response contains a document
$isSuccessfulDocument = $response->isSuccessfulDocument();

// Checks if the response contains a JSON:API document
$hasDocument = $response->hasDocument();

// Retrieves and deserializes the JSON:API document in the response body
$document = $response->document();

The Document class has various methods too:

// Retrieves the "jsonapi" member as a JsonApiObject instance
$jsonApi = $document->jsonApi();

$jsonApiVersion = $jsonApi->version();
$jsonApiMeta = $jsonApi->meta();

// Checks if the document has the "meta" member
$hasMeta = $document->hasMeta();

// Retrieves the "meta" member as an array
$meta = $document->meta();

// Checks if the document has any links
$hasLinks = $document->hasLinks();

// Retrieves the "links" member as a DocumentLinks object
$links = $document->links();

// Checks if the document has any errors
$hasErrors = $document->hasErrors();

// Counts the number of errors in the document
$errorCount = $document->errorCount();

// Retrieves the "errors" member as an array of Error objects
$errors = $document->errors();

// Retrieves the first error as an Error object or throws an exception if it is missing
$firstError = $document->error(0);

// Checks if the document contains a single resource as its primary data
$isSingleResourceDocument = $document->isSingleResourceDocument();

// Checks if the document contains a collection of resources as its primary data
$isResourceCollectionDocument = $document->isResourceCollectionDocument();

// Checks if the document contains any primary data
$hasPrimaryData = $document->hasAnyPrimaryResources();

// Returns the primary resource as a ResourceObject instance if the document is a single-resource document
// or throws an exception otherwise or when the document is empty
$primaryResource = $document->primaryResource();

// Returns the primary resources as an array of ResourceObject instances if the document is a collection document
// or throws an exception otherwise
$primaryResources = $document->primaryResources();

// Checks if there are any included resources in the document
$hasIncludedResources = $document->hasAnyIncludedResources();

// Checks if there is a specific included resource in the document
$isUserIncluded = $document->hasIncludedResource("user", "1234");

// Retrieves all the included resources as an array of ResourceObject instances
$includedResources = $document->includedResources();

The DocumentLinks class features the following methods:

// Checks if the "self" link is present
$hasSelf = $links->hasSelf();

// Returns the "self" link as a Link object or throws an exception if it is missing
$selfLink = $links->self();

// Checks if the "related" link is present
$hasRelated = $links->hasRelated();

// Returns the "related" link as a Link object or throws an exception if it is missing
$relatedLink = $links->related();

// Checks if the "first" link is present
$hasFirst = $links->hasFirst();

// Returns the "first" link as a Link object or throws an exception if it is missing
$firstLink = $links->first();

// Checks if the "last" link is present
$hasLast = $links->hasLast();

// Returns the "last" link as a Link object or throws an exception if it is missing
$lastLink = $links->last();

// Checks if the "prev" link is present
$hasPrev = $links->hasPrev();

// Returns the "prev" link as a Link object or throws an exception if it is missing
$prevLink = $links->prev();

// Checks if the "next" link is present
$hasNext = $links->hasNext();

// Returns the "next" link as a Link object or throws an exception if it is missing
$nextLink = $links->next();

// Checks if a specific link is present
$hasLink = $links->hasLink("next");

// Returns a specific link as a Link object or throws an exception if it is missing
$link = $links->link("next");

// Checks if the there is any profile defined
$hasProfiles = $links->hasAnyProfiles();

// Retrieves the profiles as an array of ProfileLink objects
$profiles = $links->profiles();

// Checks if there is a specific profile defined
$hasProfile = $links->hasProfile("https://example.com/profiles/last-modified");

// Retrieves a specific profile as a ProfileLink object
$profile = $links->profile("https://example.com/profiles/last-modified");

The Error class has the following methods:

// Returns the "id" member of the error
$id = $firstError->id();

// Checks if the error has the "meta" member
$hasMeta = $firstError->hasMeta();

// Retrieves the "meta" member as an array
$meta = $firstError->meta();

// Checks if the error has any links
$hasLinks = $firstError->hasLinks();

// Retrieves the "links" member as an ErrorLinks object
$links = $firstError->links();

// Returns the "status" member
$status = $firstError->status();

// Returns the "code" member
$code = $firstError->code();

// Returns the "title" member
$title = $firstError->title();

// Returns the "detail" member
$detail = $firstError->detail();

// Checks if the error has the "source" member
$hasSource = $firstError->hasSource();

// Returns the "source" member as an ErrorSource object
$source = $firstError->source();

The ResourceObject class has the following methods:

// Returns the type of the resource
$type = $primaryResource->type();

// Returns the id of the resource
$id = $primaryResource->id();

// Checks if the resource has the "meta" member
$hasMeta = $primaryResource->hasMeta();

// Returns the "meta" member as an array
$meta = $primaryResource->meta();

// Checks if the resource has any links
$hasLinks = $primaryResource->hasLinks();

// Returns the "links" member as a ResourceLinks object
$links = $primaryResource->links();

// Returns the attributes of the resource as an array
$attributes = $primaryResource->attributes();

// Returns the ID and attributes of the resource as an array
$idAndAttributes = $primaryResource->idAndAttributes();

// Checks if the resource has a specific attribute
$hasFirstName = $primaryResource->hasAttribute("first_name");

// Returns an attribute of the resource or null if it is missing
$firstName = $primaryResource->attribute("first_name");

// Returns an attribute of the resource or the default value if it is missing
$lastName = $primaryResource->attribute("last_name", "");

// Returns all relationships of the resource as an array of Relationship objects
$relationships = $primaryResource->relationships();

// Checks if the resource has a specific relationship
$hasAddress = $primaryResource->hasRelationship("address");

// Returns a relationship of the resource as a Relationship object or throws an exception if it is missing
$relationship = $primaryResource->relationship("address");

The Relationship object supports the following methods:

// Checks if it is a to-one relationship
$isToOneRelationship = $relationship->isToOneRelationship(); 

// Checks if it is a to-many relationship
$isToManyRelationship = $relationship->isToManyRelationship();

// Returns the name of the relationship
$name = $relationship->name();

// Checks if the relationship has the "meta" member
$hasMeta = $relationship->hasMeta();

// Returns the "meta" member of the relationship as an array
$meta = $relationship->meta();

// Returns the "links" member of the relationship as a RelationshipLinks object
$links = $relationship->links();

// Returns the first resource linkage of the relationship as an array (e.g.: ["type" => "address", "id" => "123"])
// or null if there isn't any related data
$resourceLinkage = $relationship->firstResourceLink();
 
// Returns the resource linkage as an array of array (e.g.: [["type" => "address", "id" => "123"]])
$resourceLinkage = $relationship->resourceLinks();

// Checks if a specific resource object is included
$isIncluded = $relationship->hasIncludedResource("address", "abcd");

// Returns the resource object of a to-one relationship as a `ResourceObject` instance
// or throws an exception otherwise or when the relationship is empty
$resource = $relationship->resource();

// Returns the resource objects of a to-many relationship as an array of `ResourceObject` instances
// or throws an exception otherwise
$resources = $relationship->resources();

Hydration

JSON:API responses with many related resources are not easily to process with the above approach. For example, if you want to retrieve the value of an attribute of a related resource, you need the following code:

$dogResource = $response->document()->primaryResource();

$breedName = $dogResource->relationship("breed")->resource()->attribute("name");

This is a bit too much code to write, and it gets a lot worse when you want to map complex response documents with many relationships to objects:

$dogResource = $response->document()->primaryResource();

$dog = new stdClass();
$dog->name = $dogResource->attribute("name");
$dog->age = $dogResource->attribute("age");
$dog->breed = $dogResource->relationship("breed")->resource()->attribute("name");
foreach ($dogResource->relationship("owners")->resources() as $ownerResource) {
    $owner = new stdClass();
    $owner->name = $ownerResource->attribute("name");
    
    $addressResource = $ownerResource->relationship("address")->resource();
    $owner->address = new stdClass();
    $owner->address->city = $addressResource->attribute("city");
    $owner->address->addressLine = $addressResource->attribute("city");

    $dog->owners[] = $owner;
}

This is the situation when using a hydrator can help you. Currently, Yang has only one hydrator, the ClassDocumentHydrator which - if the response was successful - maps the specified document to an stdClass along with all the resource attributes and relationships. It means that errors, links, meta data won't be present in the returned object. However, relationships are very easy to access now.

Let's use the document from the last example for demonstrating the power of hydrators:

// Check if hydration is possible
if ($document->hasAnyPrimaryResources() === false) {
    return;
}

// Hydrate the document to an stdClass
$hydrator = new ClassDocumentHydrator();
$dog = $hydrator->hydrateSingleResource($response->document());

That'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:

echo "Dog:\n";
echo "Name : " . $dog->name . "\n";
echo "Breed: " . $dog->breed->name . "\n\n";

echo "Owners:\n";
foreach ($dog->owners as $owner) {
    echo "Name   : " . $dog->owner->name . "\n";
    echo "Address: " . $dog->owner->address->city . ", " . $dog->owner->address->addressLine . "\n";
    echo "------------------\n";
}

Note: The method ClassDocumentHydrator::hydrateSingleResource() throws DocumentException when the document doesn't have any primary data or if the primary data is a collection. Otherwise - when the primary data is a single resource - an stdObject along with all the attributes and relationships is returned.

Additionally, you may use the ClassHydrator::hydrateCollection() method for retrieving many dogs:

// Check if hydration is possible
if ($document->isSingleResourceDocument()) {
    return;
}

// Hydrate the document to an array of stdClass
$hydrator = new ClassDocumentHydrator();
$dogs = $hydrator->hydrateCollection($response->document());

Note: The method ClassHydrator::hydrateCollection() throws DocumentException when the primary data is a single resource. Otherwise - when the primary data is a collection of resources - an array of stdObjects along with all the attributes and relationship is returned.

Furthermore, there is a hydrate() method available for you when you don't care if the primary data is a single resource or a collection of resources.

Note: The method ClassDocumentHydrator::hydrate() returns an empty array when the document doesn't have any primary data. It returns an array containing a single stdClass if the primary data is a single resource. Otherwise - when the primary data is a collection of resources - an array of stdObjects is returned.

Advanced Usage

Custom serialization

Sometimes you might need to be tricky to serialize the request body in a custom way. For example, if you dispatch a server request internally (within the original request), then you can send the request body as an array thanks to this feature - so you don't need to serialize at client-side and then deserialize at server-size. If you use Woohoo Labs. Yin and a custom deserializer at server-side, then this is an easy task to do.

At client-side, if you use Yang with the Request Builder, then you only have to pass a second constructor argument to it like below to take advantage of custom serialization:

// Instantiate a PSR-7 request
$request = new Request();

// Instantiate your custom serializer
$mySerializer = new MyCustomSerializer();

// Instantiate the request builder with a custom serializer
$requestBuilder = new JsonApiRequestBuilder($request, $mySerializer);

You only have to make sure that your custom serializer implements the SerializerInterface.

Custom deserialization

Sometimes you might need to be tricky to deserialize a server response in a custom way. For example, if you dispatch a server request internally (within the original request), then you can receive the response body as an array thanks to this feature - so you don't need to serialize at server-side and then deserialize at client-size. If you use Woohoo Labs. Yin and a custom serializer at server-side, then this is an easy task to do.

At client-side, if you use Yang with the default HTTP Clients then you only have to pass a second constructor argument to them like below to take advantage of custom deserialization:

use Http\Adapter\Guzzle6\Client;

// Instantiate the Guzzle HTTP Client
$guzzleClient = Client::createWithConfig([]);

// Instantiate your custom deserializer
$myDeserializer = new MyCustomDeserializer();

// Instantiate the syncronous JSON:API Client with a custom deserializer
$syncClient = new JsonApiClient($guzzleClient, $myDeserializer);

// Instantiate the asyncronous JSON:API Client with a custom deserializer
$asyncClient = new JsonApiAsyncClient($guzzleClient, $myDeserializer);

Otherwise pass your deserializer to the JsonApiResponse as its second argument like below:

// Instantiate a JSON:API response from a PSR-7 response with a custom deserializer
$response = new JsonApiResponse($psr7Response, new MyCustomDeserializer());

You only have to make sure that your custom deserializer implements the DeserializerInterface.

Examples

Have a look at the examples directory for a really basic example.

Versioning

This library follows SemVer v2.0.0.

Change Log

Please see CHANGELOG for more information what has changed recently.

Testing

Woohoo Labs. Yang has a PHPUnit test suite. To run the tests, run the following command from the project folder:

$ phpunit

Additionally, you may run docker-compose up or make test in order to execute the tests.

Contributing

Please see CONTRIBUTING for details.

Support

Please see SUPPORT for details.

Credits

License

The MIT License (MIT). Please see the License File for more information.

Comments
  • Add HydratorInterface::hydrateDocument(): stdClass

    Add HydratorInterface::hydrateDocument(): stdClass

    Would it be an idea to add

    public function hydrateObject(Document $document);
    

    to the HydratorInterface ?

    Or even (not sure about this), one with a return type

    public function hydrateObject(Document $document) : \stdClass;
    

    This way IDE autocompletion also allows hydrateObject() when the HydratorInterface is used fetch the actual implementation from a DIC.

    opened by holtkamp 11
  • Prepare for hydration of typed properties as supported by PHP 7.4

    Prepare for hydration of typed properties as supported by PHP 7.4

    As of PHP 7.4, typed properties are supported. In case typed properties are used for "mapped" classes used during resource hydration as discussed in https://github.com/woohoolabs/yang/issues/20, the property types of the used class should be respected.

    For example:

    • a property of type float can not be assigned a returned string value of 1.0
      • By detecting the type of the property, the value can be determined as (float) $value
    • a property of type DateTimeImmutable can not be assigned a returned string value of '2020-01-01T10:59:59.711148+02:00
      • By detecting the type, the value can be determined as new DateTimeImmutable($value)

    Therefore, next to the DocumentHydratorInterface, a DocumentPropertyHydrator or DocumentAttributeHydrator interface would be nice to have to use prepare for this functionality to be used here. Note this requires PHP 7.4or higher.

    Already have a working approach, just created this issue to get it out of my head and be able to discuss it before spending time on a PR and tests 😉

    @kocsismate What do you think?

    opened by holtkamp 8
  • Best practice to hydrate to a specific class?

    Best practice to hydrate to a specific class?

    Suppose I got a simple class:

    namespace My\Domain;
    class Article extends \stdClass
    {
        /** @var int */
        public $id;
    
        /** @var string */
        public $name;
    }
    

    what would be the advised approach to hydrate results from a JSON API "into" such kind of class?

    Maybe we can set some "mapping information" type => FQCN in the existing ClassDocumentHydrator which could be used when assembling a new object?

    $classMap = [
      'article' => \My\Domain\Article::class,
      'user' => \My\Domain\User::class,
      /* ... */
    ];
    $hydrator = new ClassDocumentHydrator();
    $hydrator->setClassMap($classMap);
    

    and then consider the classMap in the ClassDocumentHydrator():

    
    private function hydrateResource(ResourceObject $resource, Document $document, array &$resourceMap): object //Note the return type "was" stdClass
        {
            // Fill basic attributes of the resource
            $className = $this->classMap[$resource->type()] ?? stdClass::class;
            $result = new $className();
            $result->type = $resource->type();
            /* etc */
    }
    

    Just thinking out loud, or am I missing some existing functionality here?

    Another approach might be to make the ClassDocumentHydrator extensible (drop final) and have a protected function getClassNameForResourceType(string $resourceType) : string which defaults to 'stdClass'. This would allow users to do whatever they want 🤓

    opened by holtkamp 8
  • Unexpected Behaviour When To-One Relationship Data Is NULL

    Unexpected Behaviour When To-One Relationship Data Is NULL

    Hello!

    I'm working on a project that uses Yang to parse API responses we get from a JSONAPI server. It's worked wonderfully and I'm able to dig into the source code to understand it. I'm currently working with relationships and am running into an issue.

    Given the following JSONAPI response:

    {
      "data": {
        "type": "carts",
        "id": "d2a45f147d3508",
        "relationships": {
          "null-one": {
            "data": null
          },
          "has-one": {
            "data": {"type": "items", "id": "a0481682dabc68"}
          },
          "null-many": {
            "data": []
          },
          "has-many": {
            "data": [
              {"type": "items", "id": "b0ed0f339887c8"},
              {"type": "items", "id": "6bd5ec9c9321c3"}
            ]
          }
        }
      }
    }
    

    The null-one relationship returns {"data": null} which is JSONAPI spec but parsing it with Yang - as demonstrated by this example

    $response = new \GuzzleHttp\Psr7\Response(200, [], $json);
    $jsonapi = new \WoohooLabs\Yang\JsonApi\Response\JsonApiResponse($response);
    $document = $jsonapi->document();
    

    When running $document->toArray() I get the following results:

    $results = [
        'jsonapi' => [
            'version' => '1.0',
        ],
        'data' => [
            'type' => 'carts',
            'id' => 'd2a45f147d3508',
            'relationships' => [
                'null-one' => [
                    'data' => [], // array - expected null
                ],
                'has-one' => [
                    'data' => [
                        'type' => 'items',
                        'id' => 'a0481682dabc68',
                    ],
                ],
                'null-many' => [
                    'data' => [],
                ],
                'has-many' => [
                    'data' => [
                        [
                            'type' => 'items',
                            'id' => 'b0ed0f339887c8',
                        ],
                        [
                            'type' => 'items',
                            'id' => '6bd5ec9c9321c3',
                        ],
                    ],
                ],
            ],
        ],
    ];
    

    I would expect a relationship with its {"data": null} to return null instead of an empty array:

    // truncated to relevant relationships
    $expected = [
        'data' => [
            'type' => 'carts',
            'id' => 'd2a45f147d3508',
            'relationships' => [
                'null-one' => [
                    'data' => null, // null
                ],
                'null-many' => [
                    'data' => [],
                ],
            ],
        ],
    ];
    

    This has been causing issues with our project and I would rather not make complicated test cases before passing the proper results through.

    I've implemented a fix in WoohooLabs\Yang\JsonApi\Schema\Relationship to properly set its $isToOneRelationship to false when applicable which has the desired results.

    I have not forked the project yet, only modified local composer.json source files for debugging. I'm running short on time so I'll fork and just use that in my composer.json for now.

    Shall I make a pull request with my fixes once up? Or would you like to simply implement the following changes?


    https://github.com/woohoolabs/yang/blob/master/src/JsonApi/Schema/Relationship.php#L44

    if (self::isArrayKey($array, "data") === false) {
        $isToOneRelationship = array_key_exists("data", $array) && is_null($array["data"]);
        return self::createEmptyFromArray($name, $meta, $links, $resources, $isToOneRelationship);
    }
    

    https://github.com/woohoolabs/yang/blob/master/src/JsonApi/Schema/Relationship.php#L54

    private static function createEmptyFromArray(
        string $name,
        array $meta,
        Links $links,
        ResourceObjects $resources,
        $isToOneRelationship = null
    ): Relationship {
        return new Relationship($name, $meta, $links, [], $resources, $isToOneRelationship);
    }
    
    opened by ginnj-gilmore 7
  • Allow set filter query param directly

    Allow set filter query param directly

    Fix #30

    • add composer:v1 tag for GitHub workflow (composer v2 requires update dependencies)
    • fix broken test https://github.com/woohoolabs/yang/blob/master/tests/JsonApi/Request/JsonApiRequestBuilderTest.php#L103, provide really invalid uri
    • csfix
    opened by Insolita 5
  • ClassDocumentHydrator::hydrateSingleResource() enforces use of stdClass

    ClassDocumentHydrator::hydrateSingleResource() enforces use of stdClass

    See: https://github.com/woohoolabs/yang/blob/acdd0a11383bd23f405a4d42f4cea22f8f41eb57/src/JsonApi/Hydrator/ClassDocumentHydrator.php#L29

    Which prevents the use of a custom hydrator which returns other objects.

    PR here: https://github.com/woohoolabs/yang/pull/24

    opened by holtkamp 5
  • hasDocument and hasErrors shouldn't throw exceptions

    hasDocument and hasErrors shouldn't throw exceptions

    hasDocument and hasErrors shouldn't throw exceptions if something wrong with structure of response, but it does and breaks logic without chance to check programmatically does response returned some document or something else

    opened by baltun 5
  • php-http plugin AddHostPlugin does not work with JsonApiRequestBuilder

    php-http plugin AddHostPlugin does not work with JsonApiRequestBuilder

    Hey first off awesome library!!

    I was already playing with php-http and so I was using AddHostPlugin.

    However, the plugin is currently not useable in combination with yang because when you use the JsonApiRequestBuilder to create a request. It will set a (default) host in the request, which will have precedence over the client configured host.

    I will see if I can extend JsonApiRequestBuilder and change the behaviour of getRequest() later.

    opened by Ilyes512 4
  • Allow relationship's resource identifier objects to have a

    Allow relationship's resource identifier objects to have a "meta" value.

    Allow relationship's resource identifier objects to have a "meta" value as well, following JSON API specification: http://jsonapi.org/format/#document-resource-identifier-objects. Additionally add a utility method to retrieve a relation link's meta value.

    Considering this valid JSON API response for two "main_resource" resources with each a relationship to the same "secondary_resource" resource, but for each relationship there's also a specific value for the relation between that "main_resource" and "secondary_resource":

    {
      "data": [
        {
          "type": "main_resource",
          "id": "uuid-main-resource-1",
          "attributes": {
            "name": "Main resource"
          },
          "relationships": {
            "some-property": {
              "data": {
                "type": "secondary_resource",
                "id": "uuid-secondary-resource",
                "meta": {
                  "foobar": "Some meta value specific for relation between uuid-main-resource-1 and uuid-secondary-resource.",
                  "created": "Relationship created date: 2017-08-11"
                }
              },
              "links": {
                "self": "https//foobar.com/jsonapi/main_resource/uuid-main-resource-1/relationships/some-property",
                "related": "https//foobar.com/jsonapi/main_resource/uuid-main-resource-1/some-property"
              }
            }
          },
          "links": {
            "self": "https//foobar.com/jsonapi/main_resource/uuid-main-resource-1"
          }
        },
        {
          "type": "main_resource",
          "id": "uuid-main-resource-2",
          "attributes": {
            "name": "Main resource"
          },
          "relationships": {
            "some-property": {
              "data": {
                "type": "secondary_resource",
                "id": "uuid-secondary-resource",
                "meta": {
                  "foobar": "Some meta value specific for relation between uuid-main-resource-2 and uuid-secondary-resource.",
                  "created": "Relationship created date: 2017-08-01"
                }
              },
              "links": {
                "self": "https//foobar.com/jsonapi/main_resource/uuid-main-resource-2/relationships/some-property",
                "related": "https//foobar.com/jsonapi/main_resource/uuid-main-resource-2/some-property"
              }
            }
          },
          "links": {
            "self": "https//foobar.com/jsonapi/main_resource/uuid-main-resource-2"
          }
        }
      ],
      "links": {
        "self": "https//foobar.com/jsonapi/main_resource/uuid-main-resource?include=secondary_resource"
      },
      "included": [
        {
          "type": "secondary_resource",
          "id": "uuid-secondary-resource",
          "attributes": {
            "name": "Secondary resource"
          },
          "links": {
            "self": "https//foobar.com/jsonapi/secondary_resource/uuid-secondary-resource"
          }
        }
      ]
    }
    
    opened by huyby 4
  • "make test" fails for fresh clone + composer install

    When using PHP 7.3.9 to run:

    git clone https://github.com/woohoolabs/yang
    cd yang
    composer install  //installs PHPUnit 8.3.4
    make test
    

    results in the following error:

    This version of PHPUnit is supported on PHP 7.2, PHP 7.3, and PHP 7.4.
    You are using PHP 7.1.31 (/usr/local/bin/php).
    

    Changing the image in docker-compose.yml to php:7.2-cli or php:7.3-cli resolves this. Not sure whether this is the "best" approach, just wanted to report it 😄

    opened by holtkamp 3
  • Fix For Unexpected Behaviour When To-One Relationship Data Is NULL - Closes #7

    Fix For Unexpected Behaviour When To-One Relationship Data Is NULL - Closes #7

    Please see issue #7 for full information.

    JSONAPI spec states to-one relationships with no associated resource should return {"data": null} but Yang does not properly account for this when converting Relationship to an array. This pull request fixes Yang returning an empty to-many relationship (['data' => []]) instead of an empty to-one relationship (['data' => null]).

    opened by ginnj-gilmore 3
  • Fixing hydrator issue when relationship is not included

    Fixing hydrator issue when relationship is not included

    I was trying to use DocumentHydrator to hydrate incoming request

    I found out an issue on the Hydrator relationship is not included it gets ignored

    consider this example

    {
    	"data": {
    		"id": "",
            "type": "used_car",
    		"attributes": {
    			"year": 1972,
                "status": "draft"
    		},
    		"relationships": {
    			"brand": {
    				"data": {
    					"id": "9",
    					"type": "brand"
    				}
    			},
    			"carBodyShape": {
    				"data": {
    					"id": "2",
    					"type": "car_body_shape"
    				}
    			},
    			"carModel": {
    				"data": {
    					"id": "3",
    					"type": "car_model"
    				}
    			},
    			"carGrade": {
    				"data": {
    					"id": "4",
    					"type": "car_grade"
    				}
    			},
                "country": {
    				"data": {
    					"id": "4",
    					"type": "country"
    				}
    			},
                "city": {
    				"data": {
    					"id": "4",
    					"type": "city"
    				}
    			},
                "area": {
    				"data": {
    					"id": "4",
    					"type": "area"
    				}
    			},
                "more_licenses": {
    				"data": [
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28wec4e135",
    						"type": "used_car_license"
    					},
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ewc4e136",
    						"type": "used_car_license"
    					}
    				]
    			},
    			"licenses": {
    				"data": [
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e135",
    						"type": "used_car_license"
    					},
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e136",
    						"type": "used_car_license"
    					}
    				]
    			},
                "internalImages": {
    				"data": [
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e135",
    						"type": "used_car_image"
    					},
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e136",
    						"type": "used_car_image"
    					}
    				]
    			},
                "externalImages": {
    				"data": [
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e137",
    						"type": "used_car_image"
    					},
    					{
    						"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e138",
    						"type": "used_car_image"
    					}
    				]
    			}
    		}
    	},
    	"included": [
    		{
    			"id": "9",
    			"type": "brand",
    			"attributes": {
    				"title": "repellat"
    			}
    			
    		},
    		{
    			"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e136",
    			"type": "used_car_license",
    			"attributes": {
    				"file": 1,
    				"image_type": "front"
    			}
    			
    		},
    		{
    			"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e135",
    			"type": "used_car_license",
    			"attributes": {
    				"file": 1,
    				"image_type": "back"
    			}
    		},
            {
    			"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e136",
    			"type": "used_car_image",
    			"attributes": {
    				"file": 1,
    				"image_type": "internal"
    			}
    			
    		},
    		{
    			"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e135",
    			"type": "used_car_image",
    			"attributes": {
    				"file": 1,
    				"image_type": "internal"
    			}
    		},
            {
    			"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e137",
    			"type": "used_car_image",
    			"attributes": {
    				"file": 1,
    				"image_type": "external"
    			}
    			
    		},
    		{
    			"id": "bb4c1dba-3d9d-4df1-85d0-7cb28ec4e138",
    			"type": "used_car_image",
    			"attributes": {
    				"file": 1,
    				"image_type": "external"
    			}
    		}
    	]
    }
    

    On the last case all relationships that does not have included data gets removed from serialized to JSON object carModel, carBodyShape, carGrade will get ignored because it does not included

    This updates add an object for each not included relationship while hydrating data The object will have only id & type

    opened by BahaaAlhagar 0
  • Adopt PSR-18

    Adopt PSR-18

    Http\Client\HttpClient is deprecated, use of PSR-18 using Psr\Http\Client\ClientInterface is preferred

    Since https://github.com/woohoolabs/yang/blob/d0a085edeb9dd7b8ebaad7d9f5a3dbbc88f7149a/src/JsonApi/Client/JsonApiAsyncClient.php still depends on HTTPPlug, maybe this can be isolated in a separate package woohoolabs/yang-async?

    This would allow a smooth migration to PSR-18

    opened by holtkamp 1
  • How to share common schemas/resources/etc between yin and yang

    How to share common schemas/resources/etc between yin and yang

    I am currently writing a JSON:API server implementation which will also have clients that will consume the API. The server is Symfony 4 using woohoolabs/yin. It contains a bunch of schemas, documents, resources, hydrators, etc.

    I was looking in how to include these in a client-library, but everything extends stuff from the Woohoolabs\Yin namespace, whereas on the client side you are working from the Woohoolabs\Yang namespace.

    Yang doesn't seem to share a base with Yin that would enable me to reuse code. What is the preferred way here? Write a totally separate client library, resulting in a high amount of duplicate code?

    opened by Doqnach 2
  • Unify Schema/ResourceObject and Request/ResourceObject to improve developer experience

    Unify Schema/ResourceObject and Request/ResourceObject to improve developer experience

    Just implementing some stuff using this library :+1:

    One thing I've found rather confusing is having one ResourceObject entity for received resources and a different ResourceObject entity for when we need to create/update. Not only it was unexpected, it's cumbersome to use (class imports, converting one type into another) and very prone to error.

    Also, the Request/ResourceObject entity is write-only, and the Schema/ResourceObject entity is read-only. This severely limits what can be done with the entities.

    Use cases this separation gets in the way of include:

    • Reading a resource from the API, modifying, then sending back for updating
    • Unit tests on code that needs to evaluate ResourceObjects
    • Referencing ResourceObject on classes that use both - always need to alias imports

    These two should really be the same entity, I can't quite see any reason they should be different things entirely since they're virtually the same thing, and indeed on JSONAPI they're the same thing.

    opened by luispabon 4
Releases(3.0.0-beta1)
  • 3.0.0-beta1(Oct 23, 2021)

    ADDED:

    • #34: JsonApiClient implements PSR-18's ClientInterface

    CHANGED:

    • Increased minimum PHP version requirement to 7.4
    • PSR-18 is used instead of HTTPlug

    REMOVED:

    • The deprecated ClassHydrator has been removed
    • The deprecated HydratorInterface has been removed
    Source code(tar.gz)
    Source code(zip)
  • 2.3.2(Nov 15, 2020)

  • 2.3.1(Jun 23, 2020)

  • 2.3.0(Apr 24, 2020)

  • 2.2.1(Oct 20, 2019)

  • 2.2.0(Sep 23, 2019)

    ADDED:

    • AbstractClassDocumentHydrator as a base class for custom hydrators

    CHANGED:

    • Increased minimum PHP version requirement to 7.2
    • ClassDocumentHydrator is no longer a final class
    Source code(tar.gz)
    Source code(zip)
  • 2.1.0(Apr 17, 2019)

    ADDED:

    • #13: DocumentHydratorInterface and ClassDocumentHydrator in order to fix some issues with the HydratorInterface and ClassHydrator
    • #15: New accessor and mutator methods for WoohooLabs\Yang\JsonApi\Request\ResourceObject: id(), setId(), type(), setType(), attributes(), relationships()

    DEPRECATED:

    • HydratorInterface: use the DocumentHydratorInterface instead
    • ClassHydrator: use the ClassDocumentHydrator instead
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Mar 4, 2019)

    Changes since beta1:

    CHANGED:

    • Apply the Woohoo Labs. Coding Standard

    Full change set:

    ADDED:

    • JSON:API 1.1 related features:
      • Support for Profiles
      • Support for type links in errors
    • Document::errorCount() to easily count errors in the document
    • Support for defining a default value when using the ResourceObject::attribute() method

    CHANGED:

    • Improve type-safety by eliminating null return values (BREAKING CHANGE):
      • JsonApiResponse::document() throws an exception instead of returning null if the response doesn't contain a document.
      • Document::primaryResources() throws an exception if the document is a single-resource or error document
      • Document::primaryResource() throws an exception if the document is a collection or error document or the primary resource is missing
      • Document::resource() throws an exception instead of returning null if the requested resource is missing
      • Document::error() throws an exception instead of returning null if the document does not contain the requested error
      • Relationship::resources() throws an exception instead of returning an empty array if the relationship is a to-one
      • Relationship::resource() throws an exception instead of returning null if the relationship is a to-many or empty
      • Relationship::resourceBy() throws an exception instead of returning null if the requested resource is missing
      • ResourceObject::relationship() throws an exception instead of returning null if the requested relationship is missing
    • Move errors, links, and resources to their own namespace (BREAKING CHANGE):
      • WoohooLabs\Yang\JsonApi\Schema\Error to WoohooLabs\Yang\JsonApi\Schema\Error\Error
      • WoohooLabs\Yang\JsonApi\Schema\ErrorSource to WoohooLabs\Yang\JsonApi\Schema\Error\ErrorSource
      • WoohooLabs\Yang\JsonApi\Schema\Link to WoohooLabs\Yang\JsonApi\Schema\Link\Link
      • WoohooLabs\Yang\JsonApi\Schema\ResourceObjects to WoohooLabs\Yang\JsonApi\Schema\Resource\ResourceObjects
      • WoohooLabs\Yang\JsonApi\Schema\ResourceObject to WoohooLabs\Yang\JsonApi\Schema\Resource\ResourceObject
    • Return separate classes instead of a general Links for the different types of links (BREAKING CHANGE):
      • DocumentLinks when using Document::links()
      • ResourceLinks when using ResourceObject::links()
      • RelationshipLinks when using Relationship::links()
      • ErrorLinks when using Error::links()
    • JsonSerializer::serialize() will throw a RequestException instead of LogicException if the body is of invalid type (BREAKING CHANGE)
    • Rename JsonApi to JsonApiObject (BREAKING CHANGE)
    • Apply the Woohoo Labs. Coding Standard

    REMOVED:

    • The generic Link class (BREAKING CHANGE)

    FIXED:

    • Issues with 0 when converting to array
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0-beta1(Dec 4, 2018)

    ADDED:

    • JSON:API 1.1 related features:
      • Support for Profiles
      • Support for type links in errors
    • Document::errorCount() to easily count errors in the document
    • Support for defining default value when using the ResourceObject::attribute() method

    CHANGED:

    • Improve type-safety by eliminating null return values (BREAKING):
      • JsonApiResponse::document() throws an exception instead of returning null if the response doesn't contain a document
      • Document::primaryResources() throws an exception if the document is a single-resource or error document
      • Document::primaryResource() throws an exception if the document is a collection or error document or the primary resource is missing
      • Document::resource() throws an exception instead of returning null if the requested resource is missing
      • Document::error() throws an exception instead of returning null if the document does not contain the requested error
      • Relationship::resources() throws an exception instead of returning an empty array if the relationship is a to-one
      • Relationship::resource() throws an exception instead of returning null if the relationship is a to-many or empty
      • Relationship::resourceBy() throws an exception instead of returning null if the requested resource is missing
      • ResourceObject::relationship() throws an exception instead of returning null if the requested relationship is missing
    • Move errors, links, and resources to their own namespace (BREAKING):
      • WoohooLabs\Yang\JsonApi\Schema\Error to WoohooLabs\Yang\JsonApi\Schema\Error\Error
      • WoohooLabs\Yang\JsonApi\Schema\ErrorSource to WoohooLabs\Yang\JsonApi\Schema\Error\ErrorSource
      • WoohooLabs\Yang\JsonApi\Schema\Link to WoohooLabs\Yang\JsonApi\Schema\Link\Link
      • WoohooLabs\Yang\JsonApi\Schema\ResourceObjects to WoohooLabs\Yang\JsonApi\Schema\Resource\ResourceObjects
      • WoohooLabs\Yang\JsonApi\Schema\ResourceObject to WoohooLabs\Yang\JsonApi\Schema\Resource\ResourceObject
    • Return separate classes instead of Links for the different types of links (BREAKING):
      • DocumentLinks when using Document::links()
      • ResourceLinks when using ResourceObject::links()
      • RelationshipLinks when using Relationship::links()
      • ErrorLinks when using Error::links()
    • JsonSerializer::serialize() will throw a RequestException instead of LogicException if the body is of invalid type (BREAKING)
    • Rename JsonApi to JsonApiObject (BREAKING)

    REMOVED:

    • The generic Link class (BREAKING)

    FIXED:

    • Issues with 0 when converting to array
    Source code(tar.gz)
    Source code(zip)
  • 1.5.0(Nov 15, 2018)

  • 1.4.1(Dec 24, 2017)

  • 1.4.0(Dec 11, 2017)

  • 1.3.2(Dec 6, 2017)

    FIXED:

    • #9: Resources with an ID of "0" disappear when using ClassHydrator
    • Other issues with "0" affecting ResourceObject::toString() and JsonApiRequestBuilder::toString()
    Source code(tar.gz)
    Source code(zip)
  • 1.3.1(Nov 23, 2017)

  • 1.2.1(Nov 23, 2017)

  • 1.3.0(Oct 17, 2017)

  • 1.2.0(Oct 10, 2017)

    ADDED:

    • Ability to hydrate responses to objects via ClassHydrator
    • Ability to retrieve all relationships of a resource via ResourceObject::getRelationships()
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Sep 1, 2017)

    ADDED:

    • #4: Allow relationship's resource identifier objects to have a "meta" value

    CHANGED:

    • ToManyRelationship::addResourceIdentifier() uses fluent interface
    • Added missing parameter type declarations
    Source code(tar.gz)
    Source code(zip)
  • 1.0.1(Mar 10, 2017)

  • 1.0.0(Feb 28, 2017)

    ADDED:

    • Support for custom serialization
    • Ability to configure the JsonDeserializer

    CHANGED:

    • Increased minimum PHP version requirement to 7.0
    • Renamed DefaultDeserializer to JsonDeserializer
    • Moved JsonApiClient and JsonApiAsyncClient into the JsonApi\Client namespace
    • Moved deserializers into the JsonApi\Serializer namespace along with serializers
    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Dec 21, 2016)

    ADDED:

    • Better support for "about" links

    CHANGED:

    • Error::createFromArray() performs more type checks before instantiating an Error object
    • Renamed ErrorSource::fromArray() to ErrorSource::createFromArray()
    • ErrorSource::createFromArray() performs more type checks before instantiating an ErrorSource object
    • Added Relationships::createFromArray() instead of the constructor
    • Renamed Relationship::resourceLink to Relationship::firstResourceLink

    FIXED:

    • Representing error status as string as per the spec
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Dec 20, 2016)

    CHANGED:

    • Link::createFromArray() performs type checks before instantiating a Link object
    • Added a ResourceObject::createFromArray() instead of the constructor
    • ResourceObject::createFromArray() performs more type checks before instantiating a ResourceObject
    • The "jsonapi" member is always present when using Document::toArray()
    • The "links" member won't be present in a relationship when it is empty when using ResourceObject::toArray()

    REMOVED:

    • Links::hasLinks() method
    • JsonApi::hasJsonApi() method

    FIXED:

    • The "jsonapi" member was incorrectly recognized as jsonApi
    • If the "jsonapi" member doesn't define the version, "1.0" will be the default now as per the spec
    • Invocation of Links::hasLink() and thus Links::hasSelf() etc. methods resulted in an infinite loop
    • Relationship::toArray() showed the "data" member incorrectly for to-one relationships
    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Dec 19, 2016)

    ADDED:

    • Added JsonApiResource::setRelationship()
    • Unit tests
    • Docker support to run tests
    • Links::hasAnyLinks() method

    CHANGED:

    • Added JsonApiResource::setToOneRelationship() instead of JsonApiResource::setToOneResourceIdentifier()
    • Added JsonApiResource::setToManyRelationship() instead of JsonApiResource::addToManyResourceIdentifier()
    • A Links member won't be present in the data member when it is empty when using Document::toArray()
    • Renamed several methods of ResourceObjects (removed get prefixes)
    • Renamed JsonApiRelationshipInterface class to RelationshipInterface
    • Renamed JsonApiResource class to ResourceObject
    • Renamed JsonApiToManyRelationship class to ToManyRelationship
    • Renamed JsonApiToOneRelationship class to ToOneRelationship

    FIXED:

    • JsonApiResource didn't add relationships to the request
    • The type constructor argument became required for JsonApiResource
    • JsonApiRequestBuilder::setPort() didn't do anything
    • JsonApiRequestBuilder::setJsonApi*() methods didn't work as intended
    • JsonApiRequestBuilder::update() will now set the request method to "PATCH" instead of "UPDATE"
    • Error objects are correctly listed under the errors member when using Document::toArray()
    • Correctly transforming included member when using Document::toArray()
    • ResourceObjects::isSingleResourceDocument() Document::isResourceDocument() returned wrong value When data member was null
    • ResourceObjects::hasAnyPrimaryResources() returned wrong value When data member was null
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Dec 18, 2016)

    CHANGED:

    • Renamed JsonApiAsyncClient::requestConcurrent() to JsonApiAsyncClient::sendConcurrentAsyncRequests()
    • Renamed Document::hasPrimaryResources() to Docment::hasAnyPrimaryResources()
    • Renamed Document::hasIncludedResources() to Docment::hasAnyIncludedResources()
    • Renamed almost all methods in JsonApiRequestBuilder
    • Do not sort included resources by type and id
    • Improved documentation
    Source code(tar.gz)
    Source code(zip)
  • 0.5.0(Nov 1, 2016)

    ADDED:

    • Support for custom deserialization

    CHANGED:

    • Updated minimum PHP version requirement to 5.6
    • Renamed Resource to ResourceObject and Resources to ResourceObjects
    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Sep 16, 2016)

    ADDED:

    • Support for HTTPlug library to abstract the HTTP client away
    • JsonApiResource::setAttributes() method

    CHANGED:

    • Moved asyncronous functionalities of JsonApiClient into JsonApiAsyncClient
    • JsonApiClient::request() method was renamed to JsonApiClient::sendRequest()
    • JsonApiAsyncClient::requestAsync() method was renamed to JsonApiAsyncClient::sendAsyncRequest()

    FIXED:

    • Error when serialized response body is apparently empty even though it contains data
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Jul 30, 2016)

    ADDED:

    • Ability to define options for JsonApiClient
    • Document::error($number) utility method to retrieve only one error
    • Response::isSuccessful() to determine if the response was successful
    • Response::isSuccessful() to determine if the response was successful and contains a document
    • Support for PHPUnit 5.0

    CHANGED:

    • Improved performance

    FIXED:

    • PHP version constraint in composer.json
    • If the response didn't contain a valid JSON, Response::document() could have raised a fatal error
    • The type and id weren't included in the request body
    • Empty responses weren't considered successful by JsonApiResponse::isSuccessful()
    • Empty relationship data can now be discovered
    • Sorting does not happen on resource ID
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Oct 20, 2015)

    ADDED:

    • Ability to send multiple async requests concurrently
    • Ability to determine the type of documents and relationships

    CHANGED:

    • Shortened getter names (removed the "get" prefix)
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Oct 18, 2015)

Owner
Woohoo Labs.
Woohoo Labs.
A simple and flexible PHP middleware dispatcher based on PSR-7, PSR-11, and PSR-15

Woohoo Labs. Harmony Woohoo Labs. Harmony is a PSR-15 compatible middleware dispatcher. Harmony was born to be a totally flexible and almost invisible

Woohoo Labs. 153 Sep 5, 2022
A simple PHP package for sending messages to Slack, with a focus on ease of use and elegant syntax.

Slack for PHP | A simple PHP package for sending messages to Slack with incoming webhooks, focused on ease-of-use and elegant syntax. supports: PHP 7.

null 128 Nov 28, 2022
PSR-15 middleware to geolocate the client using the ip address

middlewares/geolocation ![SensioLabs Insight][ico-sensiolabs] Middleware to geolocate the client using the ip address and Geocoder and save the result

Middlewares 10 Mar 22, 2022
Best resources restful api for developers (with JSON:API standar specification design)

List API Best resources restful api for developers (with JSON:API standar specification design). API Resource Endpoint Name Resource Description Al Qu

Noval 2 Jan 18, 2022
Chargebee API PHP Client (for API version 2 and Product Catalog version 2.0)

chargebee-php-sdk Overview This package provides an API client for Chargebee subscription management services. It connects to Chargebee REST APIs for

GLOBALIS media systems 8 Mar 8, 2022
Simple PHP API client for tube-hosting.com rest API

Tube-Hosting API PHP client Explanation This PHP library is a simple api wrapper/client for the tube-hosting.com api. It is based on the provided docu

null 4 Sep 12, 2022
JSON:API serializer for PHP resources

kwai-jsonapi A JSON:API serializer for PHP classes using PHP attributes. Currently, this library has no support for links. Installation composer requi

Franky Braem 1 Jan 19, 2022
Laravel API 文档生成器,可以将基于 Laravel 项目的项目代码,自动生成 json 或 md 格式的描述文件。

Thresh Laravel API 文档生成器,可以将基于 Laravel 项目的项目代码,自动生成 json 或 md 格式的描述文件。 安装 $ composer require telstatic/thresh -vvv 功能 生成 Markdown 文档 生成 Postman 配置文件 生

静止 5 Jul 12, 2021
JSON API (jsonapi.org) package for Laravel applications.

cloudcreativity/laravel-json-api Status This package has now been rewritten, substantially improved and released as the laravel-json-api/laravel packa

Cloud Creativity 753 Dec 28, 2022
A based PSR-15 microframework that also sets maximum flexibility with minimum complexity and easy replaceability of the individual components, but also of the framework.

chubbyphp-framework Description A based PSR-15 microframework that also sets maximum flexibility with minimum complexity and easy replaceability of th

chubbyphp 106 Dec 9, 2022
OpenAPI(v3) Validators for Symfony http-foundation, using `league/openapi-psr7-validator` and `symfony/psr-http-message-bridge`.

openapi-http-foundation-validator OpenAPI(v3) Validators for Symfony http-foundation, using league/openapi-psr7-validator and symfony/psr-http-message

n1215 2 Nov 19, 2021
PSR-7 middleware foundation for building and dispatching middleware pipelines

laminas-stratigility From "Strata", Latin for "layer", and "agility". This package supersedes and replaces phly/conduit. Stratigility is a port of Sen

Laminas Project 47 Dec 22, 2022
Tukio is a complete and robust implementation of the PSR-14 Event Dispatcher specification

Tukio is a complete and robust implementation of the PSR-14 Event Dispatcher specification. It supports normal and debug Event Dispatchers, both runtime and compiled Providers, complex ordering of Listeners, and attribute-based registration on PHP 8.

Larry Garfield 70 Dec 19, 2022
Read and write OpenAPI 3.0.x YAML and JSON files and make the content accessible in PHP objects.

php-openapi Read and write OpenAPI 3.0.x YAML and JSON files and make the content accessible in PHP objects. It also provides a CLI tool for validatin

Carsten Brandt 399 Dec 23, 2022
Simple and effective multi-format Web API Server to host your PHP API as Pragmatic REST and / or RESTful API

Luracast Restler ![Gitter](https://badges.gitter.im/Join Chat.svg) Version 3.0 Release Candidate 5 Restler is a simple and effective multi-format Web

Luracast 1.4k Dec 14, 2022
Disable Google's FLoC with help of PSR-15 middleware

Disable Google's FLoC with PSR-15 middleware This package will help you disable Google's FLoC. Installation You can install the package via composer:

P7V 9 Dec 14, 2022
It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications

OpenAPI PSR-7 Message (HTTP Request/Response) Validator This package can validate PSR-7 messages against OpenAPI (3.0.x) specifications expressed in Y

The League of Extraordinary Packages 421 Jan 3, 2023
A PSR-15 middleware adapter for react/http

A PSR-15 middleware adapter for react/http Wraps PSR-15 middleware into coroutines using RecoilPHP making them usable within react/http as middleware.

Friends of ReactPHP 22 Nov 12, 2022
PSR-15 middleware to use Whoops as error handler

middlewares/whoops Middleware to use Whoops as error handler. Requirements PHP >= 7.2 A PSR-7 http library A PSR-15 middleware dispatcher Installation

Middlewares 31 Jun 23, 2022