mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Merge pull request #665 from mcfedr/json-serializable
Add support for JsonSerializable classes
This commit is contained in:
commit
787f69561b
94
Parser/JsonSerializableParser.php
Normal file
94
Parser/JsonSerializableParser.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by mcfedr on 30/06/15 21:03
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Parser;
|
||||||
|
|
||||||
|
class JsonSerializableParser implements ParserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function supports(array $item)
|
||||||
|
{
|
||||||
|
if (!is_subclass_of($item['class'], 'JsonSerializable')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ref = new \ReflectionClass($item['class']);
|
||||||
|
if ($ref->hasMethod('__construct')) {
|
||||||
|
foreach ($ref->getMethod('__construct')->getParameters() as $parameter) {
|
||||||
|
if (!$parameter->isOptional()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function parse(array $input)
|
||||||
|
{
|
||||||
|
/** @var \JsonSerializable $obj */
|
||||||
|
$obj = new $input['class']();
|
||||||
|
|
||||||
|
$encoded = $obj->jsonSerialize();
|
||||||
|
$parsed = $this->getItemMetaData($encoded);
|
||||||
|
|
||||||
|
if (isset($input['name']) && !empty($input['name'])) {
|
||||||
|
$output = array();
|
||||||
|
$output[$input['name']] = $parsed;
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parsed['children'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemMetaData($item)
|
||||||
|
{
|
||||||
|
$type = gettype($item);
|
||||||
|
|
||||||
|
$meta = array(
|
||||||
|
'dataType' => $type == 'NULL' ? null : $type,
|
||||||
|
'actualType' => $type,
|
||||||
|
'subType' => null,
|
||||||
|
'required' => null,
|
||||||
|
'description' => null,
|
||||||
|
'readonly' => null
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($type == 'object' && $item instanceof \JsonSerializable) {
|
||||||
|
$meta = $this->getItemMetaData($item->jsonSerialize());
|
||||||
|
$meta['class'] = get_class($item);
|
||||||
|
} elseif (($type == 'object' && $item instanceof \stdClass) || ($type == 'array' && !$this->isSequential($item))) {
|
||||||
|
$meta['dataType'] = 'object';
|
||||||
|
$meta['children'] = array();
|
||||||
|
foreach ($item as $key => $value) {
|
||||||
|
$meta['children'][$key] = $this->getItemMetaData($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for numeric sequential keys, just like the json encoder does
|
||||||
|
* Credit: http://stackoverflow.com/a/25206156/859027
|
||||||
|
*
|
||||||
|
* @param array $arr
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isSequential(array $arr)
|
||||||
|
{
|
||||||
|
for ($i = count($arr) - 1; $i >= 0; $i--) {
|
||||||
|
if (!isset($arr[$i]) && !array_key_exists($i, $arr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -69,7 +69,24 @@ class ValidationParser implements ParserInterface, PostParserInterface
|
|||||||
{
|
{
|
||||||
$className = $input['class'];
|
$className = $input['class'];
|
||||||
|
|
||||||
return $this->doParse($className, array());
|
$parsed = $this->doParse($className, array());
|
||||||
|
|
||||||
|
if (isset($input['name']) && !empty($input['name'])) {
|
||||||
|
$output = array();
|
||||||
|
$output[$input['name']] = array(
|
||||||
|
'dataType' => 'object',
|
||||||
|
'actualType' => 'object',
|
||||||
|
'class' => $className,
|
||||||
|
'subType' => null,
|
||||||
|
'required' => null,
|
||||||
|
'description' => null,
|
||||||
|
'readonly' => null,
|
||||||
|
'children' => $parsed
|
||||||
|
);
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<parameter key="nelmio_api_doc.parser.collection_parser.class">Nelmio\ApiDocBundle\Parser\CollectionParser</parameter>
|
<parameter key="nelmio_api_doc.parser.collection_parser.class">Nelmio\ApiDocBundle\Parser\CollectionParser</parameter>
|
||||||
<parameter key="nelmio_api_doc.parser.form_errors_parser.class">Nelmio\ApiDocBundle\Parser\FormErrorsParser</parameter>
|
<parameter key="nelmio_api_doc.parser.form_errors_parser.class">Nelmio\ApiDocBundle\Parser\FormErrorsParser</parameter>
|
||||||
|
<parameter key="nelmio_api_doc.parser.json_serializable_parser.class">Nelmio\ApiDocBundle\Parser\JsonSerializableParser</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
@ -68,6 +69,11 @@
|
|||||||
<service id="nelmio_api_doc.parser.form_errors_parser" class="%nelmio_api_doc.parser.form_errors_parser.class%">
|
<service id="nelmio_api_doc.parser.form_errors_parser" class="%nelmio_api_doc.parser.form_errors_parser.class%">
|
||||||
<tag name="nelmio_api_doc.extractor.parser" />
|
<tag name="nelmio_api_doc.extractor.parser" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<!-- priority=1 means it comes before the validation parser, which can often add better type information -->
|
||||||
|
<service id="nelmio_api_doc.parser.json_serializable_parser" class="%nelmio_api_doc.parser.json_serializable_parser.class%">
|
||||||
|
<tag name="nelmio_api_doc.extractor.parser" priority="1" />
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</container>
|
||||||
|
@ -164,7 +164,7 @@ class YourController
|
|||||||
* `parameters`: an array of parameters;
|
* `parameters`: an array of parameters;
|
||||||
|
|
||||||
* `input`: the input type associated to the method (currently this supports Form Types, classes with JMS Serializer
|
* `input`: the input type associated to the method (currently this supports Form Types, classes with JMS Serializer
|
||||||
metadata, and classes with Validation component metadata) useful for POST|PUT methods, either as FQCN or as form type
|
metadata, classes with Validation component metadata and classes that implement JsonSerializable) useful for POST|PUT methods, either as FQCN or as form type
|
||||||
(if it is registered in the form factory in the container).
|
(if it is registered in the form factory in the container).
|
||||||
|
|
||||||
* `output`: the output type associated with the response. Specified and parsed the same way as `input`.
|
* `output`: the output type associated with the response. Specified and parsed the same way as `input`.
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by mcfedr on 30/06/15 21:05
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Model;
|
||||||
|
|
||||||
|
class JsonSerializableOptionalConstructorTest implements \JsonSerializable
|
||||||
|
{
|
||||||
|
public function __construct($optional = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by mcfedr on 30/06/15 21:05
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Model;
|
||||||
|
|
||||||
|
class JsonSerializableRequiredConstructorTest implements \JsonSerializable
|
||||||
|
{
|
||||||
|
public function __construct($required)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
24
Tests/Fixtures/Model/JsonSerializableTest.php
Normal file
24
Tests/Fixtures/Model/JsonSerializableTest.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by mcfedr on 30/06/15 21:05
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Model;
|
||||||
|
|
||||||
|
class JsonSerializableTest implements \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'id' => 123,
|
||||||
|
'name' => 'My name',
|
||||||
|
'child' => array(
|
||||||
|
'value' => array(1, 2, 3)
|
||||||
|
),
|
||||||
|
'another' => new JsonSerializableOptionalConstructorTest()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
100
Tests/Parser/JsonSerializableParserTest.php
Normal file
100
Tests/Parser/JsonSerializableParserTest.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by mcfedr on 30/06/15 21:06
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace NelmioApiDocBundle\Tests\Parser;
|
||||||
|
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Parser\JsonSerializableParser;
|
||||||
|
|
||||||
|
class JsonSerializableParserTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var JsonSerializableParser
|
||||||
|
*/
|
||||||
|
private $parser;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->parser = new JsonSerializableParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataTestParser
|
||||||
|
*/
|
||||||
|
public function testParser($property, $expected)
|
||||||
|
{
|
||||||
|
$result = $this->parser->parse(array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableTest'));
|
||||||
|
foreach ($expected as $name => $value) {
|
||||||
|
$this->assertArrayHasKey($property, $result);
|
||||||
|
$this->assertArrayHasKey($name, $result[$property]);
|
||||||
|
$this->assertEquals($result[$property][$name], $expected[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataTestSupports
|
||||||
|
*/
|
||||||
|
public function testSupports($class, $expected)
|
||||||
|
{
|
||||||
|
$this->assertEquals($this->parser->supports(array('class' => $class)), $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataTestParser()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'property' => 'id',
|
||||||
|
'expected' => array(
|
||||||
|
'dataType' => 'integer'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'name',
|
||||||
|
'expected' => array(
|
||||||
|
'dataType' => 'string'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'child',
|
||||||
|
'expected' => array(
|
||||||
|
'dataType' => 'object',
|
||||||
|
'children' => array(
|
||||||
|
'value' => array(
|
||||||
|
'dataType' => 'array',
|
||||||
|
'actualType' => 'array',
|
||||||
|
'subType' => null,
|
||||||
|
'required' => null,
|
||||||
|
'description' => null,
|
||||||
|
'readonly' => null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataTestSupports()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableTest',
|
||||||
|
'expected' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableRequiredConstructorTest',
|
||||||
|
'expected' => false
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableOptionalConstructorTest',
|
||||||
|
'expected' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Popo',
|
||||||
|
'expected' => false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user