An open API service indexing awesome lists of open source software.

https://github.com/tblindaruk/laravel-request-mapper

mapping laravel request to the DTO objects
https://github.com/tblindaruk/laravel-request-mapper

dto laravel laravel-package request-mapping

Last synced: about 1 year ago
JSON representation

mapping laravel request to the DTO objects

Awesome Lists containing this project

README

          

# Request Mapper for Laravel


Build Status
Code Coverage
Latest Stable Version
License

This component allow you to inject DTO object mapped from the Request to the action.

1. [Install](#install)
2. [Requirements](#requirements)
3. [Basic usage](#basic)
4. [Nested object](#nested)
5. [Mapped strategies](#mapped-strategies)
6. [Create custom mapped strategy](#custom-mapped-strategy)
7. [How to create an custom exception?](#change-exception)
8. [Project example](#example)
9. [Contributing](#contributing)
10. [Licence](#licence)
11. [TODO](#todo)

1. Install

You can install this package via composer using this command:

```
composer require maksi/laravel-request-mapper
```

The package will automatically register itself.

2. Requirements

PHP 7.1 or newer and Laravel 5.5 or newer

3. Basic usage

3.1 Create an DTO object

```PHP
name = $data['name'] ?? null;
}

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

Your DTO object should extend one of RequestData classes:
- [AllRequestData](./src/Filling/RequestData/AllRequestData.php)
- [HeaderRequestData](./src/Filling/RequestData/HeaderRequestData.php)
- [JsonRequestData](./src/Filling/RequestData/JsonRequestData.php)

RequestData classes responsible for [mapped strategies](#mapped-strategies).

`$data` array in the `init` it is an `array` which return from the [mapped strategies](#mapped-strategies) classes. Basically `$data` it is some data from the `Request`.

3.2 Inject to the action

DTO object can be injected to any type of action.

```PHP
3.3 Validate DTO object

You can apply validation to the DTO object:
- before mapping data to the DTO (`laravel` validation)
- after mapping data to the DTO (`symfony annotation` validation)

3.3.1 Apply laravel validation

Laravel validation applied for the `RequestData` object before object filling.

1. You should create a class with validation rules. This class should implement `Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\ValidationRuleInterface` interface (in case, if you do no need change the validation `messages` and `customAttributes`, than you can extend `Maksi\LaravelRequestMapper\Validation\BeforeType\Laravel\AbstractValidationRule` class)

```PHP
'array|required',
'title' => 'string|required',
];
}
}

```

2. In the next you should apply this rules to the DTO object. This should be done via `annotation`.

```PHP
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

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

indicates that before filling current DTO should be appied `\Maksi\LaravelRequestMapper\Tests\Integration\LaravelNestedValidation\Stub\ValidatorRule` rules for the `data` which will be injected to the dto.

3.3.2 Apply symfony annotation validation

Annotation symfony validation applied to the properties in the `RequestData` object (So this validation appied after the creating and DTO object).

At the first you should add the `@Type(type="annotation")` annotation to the RequestData object. After this you can apply the validation to the DTO object (for more information please see symfony [validation documentation](https://symfony.com/doc/current/validation.html))

```PHP
allAge = $data['age'] ?? null;
$this->allTitle = $data['title'] ?? null;
}

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

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

```

4. Nested object validation

4.1. Symfony annotation validation

In the same way you can create an nested DTO object, for example:

Root class

```PHP
title = $data['title'] ?? null;
$this->nested = new NestedRequestDataStub($data['nested'] ?? []);
}

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

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

Nested class

```PHP
nestedTitle = $data['title'] ?? null;
}

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

```

4.2. Laravel validation for nested

So, as a laravel validation applied before filling the `RequestData` object, than you should just create the same validation class as an for no nested validation.

```PHP
'array|required',
'title' => 'string|required',
'nested.title' => 'string|required', // nested object validation
];
}
}
```

5. Mapped strategies

By default package has 3 strategies:
- [AllStrategy](./src/Filling/Strategies/AllStrategy.php)
- [HeaderStrategy](./src/Filling/Strategies/HeaderStrategy.php)
- [JsonStrategy](./src/Filling/Strategies/JsonStrategy.php)

AllStrategy - responsible for filling data from the `$request->all()` array. If you want to use this strategy, than your `RequestData` object should extend `AllRequestData` class.

HeaderStrategy - responsible for filling data from the `$request->header->all()` array. If you want to use this strategy, than your `RequestData` object should extend `HeaderRequestData` class.

JsonStrategy - responsible for filling data from the `$request->json()->all()` array. If you want to use this strategy, than your `RequestData` object should extend `JsonRequestData` class.

6. Create custom mapped strategy

You can create a custom mapped strategies for our application.

6.1 Create custom strategy

You strategy should implement [StrategyInterface](./src/Filling/Strategies/StrategyInterface.php);

```PHP
all();
}

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

}
}
```

Method `support` define is strategy available for `resolve` object. This method has 2 parameters `$request` and `$object`:
- `$request` as a `Request` instance
- `$object` - it is empty DTO instance, witch will be filled

Method `resolve` will return the array which will be injected to the DTO instance. This method accept `$request` object.

6.2 Create RequestData class for Strategy

You should extend [RequestData](./src/Filling/RequestData/RequestData.php) in case if you want to create your own strategy

```PHP
name = $data['name'] ?? null;
}

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

6.3 Register your strategy in the `ServiceProvider`

You should add instance of your `strategy` to the `Maksi\LaravelRequestMapper\StrategiesHandler` via `addStrategy` method.

```PHP
addStrategy($this->app->make(TeacherSearchStrategy::class));
}
}

```

7. Change validation exception

1. Create Exception which will extend `\Maksi\LaravelRequestMapper\Validation\ResponseException\AbstractException` and implement toResponse method

For example:

```PHP
setStatusCode(\Illuminate\Http\Response::HTTP_UNPROCESSABLE_ENTITY);
}
}
```

2. Define in `config/laravel-request-mapper.php` `exception-class` key

```PHP
\Maksi\LaravelRequestMapper\Validation\ResponseException\DefaultException::class,
];

```

8. Project example

You can see example of usage part of this package in https://github.com/E-ZSTU/rozklad-rest-api project.

Contributing

Please see [CONTRIBUTING](./CONTRIBUTING.md) for details.

License

The MIT License (MIT). Please see [License](./LICENSE) File for more information.

TODO

- [ ] think about https://www.reddit.com/r/laravel/comments/af843q/laravel_request_mapper/edx39cj
- [ ] think about https://www.reddit.com/r/laravel/comments/af843q/laravel_request_mapper/edx8mci
- [ ] delete symfony validation, since I`m not sure that is needed for the laravel community
- [ ] add integration tests for `change exception`
- [ ] add priority to the strategies
- [ ] how you can get this DTO from the middleware (just register `RequestData` as a singleton)