Added a way to group API methods by 'resource' name

This commit is contained in:
William DURAND 2012-04-12 17:24:38 +02:00
parent c4c7d14354
commit bfaa0c6adf
11 changed files with 775 additions and 401 deletions

View File

@ -20,7 +20,12 @@ class ApiDoc
/** /**
* @var string * @var string
*/ */
private $comment = null; private $description = null;
/**
* @var Boolean
*/
private $isResource = false;
public function __construct(array $data) public function __construct(array $data)
{ {
@ -39,9 +44,11 @@ class ApiDoc
} }
} }
if (isset($data['comment'])) { if (isset($data['description'])) {
$this->comment = $data['comment']; $this->description = $data['description'];
} }
$this->isResource = isset($data['resource']);
} }
public function getFilters() public function getFilters()
@ -54,8 +61,13 @@ class ApiDoc
return $this->formType; return $this->formType;
} }
public function getComment() public function getDescription()
{ {
return $this->comment; return $this->description;
}
public function isResource()
{
return $this->isResource;
} }
} }

View File

@ -28,15 +28,51 @@ class ApiDocExtractor
public function all() public function all()
{ {
$array = array(); $array = array();
$resources = array();
foreach ($this->router->getRouteCollection()->all() as $route) { foreach ($this->router->getRouteCollection()->all() as $route) {
preg_match('#(.+)::([\w]+)#', $route->getDefault('_controller'), $matches); preg_match('#(.+)::([\w]+)#', $route->getDefault('_controller'), $matches);
$method = new \ReflectionMethod($matches[1], $matches[2]); $method = new \ReflectionMethod($matches[1], $matches[2]);
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) { if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
if ($annot->isResource()) {
$resources[] = $route->getPattern();
}
$array[] = array('annotation' => $annot, 'route' => $route); $array[] = array('annotation' => $annot, 'route' => $route);
} }
} }
rsort($resources);
foreach ($array as $index => $element) {
$hasResource = false;
$pattern = $element['route']->getPattern();
foreach ($resources as $resource) {
if (0 === strpos($pattern, $resource)) {
$array[$index]['resource'] = $resource;
$hasResource = true;
break;
}
}
if (false === $hasResource) {
$array[$index]['resource'] = 'others';
}
}
usort($array, function($a, $b) {
if ($a['resource'] === $b['resource']) {
if ($a['route']->getPattern() === $b['route']->getPattern()) {
return strcmp($a['route']->getRequirement('_method'), $b['route']->getRequirement('_method'));
}
return strcmp($a['route']->getPattern(), $b['route']->getPattern());
}
return strcmp($a['resource'], $b['resource']);
});
return $array; return $array;
} }

View File

@ -33,21 +33,52 @@ abstract class AbstractFormatter implements FormatterInterface
{ {
$array = array(); $array = array();
foreach ($collection as $coll) { foreach ($collection as $coll) {
$array[] = $this->getData($coll['annotation'], $coll['route']); $resource = $coll['resource'];
if (!isset($array[$resource])) {
$array[$resource] = array();
}
$array[$resource][] = $this->getData($coll['annotation'], $coll['route']);
} }
return $this->render($array); return $this->render($array);
} }
/**
* Format a single array of data
*
* @param array $data
* @return string|array
*/
protected abstract function renderOne(array $data); protected abstract function renderOne(array $data);
/**
* Format a set of data for a given resource.
*
* @param string $resource A resource name.
* @param array $arrayOfData A set of data.
* @return string|array
*/
protected abstract function renderResourceSection($resource, array $arrayOfData);
/**
* Format a set of resource sections.
*
* @param array $collection
* @return string|array
*/
protected abstract function render(array $collection); protected abstract function render(array $collection);
/**
* @param ApiDoc $apiDoc
* @param Route $route
* @return array
*/
private function getData(ApiDoc $apiDoc, Route $route) private function getData(ApiDoc $apiDoc, Route $route)
{ {
$method = $route->getRequirement('_method'); $method = $route->getRequirement('_method');
$data = array( $data = array(
'method' => $method, 'method' => $method ?: 'ANY',
'uri' => $route->compile()->getPattern(), 'uri' => $route->compile()->getPattern(),
'requirements' => $route->compile()->getRequirements(), 'requirements' => $route->compile()->getRequirements(),
); );
@ -60,7 +91,7 @@ abstract class AbstractFormatter implements FormatterInterface
if ('PUT' === $method) { if ('PUT' === $method) {
// All parameters are optional with PUT (update) // All parameters are optional with PUT (update)
array_walk($data['parameters'], function($val, $key) use (&$data) { array_walk($data['parameters'], function($val, $key) use (&$data) {
$data['parameters'][$key]['is_required'] = false; $data['parameters'][$key]['required'] = false;
}); });
} }
} }
@ -69,8 +100,8 @@ abstract class AbstractFormatter implements FormatterInterface
$data['filters'] = $filters; $data['filters'] = $filters;
} }
if ($comment = $apiDoc->getComment()) { if ($description = $apiDoc->getDescription()) {
$data['comment'] = $comment; $data['description'] = $description;
} }
return $data; return $data;

View File

@ -12,7 +12,12 @@ class HtmlFormatter extends AbstractFormatter
*/ */
public function formatOne(ApiDoc $apiDoc, Route $route) public function formatOne(ApiDoc $apiDoc, Route $route)
{ {
return $this->renderWithLayout(parent::formatOne($apiDoc, $route)); extract(array('content' => parent::formatOne($apiDoc, $route)));
ob_start();
include __DIR__ . '/../Resources/views/formatter_resource_section.html.php';
return $this->renderWithLayout(ob_get_clean());
} }
/** /**
@ -28,14 +33,32 @@ class HtmlFormatter extends AbstractFormatter
return ob_get_clean(); return ob_get_clean();
} }
/**
* {@inheritdoc}
*/
protected function renderResourceSection($resource, array $arrayOfData)
{
$content = '';
foreach ($arrayOfData as $data) {
$content .= $this->renderOne($data);
}
extract(array('content' => $content));
ob_start();
include __DIR__ . '/../Resources/views/formatter_resource_section.html.php';
return ob_get_clean();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function render(array $collection) protected function render(array $collection)
{ {
$content = ''; $content = '';
foreach ($collection as $data) { foreach ($collection as $resource => $arrayOfData) {
$content .= $this->renderOne($data); $content .= $this->renderResourceSection($resource, $arrayOfData);
} }
return $this->renderWithLayout($content); return $this->renderWithLayout($content);
@ -43,8 +66,7 @@ class HtmlFormatter extends AbstractFormatter
private function renderWithLayout($content) private function renderWithLayout($content)
{ {
$array = array('content' => $content); extract(array('content' => $content));
extract($array);
ob_start(); ob_start();
include __DIR__ . '/../Resources/views/formatter_layout.html.php'; include __DIR__ . '/../Resources/views/formatter_layout.html.php';

View File

@ -11,8 +11,8 @@ class MarkdownFormatter extends AbstractFormatter
{ {
$markdown = sprintf("### `%s` %s ###\n", $data['method'], $data['uri']); $markdown = sprintf("### `%s` %s ###\n", $data['method'], $data['uri']);
if (isset($data['comment'])) { if (isset($data['description'])) {
$markdown .= sprintf("\n_%s_", $data['comment']); $markdown .= sprintf("\n_%s_", $data['description']);
} }
$markdown .= "\n\n"; $markdown .= "\n\n";
@ -46,8 +46,8 @@ class MarkdownFormatter extends AbstractFormatter
foreach ($data['parameters'] as $name => $parameter) { foreach ($data['parameters'] as $name => $parameter) {
$markdown .= sprintf("%s:\n\n", $name); $markdown .= sprintf("%s:\n\n", $name);
$markdown .= sprintf(" * type: %s\n", $parameter['type']); $markdown .= sprintf(" * type: %s\n", $parameter['dataType']);
$markdown .= sprintf(" * is_required: %s\n", $parameter['is_required'] ? 'true' : 'false'); $markdown .= sprintf(" * is_required: %s\n", $parameter['required'] ? 'true' : 'false');
$markdown .= "\n"; $markdown .= "\n";
} }
} }
@ -55,14 +55,29 @@ class MarkdownFormatter extends AbstractFormatter
return $markdown; return $markdown;
} }
/**
* {@inheritdoc}
*/
protected function renderResourceSection($resource, array $arrayOfData)
{
$markdown = sprintf("# %s #\n\n", $resource);
foreach ($arrayOfData as $data) {
$markdown .= $this->renderOne($data);
$markdown .= "\n";
}
return $markdown;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function render(array $collection) protected function render(array $collection)
{ {
$markdown = ''; $markdown = '';
foreach ($collection as $data) { foreach ($collection as $resource => $arrayOfData) {
$markdown .= $this->renderOne($data); $markdown .= $this->renderResourceSection($resource, $arrayOfData);
$markdown .= "\n"; $markdown .= "\n";
} }

View File

@ -15,7 +15,15 @@ class SimpleFormatter extends AbstractFormatter
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function renderOne(array $collection) protected function renderResourceSection($resource, array $arrayOfData)
{
return array($resource => $arrayOfData);
}
/**
* {@inheritdoc}
*/
protected function render(array $collection)
{ {
return $collection; return $collection;
} }

View File

@ -42,8 +42,8 @@ class FormTypeParser
} }
$parameters[$name] = array( $parameters[$name] = array(
'type' => $bestType, 'dataType' => $bestType,
'is_required' => $b->getRequired() 'required' => $b->getRequired()
); );
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,94 +1,86 @@
<li class="resource"> <li class="<?php echo strtolower($method); ?> operation">
<ul class="endpoints"> <div class="heading toggler">
<li class="endpoint"> <h3>
<ul class="operations"> <span class="http_method">
<li class="<?php echo strtolower($method); ?> operation"> <a><?php echo $method; ?></a>
<div class="heading toggler"> </span>
<h3> <span class="path">
<span class="http_method"> <?php echo $uri; ?>
<a><?php echo $method; ?></a> </span>
</span> </h3>
<span class="path"> <ul class="options">
<?php echo $uri; ?> <?php if (isset($description)) : ?>
</span> <li><?php echo $description; ?></li>
</h3> <?php endif; ?>
<ul class="options"> </ul>
<?php if (isset($comment)) : ?> </div>
<li><?php echo $comment; ?></li> <div class="content" style="display: none;">
<?php endif; ?> <?php if (isset($requirements) && !empty($requirements)) : ?>
<h4>Requirements</h4>
<table class="fullwidth">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($requirements as $key => $value) : ?>
<tr>
<td><?php echo $key; ?></td>
<td><?php echo $value; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php if (isset($filters)) : ?>
<h4>Filters</h4>
<table class="fullwidth">
<thead>
<tr>
<th>Name</th>
<th>Information</th>
</tr>
</thead>
<tbody>
<?php foreach ($filters as $name => $info) : ?>
<tr>
<td><?php echo $name; ?></td>
<td>
<ul>
<?php foreach ($info as $key => $value) : ?>
<li><em><?php echo $key; ?></em> : <?php echo $value; ?></li>
<?php endforeach; ?>
</ul> </ul>
</div> </td>
<div class="content" style="display: none;"> </tr>
<?php if (isset($requirements) && !empty($requirements)) : ?> <?php endforeach; ?>
<h4>Requirements</h4> </tbody>
<table class="fullwidth"> </table>
<thead> <?php endif; ?>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($requirements as $key => $value) : ?>
<tr>
<td><?php echo $key; ?></td>
<td><?php echo $value; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php if (isset($filters)) : ?> <?php if (isset($parameters)) : ?>
<h4>Filters</h4> <h4>Parameters</h4>
<table class="fullwidth"> <table class='fullwidth'>
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Parameter</th>
<th>Information</th> <th>Type</th>
</tr> <th>Required?</th>
</thead> </tr>
<tbody> </thead>
<?php foreach ($filters as $name => $info) : ?> <tbody>
<tr> <?php foreach ($parameters as $name => $info) : ?>
<td><?php echo $name; ?></td> <tr>
<td> <td><?php echo $name; ?></td>
<ul> <td><?php echo $info['dataType']; ?></td>
<?php foreach ($info as $key => $value) : ?> <td><?php echo $info['required'] ? 'true' : 'false'; ?></td>
<li><?php echo $key . ': ' . $value; ?></li> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</ul> </tbody>
</td> </table>
</tr> <?php endif; ?>
<?php endforeach; ?> </div>
</tbody>
</table>
<?php endif; ?>
<?php if (isset($parameters)) : ?>
<h4>Parameters</h4>
<table class='fullwidth'>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th>Required?</th>
</tr>
</thead>
<tbody>
<?php foreach ($parameters as $name => $info) : ?>
<tr>
<td><?php echo $name; ?></td>
<td><?php echo $info['type']; ?></td>
<td><?php echo $info['is_required'] ? 'true' : 'false'; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</li>
</ul>
</li>
</ul>
</li> </li>

View File

@ -10,14 +10,20 @@
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
</head> </head>
<body> <body>
<div id="header">
<h1>API documentation</h1>
</div>
<div class="container" id="resources_container"> <div class="container" id="resources_container">
<ul id="resources"> <ul id="resources">
<?php echo $content; ?> <?php echo $content; ?>
</ul> </ul>
</div> </div>
<p id="colophon">
Documentation auto-generated on <?php echo date(DATE_RFC822); ?>
</p>
<script type="text/javascript"> <script type="text/javascript">
$('.toggler').click(function() { $('.toggler').click(function() {
$(this).next().toggle(); $(this).next().slideToggle('slow');
}); });
</script> </script>
</body> </body>

View File

@ -0,0 +1,14 @@
<li class="resource">
<?php if (isset($resource)) : ?>
<div class="heading">
<h2><?php echo $resource; ?></h2>
</div>
<?php endif; ?>
<ul class="endpoints">
<li class="endpoint">
<ul class="operations">
<?php echo $content; ?>
</ul>
</li>
</ul>
</li>