From 0dc8883e0922d5865f9f4e271ea5a4ce0c1dfd34 Mon Sep 17 00:00:00 2001 From: William DURAND Date: Wed, 11 Apr 2012 20:00:21 +0200 Subject: [PATCH] Introduced ApiBundle --- Annotation/ApiDoc.php | 61 ++++++++++++++++++++++ DependencyInjection/NelmioApiExtension.php | 21 ++++++++ EventListener/RequestListener.php | 57 ++++++++++++++++++++ Formatter/ApiDocFormatter.php | 53 +++++++++++++++++++ NelmioApiBundle.php | 9 ++++ Parser/FormTypeParser.php | 53 +++++++++++++++++++ Resources/config/request_listener.xml | 19 +++++++ Resources/config/services.xml | 20 +++++++ 8 files changed, 293 insertions(+) create mode 100644 Annotation/ApiDoc.php create mode 100644 DependencyInjection/NelmioApiExtension.php create mode 100644 EventListener/RequestListener.php create mode 100644 Formatter/ApiDocFormatter.php create mode 100644 NelmioApiBundle.php create mode 100644 Parser/FormTypeParser.php create mode 100644 Resources/config/request_listener.xml create mode 100644 Resources/config/services.xml diff --git a/Annotation/ApiDoc.php b/Annotation/ApiDoc.php new file mode 100644 index 000000000..006a40e48 --- /dev/null +++ b/Annotation/ApiDoc.php @@ -0,0 +1,61 @@ +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; + } +} diff --git a/DependencyInjection/NelmioApiExtension.php b/DependencyInjection/NelmioApiExtension.php new file mode 100644 index 000000000..15b249a29 --- /dev/null +++ b/DependencyInjection/NelmioApiExtension.php @@ -0,0 +1,21 @@ +load('request_listener.xml'); + $loader->load('services.xml'); + } +} diff --git a/EventListener/RequestListener.php b/EventListener/RequestListener.php new file mode 100644 index 000000000..fa73d12ae --- /dev/null +++ b/EventListener/RequestListener.php @@ -0,0 +1,57 @@ +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)); + } + } + } +} diff --git a/Formatter/ApiDocFormatter.php b/Formatter/ApiDocFormatter.php new file mode 100644 index 000000000..d44fbe517 --- /dev/null +++ b/Formatter/ApiDocFormatter.php @@ -0,0 +1,53 @@ +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; + } +} diff --git a/NelmioApiBundle.php b/NelmioApiBundle.php new file mode 100644 index 000000000..1791149ed --- /dev/null +++ b/NelmioApiBundle.php @@ -0,0 +1,9 @@ + '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; + } +} diff --git a/Resources/config/request_listener.xml b/Resources/config/request_listener.xml new file mode 100644 index 000000000..26279e627 --- /dev/null +++ b/Resources/config/request_listener.xml @@ -0,0 +1,19 @@ + + + + + Nelmio\ApiBundle\EventListener\RequestListener + + + + + + + + + + + + diff --git a/Resources/config/services.xml b/Resources/config/services.xml new file mode 100644 index 000000000..370b52ebc --- /dev/null +++ b/Resources/config/services.xml @@ -0,0 +1,20 @@ + + + + + Nelmio\ApiBundle\Parser\FormTypeParser + Nelmio\ApiBundle\Formatter\ApiDocFormatter + + + + + + + + + + + +