承接 serhiikamolov/laravel-jsonapi 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

serhiikamolov/laravel-jsonapi

Composer 安装命令:

composer require serhiikamolov/laravel-jsonapi

包简介

A set of interfaces and classes to facilitate the construction of an efficient Json Api application with the Laravel framework.

README 文档

README

A set of interfaces and classes to facilitate the construction of an efficient JSON:API application with the Laravel framework.

Latest Stable Version License

Table of contents

Installation

This package is very easy to set up via Composer, just run from your project's root folder:

composer require serhiikamolov/laravel-jsonapi

Custom error handler

In order to make errors output compatible with json API format go to bootstrap/app.php and register the custom error handler.

$app->singleton(
   Illuminate\Contracts\Debug\ExceptionHandler::class,
   \JsonAPI\Exceptions\Handler::class
);

Or just extend default App\Exceptions\Handler from JsonAPI\Exceptions\Handler class.

Now, in case of an exception you will get such a response:

{
    "links": {
        "self": "http://127.0.0.1/api/v1/auth/login"
    },
    "errors": {
        "messages": [
            "Some internal exception"
        ]
    },
    "debug": {
        "message": "Some internal exception",
        "exception": "Exception",
        "file": "/code/app/Http/Controllers/AuthController.php",
        "line": 29,
        "trace": [...]
    }
}

Request validation class

\JsonAPI\Contracts\Request is a simple extension of the FormRequest class that returns the validation errors in compatible with the json API form

namespace App\Http\Requests\Auth;

use \JsonAPI\Contracts\Request;

class LoginRequest extends Request
{

    public function messages()
    {
        return [
            'email.required'  => 'Значення e-mail не може бути порожнім',
            'email.email'   => 'Значення e-mail не відповідає формату електронної пошти',
            'email.max'     => 'Значення e-mail не має бути таким довгим',
        ];
    }
    
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules():array
    {
        return [
            'email' => 'required|email|max:255',
            'password' => 'required|string',
        ];
    }

}

Example of a controller with request validation:

namespace App\Http\Controllers;

use JsonAPI\Response\Response;
use App\Http\Requests\Auth\LoginRequest;

class AuthController extends Controller
{
    /**
     * Request a JWT token
     *
     * @param LoginRequest $request
     * @param Response $response
     * @return JsonResponse
     */
    public function login(LoginRequest $request, Response  $response):Response
    {
        // validation is passed, you can check the user credentials now
        // and generate a JWT token 
    }      
}

Response with validation errors:

{
    "links": {
        "self": "http://127.0.0.1/api/v1/auth/login"
    },
    "errors": {
        "email": [
            "The email field is required."
        ]
    }
}

Response class

JsonAPI\Response\Response is an exension of the JsonResponse class with some additional methods.

Add additional values to the links object of the response

$response->links($array)

Return error with the specific code in the response

$response->error($statusCode, $message)  

Return response with JWT token

$response->token($token, $type = 'bearer', $expires = null)

Return a custom data object in the response

$response->data($array)

Attach a field to the response's data object

$response->attach($key, $value)

Add a debug information to the response object

$response->debug($array)

Add a meta data to the response object

$response->meta($array, $key = 'meta')

Serialize an eloquent collection or a data model

$response->serialize($collection, $serializer = new Serializer())

Paginate a data array in the response

$response->serialize($collection)->paginate()

Add a specific status code to the response

$response->code($statusCode)

Response class is an implementation of the Builder pattern thus you can use diffrent methods in a row:

public function login(LoginRequest $request, Response $response): Response
{
    ...
    return $response
        ->token((string)$token)
        ->attach('uuid', Auth::guard('api')->user()->uuid);
}

Response result:

{
    "links": {
        "self": "http://127.0.0.1/api/v1/auth/login"
    },
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMjcuMC4wLjFcL2FwaVwvdjFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNjA4Mzc0NTAyLCJleHAiOjE2MDgzNzQ1NjIsIm5iZiI6MTYwODM3NDUwMiwianRpIjoiUXRZWnpUeEhYajJyaGxHaCIsInN1YiI6MiwicHJ2IjoiY2U2ZTY0NDI4OTkwYjk4NWJjZTQ2OGZjNTlmZTUxYzNiZTljN2ZhZCJ9.PNF2-5nswNqaWS4WS1D_gemBD3IyPJVKXJohNF8mMUY",
        "token_type": "bearer",
        "expires_in": 60,
        "uuid": "40e831349e594d8e944478a243ff463f"
    }
}

Serializer class

Serializers allow complex data such as Eloquent Collections and Model instances to be converted to a simple array that can be easily rendered into JSON. Serializer class gives you a powerful, generic way to control the output of your responses.

Declaring Serializers

Let's create a simple serializer class extended from the JsonAPI\Response\Serializer class. There is a public method fields() which returns an array, and define a set of fields which will be retrieved from the given Collection or Model and put to the response.

namespace App\Http\Serializers;

use JsonAPI\Response\Serializer;

class UserSerializer extends Serializer
{
    public function fields(): array
    {
        return [
            'id',    // take data from $user->id
            'name',  // take data from $user->name
            'email', // take data from $user->email
            'uuid'   // take data from the public method defined below
        ];
    }

    /**
    * Define a custom field 
    */
    public function uuid(Model $item): string
    {
        return md5($item->id);
    }
}

Here you can notice that uuid is not taken from the database but generated from the model data. In such a way you can define any new field you need in the response or even override an existing field value.

Using Model Serializers

There is serialize method in the JsonAPI\Response\Response class which accept a Serializer instance as a second parameter.

class UsersController extends Controller
{
    /**
     * Get list of all users.
     *
     * @param Response $response
     * @return Response
     */
    public function read(Response $response): Response
    {
        $users = User::all();
        return $response->serialize($users, new UserSerializer());
    }
}

Response result

{
    "links": {
        "self": "http://127.0.0.1/api/v1/users"
    },
    "data": [
        {
            "id": 1,
            "name": "admin",
            "email": "user@email.com",
            "uuid": "40e831349e594d8e944478a243ff463f"
        }
    ]
}

Using Field Modifiers

Field modifiers can be applied to every field defined in serializer. Although, there are a few predefined modifiers: timestamp, number, trim you can define your own modifier by creating a protected method with the modifier prefix in its name. Also you can use another serializing class as a modifier, it can be useful when you have some related data to the original model.

class UserSerializer extends Serializer
{
    public function fields(): array
    {
        return [
            'id' => 'md5'                       // use custom modifier
            ...
            'created_at' => 'timestamp',        // use default modifier which 
                                                // transforms a Carbon date object 
                                                // into the unix timestamp number
            'roles' => RoleSerializer::class    // use a serializing class as a modifier
                                                // for the related data
        ];
    }

     /**
     * Define custom modifier which transforms user id to md5 hash.
     * @param int|null $value
     * @return int
     */
    protected function modifierMd5(?int $value): string
    {
        return md5($value);
    }
}

Expand Response with the Queries Log

Enable JsonAPI\Http\Middleware\JsonApiDebug middleware and expand the debug section of a response with the information from the queries log.

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    protected $middlewareGroups = [
        ...
        'api' => [
            ...
            \JsonAPI\Http\Middleware\JsonApiDebug::class                  
        ],
        ...
    ];
}

Response with the queries log.

{
  ...
  "debug": {
    "queries": {
        "total": 2,
        "list": [
            {
                "query": "select * from `users` where `id` = ? limit 1",
                "bindings": [
                    2
                ],
                "time": 20.81
            },
            {
                "query": "select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id`, `role_user`.`created_at` as `pivot_created_at`, `role_user`.`updated_at` as `pivot_updated_at` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = ? and `roles`.`deleted_at` is null",
                "bindings": [
                    2
                ],
                "time": 73.97
            }
        ]
    }
  }
}

Testing API Responses

Add JsonAPI\Traits\Tests\JsonApiAsserts trait to your default TestCase class and expand your tests with some useful asserts methods for testing API responses.

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use JsonAPI\Traits\Tests\JsonApiAsserts;

abstract class TestCase extends BaseTestCase
{
    use JsonApiAsserts;
}

Example of using additional asserts:

    /**
     * Testing GET /api/v1/entries/<id>
     */
    public function test_read()
    {
        $response = $this->get("/api/v1/entries/1");

        // expecting to get response in JSON:API format and 
        // find "id", "value", "type", "active" fields within 
        // a response's data
        $this->assertJsonApiResponse($response, [
            "id",
            "value",
            "type",
            "active",
        ]);
    }

serhiikamolov/laravel-jsonapi 适用场景与选型建议

serhiikamolov/laravel-jsonapi 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 33.92k 次下载、GitHub Stars 达 3, 最近一次更新时间为 2020 年 02 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 serhiikamolov/laravel-jsonapi 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 serhiikamolov/laravel-jsonapi 我们能提供哪些服务?
定制开发 / 二次开发

基于 serhiikamolov/laravel-jsonapi 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 33.92k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 4
  • 点击次数: 11
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 3
  • Watchers: 2
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-02-06