Introduced ApiBundle

This commit is contained in:
William DURAND 2012-04-11 20:00:21 +02:00
commit 0dc8883e09
8 changed files with 293 additions and 0 deletions

61
Annotation/ApiDoc.php Normal file
View File

@ -0,0 +1,61 @@
<?php
namespace Nelmio\ApiBundle\Annotation;
/**
* @Annotation
*/
class ApiDoc
{
/**
* @var array
*/
private $filters = array();
/**
* @var string
*/
private $formType = null;
/**
* @var string
*/
private $comment = null;
public function __construct(array $data)
{
if (isset($data['formType'])) {
$this->formType = $data['formType'];
} else if (isset($data['filters'])) {
foreach ($data['filters'] as $filter) {
if (!isset($filter['name'])) {
throw new \InvalidArgumentException('A "filter" element has to contain a "name" attribute');
}
$name = $filter['name'];
unset($filter['name']);
$this->filters[$name] = $filter;
}
}
if (isset($data['comment'])) {
$this->comment = $data['comment'];
}
}
public function getFilters()
{
return $this->filters;
}
public function getFormType()
{
return $this->formType;
}
public function getComment()
{
return $this->comment;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Nelmio\ApiBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
class NelmioApiExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('request_listener.xml');
$loader->load('services.xml');
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace Nelmio\ApiBundle\EventListener;
use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiBundle\Formatter\ApiDocFormatter;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Routing\RouterInterface;
class RequestListener
{
protected $annotationClass = 'Nelmio\\ApiBundle\\Annotation\\ApiDoc';
protected $reader;
protected $router;
protected $formatter;
public function __construct(Reader $reader, RouterInterface $router, ApiDocFormatter $formatter)
{
$this->reader = $reader;
$this->router = $router;
$this->formatter = $formatter;
}
/**
* {@inheritdoc}
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if (!$request->get('_doc')) {
return;
}
preg_match('#(.+)::([\w]+)#', $request->get('_controller'), $matches);
$method = new \ReflectionMethod($matches[1], $matches[2]);
$route = $request->get('_route');
if ($annot = $this->reader->getMethodAnnotation($method, $this->annotationClass)) {
if ($route = $this->router->getRouteCollection()->get($route)) {
$result = $this->formatter->format($annot, $route);
$event->setResponse(new JsonResponse($result));
}
}
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Nelmio\ApiBundle\Formatter;
use Nelmio\ApiBundle\Annotation\ApiDoc;
use Nelmio\ApiBundle\Parser\FormTypeParser;
use Symfony\Component\Routing\Route;
class ApiDocFormatter
{
/**
* @var \Nelmio\ApiBundle\Parser\FormTypeParser
*/
protected $parser;
public function __construct(FormTypeParser $parser)
{
$this->parser = $parser;
}
public function format(ApiDoc $apiDoc, Route $route)
{
$method = $route->getRequirement('_method');
$data = array(
'method' => $method,
'uri' => $route->compile()->getPattern(),
'requirements' => $route->compile()->getRequirements(),
);
unset($data['requirements']['_method']);
if (null !== $formType = $apiDoc->getFormType()) {
$data['parameters'] = $this->parser->parse(new $formType());
if ('PUT' === $method) {
// All parameters are optional with PUT (update)
array_walk($data['parameters'], function($val, $key) use (&$data) {
$data['parameters'][$key]['is_required'] = false;
});
}
}
if ($filters = $apiDoc->getFilters()) {
$data['filters'] = $filters;
}
if ($comment = $apiDoc->getComment()) {
$data['comment'] = $comment;
}
return $data;
}
}

9
NelmioApiBundle.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace Nelmio\ApiBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class NelmioApiBundle extends Bundle
{
}

53
Parser/FormTypeParser.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace Nelmio\ApiBundle\Parser;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormFactoryInterface;
class FormTypeParser
{
protected $formFactory;
protected $mapTypes = array(
'text' => 'string',
'date' => 'date',
'datetime' => 'datetime',
'checkbox' => 'boolean',
'time' => 'time',
'number' => 'float',
'integer' => 'int',
'textarea' => 'string',
);
public function __construct(FormFactoryInterface $formFactory)
{
$this->formFactory = $formFactory;
}
public function parse(AbstractType $type)
{
$builder = $this->formFactory->createBuilder($type);
$parameters = array();
foreach ($builder->all() as $name => $child) {
$b = $builder->create($name, $child['type'], $child['options']);
$bestType = '';
foreach ($b->getTypes() as $type) {
if (isset($this->mapTypes[$type->getName()])) {
$bestType = $this->mapTypes[$type->getName()];
}
}
$parameters[] = array(
'name' => $name,
'type' => $bestType,
'is_required' => $b->getRequired()
);
}
return $parameters;
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="nelmio.api.event_listener.request.class">Nelmio\ApiBundle\EventListener\RequestListener</parameter>
</parameters>
<services>
<service id="nelmio.api.event_listener.request" class="%nelmio.api.event_listener.request.class%">
<argument type="service" id="annotation_reader" />
<argument type="service" id="router" />
<argument type="service" id="nelmio.api.formatter.api_doc_formatter" />
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
</service>
</services>
</container>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="nelmio.api.parser.form_type_parser.class">Nelmio\ApiBundle\Parser\FormTypeParser</parameter>
<parameter key="nelmio.api.formatter.api_doc_formatter.class">Nelmio\ApiBundle\Formatter\ApiDocFormatter</parameter>
</parameters>
<services>
<service id="nelmio.api.parser.form_type_parser" class="%nelmio.api.parser.form_type_parser.class%">
<argument type="service" id="form.factory" />
</service>
<service id="nelmio.api.formatter.api_doc_formatter" class="%nelmio.api.formatter.api_doc_formatter.class%">
<argument type="service" id="nelmio.api.parser.form_type_parser" />
</service>
</services>
</container>