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
*/
private $comment = null;
private $description = null;
/**
* @var Boolean
*/
private $isResource = false;
public function __construct(array $data)
{
@ -39,9 +44,11 @@ class ApiDoc
}
}
if (isset($data['comment'])) {
$this->comment = $data['comment'];
if (isset($data['description'])) {
$this->description = $data['description'];
}
$this->isResource = isset($data['resource']);
}
public function getFilters()
@ -54,8 +61,13 @@ class ApiDoc
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()
{
$array = array();
$resources = array();
foreach ($this->router->getRouteCollection()->all() as $route) {
preg_match('#(.+)::([\w]+)#', $route->getDefault('_controller'), $matches);
$method = new \ReflectionMethod($matches[1], $matches[2]);
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
if ($annot->isResource()) {
$resources[] = $route->getPattern();
}
$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;
}

View File

@ -33,21 +33,52 @@ abstract class AbstractFormatter implements FormatterInterface
{
$array = array();
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);
}
/**
* Format a single array of data
*
* @param array $data
* @return string|array
*/
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);
/**
* @param ApiDoc $apiDoc
* @param Route $route
* @return array
*/
private function getData(ApiDoc $apiDoc, Route $route)
{
$method = $route->getRequirement('_method');
$data = array(
'method' => $method,
'method' => $method ?: 'ANY',
'uri' => $route->compile()->getPattern(),
'requirements' => $route->compile()->getRequirements(),
);
@ -60,7 +91,7 @@ abstract class AbstractFormatter implements FormatterInterface
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;
$data['parameters'][$key]['required'] = false;
});
}
}
@ -69,8 +100,8 @@ abstract class AbstractFormatter implements FormatterInterface
$data['filters'] = $filters;
}
if ($comment = $apiDoc->getComment()) {
$data['comment'] = $comment;
if ($description = $apiDoc->getDescription()) {
$data['description'] = $description;
}
return $data;

View File

@ -12,7 +12,12 @@ class HtmlFormatter extends AbstractFormatter
*/
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();
}
/**
* {@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}
*/
protected function render(array $collection)
{
$content = '';
foreach ($collection as $data) {
$content .= $this->renderOne($data);
foreach ($collection as $resource => $arrayOfData) {
$content .= $this->renderResourceSection($resource, $arrayOfData);
}
return $this->renderWithLayout($content);
@ -43,8 +66,7 @@ class HtmlFormatter extends AbstractFormatter
private function renderWithLayout($content)
{
$array = array('content' => $content);
extract($array);
extract(array('content' => $content));
ob_start();
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']);
if (isset($data['comment'])) {
$markdown .= sprintf("\n_%s_", $data['comment']);
if (isset($data['description'])) {
$markdown .= sprintf("\n_%s_", $data['description']);
}
$markdown .= "\n\n";
@ -46,8 +46,8 @@ class MarkdownFormatter extends AbstractFormatter
foreach ($data['parameters'] as $name => $parameter) {
$markdown .= sprintf("%s:\n\n", $name);
$markdown .= sprintf(" * type: %s\n", $parameter['type']);
$markdown .= sprintf(" * is_required: %s\n", $parameter['is_required'] ? 'true' : 'false');
$markdown .= sprintf(" * type: %s\n", $parameter['dataType']);
$markdown .= sprintf(" * is_required: %s\n", $parameter['required'] ? 'true' : 'false');
$markdown .= "\n";
}
}
@ -55,14 +55,29 @@ class MarkdownFormatter extends AbstractFormatter
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}
*/
protected function render(array $collection)
{
$markdown = '';
foreach ($collection as $data) {
$markdown .= $this->renderOne($data);
foreach ($collection as $resource => $arrayOfData) {
$markdown .= $this->renderResourceSection($resource, $arrayOfData);
$markdown .= "\n";
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,94 +1,86 @@
<li class="resource">
<ul class="endpoints">
<li class="endpoint">
<ul class="operations">
<li class="<?php echo strtolower($method); ?> operation">
<div class="heading toggler">
<h3>
<span class="http_method">
<a><?php echo $method; ?></a>
</span>
<span class="path">
<?php echo $uri; ?>
</span>
</h3>
<ul class="options">
<?php if (isset($comment)) : ?>
<li><?php echo $comment; ?></li>
<?php endif; ?>
<li class="<?php echo strtolower($method); ?> operation">
<div class="heading toggler">
<h3>
<span class="http_method">
<a><?php echo $method; ?></a>
</span>
<span class="path">
<?php echo $uri; ?>
</span>
</h3>
<ul class="options">
<?php if (isset($description)) : ?>
<li><?php echo $description; ?></li>
<?php endif; ?>
</ul>
</div>
<div class="content" style="display: none;">
<?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>
</div>
<div class="content" style="display: none;">
<?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; ?>
</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><?php echo $key . ': ' . $value; ?></li>
<?php endforeach; ?>
</ul>
</td>
</tr>
<?php endforeach; ?>
</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>
<?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['dataType']; ?></td>
<td><?php echo $info['required'] ? 'true' : 'false'; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</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>
</head>
<body>
<div id="header">
<h1>API documentation</h1>
</div>
<div class="container" id="resources_container">
<ul id="resources">
<?php echo $content; ?>
</ul>
</div>
<p id="colophon">
Documentation auto-generated on <?php echo date(DATE_RFC822); ?>
</p>
<script type="text/javascript">
$('.toggler').click(function() {
$(this).next().toggle();
$(this).next().slideToggle('slow');
});
</script>
</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>