Skip to content

Instantly share code, notes, and snippets.

@walva
Last active February 17, 2020 15:07
Show Gist options
  • Save walva/0cb89b7f7f8cda59313c757a6573ee6d to your computer and use it in GitHub Desktop.
Save walva/0cb89b7f7f8cda59313c757a6573ee6d to your computer and use it in GitHub Desktop.
Use NelmioAPiDocBundle with JmsSerializerBundle : handling snakecase and groups in the same time
services:
myapp.api_doc.parser.validation_parser:
class: SulivanDotEu\ApiBundle\Parser\SnakeCaseValidationParser
arguments:
- "@validator.mapping.class_metadata_factory"
tags:
- { name: nelmio_api_doc.extractor.parser }
<?php
/**
* @ApiDoc(
* resource=true,
* views = { "default", "partner" },
* description="Ask for an offer",
* input= {
* "class"="Company\ThirdPartyAPIBundle\Action\GetOfferAction",
* "groups"={"offer", "Default"},
* "parsers" = {
* "SulivanDotEu\ApiBundle\Parser\SnakeCaseValidationParser",
* "Nelmio\ApiDocBundle\Parser\JmsMetadataParser"
* }
* },
* output= {
* "class"="Company\ThirdPartyAPIBundle\Action\GetOfferAction",
* "groups"={"offer", "Default", "read"},
* "parsers" = {
* "Nelmio\ApiDocBundle\Parser\JmsMetadataParser",
* "SulivanDotEu\ApiBundle\Parser\SnakeCaseValidationParser"
* }
* })
**/
public function postOfferAction( Request $request ) {
// ...
}
<?php
namespace SulivanDotEu\ApiBundle\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Parser\ValidationParser;
use Symfony\Component\Validator\Mapping\PropertyMetadata;
class SnakeCaseGroupsValidationParser extends ValidationParser
{
/**
* {@inheritdoc}
*/
public function parse( array $input )
{
$className = $input[ 'class' ];
$groups = isset($input[ 'groups' ])?$input[ 'groups' ]:["Default"];
$parsed = $this->doParse( $className, [], $groups );
if ( isset( $input[ 'name' ] ) && ! empty( $input[ 'name' ] ) )
{
$output = [];
$output[ $input[ 'name' ] ] = [
'dataType' => 'object',
'actualType' => 'object',
'class' => $className,
'subType' => NULL,
'required' => NULL,
'description' => NULL,
'readonly' => NULL,
'children' => $parsed,
];
return $output;
}
return $parsed;
}
/**
* Recursively parse constraints.
*
* @param $className
* @param array $visited
*
* @return array
*/
protected function doParse( $className, array $visited, $groups = [] )
{
$params = [];
$classdata = $this->factory->getMetadataFor( $className );
$properties = $classdata->getConstrainedProperties();
$refl = $classdata->getReflectionClass();
$defaults = $refl->getDefaultProperties();
foreach ( $properties as $property )
{
$vparams = [];
$vparams[ 'default' ] = isset( $defaults[ $property ] ) ? $defaults[ $property ] : NULL;
$pds = $classdata->getPropertyMetadata( $property );
foreach ( $pds as $propdata )
{
// apply exclusion strategies
if ( TRUE === $this->shouldSkipProperty( $propdata, $groups ) )
{
dump("CONTINUE");
continue 2;
}
$constraints = $propdata->getConstraints();
foreach ( $constraints as $constraint )
{
$vparams = $this->parseConstraint( $constraint, $vparams, $className, $visited );
}
}
if ( isset( $vparams[ 'format' ] ) )
{
$vparams[ 'format' ] = join( ', ', $vparams[ 'format' ] );
}
foreach ( [ 'dataType', 'readonly', 'required', 'subType' ] as $reqprop )
{
if ( ! isset( $vparams[ $reqprop ] ) )
{
$vparams[ $reqprop ] = NULL;
}
}
// check for nested classes with All constraint
if ( isset( $vparams[ 'class' ] ) && ! in_array( $vparams[ 'class' ], $visited ) && NULL !== $this->factory->getMetadataFor( $vparams[ 'class' ] ) )
{
$visited[] = $vparams[ 'class' ];
$vparams[ 'children' ] = $this->doParse( $vparams[ 'class' ], $visited );
}
$vparams[ 'actualType' ] = isset( $vparams[ 'actualType' ] ) ? $vparams[ 'actualType' ] : DataTypes::STRING;
$property = ltrim( strtolower( preg_replace( '/[A-Z]/', '_$0', $property ) ) );
$params[ $property ] = $vparams;
}
return $params;
}
protected function shouldSkipProperty( PropertyMetadata $propdata, $groups = array() )
{
return count(array_intersect($groups, array_keys($propdata->constraintsByGroup))) == 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment