All Projects → nilportugues → Symfony Jsonapi

nilportugues / Symfony Jsonapi

Licence: mit
JSON API Transformer Bundle for Symfony 2 and Symfony 3

Projects that are alternatives of or similar to Symfony Jsonapi

Laravel5 Jsonapi
Laravel 5 JSON API Transformer Package
Stars: ✭ 313 (+174.56%)
Mutual labels:  api, microservices, microservice, json-api, jsonapi, json, transformer
Jsonapi Utils
Build JSON API-compliant APIs on Rails with no (or less) learning curve.
Stars: ✭ 191 (+67.54%)
Mutual labels:  api, json-api, jsonapi, json, serializer
Jsonapi Rb
Efficiently produce and consume JSON API documents.
Stars: ✭ 219 (+92.11%)
Mutual labels:  api, json-api, jsonapi, json, serialization
laravel5-jsonapi-dingo
Laravel5 JSONAPI and Dingo together to build APIs fast
Stars: ✭ 29 (-74.56%)
Mutual labels:  serialization, json-api, serializer, jsonapi, transformer
Jsonapi Rails
Rails gem for fast jsonapi-compliant APIs.
Stars: ✭ 242 (+112.28%)
Mutual labels:  api, json-api, jsonapi, json, serialization
Dictfier
Python library to convert/serialize class instances(Objects) both flat and nested into a dictionary data structure. It's very useful in converting Python Objects into JSON format
Stars: ✭ 67 (-41.23%)
Mutual labels:  api, json-api, json, serializer, serialization
Graphiti
Stylish Graph APIs
Stars: ✭ 783 (+586.84%)
Mutual labels:  api, microservices, microservice, json-api
Json Api Dart
JSON:API client for Dart/Flutter
Stars: ✭ 53 (-53.51%)
Mutual labels:  api, json-api, jsonapi, json
Jsonapi parameters
Rails-way to consume JSON:API input
Stars: ✭ 50 (-56.14%)
Mutual labels:  api, json-api, jsonapi, json
Jsonapi.rb
Lightweight, simple and maintained JSON:API support for your next Ruby HTTP API.
Stars: ✭ 116 (+1.75%)
Mutual labels:  api, json-api, jsonapi, serializer
Core
The server component of API Platform: hypermedia and GraphQL APIs in minutes
Stars: ✭ 2,004 (+1657.89%)
Mutual labels:  api, jsonapi, symfony, symfony-bundle
php-json-api
JSON API transformer outputting valid (PSR-7) API Responses.
Stars: ✭ 68 (-40.35%)
Mutual labels:  serialization, json-api, serializer, transformer
php-serializer
Serialize PHP variables, including objects, in any format. Support to unserialize it too.
Stars: ✭ 47 (-58.77%)
Mutual labels:  serialization, json-api, jsonapi, transformer
laravel5-hal-json
Laravel 5 HAL+JSON API Transformer Package
Stars: ✭ 15 (-86.84%)
Mutual labels:  serialization, serializer, jsonapi, transformer
Server
Serve your Rubix ML models in production with scalable stand-alone model inference servers.
Stars: ✭ 30 (-73.68%)
Mutual labels:  api, microservice, json-api
Api Platform
Create REST and GraphQL APIs, scaffold Jamstack webapps, stream changes in real-time.
Stars: ✭ 7,144 (+6166.67%)
Mutual labels:  api, symfony, symfony-bundle
Eminim
JSON serialization framework for Nim, works from a Stream directly to any type and back. Depends only on stdlib.
Stars: ✭ 32 (-71.93%)
Mutual labels:  json, serializer, serialization
Google Analytics Api Symfony
Google Analytics API Symfony Bundle
Stars: ✭ 43 (-62.28%)
Mutual labels:  api, symfony, symfony-bundle
Jsonapi
[Bolt Extension] JSON API for Bolt CMS
Stars: ✭ 55 (-51.75%)
Mutual labels:  api, jsonapi, json
Up
Up focuses on deploying "vanilla" HTTP servers so there's nothing new to learn, just develop with your favorite existing frameworks such as Express, Koa, Django, Golang net/http or others.
Stars: ✭ 8,439 (+7302.63%)
Mutual labels:  api, microservices, microservice

Symfony JSON-API Transformer Bundle

For Symfony 2 and Symfony 3

Scrutinizer Code Quality SensioLabsInsight Latest Stable Version Total Downloads License Donate

Installation

Step 1: Download the Bundle

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

$ composer require nilportugues/jsonapi-bundle

Step 2: Enable the Bundle

Then, enable the bundle by adding it to the list of registered bundles in the app/AppKernel.php file of your project:

<?php
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new NilPortugues\Symfony\JsonApiBundle\NilPortuguesSymfonyJsonApiBundle(),
        );
        // ...
    }
    // ...
}

Usage

Creating the mappings

Mapping directory

Mapping files should be located at the app/config/serializer directory. This directory must be created.

It can be also be customized and placed elsewhere by editing the app/config/config.yml configuration file:

# app/config/config.yml

nilportugues_json_api:
    mappings: 
        - "%kernel.root_dir%/config/serializer/"
        - @AppBundle/Product/config/Mappings

Mapping files

The JSON-API transformer works by transforming an existing PHP object into its JSON representation. For each object, a mapping file is required.

Mapping files must be placed in the mappings directory. The expected mapping file format is .yml and will allow you to rename, hide and create links relating all of your data.

For instance, here's a quite complex Post object to demonstrate how it works:

$post = new Post(
    new PostId(9),
    'Hello World',
    'Your first post',
    new User(
        new UserId(1),
        'Post Author'
    ),
    [
        new Comment(
            new CommentId(1000),
            'Have no fear, sers, your king is safe.',
            new User(new UserId(2), 'Barristan Selmy'),
            [
                'created_at' => (new DateTime('2015/07/18 12:13:00'))->format('c'),
                'accepted_at' => (new DateTime('2015/07/19 00:00:00'))->format('c'),
            ]
        ),
    ]
);

And the series of mapping files required:

# app/config/serializer/acme_domain_dummy_post.yml

mapping:
  class: Acme\Domain\Dummy\Post
  alias: Message
  aliased_properties:
    author: author
    title: headline
    content: body
  hide_properties: []
  id_properties:
    - postId
  urls:
    self: get_post ## @Route name
    comments: get_post_comments ## @Route name
  relationships:
    author:
      related: get_post_author ## @Route name
      self: get_post_author_relationship  ## @Route name
# app/config/serializer/acme_domain_dummy_value_object_post_id.yml

mapping:
  class: Acme\Domain\Dummy\ValueObject\PostId
  aliased_properties: []
  hide_properties: []
  id_properties:
  - postId
  urls:
    self: get_post  ## @Route name
  relationships:
    comment:
      self: get_post_comments_relationship  ## @Route name
# app/config/serializer/acme_domain_dummy_comment.yml

mapping:
  class: Acme\Domain\Dummy\Comment
  aliased_properties: []
  hide_properties: []
  id_properties:
    - commentId
  urls:
    self: get_comment ## @Route name
  relationships:
    post:
      self: get_post_comments_relationship ## @Route name
# app/config/serializer/acme_domain_dummy_value_object_comment_id.yml

mapping:
  class: Acme\Domain\Dummy\ValueObject\CommentId
  aliased_properties: []
  hide_properties: []
  id_properties:
    - commentId
  urls:
    self: get_comment ## @Route name
  relationships:
    post:
      self: get_post_comments_relationship ## @Route name
# app/config/serializer/acme_domain_dummy_user.yml

mapping:
  class: Acme\Domain\Dummy\User
  aliased_properties: []
  hide_properties: []
  id_properties:
  - userId
  urls:
    self: get_user
    friends: get_user_friends  ## @Route name
    comments: get_user_comments  ## @Route name
# app/config/serializer/acme_domain_dummy_value_object_user_id.yml

mapping:
  class: Acme\Domain\Dummy\ValueObject\UserId
  aliased_properties: []
  hide_properties: []
  id_properties:
  - userId
  urls:
    self: get_user  ## @Route name
    friends: get_user_friends  ## @Route name
    comments: get_user_comments  ## @Route name

Outputing API Responses

It is really easy, just get an instance of the JsonApiSerializer from the Service Container and pass the object to its serialize() method. Output will be valid JSON-API.

Here's an example of a Post object being fetched from a Doctrine repository.

Finally, a helper trait, JsonApiResponseTrait is provided to write fully compilant responses wrapping the PSR-7 Response objects provided by the original JSON API Transformer library.

<?php
namespace AppBundle\Controller;

use NilPortugues\Symfony\JsonApiBundle\Serializer\JsonApiResponseTrait;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class PostController extends Controller
{
    use JsonApiResponseTrait;

    /**
     * @\Symfony\Component\Routing\Annotation\Route("/post/{postId}", name="get_post")
     *
     * @param $postId
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function getPostAction($postId)
    {
        $post = $this->get('doctrine.post_repository')->find($postId);
        
        $serializer = $this->get('nil_portugues.serializer.json_api_serializer');

        /** @var \NilPortugues\Api\JsonApi\JsonApiTransformer $transformer */
        $transformer = $serializer->getTransformer();
        $transformer->setSelfUrl($this->generateUrl('get_post', ['postId' => $postId], true));
        $transformer->setNextUrl($this->generateUrl('get_post', ['postId' => $postId+1], true));

        return $this->response($serializer->serialize($post));
    }
} 

Output:

HTTP/1.1 200 OK
Cache-Control: private, max-age=0, must-revalidate
Content-type: application/vnd.api+json
{
    "data": {
        "type": "message",
        "id": "9",
        "attributes": {
            "headline": "Hello World",
            "body": "Your first post"
        },
        "links": {
            "self": {
                "href": "http://example.com/posts/9"
            },
            "comments": {
                "href": "http://example.com/posts/9/comments"
            }
        },
        "relationships": {
            "author": {
                "links": {
                    "self": {
                        "href": "http://example.com/posts/9/relationships/author"
                    },
                    "related": {
                        "href": "http://example.com/posts/9/author"
                    }
                },
                "data": {
                    "type": "user",
                    "id": "1"
                }
            }
        }
    },
    "included": [
        {
            "type": "user",
            "id": "1",
            "attributes": {
                "name": "Post Author"
            },
            "links": {
                "self": {
                    "href": "http://example.com/users/1"
                },
                "friends": {
                    "href": "http://example.com/users/1/friends"
                },
                "comments": {
                    "href": "http://example.com/users/1/comments"
                }
            }
        },
        {
            "type": "user",
            "id": "2",
            "attributes": {
                "name": "Barristan Selmy"
            },
            "links": {
                "self": {
                    "href": "http://example.com/users/2"
                },
                "friends": {
                    "href": "http://example.com/users/2/friends"
                },
                "comments": {
                    "href": "http://example.com/users/2/comments"
                }
            }
        },
        {
            "type": "comment",
            "id": "1000",
            "attributes": {
                "dates": {
                    "created_at": "2015-08-13T21:11:07+02:00",
                    "accepted_at": "2015-08-13T21:46:07+02:00"
                },
                "comment": "Have no fear, sers, your king is safe."
            },
            "relationships": {
                "user": {
                    "data": {
                        "type": "user",
                        "id": "2"
                    }
                }
            },            
            "links": {
                "self": {
                    "href": "http://example.com/comments/1000"
                }
            }
        }
    ],
    "links": {
        "self": {
            "href": "http://example.com/posts/9"
        },
        "next": {
            "href": "http://example.com/posts/10"
        }
    },
    "jsonapi": {
        "version": "1.0"
    }
}

Request objects

JSON API comes with a helper Request class, NilPortugues\Api\JsonApi\Http\Request\Request(ServerRequestInterface $request), implementing the PSR-7 Request Interface. Using this request object will provide you access to all the interactions expected in a JSON API:

JSON API Query Parameters:
  • &fields[resource]=field1,field2 will only show the specified fields for a given resource.
  • &include=resource show the relationship for a given resource.
  • &include=resource.resource2 show the relationship field for those depending on resource2.
  • &sort=field1,-field2 sort by field2 as DESC and field1 as ASC.
  • &sort=-field1,field2 sort by field1 as DESC and field2 as ASC.
  • &page[number] will return the current page elements in a page-based pagination strategy.
  • &page[size] will return the total amout of elements in a page-based pagination strategy.
  • &page[limit] will return the limit in a offset-based pagination strategy.
  • &page[offset] will return the offset value in a offset-based pagination strategy.
  • &page[cursor] will return the cursor value in a cursor-based pagination strategy.
  • &filter will return data passed in the filter param.
NilPortugues\Api\JsonApi\Http\Request\Request

Given the query parameters listed above, Request implements helper methods that parse and return data already prepared.

namespace \NilPortugues\Api\JsonApi\Http\Request;

class Request
{
  public function __construct(ServerRequestInterface $request = null) { ... }
  public function getIncludedRelationships() { ... }
  public function getSort() { ... }
  public function getPage() { ... }
  public function getFilters() { ... }
  public function getFields() { ... }
}

Response objects (JsonApiResponseTrait)

The following JsonApiResponseTrait methods are provided to return the right headers and HTTP status codes are available:

    private function errorResponse($json);
    private function resourceCreatedResponse($json);
    private function resourceDeletedResponse($json);
    private function resourceNotFoundResponse($json);
    private function resourcePatchErrorResponse($json);
    private function resourcePostErrorResponse($json);
    private function resourceProcessingResponse($json);
    private function resourceUpdatedResponse($json);
    private function response($json);
    private function unsupportedActionResponse($json);

Integration with NelmioApiDocBundleBundle

The NelmioApiDocBundle is a very well known bundle used to document APIs. Integration with the current bundle is terrible easy.

Here's an example following the PostContoller::getPostAction() provided before:

<?php
namespace AppBundle\Controller;

use NilPortugues\Symfony\JsonApiBundle\Serializer\JsonApiResponseTrait;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class PostController extends Controller
{
    use JsonApiResponseTrait;

    /**
     * Get a Post by its identifier. Will return Post, Comments and User data.
     *
     * @Nelmio\ApiDocBundle\Annotation\ApiDoc(
     *  resource=true,
     *  description="Get a Post by its unique id",
     * )
     *
     * @Symfony\Component\Routing\Annotation\Route("/post/{postId}", name="get_post")
     * @Sensio\Bundle\FrameworkExtraBundle\Configuration\Method({"GET"})
     *
     * @param $postId
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function getPostAction($postId)
    {
        $post = $this->get('doctrine.post_repository')->find($postId);
        
        $serializer = $this->get('nil_portugues.serializer.json_api_serializer');

        /** @var \NilPortugues\Api\JsonApi\JsonApiTransformer $transformer */
        $transformer = $serializer->getTransformer();
        $transformer->setSelfUrl($this->generateUrl('get_post', ['postId' => $postId], true));
        $transformer->setNextUrl($this->generateUrl('get_post', ['postId' => $postId+1], true));

        return $this->response($serializer->serialize($post));
    }
} 

And the recommended configuration to be added in app/config/config.yml

#app/config/config.yml

nelmio_api_doc:
  sandbox:
        authentication:
          name: access_token
          delivery: http
          type:     basic
          custom_endpoint: false
        enabled:  true
        endpoint: ~
        accept_type: ~
        body_format:
            formats: []
            default_format: form
        request_format:
            formats:
                json: application/vnd.api+json
            method: accept_header
            default_format: json
        entity_to_choice: false

Quality

To run the PHPUnit tests at the command line, go to the tests directory and issue phpunit.

This library attempts to comply with PSR-1, PSR-2, PSR-4 and PSR-7.

If you notice compliance oversights, please send a patch via Pull Request.

Contribute

Contributions to the package are always welcome!

Support

Get in touch with me using one of the following means:

Authors

License

The code base is licensed under the MIT license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].