Allow translate rules descriptions.

This commit is contained in:
Ferreyra, Maximiliano Gastón
2016-06-18 20:22:34 -03:00
parent a2beb8d9f3
commit 2d10144a6b
8 changed files with 265 additions and 40 deletions

View File

@@ -47,7 +47,18 @@ Option | Description
`noResponseCalls` | Disable API response calls
`actAsUserId` | The user ID to use for authenticated API response calls
`router` | The router to use, when processing the route files (can be Laravel or Dingo - defaults to Laravel)
`bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id`
`bindings` | List of route bindings that should be replaced when trying to retrieve route results. Syntax format: `binding_one,id|binding_two,id`
## Publish rule descriptions for customisation or translate.
For default, this package returns the descriptions in the main language of the application. But provides the possibility of publish the language files for customisation or translate.
```sh
$ php artisan vendor:publish
```
After to publish you can customize or translate the descriptions in the language you want by editing the files in `public/vendor/apidoc/resources/lang`.
### How does it work?

View File

@@ -16,6 +16,11 @@ class ApiDocGeneratorServiceProvider extends ServiceProvider
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../../resources/views/', 'apidoc');
$this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'apidoc');
$this->publishes([
__DIR__.'/../../resources/lang' => resource_path('lang/vendor/apidoc'),
]);
}
/**

View File

@@ -3,11 +3,12 @@
namespace Mpociot\ApiDoc\Generators;
use Faker\Factory;
use ReflectionClass;
use Illuminate\Support\Str;
use phpDocumentor\Reflection\DocBlock;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description;
use ReflectionClass;
use phpDocumentor\Reflection\DocBlock;
abstract class AbstractGenerator
{
@@ -196,102 +197,102 @@ abstract class AbstractGenerator
break;
case 'after':
$attributeData['type'] = 'date';
$attributeData['description'][] = 'Must be a date after: `'.date(DATE_RFC850, strtotime($parameters[0])).'`';
$attributeData['description'][] = Description::parse($rule)->with(date(DATE_RFC850, strtotime($parameters[0])))->getDescription();
$attributeData['value'] = date(DATE_RFC850, strtotime('+1 day', strtotime($parameters[0])));
break;
case 'alpha':
$attributeData['description'][] = 'Only alphabetic characters allowed';
$attributeData['description'][] = Description::parse($rule)->getDescription();
$attributeData['value'] = $faker->word;
break;
case 'alpha_dash':
$attributeData['description'][] = 'Allowed: alpha-numeric characters, as well as dashes and underscores.';
$attributeData['description'][] = Description::parse($rule)->getDescription();
break;
case 'alpha_num':
$attributeData['description'][] = 'Only alpha-numeric characters allowed';
$attributeData['description'][] = Description::parse($rule)->getDescription();
break;
case 'in':
$attributeData['description'][] = $this->fancyImplode($parameters, ', ', ' or ');
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
$attributeData['value'] = $faker->randomElement($parameters);
break;
case 'not_in':
$attributeData['description'][] = 'Not in: '.$this->fancyImplode($parameters, ', ', ' or ');
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
$attributeData['value'] = $faker->word;
break;
case 'min':
$attributeData['description'][] = 'Minimum: `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'max':
$attributeData['description'][] = 'Maximum: `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'between':
$attributeData['type'] = 'numeric';
$attributeData['description'][] = 'Between: `'.$parameters[0].'` and `'.$parameters[1].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
$attributeData['value'] = $faker->numberBetween($parameters[0], $parameters[1]);
break;
case 'before':
$attributeData['type'] = 'date';
$attributeData['description'][] = 'Must be a date preceding: `'.date(DATE_RFC850, strtotime($parameters[0])).'`';
$attributeData['description'][] = Description::parse($rule)->with(date(DATE_RFC850, strtotime($parameters[0])))->getDescription();
$attributeData['value'] = date(DATE_RFC850, strtotime('-1 day', strtotime($parameters[0])));
break;
case 'date_format':
$attributeData['type'] = 'date';
$attributeData['description'][] = 'Date format: `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'different':
$attributeData['description'][] = 'Must have a different value than parameter: `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'digits':
$attributeData['type'] = 'numeric';
$attributeData['description'][] = 'Must have an exact length of `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
$attributeData['value'] = $faker->randomNumber($parameters[0], true);
break;
case 'digits_between':
$attributeData['type'] = 'numeric';
$attributeData['description'][] = 'Must have a length between `'.$parameters[0].'` and `'.$parameters[1].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'image':
$attributeData['type'] = 'image';
$attributeData['description'][] = 'Must be an image (jpeg, png, bmp, gif, or svg)';
$attributeData['description'][] = Description::parse($rule)->getDescription();
break;
case 'json':
$attributeData['type'] = 'string';
$attributeData['description'][] = 'Must be a valid JSON string.';
$attributeData['description'][] = Description::parse($rule)->getDescription();
$attributeData['value'] = json_encode(['foo', 'bar', 'baz']);
break;
case 'mimetypes':
case 'mimes':
$attributeData['description'][] = 'Allowed mime types: '.$this->fancyImplode($parameters, ', ', ' or ');
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
break;
case 'required_if':
$attributeData['description'][] = 'Required if `'.$parameters[0].'` is `'.$parameters[1].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'required_unless':
$attributeData['description'][] = 'Required unless `'.$parameters[0].'` is `'.$parameters[1].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'required_with':
$attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' or ').' are present.';
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
break;
case 'required_with_all':
$attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' and ').' are present.';
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
break;
case 'required_without':
$attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' or ').' are not present.';
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
break;
case 'required_without_all':
$attributeData['description'][] = 'Required if the parameters '.$this->fancyImplode($parameters, ', ', ' and ').' are not present.';
$attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
break;
case 'same':
$attributeData['description'][] = 'Must be the same as `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'size':
$attributeData['description'][] = 'Must have the size of `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'timezone':
$attributeData['description'][] = 'Must be a valid timezone identifier';
$attributeData['description'][] = Description::parse($rule)->getDescription();
$attributeData['value'] = $faker->timezone;
break;
case 'exists':
$attributeData['description'][] = 'Valid '.Str::singular($parameters[0]).' '.$parameters[1];
$attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $parameters[1]])->getDescription();
break;
case 'active_url':
$attributeData['type'] = 'url';
@@ -299,7 +300,7 @@ abstract class AbstractGenerator
break;
case 'regex':
$attributeData['type'] = 'string';
$attributeData['description'][] = 'Must match this regular expression: `'.$parameters[0].'`';
$attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
break;
case 'boolean':
$attributeData['value'] = true;

View File

@@ -0,0 +1,75 @@
<?php
namespace Mpociot\ApiDoc\Parsers;
class RuleDescriptionParser
{
private $rule;
private $parameters = [];
/**
* RuleDescriptionParser constructor.
*
* @param null $rule
*/
public function __construct($rule = null)
{
$this->rule = $rule;
}
/**
* Returns the description in the main language of the application.
*
* @return array|string
*/
public function getDescription()
{
$key = "apidoc::rules.{$this->rule}";
$description = $this->parameters ? $this->translateWithAttributes($key) : trans($key);
return $description != $key ? $description : [];
}
/**
* Sets the parameters for the description string.
* @param string|array $parameters
*
* @return $this
*/
public function with($parameters)
{
is_array($parameters) ? $this->parameters += $parameters : $this->parameters[] = $parameters;
return $this;
}
/**
* Returns the description string with the replaced attributes.
* @param $key
*
* @return string
*/
protected function translateWithAttributes($key)
{
$translate = trans($key);
foreach ($this->parameters as $parameter) {
$translate = preg_replace('/:attribute/', $parameter, $translate, 1);
}
return $translate;
}
/**
* Provides a named constructor.
* @param null $rule
*
* @return static
*/
public static function parse($rule = null)
{
return new static($rule);
}
}

View File

@@ -0,0 +1,33 @@
<?php
return [
'after' => 'Must be a date after: `:attribute`',
'alpha' => 'Only alphabetic characters allowed',
'alpha_dash' => 'Allowed: alpha-numeric characters, as well as dashes and underscores.',
'alpha_num' => 'Only alpha-numeric characters allowed',
'in' => ':attribute',
'not_in' => 'Not in: :attribute',
'min' => 'Minimum: `:attribute`',
'max' => 'Maximum: `:attribute`',
'between' => 'Between: `:attribute` and `:attribute`',
'before' => 'Must be a date preceding: `:attribute`',
'date_format' => 'Date format: `:attribute`',
'different' => 'Must have a different value than parameter: `:attribute`',
'digits' => 'Must have an exact length of `:attribute`',
'digits_between' => 'Must have a length between `:attribute` and `:attribute`',
'image' => 'Must be an image (jpeg, png, bmp, gif, or svg)',
'json' => 'Must be a valid JSON string.',
'mimetypes' => 'Allowed mime types: :attribute',
'mimes' => 'Allowed mime types: :attribute',
'required_if' => 'Required if `:attribute` is `:attribute`',
'required_unless' => 'Required unless `:attribute` is `:attribute`',
'required_with' => 'Required if the parameters :attribute are present.',
'required_with_all' => 'Required if the parameters :attribute are present.',
'required_without' => 'Required if the parameters :attribute are not present.',
'required_without_all' => 'Required if the parameters :attribute are not present.',
'same' => 'Must be the same as `:attribute`',
'size' => 'Must have the size of `:attribute`',
'timezone' => 'Must be a valid timezone identifier',
'exists' => 'Valid :attribute :attribute',
'regex' => 'Must match this regular expression: `:attribute`',
];

View File

@@ -3,11 +3,12 @@
namespace Mpociot\ApiDoc\Tests;
use Illuminate\Routing\Route;
use Mpociot\ApiDoc\Generators\LaravelGenerator;
use Orchestra\Testbench\TestCase;
use Mpociot\ApiDoc\Tests\Fixtures\TestRequest;
use Mpociot\ApiDoc\Tests\Fixtures\TestController;
use Illuminate\Support\Facades\Route as RouteFacade;
use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider;
use Mpociot\ApiDoc\Generators\LaravelGenerator;
use Mpociot\ApiDoc\Tests\Fixtures\TestController;
use Mpociot\ApiDoc\Tests\Fixtures\TestRequest;
use Orchestra\Testbench\TestCase;
class ApiDocGeneratorTest extends TestCase
{
@@ -16,6 +17,13 @@ class ApiDocGeneratorTest extends TestCase
*/
protected $generator;
protected function getPackageProviders($app)
{
return [
ApiDocGeneratorServiceProvider::class,
];
}
/**
* Setup the test environment.
*/

View File

@@ -3,11 +3,12 @@
namespace Mpociot\ApiDoc\Tests;
use Dingo\Api\Provider\LaravelServiceProvider;
use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController;
use Orchestra\Testbench\TestCase;
use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider;
use Mpociot\ApiDoc\Generators\DingoGenerator;
use Mpociot\ApiDoc\Tests\Fixtures\TestRequest;
use Mpociot\ApiDoc\Tests\Fixtures\DingoTestController;
use Mpociot\ApiDoc\Tests\Fixtures\TestController;
use Mpociot\ApiDoc\Tests\Fixtures\TestRequest;
use Orchestra\Testbench\TestCase;
class DingoGeneratorTest extends TestCase
{
@@ -20,6 +21,7 @@ class DingoGeneratorTest extends TestCase
{
return [
LaravelServiceProvider::class,
ApiDocGeneratorServiceProvider::class,
];
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Mpociot\ApiDoc\Tests;
use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider;
use Mpociot\ApiDoc\Parsers\RuleDescriptionParser;
use Orchestra\Testbench\TestCase;
class RuleDescriptionParserTest extends TestCase
{
public function testReturnsAnEmptyDescriptionIfARuleIsNotParsed()
{
$rule = new RuleDescriptionParser();
$this->assertEmpty($rule->getDescription());
}
public function testProvidesANamedContructor()
{
$this->assertInstanceOf(RuleDescriptionParser::class, RuleDescriptionParser::parse());
}
public function testReturnsADescriptionInTheMainLanguageOfTheApplication()
{
$expected = 'Only alphabetic characters allowed';
$rule = new RuleDescriptionParser('alpha');
$this->assertEquals($expected, $rule->getDescription());
}
public function testReturnsAnEmptyDescriptionIfNotAvailable()
{
$rule = new RuleDescriptionParser('dummy_rule');
$description = $rule->getDescription();
$this->assertEmpty($description);
}
public function testAllowsToPassParametersToTheDescription()
{
$expected = 'Must have an exact length of `2`';
$rule = new RuleDescriptionParser('digits');
$actual = $rule->with(2)->getDescription();
$this->assertEquals($expected, $actual);
}
public function testOnlyPassesParametersIfTheDescriptionAllows()
{
$expected = 'Only alphabetic characters allowed';
$rule = new RuleDescriptionParser('alpha');
$actual = $rule->with("dummy parameter")->getDescription();
$this->assertEquals($expected, $actual);
}
public function testAllowsToPassMultipleParametersToTheDescription()
{
$expected = 'Required if `2 + 2` is `4`';
$rule = new RuleDescriptionParser('required_if');
$actual = $rule->with(['2 + 2', 4])->getDescription();
$this->assertEquals($expected, $actual);
}
/**
* @param \Illuminate\Foundation\Application $app
*
* @return array
*/
protected function getPackageProviders($app)
{
return [ApiDocGeneratorServiceProvider::class];
}
/**
* Define environment setup.
*
* @param \Illuminate\Foundation\Application $app
* @return void
*/
protected function getEnvironmentSetUp($app)
{
$app['config']->set('app.locale', 'en');
}
}