{"id":13752921,"url":"https://github.com/nilportugues/laravel5-jsonapi-dingo","last_synced_at":"2025-09-16T20:48:33.241Z","repository":{"id":57027211,"uuid":"62423220","full_name":"nilportugues/laravel5-jsonapi-dingo","owner":"nilportugues","description":"Laravel5 JSONAPI and Dingo together to build APIs fast","archived":false,"fork":false,"pushed_at":"2017-04-06T14:47:08.000Z","size":16,"stargazers_count":30,"open_issues_count":6,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-08T07:51:26.876Z","etag":null,"topics":["api","dingo","json","json-api","jsonapi","laravel","laravel5","marshaller","microservice","microservices","php","php7","serialization","serializer","transformer"],"latest_commit_sha":null,"homepage":"http://nilportugues.com","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nilportugues.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-01T22:34:48.000Z","updated_at":"2022-04-01T18:50:20.000Z","dependencies_parsed_at":"2022-08-23T16:20:35.384Z","dependency_job_id":null,"html_url":"https://github.com/nilportugues/laravel5-jsonapi-dingo","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nilportugues/laravel5-jsonapi-dingo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilportugues%2Flaravel5-jsonapi-dingo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilportugues%2Flaravel5-jsonapi-dingo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilportugues%2Flaravel5-jsonapi-dingo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilportugues%2Flaravel5-jsonapi-dingo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nilportugues","download_url":"https://codeload.github.com/nilportugues/laravel5-jsonapi-dingo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilportugues%2Flaravel5-jsonapi-dingo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275482966,"owners_count":25473109,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-16T02:00:10.229Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","dingo","json","json-api","jsonapi","laravel","laravel5","marshaller","microservice","microservices","php","php7","serialization","serializer","transformer"],"created_at":"2024-08-03T09:01:12.662Z","updated_at":"2025-09-16T20:48:33.214Z","avatar_url":"https://github.com/nilportugues.png","language":"PHP","funding_links":["https://paypal.me/nilportugues"],"categories":["transformer"],"sub_categories":[],"readme":"# Laravel 5 JSON API Mappings with Dingo\n\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nilportugues/laravel5-jsonapi-dingo/badges/quality-score.png??b=master)](https://scrutinizer-ci.com/g/nilportugues/laravel5-jsonapi-dingo/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/c4fb1b65-e649-464f-b78c-e5802743a78f/mini.png?)](https://insight.sensiolabs.com/projects/c4fb1b65-e649-464f-b78c-e5802743a78f) \n[![Latest Stable Version](https://poser.pugx.org/nilportugues/laravel5-json-api-dingo/v/stable)](https://packagist.org/packages/nilportugues/laravel5-json-api-dingo) \n[![Total Downloads](https://poser.pugx.org/nilportugues/laravel5-json-api-dingo/downloads)](https://packagist.org/packages/nilportugues/laravel5-json-api-dingo) \n[![License](https://poser.pugx.org/nilportugues/laravel5-json-api-dingo/license)](https://packagist.org/packages/nilportugues/laravel5-json-api-dingo) \n[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://paypal.me/nilportugues)\n\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n- [JsonApiController](#jsonapicontroller)\n- [Examples: Consuming the API](#examples-consuming-the-api)\n  - [GET](#get)\n  - [POST](#post)\n  - [PUT](#put)\n  - [PATCH](#patch)\n  - [DELETE](#delete)\n- [GET Query Params: include, fields, sort and page](#get-query-params-include-fields-sort-and-page)\n- [POST/PUT/PATCH with Relationships](#postputpatch-with-relationships)\n- [Custom Response Headers](#custom-response-headers)\n- [Common Errors and Solutions](#common-errors-and-solutions)\n\nThis package makes [Laravel 5 JSON API Server Package](https://github.com/nilportugues/laravel5-jsonapi)  and [Dingo](https://github.com/dingo/api) play well together by using Dingo's API routing system instead of Laravel's.\n\nBy doing this you get the automatic formatting of the JSON API mappings and the versioning and security offered by Dingo.\n\n\n## Features \n\n- Package provides a full implementation of the **[JSON API](https://github.com/json-api/json-api)** specification, and is **featured** on the official site!\n- A **JSON API Transformer** that will allow you to convert any mapped object into a valid JSON API resource.\n- Controller boilerplate to write a fully compiliant **JSON API Server** using your **exisiting Eloquent Models**.\n- **Dingo features such as:**\n    - Multiple Authentication Adapters\n    - API Versioning\n    - Rate Limiting\n    - Internal Requests\n    - API Blueprint Documentation\n\n## Installation\n\nUse [Composer](https://getcomposer.org) to install the package:\n\n```\n$ composer require nilportugues/laravel5-json-api-dingo\n```\n\nNow run the following artisan command: \n\n```\n$ php artisan vendor:publish\n```\n\n## Configuration\n\nOpen up `config/app.php` and add the following line under `providers` array:\n\n```php\n'providers' =\u003e [\n    //...\n    NilPortugues\\Laravel5\\JsonApiDingo\\Laravel5JsonApiDingoServiceProvider::class,\n],\n```\n\n## Usage\n\n### Defining routes\n\nWe will be planning the resources ahead its implementation. All routes require to have a name.  \n\nThis is how our `app/Http/routes.php` will look:\n\n\n```php\n\u003c?php\n$api = app('Dingo\\Api\\Routing\\Router');\n\n$api-\u003eversion('v1', function ($api) {\n    $api-\u003eresource('employees', 'EmployeesController');    \n    \n    $api-\u003eget('employees/{employee_id}/orders', [ \n        'as' =\u003e 'employees.orders',\n        'uses' =\u003e 'EmployeesController@getOrdersByEmployee'\n    ]);\n});\n\n//...\n```\n\n### Definition\n\n\nFirst, let's define the Models for `Employees` and `Orders` using Eloquent.\n\n\n**Employees (Eloquent Model)**\n```php\n\u003c?php namespace App\\Model\\Database;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Validation\\ValidatesRequests;\n\nclass Employees extends Model\n{\n    public $timestamps = false;\n    protected $table = 'employees';    \n    protected $primaryKey = 'id';\n    protected $appends = ['full_name'];\n\n    /**\n     * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasOne\n     */\n    public function latestOrders()\n    {\n        return $this-\u003ehasMany(Orders::class, 'employee_id')-\u003elimit(10);\n    }\n\n    /**\n     * @return string\n     */\n    public function getFullNameAttribute()\n    {\n        return $this-\u003efirst_name.' '.$this-\u003elast_name;\n    }\n}\n\n```\n\n**Employees SQL**\n\n```sql\nCREATE TABLE `employees` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `company` varchar(50) DEFAULT NULL,\n  `last_name` varchar(50) DEFAULT NULL,\n  `first_name` varchar(50) DEFAULT NULL,\n  `email_address` varchar(50) DEFAULT NULL,\n  `job_title` varchar(50) DEFAULT NULL,\n  `business_phone` varchar(25) DEFAULT NULL,\n  `home_phone` varchar(25) DEFAULT NULL,\n  `mobile_phone` varchar(25) DEFAULT NULL,\n  `fax_number` varchar(25) DEFAULT NULL,\n  `address` longtext,\n  `city` varchar(50) DEFAULT NULL,\n  `state_province` varchar(50) DEFAULT NULL,\n  `zip_postal_code` varchar(15) DEFAULT NULL,\n  `country_region` varchar(50) DEFAULT NULL,\n  `web_page` longtext,\n  `notes` longtext,\n  `attachments` longblob,\n  PRIMARY KEY (`id`),\n  KEY `city` (`city`),\n  KEY `company` (`company`),\n  KEY `first_name` (`first_name`),\n  KEY `last_name` (`last_name`),\n  KEY `zip_postal_code` (`zip_postal_code`),\n  KEY `state_province` (`state_province`)\n) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;\nINSERT INTO `employees` (`id`, `company`, `last_name`, `first_name`, `email_address`, `job_title`, `business_phone`, `home_phone`, `mobile_phone`, `fax_number`, `address`, `city`, `state_province`, `zip_postal_code`, `country_region`, `web_page`, `notes`, `attachments`)\nVALUES\n    (10, 'Acme Industries', 'Smith', 'Mike', 'mike.smith@mail.com', 'Horticultarlist', '0118 9843212', NULL, NULL, NULL, '343 Friary Road', 'Manchester', 'Lancs.', 'M3 3DL', 'United Kingdom', NULL, NULL, NULL);\n\n```\n\n**Orders (Eloquent Model)**\n\n```php\n\u003c?php namespace App\\Model\\Database;\n\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Orders extends Model\n{   \n    public $timestamps = false;\n    protected $table = 'orders';\n    protected $primaryKey = 'id';\n\n    /**\n     * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasOne\n     */\n    public function employee()\n    {\n        return $this-\u003ebelongsTo(Employees::class, 'employee_id');\n    }\n}\n```\n\n**Orders SQL**\n\n```sql\nCREATE TABLE `orders` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `employee_id` int(11) DEFAULT NULL,\n  `customer_id` int(11) DEFAULT NULL,\n  `order_date` datetime DEFAULT NULL,\n  `shipped_date` datetime DEFAULT NULL,\n  `shipper_id` int(11) DEFAULT NULL,\n  `ship_name` varchar(50) DEFAULT NULL,\n  `ship_address` longtext,\n  `ship_city` varchar(50) DEFAULT NULL,\n  `ship_state_province` varchar(50) DEFAULT NULL,\n  `ship_zip_postal_code` varchar(50) DEFAULT NULL,\n  `ship_country_region` varchar(50) DEFAULT NULL,\n  `shipping_fee` decimal(19,4) DEFAULT '0.0000',\n  `taxes` decimal(19,4) DEFAULT '0.0000',\n  `payment_type` varchar(50) DEFAULT NULL,\n  `paid_date` datetime DEFAULT NULL,\n  `notes` longtext,\n  `tax_rate` double DEFAULT '0',\n  `tax_status_id` tinyint(4) DEFAULT NULL,\n  `status_id` tinyint(4) DEFAULT '0',\n  PRIMARY KEY (`id`),\n  KEY `customer_id` (`customer_id`),\n  KEY `employee_id` (`employee_id`),\n  KEY `id` (`id`),\n  KEY `shipper_id` (`shipper_id`),\n  KEY `tax_status` (`tax_status_id`),\n  KEY `ship_zip_postal_code` (`ship_zip_postal_code`),\n  KEY `fk_orders_orders_status1` (`status_id`),  \n  CONSTRAINT `fk_orders_employees1` FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION\n) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=utf8;\nINSERT INTO `orders` (`id`, `employee_id`, `customer_id`, `order_date`, `shipped_date`, `shipper_id`, `ship_name`, `ship_address`, `ship_city`, `ship_state_province`, `ship_zip_postal_code`, `ship_country_region`, `shipping_fee`, `taxes`, `payment_type`, `paid_date`, `notes`, `tax_rate`, `tax_status_id`, `status_id`)\nVALUES\n    (82, 10, NULL, '2015-03-12 00:00:00', '2015-03-12 00:00:00', NULL, NULL, '43, Borrowed Drive', 'New Oreleans', 'Louisiana', '4322', 'USA', 1.4000, 0.0000, NULL, NULL, NULL, 0, NULL, 0);\n\n```\n\nFollow up, we'll be creating Transformers. One Transformer is required for each class and it must implement the `\\NilPortugues\\Api\\Mappings\\JsonApiMapping` interface.\n\nWe will be placing these files at `app/Model/Api`:\n\n**EmployeesTransformer**\n\n```php\n\u003c?php namespace App\\Model\\Api;\n\nuse App\\Model\\Database\\Employees;\nuse NilPortugues\\Api\\Mappings\\JsonApiMapping;\n\nclass EmployeesTransformer implements JsonApiMapping\n{\n    /**\n     * Returns a string with the full class name, including namespace.\n     *\n     * @return string\n     */\n    public function getClass()\n    {\n        return Employees::class;\n    }\n\n    /**\n     * Returns a string representing the resource name \n     * as it will be shown after the mapping.\n     *\n     * @return string\n     */\n    public function getAlias()\n    {\n        return 'employee';\n    }\n\n    /**\n     * Returns an array of properties that will be renamed.\n     * Key is current property from the class. \n     * Value is the property's alias name.\n     *\n     * @return array\n     */\n    public function getAliasedProperties()\n    {\n        return [\n            'last_name' =\u003e 'surname',\n            \n        ];\n    }\n\n    /**\n     * List of properties in the class that will be  ignored by the mapping.\n     *\n     * @return array\n     */\n    public function getHideProperties()\n    {\n        return [\n            'attachments'\n        ];\n    }\n\n    /**\n     * Returns an array of properties that are used as an ID value.\n     *\n     * @return array\n     */\n    public function getIdProperties()\n    {\n        return ['id'];\n    }\n\n    /**\n     * Returns a list of URLs. This urls must have placeholders \n     * to be replaced with the getIdProperties() values.\n     *\n     * @return array\n     */\n    public function getUrls()\n    {\n        return [\n            'self' =\u003e ['name' =\u003e 'employees.show', 'as_id' =\u003e 'id'],\n            'employees' =\u003e ['name' =\u003e 'employees.index'],\n            'employee_orders' =\u003e ['name' =\u003e 'employees.orders', 'as_id' =\u003e 'id']\n        ];\n    }\n\n    /**\n     * Returns an array containing the relationship mappings as an array.\n     * Key for each relationship defined must match a property of the mapped class.\n     *\n     * @return array\n     */\n    public function getRelationships()\n    {\n        return [];\n    }\n       \n    /**\n     * Returns an array of properties that are mandatory to be passed in when doing create or update.\n     *\n     * @return array\n     */\n    public function getRequiredProperties() {\n        return [];\n    }\n} \n```\n\nSame goes for `Orders`,  these files will also be placed at `app/Model/Api`:\n\n**OrdersTransformer**\n\n```php\n\u003c?php namespace App\\Model\\Api;\n\nuse App\\Model\\Database\\Orders;\nuse NilPortugues\\Api\\Mappings\\JsonApiMapping;\n\nclass OrdersTransformer implements JsonApiMapping\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function getClass()\n    {\n        return Orders::class;\n    }\n    /**\n     * {@inheritDoc}\n     */\n    public function getAlias()\n    {\n        return 'order';\n    }\n    /**\n     * {@inheritDoc}\n     */\n    public function getAliasedProperties()\n    {\n        return [];\n    }\n    /**\n     * {@inheritDoc}\n     */\n    public function getHideProperties()\n    {\n        return [];\n    }\n    /**\n     * {@inheritDoc}\n     */\n    public function getIdProperties()\n    {\n        return ['id'];\n    }\n    /**\n     * {@inheritDoc}\n     */\n    public function getUrls()\n    {\n        return [\n            'self'     =\u003e ['name' =\u003e 'orders.show', 'as_id' =\u003e 'id'],\n            'employee' =\u003e ['name' =\u003e 'employees.show', 'as_id' =\u003e 'employee_id'],\n        ];\n    }\n    /**\n     * {@inheritDoc}\n     */\n    public function getRelationships()\n    {\n        return [];\n    }\n    \n    /**\n     * Returns an array of properties that are mandatory to be passed in when doing create or update.\n     *\n     * @return array\n     */\n    public function getRequiredProperties() {\n        return [];\n    }\n} \n```\n\n\n#### Usage\n\nOpen `config/jsonapi.php`. This file should return an array returning all the class mappings.\n\n\n```php\n\u003c?php\nuse App\\Model\\Api\\EmployeesTransformer;\nuse App\\Model\\Api\\OrdersTransformer;\n\nreturn [\n    EmployeesTransformer::class,\n    OrdersTransformer::class,\n];\n```\n\n## JsonApiController\n\nWhether it's Laravel 5 or Lumen, usage is exactly the same. \n\nLet's create a new controller that extends the `JsonApiController` provided by this package, as follows:\n\n**Lumen users must extends from `LumenJsonApiController` not `JsonApiController`**.\n\n```php\n\u003c?php namespace App\\Http\\Controllers;\n\nuse App\\Model\\Database\\Employees;\nuse NilPortugues\\Laravel5\\JsonApi\\Controller\\JsonApiController;\n\nclass EmployeesController extends JsonApiController\n{\n    /**\n     * Return the Eloquent model that will be used \n     * to model the JSON API resources. \n     *\n     * @return \\Illuminate\\Database\\Eloquent\\Model\n     */\n    public function getDataModel()\n    {\n        return new Employees();\n    }\n}\n```\n\n\nIn case you need to overwrite any default behaviour, the **JsonApiController** methods are:\n\n```php\n//Constructor and defined actions\npublic function __construct(JsonApiSerializer $serializer);\npublic function listAction();\npublic function getAction(Request $request);\npublic function postAction(Request $request);\npublic function patchAction(Request $request);\npublic function putAction(Request $request);\npublic function deleteAction(Request $request);\n\n//Methods returning callables that access the persistence layer\nprotected function totalAmountResourceCallable();\nprotected function listResourceCallable();\nprotected function findResourceCallable(Request $request);\nprotected function createResourceCallable();\nprotected function updateResourceCallable();\n\n//Allows modification of the response object\nprotected function addHeaders(Response $response);\n```\n\n\nBut wait! We're missing out one action, `EmployeesController@getOrdersByEmployee`. \n\nAs the name suggests, it should list orders, so the behaviour should be the same as the one of `ListAction`.\n\nIf you look inside the `listAction`you'll find a code similar to the one below, but we just ajusted the behaviour and used it in our controller to support an additional action:\n\n```php\n\u003c?php namespace App\\Http\\Controllers;\n\nuse App\\Model\\Database\\Employees;\nuse App\\Model\\Database\\Orders;\nuse NilPortugues\\Laravel5\\JsonApi\\Controller\\JsonApiController;\n\nclass EmployeesController extends JsonApiController\n{\n    /**\n     * Return the Eloquent model that will be used \n     * to model the JSON API resources. \n     *\n     * @return \\Illuminate\\Database\\Eloquent\\Model\n     */\n    public function getDataModel()\n    {\n        return new Employees();\n    }    \n    \n    /**\n     * @param Request $request\n     *\n     * @return \\Symfony\\Component\\HttpFoundation\\Response\n     */\n    public function getOrdersByEmployee(Request $request)\n    {       \n        $apiRequest = RequestFactory::create();\n        $page = $apiRequest-\u003egetPage();\n\n        if (!$page-\u003esize()) {\n            $page-\u003esetSize(10); //Default elements per page\n        }\n\n        $resource = new ListResource(\n            $this-\u003eserializer,\n            $page,\n            $apiRequest-\u003egetFields(),\n            $apiRequest-\u003egetSort(),\n            $apiRequest-\u003egetIncludedRelationships(),\n            $apiRequest-\u003egetFilters()\n        );\n        \n        $totalAmount = function() use ($request) {\n            $id = (new Orders())-\u003egetKeyName();\n            return Orders::query()\n                -\u003ewhere('employee_id', '=', $request-\u003eemployee_id)\n                -\u003eget([$id])\n                -\u003ecount();\n        };\n\n        $results = function()  use ($request) {\n            return EloquentHelper::paginate(\n                $this-\u003eserializer,\n                Orders::query()\n                    -\u003ewhere('employee_id', '=', $request-\u003eemployee_id)\n            )-\u003eget();\n        };\n\n        $uri = route('employees.orders', ['employee_id' =\u003e $request-\u003eemployee_id]);\n        \n        return $resource-\u003eget($totalAmount, $results, $uri, Orders::class);\n    }\n}\n```\n\n\nAnd you're ready to go. Yes, it is **THAT** simple!\n\n\n## Examples: Consuming the API\n\n### GET\n\nThis is the output for `EmployeesController@getAction` being consumed from command-line method issuing: `curl -X GET \"http://localhost:9000/employees/1\"`.\n\n**Output:**\n\n```\nHTTP/1.1 200 OK\nCache-Control: private, max-age=0, must-revalidate\nContent-type: application/vnd.api+json\n```\n\n```json\n{\n    \"data\": {\n        \"type\": \"employee\",\n        \"id\": \"1\",\n        \"attributes\": {\n            \"company\": \"Northwind Traders\",\n            \"surname\": \"Freehafer\",\n            \"first_name\": \"Nancy\",\n            \"email_address\": \"nancy@northwindtraders.com\",\n            \"job_title\": \"Sales Representative\",\n            \"business_phone\": \"(123)555-0100\",\n            \"home_phone\": \"(123)555-0102\",\n            \"mobile_phone\": null,\n            \"fax_number\": \"(123)555-0103\",\n            \"address\": \"123 1st Avenue\",\n            \"city\": \"Seattle\",\n            \"state_province\": \"WA\",\n            \"zip_postal_code\": \"99999\",\n            \"country_region\": \"USA\",\n            \"web_page\": \"http://northwindtraders.com\",\n            \"notes\": null,\n            \"full_name\": \"Nancy Freehafer\"\n        },\n        \"links\": {\n            \"self\": {\n                \"href\": \"http://localhost:9000/employees/1\"\n            },\n            \"employee_orders\": {\n                \"href\": \"http://localhost:9000/employees/1/orders\"\n            }\n        },\n        \"relationships\": {\n            \"latest_orders\": [\n                {\n                    \"data\": {\n                        \"type\": \"order\",\n                        \"id\": \"71\"\n                    }\n                }\n            ]\n        }\n    },\n    \"included\": [        \n        {\n            \"type\": \"order\",\n            \"id\": \"71\",\n            \"attributes\": {\n                \"employee_id\": \"1\",\n                \"customer_id\": \"1\",\n                \"order_date\": \"2006-05-24 00:00:00\",\n                \"shipped_date\": null,\n                \"shipper_id\": \"3\",\n                \"ship_name\": \"Anna Bedecs\",\n                \"ship_address\": \"123 1st Street\",\n                \"ship_city\": \"Seattle\",\n                \"ship_state_province\": \"WA\",\n                \"ship_zip_postal_code\": \"99999\",\n                \"ship_country_region\": \"USA\",\n                \"shipping_fee\": \"0.0000\",\n                \"taxes\": \"0.0000\",\n                \"payment_type\": null,\n                \"paid_date\": null,\n                \"notes\": null,\n                \"tax_rate\": \"0\",\n                \"tax_status_id\": null,\n                \"status_id\": \"0\"\n            },\n            \"links\": {\n                \"self\": {\n                    \"href\": \"http://localhost:9000/orders/71\"\n                },\n                \"employee\": {\n                    \"href\": \"http://localhost:9000/employees/1\"\n                }\n            }\n        }\n    ],\n    \"links\": {\n        \"employees\": {\n            \"href\": \"http://localhost:9000/employees\"\n        },\n        \"employee_orders\": {\n            \"href\": \"http://localhost:9000/employees/1/orders\"\n        }\n    },\n    \"jsonapi\": {\n        \"version\": \"1.0\"\n    }\n}\n```\n\n\n### POST\n\nPOST requires all member attributes to be accepted, even those hidden by the mapper. \n\nFor instance, `attachments` member was hidden, but it is required, so it needs to be passed in with a valid value. On the other hand, `full_name` member value must not be passed in as an attribute or resource creation will fail.\n\nPassing and `id` is optional and will be used instead of a server-side generated value if provided.\n\nSending the following data to the server using `POST`to the following URI  `http://localhost:9000/employees`: \n\n```json\n{\n    \"data\": {\n        \"type\": \"employee\",\n        \"attributes\": {\n            \"company\": \"NilPortugues.com\",\n            \"surname\": \"Portugués\",\n            \"first_name\": \"Nil\",\n            \"email_address\": \"nilportugues@example.com\",\n            \"job_title\": \"Web Developer\",\n            \"business_phone\": \"(123)555-0100\",\n            \"home_phone\": \"(123)555-0102\",\n            \"mobile_phone\": null,\n            \"fax_number\": \"(123)555-0103\",\n            \"address\": \"Plaça Catalunya 1\",\n            \"city\": \"Barcelona\",\n            \"state_province\": \"Barcelona\",\n            \"zip_postal_code\": \"08028\",\n            \"country_region\": \"Spain\",\n            \"web_page\": \"http://nilportugues.com\",\n            \"notes\": null,\n            \"attachments\": null\n        }\n    }        \n}        \n```\n\nWill produce: \n\n```\nHTTP/1.1 201 Created\nCache-Control: private, max-age=0, must-revalidate\nContent-type: application/vnd.api+json\nLocation: http://localhost:9000/employees/10\n```\n\nNotice how 201 HTTP Status Code is returned and Location header too. Also `attachments` is not there anymore, and `full_name` was displayed.\n\n```json\n{\n    \"data\": {\n        \"type\": \"employee\",\n        \"id\": \"10\",\n        \"attributes\": {\n            \"company\": \"NilPortugues.com\",\n            \"surname\": \"Portugués\",\n            \"first_name\": \"Nil\",\n            \"email_address\": \"nilportugues@example.com\",\n            \"job_title\": \"Web Developer\",\n            \"business_phone\": \"(123)555-0100\",\n            \"home_phone\": \"(123)555-0102\",\n            \"mobile_phone\": null,\n            \"fax_number\": \"(123)555-0103\",\n            \"address\": \"Plaça Catalunya 1\",\n            \"city\": \"Barcelona\",\n            \"state_province\": \"Barcelona\",\n            \"zip_postal_code\": \"08028\",\n            \"country_region\": \"Spain\",\n            \"web_page\": \"http://nilportugues.com\",\n            \"notes\": null,\n            \"full_name\": \"Nil Portugués\"\n        },\n        \"links\": {\n            \"self\": {\n                \"href\": \"http://localhost:9000/employees/10\"\n            },\n            \"employee_orders\": {\n                \"href\": \"http://localhost:9000/employees/10/orders\"\n            }\n        }\n    },\n    \"links\": {\n        \"employees\": {\n            \"href\": \"http://localhost:9000/employees\"\n        },\n        \"employee_orders\": {\n            \"href\": \"http://localhost:9000/employees/10/orders\"\n        }\n    },\n    \"jsonapi\": {\n        \"version\": \"1.0\"\n    }\n}\n```\n\n### PUT\n\nPUT requires all member attributes to be accepted, just like POST.\n\nFor the sake of this example, we'll just send in a new `job_title` value, and keep everything else exactly the same.\n\nIt's important to notice this time we are required to pass in the `id`, even if it has been passed in by the URI, and of course the `id` values must match. Otherwise it will fail.\n\n\nSending the following data to the server using `PUT`to the following URI  `http://localhost:9000/employees/10`: \n\n```json\n{\n  \"data\": {\n    \"type\": \"employee\",\n    \"id\": 10,\n    \"attributes\": {\n      \"company\": \"NilPortugues.com\",\n      \"surname\": \"Portugués\",\n      \"first_name\": \"Nil\",\n      \"email_address\": \"nilportugues@example.com\",\n      \"job_title\": \"Full Stack Web Developer\",\n      \"business_phone\": \"(123)555-0100\",\n      \"home_phone\": \"(123)555-0102\",\n      \"mobile_phone\": null,\n      \"fax_number\": \"(123)555-0103\",\n      \"address\": \"Plaça Catalunya 1\",\n      \"city\": \"Barcelona\",\n      \"state_province\": \"Barcelona\",\n      \"zip_postal_code\": \"08028\",\n      \"country_region\": \"Spain\",\n      \"web_page\": \"http://nilportugues.com\",\n      \"notes\": null,\n      \"attachments\": null\n    }\n  }\n}\n```\n\n\nWill produce: \n\n```\nHTTP/1.1 200 OK\nCache-Control: private, max-age=0, must-revalidate\nContent-type: application/vnd.api+json\n```\n\n```json\n{\n    \"data\": {\n        \"type\": \"employee\",\n        \"id\": \"10\",\n        \"attributes\": {\n            \"company\": \"NilPortugues.com\",\n            \"surname\": \"Portugués\",\n            \"first_name\": \"Nil\",\n            \"email_address\": \"contact@nilportugues.com\",\n            \"job_title\": \"Full Stack Web Developer\",\n            \"business_phone\": \"(123)555-0100\",\n            \"home_phone\": \"(123)555-0102\",\n            \"mobile_phone\": null,\n            \"fax_number\": \"(123)555-0103\",\n            \"address\": \"Plaça Catalunya 1\",\n            \"city\": \"Barcelona\",\n            \"state_province\": \"Barcelona\",\n            \"zip_postal_code\": \"08028\",\n            \"country_region\": \"Spain\",\n            \"web_page\": \"http://nilportugues.com\",\n            \"notes\": null,\n            \"full_name\": \"Nil Portugués\"\n        },\n        \"links\": {\n            \"self\": {\n                \"href\": \"http://localhost:9000/employees/10\"\n            },\n            \"employee_orders\": {\n                \"href\": \"http://localhost:9000/employees/10/orders\"\n            }\n        }\n    },\n    \"included\": [],\n    \"links\": {\n        \"employees\": {\n            \"href\": \"http://localhost:9000/employees\"\n        },\n        \"employee_orders\": {\n            \"href\": \"http://localhost:9000/employees/10/orders\"\n        }\n    },\n    \"jsonapi\": {\n        \"version\": \"1.0\"\n    }\n}\n```\n\n\n### PATCH\n\nPATCH allows partial updates, unlike PUT. \n\nWe are required to pass in the `id` member, even if it has been passed in by the URI, and of course the `id` values must match. Otherwise it will fail.\n\nFor instance, sending the following data to the server using the following URI  `http://localhost:9000/employees/10`: \n\n```json\n{\n  \"data\": {\n    \"type\": \"employee\",\n    \"id\": 10,\n    \"attributes\": {\n      \"email_address\": \"contact@nilportugues.com\"\n    }\n  }\n}\n```\n\nWill produce: \n\n```\nHTTP/1.1 200 OK\nCache-Control: private, max-age=0, must-revalidate\nContent-type: application/vnd.api+json\n```\n\n```json\n{\n    \"data\": {\n        \"type\": \"employee\",\n        \"id\": \"10\",\n        \"attributes\": {\n            \"company\": \"NilPortugues.com\",\n            \"surname\": \"Portugués\",\n            \"first_name\": \"Nil\",\n            \"email_address\": \"contact@nilportugues.com\",\n            \"job_title\": \"Full Stack Web Developer\",\n            \"business_phone\": \"(123)555-0100\",\n            \"home_phone\": \"(123)555-0102\",\n            \"mobile_phone\": null,\n            \"fax_number\": \"(123)555-0103\",\n            \"address\": \"Plaça Catalunya 1\",\n            \"city\": \"Barcelona\",\n            \"state_province\": \"Barcelona\",\n            \"zip_postal_code\": \"08028\",\n            \"country_region\": \"Spain\",\n            \"web_page\": \"http://nilportugues.com\",\n            \"notes\": null,\n            \"full_name\": \"Nil Portugués\"\n        },\n        \"links\": {\n            \"self\": {\n                \"href\": \"http://localhost:9000/employees/10\"\n            },\n            \"employee_orders\": {\n                \"href\": \"http://localhost:9000/employees/10/orders\"\n            }\n        }\n    },\n    \"included\": [],\n    \"links\": {\n        \"employees\": {\n            \"href\": \"http://localhost:9000/employees\"\n        },\n        \"employee_orders\": {\n            \"href\": \"http://localhost:9000/employees/10/orders\"\n        }\n    },\n    \"jsonapi\": {\n        \"version\": \"1.0\"\n    }\n}\n```\n\n\n\n### DELETE\n\nDELETE is the easiest method to use, as it does not require body. Just issue a DELETE to `http://localhost:9000/employees/10/` and `Employee` with `id 10` will be gone.\n\nIt will produce the following output: \n\n```\nHTTP/1.1 204 No Content\nCache-Control: private, max-age=0, must-revalidate\nContent-type: application/vnd.api+json\n```\n\nAnd notice how response will be empty:\n\n```\n```\n\n\u003cbr\u003e\n\n## GET Query Params: include, fields, sort and page\n\nAccording to the standard, for GET method, it is possible to:\n- Show only those fields requested using `fields`query parameter.\n    - \u0026fields[resource]=field1,field2\n    \nFor instance, passing `/employees/10?fields[employee]=company,first_name` will produce the following output: \n\n```json\n{\n    \"data\": {\n        \"type\": \"employee\",\n        \"id\": \"10\",\n        \"attributes\": {\n            \"company\": \"NilPortugues.com\",\n            \"first_name\": \"Nil\"\n        },\n        \"links\": {\n            \"self\": {\n                \"href\": \"http://localhost:9000/employees/10\"\n            },\n            \"employee_orders\": {\n                \"href\": \"http://localhost:9000/employees/10/orders\"\n            }\n        }\n    },\n    \"links\": {\n        \"employees\": {\n            \"href\": \"http://localhost:9000/employees\"\n        },\n        \"employee_orders\": {\n            \"href\": \"http://localhost:9000/employees/10/orders\"\n        }\n    },\n    \"jsonapi\": {\n        \"version\": \"1.0\"\n    }\n}\n```\n    \n- Show only those `include` resources by passing in the relationship between them separated by dot, or just pass in list of resources separated by comma.\n    - \u0026include=resource1\n    - \u0026include=resource1.resource2,resource2.resource3\n    \n    \nFor instance, `/employees?include=order` will only load order type data inside `include` member, but `/employees?include=order.employee` will only load those orders related to the `employee` type.\n\n- Sort results using `sort` and passing in the member names of the main resource defined in `data[type]` member. If it starts with a `-` order is `DESCENDING`, otherwise it's `ASCENDING`.\n\n  - \u0026sort=field1,-field2\n  - \u0026sort=-field1,field2\n  \nFor instance: `/employees?sort=surname,-first_name`  \n\n- Pagination is also defined to allow doing page pagination, cursor pagination or offset pagination.\n  - \u0026page[number]\n  - \u0026page[limit]\n  - \u0026page[cursor]\n  - \u0026page[offset]\n  - \u0026page[size]\n  \nFor instance: `/employees?page[number]=1\u0026page[size]=10`  \n\n## POST/PUT/PATCH with Relationships\n\nThe JSON API allows resource creation and modification and passing in `relationships` that will create or alter existing resources too. \n\nLet's say we want to create a new `Employee` and pass in its first `Order`too. \n\nThis could be done issuing 2 `POST` to the end-points (one for Employee, one for Order) or pass in the first `Order` as a `relationship` with our `Employee`, for instance:\n\n```json\n{\n  \"data\": {\n    \"type\": \"employee\",\n    \"attributes\": {\n        \"company\": \"Northwind Traders\",\n        \"surname\": \"Giussani\",\n        \"first_name\": \"Laura\",\n        \"email_address\": \"laura@northwindtraders.com\",\n        \"job_title\": \"Sales Coordinator\",\n        \"business_phone\": \"(123)555-0100\",\n        \"home_phone\": \"(123)555-0102\",\n        \"mobile_phone\": null,\n        \"fax_number\": \"(123)555-0103\",\n        \"address\": \"123 8th Avenue\",\n        \"city\": \"Redmond\",\n        \"state_province\": \"WA\",\n        \"zip_postal_code\": \"99999\",\n        \"country_region\": \"USA\",\n        \"web_page\": \"http://northwindtraders.com\",\n        \"notes\": \"Reads and writes French.\",\n        \"full_name\": \"Laura Giussani\"\n    },    \n    \"relationships\": {\n      \"order\": {\n        \"data\": [\n          {\n            \"type\": \"order\",\n            \"attributes\": {\n              \"customer_id\": \"28\",\n              \"order_date\": \"2006-05-11 00:00:00\",\n              \"shipped_date\": \"2006-05-11 00:00:00\",\n              \"shipper_id\": \"3\",\n              \"ship_name\": \"Amritansh Raghav\",\n              \"ship_address\": \"789 28th Street\",\n              \"ship_city\": \"Memphis\",\n              \"ship_state_province\": \"TN\",\n              \"ship_zip_postal_code\": \"99999\",\n              \"ship_country_region\": \"USA\",\n              \"shipping_fee\": \"10.0000\",\n              \"taxes\": \"0.0000\",\n              \"payment_type\": \"Check\",\n              \"paid_date\": \"2006-05-11 00:00:00\",\n              \"notes\": null,\n              \"tax_rate\": \"0\",\n              \"tax_status_id\": null,\n              \"status_id\": \"0\"\n            }\n          }\n        ]\n      }\n    }    \n  }\n}       \n```\n\nDue to the existance of this use case, we'll have to ajust our Controller implementation overwriting some methods provided by the **JsonApiController**: `createResourceCallable`, `updateResourceCallable` and `patchResourceCallable`.\n\nHere's how it would be done for `createResourceCallable`.\n\n\n```php\n\u003c?php namespace App\\Http\\Controllers;\n\nuse App\\Model\\Database\\Employees;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Facades\\DB;\nuse NilPortugues\\Api\\JsonApi\\Server\\Errors\\Error;\nuse NilPortugues\\Api\\JsonApi\\Server\\Errors\\ErrorBag;\nuse NilPortugues\\Laravel5\\JsonApi\\Controller\\JsonApiController;\n\nclass EmployeesController extends JsonApiController\n{\n    /**\n     * Now you can actually create Employee and Orders at once.\n     * Use transactions - DB::beginTransaction() for data integrity!\n     *\n     * @return callable\n     */\n    protected function createResourceCallable()\n    {\n        $createOrderResource = function (Model $model, array $data) {\n            if (!empty($data['relationships']['order']['data'])) {\n                $orderData = $data['relationships']['order']['data'];\n\n                if (!empty($orderData['type'])) {\n                    $orderData = [$orderData];\n                }\n\n                foreach ($orderData as $order) {\n                    $attributes = array_merge($order['attributes'], ['employee_id' =\u003e $model-\u003egetKey()]);\n                    Orders::create($attributes);\n                }\n            }\n        };\n\n        return function (array $data, array $values, ErrorBag $errorBag) use ($createOrderResource) {\n\n            $attributes = [];\n            foreach ($values as $name =\u003e $value) {\n                $attributes[$name] = $value;\n            }\n\n            if (!empty($data['id'])) {\n                $attributes[$this-\u003egetDataModel()-\u003egetKeyName()] = $values['id'];\n            }\n\n            DB::beginTransaction();\n            try {\n                $model = $this-\u003egetDataModel()-\u003ecreate($attributes);\n                $createOrderResource($model, $data);\n                DB::commit();\n                return $model;\n                \n            } catch(\\Exception $e) {\n                DB::rollback();\n                $errorBag[] = new Error('creation_error', 'Resource could not be created');\n                throw $e;\n            }\n        };\n    }\n\n}\n```\n\nIt is important, in order to use Transactions, do define in `Eloquent` models the `$fillable` values. \n\nHere's how `Employees` and `Orders` look like with `$fillable` defined.\n\n\n**Employees (Eloquent Model) with $fillable**\n```php\n\u003c?php namespace App\\Model\\Database;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Validation\\ValidatesRequests;\n\nclass Employees extends Model\n{\n    public $timestamps = false;\n    protected $table = 'employees';    \n    protected $primaryKey = 'id';\n    protected $appends = ['full_name'];\n    \n    /**\n     * @var array\n     */\n    protected $fillable = [\n        'company',\n        'last_name',\n        'first_name',\n        'email_address',\n        'job_title',\n        'business_phone',\n        'home_phone',\n        'mobile_phone',\n        'fax_number',\n        'address',\n        'city',\n        'state_province',\n        'zip_postal_code',\n        'country_region',\n        'web_page',\n        'notes',\n        'attachments',\n    ];\n\n    /**\n     * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasOne\n     */\n    public function latestOrders()\n    {\n        return $this-\u003ehasMany(Orders::class, 'employee_id')-\u003elimit(10);\n    }\n\n    /**\n     * @return string\n     */\n    public function getFullNameAttribute()\n    {\n        return $this-\u003efirst_name.' '.$this-\u003elast_name;\n    }\n}\n\n```\n\n\n**Orders (Eloquent Model) with $fillable**\n\n```php\n\u003c?php namespace App\\Model\\Database;\n\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Orders extends Model\n{   \n    public $timestamps = false;\n    protected $table = 'orders';\n    protected $primaryKey = 'id';\n    \n    /**\n     * @var array\n     */\n    protected $fillable = [\n        'employee_id',\n        'customer_id',\n        'order_date',\n        'shipped_date',\n        'shipper_id',\n        'ship_name',\n        'ship_address',\n        'ship_city',\n        'ship_state_province',\n        'ship_zip_postal_code',\n        'ship_country_region',\n        'shipping_fee',\n        'taxes',\n        'payment_type',\n        'paid_date',\n        'notes',\n        'tax_rate',\n        'tax_status_id',\n        'status_id',\n    ];\n    \n    /**\n     * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasOne\n     */\n    public function employee()\n    {\n        return $this-\u003ebelongsTo(Employees::class, 'employee_id');\n    }\n}\n```\n\n\n\n## Custom Response Headers\n\nAdding custom response headers can be done for multiple reasons: *versioning, setting expire headers, caching, setting private or public the served content...*\n\nIn order to do this, it's as simple as overwriting the JsonApiController `addHeaders` method. For instance, let's use the EmployeeController as an example: \n\n\n```php\n\u003c?php namespace App\\Http\\Controllers;\n\nuse App\\Model\\Database\\Employees;\nuse NilPortugues\\Laravel5\\JsonApi\\Controller\\JsonApiController;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass EmployeesController extends JsonApiController\n{\n    //All your supported methods...\n    \n    /**\n     * @param Response $response\n     *\n     * @return \\Symfony\\Component\\HttpFoundation\\Response\n     */\n    protected function addHeaders(Response $response) {\n        $response-\u003eheaders-\u003eset('X-API-Version', '1.0');\n        $response-\u003esetPublic();\n        $response-\u003esetMaxAge(60);\n        $response-\u003esetSharedMaxAge(60);\n\n        return $response;\n    }\n}    \n```\n\nNow all supported actions will include the added custom headers.\n\n\n# Common Errors and Solutions\n\n### \"Undefined index: @type\"\n\nThis usually happens because you did not write the namespace of your `Mapping` in `config/jsonapi.php`. \nDouble check, if missing, add it and refresh the resource. It should be gone!\n\n## Contribute\n\nContributions to the package are always welcome!\n\n* Report any bugs or issues you find on the [issue tracker](https://github.com/nilportugues/laravel5-jsonapi-dingo/issues/new).\n* You can grab the source code at the package's [Git repository](https://github.com/nilportugues/laravel5-jsonapi-dingo).\n\n## Support\n\nGet in touch with me using one of the following means:\n\n - Emailing me at \u003ccontact@nilportugues.com\u003e\n - Opening an [Issue](https://github.com/nilportugues/laravel5-jsonapi-dingo/issues/new)\n\n## Authors\n\n* [Nil Portugués Calderó](http://nilportugues.com)\n* [The Community Contributors](https://github.com/nilportugues/laravel5-jsonapi-dingo/graphs/contributors)\n\n\n## License\nThe code base is licensed under the [MIT license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnilportugues%2Flaravel5-jsonapi-dingo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnilportugues%2Flaravel5-jsonapi-dingo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnilportugues%2Flaravel5-jsonapi-dingo/lists"}