maksi/laravel-request-mapper

0.3.2 2019-01-01 19:01 UTC

This package is auto-updated.

Last update: 2024-09-18 21:18:21 UTC


README

Build Status Code Coverage Latest Stable Version License

此组件允许您将请求映射的DTO对象注入到动作中。

  1. 安装
  2. 要求
  3. 基本用法
  4. 嵌套对象
  5. 映射策略
  6. 创建自定义映射策略
  7. 如何创建自定义异常?
  8. 项目示例
  9. 贡献
  10. 许可证
  11. 待办事项

1. 安装

您可以通过composer使用以下命令安装此包

composer require maksi/laravel-request-mapper

该包将自动注册自己。

2. 要求

PHP 7.1或更高版本和Laravel 5.5或更高版本

3. 基本用法

3.1 创建DTO对象

<?php
declare(strict_types = 1);

use Maksi\LaravelRequestMapper\Filling\RequestData\AllRequestData;

final class RoomSearchRequestData extends AllRequestData
{
    private $name;
 
    protected function init(array $data): void
    {
        $this->name = $data['name'] ?? null;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

您的DTO对象应扩展RequestData类之一

RequestData类负责映射策略

init中的$data数组是一个array,它由映射策略类返回。基本而言,$data是来自Request的一些数据。

3.2 注入到动作

DTO对象可以注入到任何类型的动作中。

<?php
declare(strict_types = 1);

/**
 * @package App\Http\Controller
 */
class RoomSearchController
{
 ...
    public function __invoke(RoomSearchRequestData $payload) // DTO object injected
    {
        
    }
}

3.3 验证DTO对象

您可以对DTO对象应用验证

  • 在映射数据到DTO之前(laravel验证)
  • 在映射数据到DTO之后(symfony annotation验证)

3.3.1 应用laravel验证

在对象填充之前,为RequestData对象应用laravel验证。

  1. 您应该创建一个包含验证规则的类。这个类应该实现Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\ValidationRuleInterface接口(如果不需要更改验证messagescustomAttributes,则可以扩展Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\AbstractValidationRule类)
<?php
declare(strict_types = 1);

namespace Maksi\LaravelRequestMapper\Tests\Integration\LaravelNestedValidation\Stub;

use Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\AbstractValidationRule;

class ValidatorRule extends AbstractValidationRule
{
    public function rules(): array
    {
        return [
            'nested' => 'array|required',
            'title' => 'string|required',
        ];
    }
}
  1. 接下来,您应该将此规则应用到DTO对象上。这应该通过annotation来完成。
<?php
declare(strict_types = 1);

namespace Maksi\LaravelRequestMapper\Tests\Integration\LaravelNestedValidation\Stub;

use Maksi\LaravelRequestMapper\Filling\RequestData\JsonRequestData;
use Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\Annotation\ValidationClass;

/**
 * @ValidationClass(class="\Maksi\LaravelRequestMapper\Tests\Integration\LaravelNestedValidation\Stub\ValidatorRule")
 */
class RootRequestDataStub extends JsonRequestData
{
    private $title;

    private $nested;

    protected function init(array $data): void
    {
        $this->title = $data['title'] ?? null;
        $this->nested = new NestedRequestDataStub($data['nested'] ?? []);
    }

    public function getTitle(): string
    {
        return $this->title;
    }

    public function getNested(): NestedRequestDataStub
    {
        return $this->nested;
    }
}

string

@ValidationClass(class="\Maksi\LaravelRequestMapper\Tests\Integration\LaravelNestedValidation\Stub\ValidatorRule")

表示在填充当前DTO之前,应应用\Maksi\LaravelRequestMapper\Tests\Integration\LaravelNestedValidation\Stub\ValidatorRule规则到将要注入到dto的data

3.3.2 应用symfony annotation验证

RequestData对象的属性上应用symfony annotation验证(所以,在创建DTO对象之后应用此验证)

首先,您应该在RequestData对象上添加@Type(type="annotation")注释。之后,您可以将验证应用到DTO对象上(有关更多信息,请参阅symfony 验证文档

<?php
declare(strict_types = 1);

namespace Maksi\LaravelRequestMapper\Tests\Integration\AnnotationValidation\Stub;

use Maksi\LaravelRequestMapper\Filling\RequestData\AllRequestData;
use Maksi\LaravelRequestMapper\Validation\Annotation\Type;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Type(type="annotation")
 */
class AllRequestDataStub extends AllRequestData
{
    /**
     * @Assert\Type(type="int")
     * @Assert\NotBlank()
     */
    private $allAge;

    /**
     * @var string
     * @Assert\NotBlank()
     */
    private $allTitle;

    protected function init(array $data): void
    {
        $this->allAge = $data['age'] ?? null;
        $this->allTitle = $data['title'] ?? null;
    }

    public function getAllTitle(): string
    {
        return $this->allTitle;
    }

    public function getAllAge(): int
    {
        return $this->allAge;
    }
}

4. 嵌套对象验证

4.1. Symfony annotation验证

您可以使用相同的方式创建嵌套DTO对象,例如:

根类

<?php
declare(strict_types = 1);

namespace Maksi\LaravelRequestMapper\Tests\Integration\AnnotationNestedValidation\Stub;

use Maksi\LaravelRequestMapper\Filling\RequestData\JsonRequestData;
use Maksi\LaravelRequestMapper\Validation\Annotation\Type;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Type(type="annotation")
 */
class RootRequestDataStub extends JsonRequestData
{
    /**
     * @Assert\NotBlank()
     * @Assert\Type(type="string")
     */
    private $title;

    /**
     * @Assert\Valid()
     */
    private $nested; // this property should have `Valid` annotation for validate nested object

    protected function init(array $data): void
    {
        $this->title = $data['title'] ?? null;
        $this->nested = new NestedRequestDataStub($data['nested'] ?? []);
    }

    public function getTitle(): string
    {
        return $this->title;
    }

    public function getNested(): NestedRequestDataStub
    {
        return $this->nested;
    }
}

嵌套类

<?php
declare(strict_types = 1);

namespace Maksi\LaravelRequestMapper\Tests\Integration\AnnotationNestedValidation\Stub;

use Maksi\LaravelRequestMapper\Filling\RequestData\JsonRequestData;
use Symfony\Component\Validator\Constraints as Assert;

class NestedRequestDataStub extends JsonRequestData
{
    /**
     * @Assert\NotBlank()
     * @Assert\Type(type="string")
     */
    private $nestedTitle;

    protected function init(array $data): void
    {
        $this->nestedTitle = $data['title'] ?? null;
    }

    public function getTitle(): string
    {
        return $this->nestedTitle;
    }
}

4.2. 嵌套的Laravel验证

因此,由于laravel验证在填充RequestData对象之前应用,因此您只需创建与无嵌套验证相同的验证类即可。

<?php
use Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\AbstractValidationRule;

class ValidatorRule extends AbstractValidationRule
{
    /**
     * @return array
     */
    public function rules(): array
    {
        return [
            'nested' => 'array|required',
            'title' => 'string|required',
            'nested.title' => 'string|required', // nested object validation
        ];
    }
}

5. 映射策略

默认情况下,包包含3种策略

AllStrategy - 负责从$request->all()数组填充数据。如果您想使用此策略,则您的RequestData对象应扩展AllRequestData类。

HeaderStrategy - 负责从 $request->header->all() 数组中填充数据。如果您想使用此策略,则您的 RequestData 对象应扩展 HeaderRequestData 类。

JsonStrategy - 负责从 $request->json()->all() 数组中填充数据。如果您想使用此策略,则您的 RequestData 对象应扩展 JsonRequestData 类。

6. 创建自定义映射策略

您可以为我们的应用程序创建自定义映射策略。

6.1 创建自定义策略

您的策略应实现 StrategyInterface;

<?php
declare(strict_types = 1);

namespace App\Http\RequestDataStrategy;

use App\Http\RequestData\TeacherSearchRequestData;
use Illuminate\Http\Request;
use Maksi\LaravelRequestMapper\Filling\Strategies\StrategyInterface;
use Maksi\LaravelRequestMapper\Filling\RequestData\RequestData;

class TeacherSearchStrategy implements StrategyInterface
{
    public function resolve(Request $request): array
    {
        return $request->all();
    }

    public function support(Request $request, RequestData $object): bool
    {
        return $object instanceof TeacherSearchRequestData
            && $request->routeIs('teacher-search');

    }
}

方法 support 定义策略是否可用于 resolve 对象。此方法有两个参数 $request$object

  • $requestRequest 实例
  • $object - 这是一个空的 DTO 实例,将被填充

方法 resolve 将返回将被注入到 DTO 实例中的数组。此方法接受 $request 对象。

6.2 为策略创建 RequestData 类

如果您想创建自己的策略,应扩展 RequestData

<?php
declare(strict_types = 1);

namespace App\Http\RequestData;

use Maksi\LaravelRequestMapper\Filling\RequestData\RequestData;
use Symfony\Component\Validator\Constraints as Assert;

final class TeacherSearchRequestData extends RequestData
{
    /**
     * @var string
     *
     * @Assert\NotBlank()
     * @Assert\Type(type="string")
     */
    private $name;

    protected function init(array $data): void
    {
        $this->name = $data['name'] ?? null;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

6.3 在 ServiceProvider 中注册您的策略

您应通过 addStrategy 方法将您的 strategy 实例添加到 Maksi\LaravelRequestMapper\StrategiesHandler

<?php
declare(strict_types = 1);

namespace App\Http\Provider;

use App\Http\RequestDataStrategy\TeacherSearchStrategy;
use Illuminate\Support\ServiceProvider;
use Maksi\LaravelRequestMapper\FillingChainProcessor;

/**
 * Class RequestMapperProvider
 *
 * @package App\Http\Provider
 */
class RequestMapperProvider extends ServiceProvider
{
    /**
     * @param FillingChainProcessor $fillingChainProcessor
     */
    public function boot(FillingChainProcessor $fillingChainProcessor): void
    {
        $fillingChainProcessor->addStrategy($this->app->make(TeacherSearchStrategy::class));
    }
}

7. 修改验证异常

  1. 创建一个扩展 \Maksi\LaravelRequestMapper\Validation\ResponseException\AbstractException 并实现 toResponse 方法的异常。

例如

<?php

class StringException extends \Maksi\LaravelRequestMapper\Validation\ResponseException\AbstractException
                                implements \Illuminate\Contracts\Support\Responsable
{
    /**
     * Create an HTTP response that represents the object.
     *
     * @param  \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function toResponse($request)
    {
        return \Illuminate\Http\JsonResponse::create('Invalid data provided')
                            ->setStatusCode(\Illuminate\Http\Response::HTTP_UNPROCESSABLE_ENTITY);
    }
}
  1. config/laravel-request-mapper.php 中定义 exception-class
<?php
declare(strict_types = 1);

return [
    'exception-class' => \Maksi\LaravelRequestMapper\Validation\ResponseException\DefaultException::class,
];

8. 项目示例

您可以在 https://github.com/E-ZSTU/rozklad-rest-api 项目中看到此包的使用示例。

贡献

有关详细信息,请参阅 CONTRIBUTING

许可证

MIT 许可证 (MIT)。有关更多信息,请参阅 许可证 文件。

待办事项