add api docs via swagger and use jms serializer

This commit is contained in:
Kacper Donat 2019-12-08 23:06:19 +01:00
parent 14755d8343
commit 49538b037d
32 changed files with 4503 additions and 3108 deletions

View File

@ -7,10 +7,12 @@
"ext-ctype": "*",
"ext-iconv": "*",
"ext-json": "*",
"jms/serializer-bundle": "^3.5",
"nelmio/api-doc-bundle": "^3.5",
"nesbot/carbon": "^1.33",
"ocramius/proxy-manager": "^2.0",
"sensio/framework-extra-bundle": "^5.2",
"symfony/asset": "*",
"symfony/asset": "4.1.*",
"symfony/console": "*",
"symfony/flex": "^1.1",
"symfony/framework-bundle": "*",
@ -33,7 +35,7 @@
"sort-packages": true,
"secure-http": false,
"platform": {
"php": "7.1.12"
"php": "7.3.12"
}
},
"autoload": {

2499
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,4 +9,6 @@ return [
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
];

View File

@ -0,0 +1,7 @@
jms_serializer:
visitors:
json_serialization:
options:
- JSON_PRETTY_PRINT
- JSON_UNESCAPED_SLASHES
- JSON_PRESERVE_ZERO_FRACTION

View File

@ -0,0 +1,13 @@
jms_serializer:
visitors:
xml_serialization:
format_output: '%kernel.debug%'
# metadata:
# auto_detection: false
# directories:
# any-name:
# namespace_prefix: "My\\FooBundle"
# path: "@MyFooBundle/Resources/config/serializer"
# another-name:
# namespace_prefix: "My\\BarBundle"
# path: "@MyBarBundle/Resources/config/serializer"

View File

@ -0,0 +1,19 @@
nelmio_api_doc:
documentation:
info:
title: Czy Dojadę?
version: 0.1.0
parameters:
provider:
in: path
name: provider
type: string
description: Data provider identificator, e.g. trojmiasto for ZTM Gdańsk.
required: true
areas:
path_patterns:
- ^/[^\/]+/api(?!/doc$) # Accepts routes under /api except /api/doc

View File

@ -0,0 +1,6 @@
jms_serializer:
visitors:
json_serialization:
options:
- JSON_UNESCAPED_SLASHES
- JSON_PRESERVE_ZERO_FRACTION

View File

@ -0,0 +1,12 @@
# Expose your documentation as JSON swagger compliant
app.swagger:
path: /api/doc.json
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger }
## Requires the Asset component and the Twig bundle
## $ composer require twig asset
app.swagger_ui:
path: /api/doc
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger_ui }

View File

@ -42,6 +42,15 @@ services:
assets.modified_time_version_strategy:
class: App\Asset\ModifiedTimeVersionStrategy
#eerialziser
jms_serializer.serialized_name_annotation_strategy:
class: JMS\Serializer\Naming\SerializedNameAnnotationStrategy
arguments:
- '@jms_serializer.identical_property_naming_strategy'
App\Serialization\CarbonHandler:
arguments: ['@jms_serializer.datetime_handler']
#proxy configuration
proxy.locator:
class: 'ProxyManager\FileLocator\FileLocator'

View File

@ -32,6 +32,7 @@ if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
Request::setTrustedHosts(explode(',', $trustedHosts));
}
\Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredName('alias');
$kernel = new Kernel($env, $debug);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);

View File

@ -35,10 +35,10 @@ export class FinderComponent extends Vue {
this.state = 'fetching';
const response = await fetch(urls.prepare(urls.stops.search, { name: this.filter }));
const response = await fetch(urls.prepare(urls.stops.grouped, { name: this.filter }));
if (response.ok) {
this.found = await response.json();
this.found = (await response.json()).reduce((accumulator, { name, stops }) => Object.assign(accumulator, { [name]: stops }), {});
this.state = 'ready';
} else {
this.state = 'error';

View File

@ -51,10 +51,10 @@ export default {
departures: `${base}/departures`,
messages: `${base}/messages`,
stops: {
all: `${base}/stops`,
search: `${base}/stops/search`,
get: `${base}/stops/{id}`,
tracks: `${base}/stops/{id}/tracks`
all: `${base}/stops`,
grouped: `${base}/stops/groups`,
get: `${base}/stops/{id}`,
tracks: `${base}/stops/{id}/tracks`
},
prepare: (url: string, params: UrlParams = { }) => prepare(url, Object.assign({}, { provider: window['data'].provider }, params))
}

106
ruleset.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0"?>
<rulset name="Kadet.CzyDojade">
<description>Czy Dojadę ruleset</description>
<arg name="colors"/>
<arg name="parallel" value="75"/>
<rule ref="Internal.Tokenizer.Exception">
<type>error</type>
</rule>
<!-- PSR 2 -->
<rule ref="PSR2">
<exclude name="PSR2.Namespaces"/>
<exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
<exclude name="PSR2.Classes.MethodDeclaration.Underscore"/>
</rule>
<!-- No More Than 2 lines -->
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
<properties>
<property name="ignoreBlankLines" value="false"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile">
<severity>10</severity>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EndFile">
<severity>10</severity>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
<severity>5</severity>
</rule>
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<!-- Make this sniff more sensitive to commented out code blocks. -->
<rule ref="Squiz.PHP.CommentedOutCode">
<properties>
<property name="maxPercentage" value="60"/>
</properties>
</rule>
<!-- Line length -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
<property name="absoluteLineLimit" value="180"/>
</properties>
</rule>
<!-- Linus rule -->
<rule ref="Generic.Metrics.NestingLevel">
<properties>
<property name="nestingLevel" value="3"/>
<property name="absoluteNestingLevel" value="6"/>
</properties>
<severity>50</severity>
</rule>
<rule ref="Generic.Strings.UnnecessaryStringConcat">
<properties>
<property name="allowMultiline" value="true"/>
</properties>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration">
<exclude name="Squiz.Arrays.ArrayDeclaration.KeyNotAligned"/>
<exclude name="Squiz.Arrays.ArrayDeclaration.ValueNotAligned"/>
<exclude name="Squiz.Arrays.ArrayDeclaration.CloseBraceNotAligned"/>
<exclude name="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed"/>
</rule>
<rule ref="Generic.Arrays.ArrayIndent"/>
<rule ref="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.MultiLineNotAllowed">
<severity>1</severity>
</rule>
<!-- Ban some functions -->
<rule ref="Generic.PHP.ForbiddenFunctions">
<properties>
<property name="forbiddenFunctions" type="array">
<element key="sizeof" value="count"/>
<element key="delete" value="unset"/>
<element key="print" value="echo"/>
<element key="is_null" value="null"/>
<element key="create_function" value="null"/>
</property>
</properties>
</rule>
<!-- a feee -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
</rulset>

View File

@ -7,6 +7,8 @@ use App\Controller\Controller;
use App\Model\Departure;
use App\Provider\DepartureRepository;
use App\Provider\StopRepository;
use Nelmio\ApiDocBundle\Annotation\Model;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
@ -18,17 +20,22 @@ use Symfony\Component\Routing\Annotation\Route;
class DeparturesController extends Controller
{
/**
* @Route("/{id}")
* @Route("/{stop}", methods={"GET"})
* @SWG\Response(
* description="Gets departures from particular stop.",
* response=200,
* @SWG\Schema(type="array", @SWG\Items(ref=@Model(type=Departure::class)))
* )
*/
public function stop(DepartureRepository $departures, StopRepository $stops, $id)
public function stop(DepartureRepository $departures, StopRepository $stops, $stop)
{
$stop = $stops->getById($id);
$stop = $stops->getById($stop);
return $this->json($departures->getForStop($stop));
}
/**
* @Route("/")
* @Route("/", methods={"GET"})
*/
public function stops(DepartureRepository $departures, StopRepository $stops, Request $request)
{

View File

@ -5,14 +5,20 @@ namespace App\Controller\Api\v1;
use App\Controller\Controller;
use App\Provider\MessageRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Nelmio\ApiDocBundle\Annotation\Model;
use Swagger\Annotations as SWG;
use Symfony\Component\Routing\Annotation\Route;
use App\Model\Message;
/**
* @Route("/messages")
* @SWG\Tag(name="Messages")
* @SWG\Parameter(ref="#/parameters/provider")
*/
class MessagesController extends Controller
{
/**
* @SWG\Response(response=200, description="Returns all messages from carrier at given moment.", @SWG\Schema(type="array", @SWG\Items(ref=@Model(type=Message::class))))
* @Route("/", methods={"GET"})
*/
public function all(MessageRepository $messages)

View File

@ -5,9 +5,12 @@ namespace App\Controller\Api\v1;
use App\Controller\Controller;
use App\Model\Stop;
use App\Model\StopGroup;
use App\Provider\StopRepository;
use App\Provider\TrackRepository;
use App\Service\Proxy\ReferenceFactory;
use Nelmio\ApiDocBundle\Annotation\Model;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
@ -16,34 +19,95 @@ use Symfony\Component\Routing\Annotation\Route;
*
* @package App\Controller
* @Route("/stops")
*
* @SWG\Tag(name="stops")
* @SWG\Parameter(ref="#/parameters/provider")
*/
class StopsController extends Controller
{
/**
* @SWG\Response(
* response=200,
* description="Returns all stops for specific provider, e.g. ZTM Gdańsk.",
* @SWG\Schema(type="array", @SWG\Items(ref=@Model(type=Stop::class)))
* )
*
* @SWG\Parameter(
* name="id",
* in="query",
* type="array",
* description="Stop identificators to retrieve at once. Can be used to bulk load data. If not specified will return all data.",
* @SWG\Items(type="string")
* )
*
* @Route("/", methods={"GET"})
*/
public function index(Request $request, StopRepository $stops)
{
$result = $request->query->has('id')
? $stops->getManyById($request->query->get('id'))
: $stops->getAllGroups();
switch (true) {
case $request->query->has('id'):
$result = $stops->getManyById($request->query->get('id'));
break;
case $request->query->has('name'):
$result = $stops->findGroupsByName($request->query->get('name'));
break;
default:
$result = $stops->getAllGroups();
}
return $this->json($result->all());
}
/**
* @Route("/search", methods={"GET"})
* @SWG\Response(
* response=200,
* description="Returns grouped stops for specific provider, e.g. ZTM Gdańsk.",
* @SWG\Schema(type="array", @SWG\Items(ref=@Model(type=StopGroup::class)))
* )
*
* @SWG\Parameter(
* name="name",
* in="query",
* type="string",
* description="Part of the stop name to search for.",
* )
*
* @Route("/groups", methods={"GET"})
*/
public function find(Request $request, StopRepository $stops)
public function groups(Request $request, StopRepository $stops)
{
$result = $request->query->has('name')
? $stops->findGroupsByName($request->query->get('name'))
: $stops->getAllGroups();
switch (true) {
case $request->query->has('id'):
$result = $stops->getManyById($request->query->get('id'));
break;
case $request->query->has('name'):
$result = $stops->findGroupsByName($request->query->get('name'));
break;
default:
$result = $stops->getAllGroups();
}
return $this->json($result->all());
}
/**
* @SWG\Response(
* response=200,
* description="Returns specific stop referenced via identificator.",
* @SWG\Schema(ref=@Model(type=Stop::class))
* )
*
* @SWG\Parameter(
* name="id",
* in="path",
* type="string",
* description="Stop identificator as provided by data provider."
* )
*
* @Route("/{id}", methods={"GET"})
*/
public function one(Request $request, StopRepository $stops, $id)

View File

@ -4,10 +4,13 @@ namespace App\Controller\Api\v1;
use App\Controller\Controller;
use App\Model\Stop;
use App\Model\Track;
use App\Provider\TrackRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Nelmio\ApiDocBundle\Annotation\Model;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use function App\Functions\encapsulate;
/**
@ -16,7 +19,12 @@ use function App\Functions\encapsulate;
class TracksController extends Controller
{
/**
* @Route("/")
* @SWG\Response(
* response=200,
* description="Returns all tracks for specific provider, e.g. ZTM Gdańsk.",
* )
* @SWG\Tag(name="tracks")
* @Route("/", methods={"GET"})
*/
public function index(Request $request, TrackRepository $repository)
{
@ -28,7 +36,12 @@ class TracksController extends Controller
case $request->query->has('id'):
return $this->byId($request, $repository);
default:
throw new BadRequestHttpException(sprintf('At least one parameter of %s must be set.', implode(', ', ['stop', 'line', 'id'])));
throw new BadRequestHttpException(
sprintf(
'At least one parameter of %s must be set.',
implode(', ', ['stop', 'line', 'id'])
)
);
}
}

View File

@ -4,8 +4,21 @@
namespace App\Controller;
use JMS\Serializer\SerializerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller as SymfonyController;
use Symfony\Component\HttpFoundation\JsonResponse;
abstract class Controller extends SymfonyController
{
private $serializer;
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
protected function json($data, int $status = 200, array $headers = [], array $context = []): JsonResponse
{
return new JsonResponse($this->serializer->serialize($data, "json"), $status, $headers, true);
}
}

View File

@ -26,7 +26,7 @@ class MainController extends Controller
$state = json_decode($request->query->get('state', '{}'), true) ?: [];
$state = array_merge([
'version' => 1,
'stops' => []
'stops' => [],
], $state);
return $this->render('app.html.twig', compact('state', 'provider'));

View File

@ -2,6 +2,7 @@
namespace App;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\FileResource;

View File

@ -3,6 +3,7 @@
namespace App\Model;
use Carbon\Carbon;
use JMS\Serializer\Annotation as Serializer;
class Departure implements Fillable
{
@ -104,6 +105,7 @@ class Departure implements Fillable
$this->stop = $stop;
}
/** @Serializer\VirtualProperty() */
public function getDelay(): ?int
{
return $this->getEstimated()

View File

@ -3,6 +3,8 @@
namespace App\Model;
use Carbon\Carbon;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
class Message implements Fillable
{
@ -14,18 +16,23 @@ class Message implements Fillable
/**
* Message content.
* @Serializer\Type("string")
* @var string
*/
private $message;
/**
* Message type, see TYPE_* constants
* @Serializer\Type("string")
* @SWG\Property(type="string", enum={ Message::TYPE_INFO, Message::TYPE_BREAKDOWN, Message::TYPE_UNKNOWN })
* @var string
*/
private $type = self::TYPE_UNKNOWN;
/**
* Message validity time span start
* @Serializer\Type("Carbon")
* @SWG\Property(type="string")
* @var Carbon|null
*/
private $validFrom;
@ -33,6 +40,8 @@ class Message implements Fillable
/**
* Message validity time span end
* @var Carbon|null
* @Serializer\Type("Carbon")
* @SWG\Property(type="string")
*/
private $validTo;

View File

@ -2,11 +2,15 @@
namespace App\Model;
use Swagger\Annotations as SWG;
trait ReferableTrait
{
/**
* Identifier coming from provider service
* @var string
*
* @SWG\Property(example="1045")
*/
private $id;

View File

@ -2,11 +2,18 @@
namespace App\Model;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Tightenco\Collect\Support\Arr;
/**
* Class Stop
*
* @package App\Model
*/
class Stop implements Referable, Fillable, NormalizableInterface
{
use FillTrait, ReferableTrait;
@ -14,36 +21,45 @@ class Stop implements Referable, Fillable, NormalizableInterface
/**
* Stop name
* @var string
* @SWG\Property(example="Jasień PKM")
* @Serializer\Type("string")
*/
private $name;
/**
* Optional stop description, should not be longer than 255 chars
* Optional stop description, should not be longer than 255 chars.
* @var string|null
* @Serializer\Type("string")
*/
private $description;
/**
* Optional stop variant - for example number of shed
* Optional stop variant - for example number of shed.
* @var string|null
* @SWG\Property(example="01")
* @Serializer\Type("string")
*/
private $variant;
/**
* Latitude of stop
* @var float|null
* @Serializer\Exclude()
*/
private $latitude;
/**
* Longitude of stop
* @var float|null
* @Serializer\Exclude()
*/
private $longitude;
/**
* True if stop is available only on demand
* @var bool
* @Serializer\Type("bool")
* @SWG\Property(example=false)
*/
private $onDemand = false;
@ -77,7 +93,6 @@ class Stop implements Referable, Fillable, NormalizableInterface
$this->variant = $variant;
}
/** @Groups({"hidden"}) */
public function getLatitude(): ?float
{
return $this->latitude;
@ -88,7 +103,6 @@ class Stop implements Referable, Fillable, NormalizableInterface
$this->latitude = $latitude;
}
/** @Groups({"hidden"}) */
public function getLongitude(): ?float
{
return $this->longitude;
@ -99,6 +113,12 @@ class Stop implements Referable, Fillable, NormalizableInterface
$this->longitude = $longitude;
}
/**
* @return string[]
* @Serializer\VirtualProperty()
* @Serializer\Type("array<string>")
* @SWG\Property(type="array", @SWG\Items(type="string", example="1"))
*/
public function getLocation(): array
{
return [ $this->latitude, $this->longitude ];
@ -106,7 +126,7 @@ class Stop implements Referable, Fillable, NormalizableInterface
public function setLocation(array $location)
{
list($this->latitude, $this->longitude) = $location;
[$this->latitude, $this->longitude] = $location;
}
public function isOnDemand(): bool

View File

@ -2,16 +2,42 @@
namespace App\Model;
use JMS\Serializer\Annotation as Serializer;
use Nelmio\ApiDocBundle\Annotation\Model;
use Swagger\Annotations as SWG;
use Tightenco\Collect\Support\Collection;
use App\Model\Stop;
class StopGroup extends Collection
/**
* Class StopGroup
*
* @package App\Model
*/
class StopGroup
{
/**
* Name of stop group
* Name of stop group.
* @SWG\Property(example="Jasień PKM")
* @Serializer\Type("string")
* @var string
*/
private $name;
/**
* All stops in group.
* @var Collection|Stop[]
* @SWG\Property(
* type="array",
* @SWG\Items(ref=@Model(type=Stop::class))
* )
*/
private $stops;
public function __construct()
{
$this->stops = new Collection();
}
public function getName(): string
{
return $this->name;
@ -21,4 +47,14 @@ class StopGroup extends Collection
{
$this->name = $name;
}
public function setStops($stops)
{
$this->stops = new Collection($stops);
}
public function getStops()
{
return $this->stops;
}
}

View File

@ -2,6 +2,7 @@
namespace App\Model;
use Swagger\Annotations as SWG;
use Tightenco\Collect\Support\Collection;
class Track implements Referable, Fillable
@ -29,11 +30,11 @@ class Track implements Referable, Fillable
/**
* Stops in track
* @var Collection
* @var Stop[]|Collection
* @SWG\Property(type="Stop")
*/
private $stops;
/**
* Track constructor.
*/

View File

@ -56,11 +56,13 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
{
return $stops->groupBy(function (Stop $stop) {
return $stop->getName();
})->map(function ($group, $key) {
$group = new StopGroup($group);
})->map(function ($stops, $key) {
$group = new StopGroup();
$group->setName($key);
$group->setStops($stops);
return $group;
});
})->values();
}
}
}

View File

@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Serialization;
use Carbon\Carbon;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\DateHandler;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
use JMS\Serializer\Visitor\SerializationVisitorInterface;
use Tightenco\Collect\Support\Collection;
/**
* Class LaravelCollectionHandler
*
* Shamelessly copied from https://github.com/schmittjoh/serializer/blob/master/src/Handler/ArrayCollectionHandler.php
*
* @package App\Serialization
*/
final class CarbonHandler implements SubscribingHandlerInterface
{
private $dateTimeHandler;
public function __construct(DateHandler $dateHandler)
{
$this->dateTimeHandler = $dateHandler;
}
/**
* {@inheritdoc}
*/
public static function getSubscribingMethods()
{
$methods = [];
$formats = ['json', 'xml', 'yml'];
$collectionTypes = [
'Carbon',
Carbon::class,
];
foreach ($collectionTypes as $type) {
foreach ($formats as $format) {
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
'type' => $type,
'format' => $format,
'method' => 'serialize',
];
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
'type' => $type,
'format' => $format,
'method' => 'deserialize',
];
}
}
return $methods;
}
/**
* @return array|\ArrayObject
*/
public function serialize(
SerializationVisitorInterface $visitor,
Carbon $date,
array $type,
SerializationContext $context
) {
return $this->dateTimeHandler->serializeDateTime($visitor, $date, $type, $context);
}
/**
* @param mixed $data
*/
public function deserialize(
DeserializationVisitorInterface $visitor,
$data,
array $type,
DeserializationContext $context
): Collection {
return new Collection($visitor->visitArray($data, $type));
}
}

View File

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace App\Serialization;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
use JMS\Serializer\Visitor\SerializationVisitorInterface;
use Tightenco\Collect\Support\Collection;
/**
* Class LaravelCollectionHandler
*
* Shamelessly copied from https://github.com/schmittjoh/serializer/blob/master/src/Handler/ArrayCollectionHandler.php
*
* @package App\Serialization
*/
final class LaravelCollectionHandler implements SubscribingHandlerInterface
{
/**
* {@inheritdoc}
*/
public static function getSubscribingMethods()
{
$methods = [];
$formats = ['json', 'xml', 'yml'];
$collectionTypes = [
'Collection',
Collection::class,
];
foreach ($collectionTypes as $type) {
foreach ($formats as $format) {
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
'type' => $type,
'format' => $format,
'method' => 'serializeCollection',
];
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
'type' => $type,
'format' => $format,
'method' => 'deserializeCollection',
];
}
}
return $methods;
}
/**
* @return array|\ArrayObject
*/
public function serializeCollection(
SerializationVisitorInterface $visitor,
Collection $collection,
array $type,
SerializationContext $context
) {
// We change the base type, and pass through possible parameters.
$type['name'] = 'array';
$result = $visitor->visitArray($collection->all(), $type);
return $result;
}
/**
* @param mixed $data
*/
public function deserializeCollection(
DeserializationVisitorInterface $visitor,
$data,
array $type,
DeserializationContext $context
): Collection {
// See above.
$type['name'] = 'array';
return new Collection($visitor->visitArray($data, $type));
}
}

View File

@ -65,15 +65,89 @@
"doctrine/reflection": {
"version": "v1.0.0"
},
"exsyst/swagger": {
"version": "v0.4.1"
},
"hoa/compiler": {
"version": "3.17.08.08"
},
"hoa/consistency": {
"version": "1.17.05.02"
},
"hoa/event": {
"version": "1.17.01.13"
},
"hoa/exception": {
"version": "1.17.01.16"
},
"hoa/file": {
"version": "1.17.07.11"
},
"hoa/iterator": {
"version": "2.17.01.10"
},
"hoa/math": {
"version": "1.17.05.16"
},
"hoa/protocol": {
"version": "1.17.01.14"
},
"hoa/regex": {
"version": "1.17.01.13"
},
"hoa/stream": {
"version": "1.17.02.21"
},
"hoa/ustring": {
"version": "4.17.01.16"
},
"hoa/visitor": {
"version": "2.17.01.16"
},
"hoa/zformat": {
"version": "1.17.01.10"
},
"jdorn/sql-formatter": {
"version": "v1.2.17"
},
"jms/metadata": {
"version": "1.7.0"
},
"jms/serializer": {
"version": "1.14.0"
},
"jms/serializer-bundle": {
"version": "3.0",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "master",
"version": "3.0",
"ref": "384cec52df45f3bfd46a09930d6960a58872b268"
},
"files": [
"config/packages/dev/jms_serializer.yaml",
"config/packages/jms_serializer.yaml",
"config/packages/prod/jms_serializer.yaml"
]
},
"kadet/functional": {
"version": "dev-master"
},
"kylekatarnls/update-helper": {
"version": "1.2.0"
},
"monolog/monolog": {
"version": "1.23.0"
},
"nelmio/api-doc-bundle": {
"version": "3.0",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "master",
"version": "3.0",
"ref": "c8e0c38e1a280ab9e37587a8fa32b251d5bc1c94"
}
},
"nesbot/carbon": {
"version": "1.33.0"
},
@ -83,6 +157,9 @@
"ocramius/proxy-manager": {
"version": "2.2.1"
},
"php": {
"version": "7.3.12"
},
"phpdocumentor/reflection-common": {
"version": "1.0.1"
},
@ -191,6 +268,9 @@
"ref": "18ebf5a940573a20de06f9c4060101eeb438cf3d"
}
},
"symfony/options-resolver": {
"version": "v4.1.12"
},
"symfony/orm-pack": {
"version": "v1.0.5"
},
@ -224,6 +304,9 @@
"symfony/serializer-pack": {
"version": "v1.0.1"
},
"symfony/stopwatch": {
"version": "v4.1.12"
},
"symfony/translation": {
"version": "3.3",
"recipe": {
@ -274,5 +357,8 @@
},
"zendframework/zend-eventmanager": {
"version": "3.2.1"
},
"zircote/swagger-php": {
"version": "2.0.14"
}
}

View File

@ -0,0 +1,36 @@
{% extends '@!NelmioApiDoc/SwaggerUi/index.html.twig' %}
{% block stylesheets %}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700">
{{ parent() }}
<style type="text/css" rel="stylesheet">
#formats {
font-family: Open Sans,sans-serif;
}
.swagger-ui .opblock-tag,
.swagger-ui .opblock .opblock-section-header label,
.swagger-ui .opblock .opblock-section-header h4,
.swagger-ui .opblock .opblock-summary-method,
.swagger-ui .tab li,
.swagger-ui .scheme-container .schemes>label,
.swagger-ui .loading-container .loading:after,
.swagger-ui .btn,
.swagger-ui .btn.cancel,
.swagger-ui select,
.swagger-ui label,
.swagger-ui .dialog-ux .modal-ux-content h4,
.swagger-ui .dialog-ux .modal-ux-header h3,
.swagger-ui section.models h4,
.swagger-ui section.models h5,
.swagger-ui .model-title,
.swagger-ui .parameter__name,
.swagger-ui .topbar a,
.swagger-ui .topbar .download-url-wrapper .download-url-button,
.swagger-ui .info .title small pre,
.swagger-ui .scopes h2,
.swagger-ui .errors-wrapper hgroup h4 {
font-family: Open Sans,sans-serif!important;
}
</style>
{% endblock stylesheets %}

4382
yarn.lock

File diff suppressed because it is too large Load Diff