assuming you are providing some RESTful/Web APIs, then you are familiar with the tasks of
Somehow these things are disconnected to each other, that means the documentation of the API is normally not used for validating incoming data. Neither it is used for writing a DTO class. As well all the tasks are repetitive, manual and error prone.
This is where Valgene kicks in and reduces a lot of pain.
Valgene (Validation Generator) generates validator and DTO boiler plate code from your OpenAPI specs.
so lets assume you have an API spec like the following that defines 1 endpoint that accepts incoming data, that is [POST] /pets
.
paths:
/pets:
post:
description: Creates a new pet in the store. Duplicates are allowed
operationId: addPet
requestBody:
description: Pet to add to the store
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewPet'
The payload of the endpoint is expected to be like:
NewPet:
required:
- name
properties:
name:
type: string
tag:
type: string
when invoking valgene
valgene --template php5.5 --spec petstore-expanded.yaml --option 'php.namespace:\My\PetStore\Api'
it will generate a Validator, DTO and some Exception classes:
valgene --template php5.5 --spec petstore-expanded.yaml --option 'php.namespace:\My\PetStore\Api'
> processing petstore-expanded.yaml:
- route [POST] /pets
> generating:
- PostAddPet/NewPetDto.php
- PostAddPet/NewPetDtoValidator.php
- Exception/MissingFieldException.php
- Exception/FieldException.php
- Exception/InvalidFieldException.php
Generated Validator looks like this:
<?php
namespace \My\PetStore\Api\PostAddPet;
use \My\PetStore\Api\Exception\InvalidFieldException;
use \My\PetStore\Api\Exception\MissingFieldException;
/**
* GENERATED CODE - DO NOT MODIFY BY HAND
*/
class NewPetDtoValidator
{
/**
* @param array $json
* @throws MissingFieldException
* @throws InvalidFieldException
*/
public function validate($json)
{
$this->isNameValid($json, true);
$this->isTagValid($json, false);
}
/**
* @param array $json
* @param bool $isRequired
*/
protected function isNameValid($json, $isRequired)
{
$field = NewPetDto::PROPERTY_NAME;
if (!array_key_exists($field, $json)) {
if ($isRequired) {
throw new MissingFieldException($field, $json);
}
}
$value = $json[$field];
if (!is_string($value)) {
throw new InvalidFieldException($field, $json, 'datatype is not string');
}
}
/**
* @param array $json
* @param bool $isRequired
*/
protected function isTagValid($json, $isRequired)
{
$field = NewPetDto::PROPERTY_TAG;
if (!array_key_exists($field, $json)) {
if ($isRequired) {
throw new MissingFieldException($field, $json);
}
}
$value = $json[$field];
if (!is_string($value)) {
throw new InvalidFieldException($field, $json, 'datatype is not string');
}
}
}
Generated DTO looks like this:
<?php
namespace \My\Sample\Api\PostPets;
/**
* GENERATED CODE - DO NOT MODIFY BY HAND
*/
class NewPetDto
{
const PROPERTY_NAME = 'name';
const PROPERTY_TAG = 'tag';
/** @var string $name */
public $name;
/** @var string $tag */
public $tag;
/**
* @param array $payload
* @return NewPetDto
*/
public static function fromArray(array $payload)
{
$self = new static();
$self->name = $payload[static::PROPERTY_NAME];
$self->tag = $payload[static::PROPERTY_TAG];
return $self;
}
}
Be aware that the finally generated code is totally customizable and the shown example is very opinionated. To enable your custom Templates you copy the files from the template folder to a folder on you local disk. Then you can customize them and pass the path of the folder as an argument like:
valgene --template-folder $PWD/my-custom-php-templates --spec petstore-expanded.yaml --option 'php.namespace:\My\PetStore\Api'
Further reading of variables available in templates you can find here
pub global activate valgene_cli
as seen above there is a --template
parameter that allows to switch the generated language/template.
valgene --template php5.5 --spec petstore-expanded.yaml --option 'php.namespace:\My\PetStore\Api'
In fact the code generators itself are just a couple of templates that getting rendered by the valgene engine. The template language itself is Mustache and therefore you can customize the code that is generated pretty easy.