Download the PHP package undabot/json-api-symfony without Composer

On this page you can find all versions of the php package undabot/json-api-symfony. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package json-api-symfony

json-api-symfony

This library was created with the idea of returning JSON:API compliant responses using Symfony as an application infrastructure provider. Library provides developers with support for having objects inside the application and "attaching" them to the response class. The end result is a response class with JSON:API data structure. The library also provides support for handling request query parameters and parsing JSON:API compliant request body into separate PHP classes.

The library itself is a wrapper around the Symfony framework, and it uses json-api-core library to do the heavy lifting when creating JSON:API compliant requests and responses.

This document covers following sections:

Usage

This section covers usage examples and a detailed explanation of library elements. After going through it, you will have more than sufficient knowledge of using the library, without the need for further help.


Before we proceed, here are a few notes:

How to return the JSON:API compliant response?

To return JSON:API compliant response, you have to go through a couple of steps - what you need is read or write side and responder. Both read and write sides logically consist of several classes:

Responder serves as a glue, mapping the entities to models.

Before going deeper into the read and write models, responders and controllers, it's a good idea to describe how we distinguish attributes from relations in our models. To recognise which property is attribute and which one is relation we use annotations. Each model should have one "main" annotation that determines its type, a top-level member for any resource object. This annotation is placed just above the class declaration, and it looks like this:

Apart from @ResourceType annotation, there are three more - @Attribute, @ToOne and @ToMany.

@Attribute annotation says that the property is considered an attribute.

@ToOne and @ToMany annotations say that the property is a relationship. Relations must consist of name and type value inside ToOne and ToMany annotations, e.g.

Name value is what we want to show in response. For this example, article_comments is the name for this relationship that will be returned in the response. If no name is defined in the annotation the relationship will inherit the property name.\ Type value is the resource type of relation to which we're referring. Here, we're referring to comments, meaning that a model of type comments related to this model is part of the codebase. Keep in mind that the library links only types of the exact name, so if your model is of type comment, and you make a mistake and write plural library will throw an error.

Relationships can be nullable, and to add a nullable relationship to the model, you just need to assign a bool value to nullable property inside the annotation, like in the following example. Don't forget to null safe type-hint your property in that case, and remember - relationships are not nullable by default.

With this knowledge, lets dive into the more concrete examples.

Write side

The request and response lifecycle consists of receiving the data from the client and returning the data to the client - write and read side. When receiving data, it must be sent through the request's body as a JSON string compliant with JSON:API. This library allows us to fetch given data, validate it and convert it to PHP class.

Create

The write model consists of the annotated properties that build the resource we are about to create. So if we're about to create article with id, title and some related comments this is how the create (write) model would look like.

This class is created entirely from request data, not from another class, so it has only constructor. Each property has an annotation stating whether it is a relation or an attribute. It is important that each relation has its name and type value inside ToOne and ToMany annotation.\ As you will see later in the read model, we will usually have the same properties in the read and write model. So if you have that case, you can combine them in the same model and have, e.g. ArticleModel. Also, if your update model is the same as the write model, you can combine them into one and have one write model (for create and update), and one read model.

Now when you know how to create a model for your write side, let's see what else we need. Suppose you already have an article entity what we're missing here is a controller.\ To extract the data from the request into the model, we'll have to inject the entire request. Below is an example in which we'll use CreateResourceRequestInterface (and its concrete implementation) for help.

Update

When updating a resource, the client may send you some of the fields that are part of the read model, not an entire model. E.g. if we're updating Article with content, there is no need to send the title or some other property. However, for the mentioned case, we need some model and need to have the option to create it from the current state. So we can use the write model from the above example, but we'll have to add fromSomething method and then use it inside the controller like in the example below.

If you are to use the same model to create and update resource, this model needs to have fromSomething method. As mentioned above, you can call it ArticleModel, and it would look like this:

In practice, fromSomething method is called fromEntity since we mostly create update/read models from entities. If you, for example, use view models then you can write it like this:

Read side

Like the write model, the read model is a class with annotated properties that you want to return to the client. E.g. if you need to return this JSON:API response:

You would create this model:

Same as in create and/or update model, read model consists of properties with annotations. Each property has annotation that states whether is it a relation or attribute.

Another part of this class is the static method fromSomething used when creating the read model. As previously mentioned, we often use fromEntity naming. In addition, we often use other namings for this method, such as fromValueObject or fromAggregate. The data flow that we usually use is similar to this:

  1. We fetch data from the database as an entity and work with that entity through our application.
  2. We use the entity until the moment in which we need to return the response.
  3. At that moment, we pass the entity to this library to create a proper read model.

With everything previously written we have covered the basic model. What if the model is not so basic, and it requires includes, sorting, filtering, etc?

Includes

We often need to return objects related to the given resource. Maybe our client needs more than id and type of the relation, and we need to include the details of all comments and author in our example. E.g. our client needs a response similar to the one below.

To return more than just a resource pointer to the client, we need to pass an array of objects that we need to return as the second argument of the called method. For example, if we need to include details of all comments related to the article, we would pass an array of comment entities as a second argument of the method called from the Responder. Like this:

Whether the client requested explicitly requested includes, or the response needs to have all the possible includes, Responder needs to have the mapping for the read model of each object that is returned in the response.

When returning includes inside a list of objects (e.g. returning a list of articles from the /articles endpoint) there is a chance that different resources will have same relations. In that case we want to include the relation only once. E.g., if we have a list of articles and some articles have the same author, we want to include that author only once. We can use \Undabot\SymfonyJsonApi\Model\Collection\UniqueCollection for that.

Here are some examples:

Request filters

If the endpoint requires filters, this is how to add them. First, allow the filters by calling the allowFilters method. This method receives an array of strings, which are actually filter names. After allowing the filters you can read their values by calling $request->getFilterSet()?->getFilterValue('filter_name'). Keep in mind that filtering is supported on endpoints that deal with collections.

Pagination

The library can currently read page and offset based pagination. Page based pagination is automatically converted to an offset based pagination. So no matter which one the client sends to the server, we can read them both like in the example below.

Sorting

Similar to filtering, sorting needs to be allowed first. The example below shows how to enable and read sorting values.

Fields

We can allow client calling only some fields:

Responder

Responder is a glue that we need to link the entities with their models. It holds the array of (entity) classes mapped to a callable inside a model.

Every Responder you create, and you can have more of them in your project, should extend \Undabot\SymfonyJsonApi\Http\Service\Responder\AbstractResponder\AbstractResponderclass and implement getMap() method in the following way.

This is how the Responder is used in a controller to return the single resource (getById endpoint).

This is how the Responder is used in a controller to return the collection of resources (list endpoint). Responder will convert each entity from the array of entites to read model.

As you can see, Responder supports several different methods, which you can use depending on the response you need to return to the client. Each method accepts a data object (or collection/array of data objects) along with some other optional arguments and constructs a DTO representing the JSON:API compliant response. This is the list of the supported methods:

ViewResponseSubscriber (\Undabot\SymfonyJsonApi\Http\EventSubscriber\ViewResponseSubscriber) will then encode the response generated by the Responder to the JSON:API compliant JSON response. Furthermore, it will add the correct HTTP status code to the response, e.g. 201 if the ResourceCreatedMethod has been called from the Responder, or 204 if you have called ResourceDeletedResponse.

resourceCollection(...) method

Accepts an array of data objects you have defined an encoding map entry for in the Responder and converts them to a ResourceCollectionResponse.

resourceObjectCollection(...) method

Accepts an array of objects you have defined an encoding map entry for in the Responder and converts them to a ResourceCollectionResponse.

resource(...) method

Accepts data, e.g. single object that will be converted to a ResourceResponse. Data can also be null if no data is present. For example, if someone requests /user/1/car and car is to one relation which is NOT present on the user because the user doesn't own the car. In this example, the user with id 1 exists in the database.

resourceCreated(...) method

Accepts data, e.g. single object that will be converted to a ResourceCreatedResponse.

resourceUpdated(...) method

Accepts data, e.g. single object that will be converted to a ResourceUpdatedResponse.

resourceDeleted(...) method

ResourceDeletedResponse response is basically 204 HTTP status code with no content.

Configuration

Exception listener has default priority of -128 but it can be configured by creating config/packages/json_api_symfony.yaml with following parameters

Development

There is a custom docker image that you can use for development. This repository is mounted inside the container, and any changes made to the files are automatically propagated into the container. You can use the container to run tests and check for compatibility issues. There isn't any syncing since the filesystem points to the 2 locations simultaneously.

Use the script called dev.sh to manage the image. Here are the available commands:

Glossary

Term Description
Entity Domain object that is modeled and used in the application. It might map to a single row in a relational database, but it won't necessarily do so.
(API) Model Domain representation for specific API. Data-transfer object, POPO that contains only values of attributes and identifiers of related resources.
(JSON:API) Resource Object representation of JSON:API resource defined by the JSON:API specification.

Performance

Performance is an important part of every backend application. We tested returning a list of resources on the 1 vCPU (1797.917 MHz) server with 1 GB of RAM. The installed PHP version was 8.0.3 and Symfony packages were 6.0.*. xDebug was disabled.

The tested request was a list of 10 resources with included relationships.

Example:

The response was around 17.5 kb and the average response time was 158 ms.

The same response was tested without the library in a way that it was assembled inside the controller by looping through data and creating JSON:API compatible PHP array which was returned in JsonResponse.

The response was around 17.5 kb and the average response time was 140 ms.

The same resources were returned as plain response without any standardization.

Example:

The response was around 6 kb and the average response time was 103 ms.


All versions of json-api-symfony with dependencies

PHP Build Version
Package Version
Requires php Version ^8
ext-json Version *
undabot/json-api-core Version 2.1.7
symfony/http-kernel Version ^5.0 || ^6.0
symfony/validator Version ^5.0 || ^6.0
symfony/orm-pack Version ^2.1
doctrine/annotations Version ^1.12
symfony/property-access Version ^5.0 || ^6.0
symfony/yaml Version ^5.0 || ^6.0
ramsey/uuid Version ^4.1
beberlei/assert Version ^3.3
sensio/framework-extra-bundle Version ^6.1
symfony/serializer Version ^5.0 || ^6.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package undabot/json-api-symfony contains the following files

Loading the files please wait ....