From f7d2a9a80d7b3c8e97adef6c65aaef450881e36b Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Mon, 29 Jul 2019 11:52:31 +0200 Subject: [PATCH 1/8] add rule factory --- src/Translatable/Validation/RuleFactory.php | 111 ++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/Translatable/Validation/RuleFactory.php diff --git a/src/Translatable/Validation/RuleFactory.php b/src/Translatable/Validation/RuleFactory.php new file mode 100644 index 00000000..937d3c77 --- /dev/null +++ b/src/Translatable/Validation/RuleFactory.php @@ -0,0 +1,111 @@ +format = $format; + $this->prefix = $prefix; + $this->suffix = $suffix; + } + + public static function make(array $rules, int $format = self::FORMAT_ARRAY, string $prefix = '%', string $suffix = '%', ?array $locales = null): array + { + $factory = new static($format, $prefix, $suffix); + + $factory->setLocales($locales); + + return $factory->parse($rules); + } + + public function setLocales(?array $locales = null): self + { + /** @var Locales */ + $helper = app(Locales::class); + + if (is_null($locales)) { + $this->locales = $helper->all(); + + return $this; + } + + foreach($locales as $locale) { + if(!$helper->has($locale)) { + throw new InvalidArgumentException(sprintf('The locale [%s] is not defined in available locales.', $locale)); + } + } + + $this->locales = $locales; + + return $this; + } + + public function parse(array $input): array + { + $rules = []; + + foreach ($input as $key => $value) { + if(!$this->isTranslatable($key)) { + $rules[$key] = $value; + continue; + } + + foreach($this->locales as $locale) { + $rules[$this->formatKey($locale, $key)] = $value; + } + } + + return $rules; + } + + protected function formatKey(string $locale, string $key): string + { + switch ($this->format) { + case self::FORMAT_ARRAY: + return preg_replace($this->getPattern(), $locale.'.$1', $key); + case self::FORMAT_KEY: + return preg_replace($this->getPattern(), '$1:'.$locale, $key); + } + } + + protected function getPattern(): string + { + $prefix = preg_quote($this->prefix); + $suffix = preg_quote($this->suffix); + + return '/'.$prefix.'([^\.'.$prefix.$suffix.']+)'.$suffix.'/'; + } + + protected function isTranslatable(string $key): bool + { + return strpos($key, $this->prefix) !== false && strpos($key, $this->suffix) !== false; + } +} From 373453c57564cfac660931e9f45b131f11e834aa Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Mon, 29 Jul 2019 11:52:42 +0200 Subject: [PATCH 2/8] add rule factory unittests --- tests/ValidationTest.php | 231 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 tests/ValidationTest.php diff --git a/tests/ValidationTest.php b/tests/ValidationTest.php new file mode 100644 index 00000000..8d330ef1 --- /dev/null +++ b/tests/ValidationTest.php @@ -0,0 +1,231 @@ + 'required', + 'author_id' => [ + 'required', + 'int', + ], + ]; + + $this->assertEquals($rules, RuleFactory::make($rules)); + } + + public function test_format_array_it_replaces_single_key() + { + $rules = [ + 'title' => 'required', + '%content%' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'en.content' => 'required', + 'de.content' => 'required', + 'de-DE.content' => 'required', + 'de-AT.content' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY)); + } + + public function test_format_array_it_replaces_sub_key() + { + $rules = [ + 'title' => 'required', + 'translations.%content%' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content' => 'required', + 'translations.de.content' => 'required', + 'translations.de-DE.content' => 'required', + 'translations.de-AT.content' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY)); + } + + public function test_format_array_it_replaces_middle_key() + { + $rules = [ + 'title' => 'required', + 'translations.%content%.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content.body' => 'required', + 'translations.de.content.body' => 'required', + 'translations.de-DE.content.body' => 'required', + 'translations.de-AT.content.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY)); + } + + public function test_format_array_it_replaces_middle_key_with_custom_prefix() + { + $rules = [ + 'title' => 'required', + 'translations.{content%.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content.body' => 'required', + 'translations.de.content.body' => 'required', + 'translations.de-DE.content.body' => 'required', + 'translations.de-AT.content.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '{')); + } + + public function test_format_array_it_replaces_middle_key_with_custom_suffix() + { + $rules = [ + 'title' => 'required', + 'translations.%content}.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content.body' => 'required', + 'translations.de.content.body' => 'required', + 'translations.de-DE.content.body' => 'required', + 'translations.de-AT.content.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '%', '}')); + } + + public function test_format_array_it_replaces_middle_key_with_custom_delimiters() + { + $rules = [ + 'title' => 'required', + 'translations.{content}.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content.body' => 'required', + 'translations.de.content.body' => 'required', + 'translations.de-DE.content.body' => 'required', + 'translations.de-AT.content.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '{', '}')); + } + + public function test_format_array_it_replaces_middle_key_with_custom_regex_delimiters() + { + $rules = [ + 'title' => 'required', + 'translations.$content$.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content.body' => 'required', + 'translations.de.content.body' => 'required', + 'translations.de-DE.content.body' => 'required', + 'translations.de-AT.content.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '$', '$')); + } + + public function test_format_key_it_replaces_single_key() + { + $rules = [ + 'title' => 'required', + '%content%' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'content:en' => 'required', + 'content:de' => 'required', + 'content:de-DE' => 'required', + 'content:de-AT' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_KEY)); + } + + public function test_format_key_it_replaces_sub_key() + { + $rules = [ + 'title' => 'required', + 'translations.%content%' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.content:en' => 'required', + 'translations.content:de' => 'required', + 'translations.content:de-DE' => 'required', + 'translations.content:de-AT' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_KEY)); + } + + public function test_format_key_it_replaces_middle_key() + { + $rules = [ + 'title' => 'required', + 'translations.%content%.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.content:en.body' => 'required', + 'translations.content:de.body' => 'required', + 'translations.content:de-DE.body' => 'required', + 'translations.content:de-AT.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_KEY)); + } + + public function test_it_replaces_key_with_custom_locales() + { + $rules = [ + 'title' => 'required', + 'translations.%content%.body' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + 'translations.en.content.body' => 'required', + 'translations.de.content.body' => 'required', + ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '%', '%', [ + 'en', + 'de', + ])); + } + + public function test_it_throws_exception_with_undefined_locales() + { + $this->expectException(InvalidArgumentException::class); + + $rules = [ + 'title' => 'required', + 'translations.$content$.body' => 'required', + ]; + + RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '%', '%', [ + 'en', + 'de', + 'at', + ]); + } + + protected function setUp(): void + { + $this->setUpTheTestEnvironment(); + app('config')->set('translatable.locales', [ + 'en', + 'de' => [ + 'DE', + 'AT', + ], + ]); + $this->getLocalesHelper()->load(); + } + + private function getLocalesHelper(): Locales + { + return app(Locales::class); + } +} From 08e38b00952efa776383a10c098067c5bc1411fc Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Mon, 29 Jul 2019 11:54:08 +0200 Subject: [PATCH 3/8] fix php cs --- src/Translatable/Validation/RuleFactory.php | 10 +++++----- tests/TestsBase.php | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Translatable/Validation/RuleFactory.php b/src/Translatable/Validation/RuleFactory.php index 937d3c77..5dc200e2 100644 --- a/src/Translatable/Validation/RuleFactory.php +++ b/src/Translatable/Validation/RuleFactory.php @@ -2,8 +2,8 @@ namespace Astrotomic\Translatable\Validation; -use Astrotomic\Translatable\Locales; use InvalidArgumentException; +use Astrotomic\Translatable\Locales; class RuleFactory { @@ -57,8 +57,8 @@ public function setLocales(?array $locales = null): self return $this; } - foreach($locales as $locale) { - if(!$helper->has($locale)) { + foreach ($locales as $locale) { + if (! $helper->has($locale)) { throw new InvalidArgumentException(sprintf('The locale [%s] is not defined in available locales.', $locale)); } } @@ -73,12 +73,12 @@ public function parse(array $input): array $rules = []; foreach ($input as $key => $value) { - if(!$this->isTranslatable($key)) { + if (! $this->isTranslatable($key)) { $rules[$key] = $value; continue; } - foreach($this->locales as $locale) { + foreach ($this->locales as $locale) { $rules[$this->formatKey($locale, $key)] = $value; } } diff --git a/tests/TestsBase.php b/tests/TestsBase.php index 3344968b..f2116d19 100644 --- a/tests/TestsBase.php +++ b/tests/TestsBase.php @@ -1,8 +1,8 @@ Date: Mon, 29 Jul 2019 12:04:19 +0200 Subject: [PATCH 4/8] fix codeclimate --- .codeclimate.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index 1d955bb3..675fe8ed 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -6,6 +6,8 @@ checks: threshold: 6 complex-logic: enabled: true + config: + threshold: 6 file-lines: enabled: false method-complexity: From 9fd5cce981cb5b18cc805fbb10298646fdaad180 Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Mon, 29 Jul 2019 12:06:44 +0200 Subject: [PATCH 5/8] fix codeclimate --- .codeclimate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.codeclimate.yml b/.codeclimate.yml index 675fe8ed..6078e07b 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -6,12 +6,12 @@ checks: threshold: 6 complex-logic: enabled: true - config: - threshold: 6 file-lines: enabled: false method-complexity: enabled: true + config: + threshold: 6 method-count: enabled: true method-lines: From 5e14c122b42637044ab2203d63639bdcc0b9e073 Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Wed, 7 Aug 2019 11:27:25 +0200 Subject: [PATCH 6/8] add rule factory configuration --- src/Translatable/Validation/RuleFactory.php | 13 +++--- src/config/translatable.php | 15 +++++++ tests/ValidationTest.php | 48 +++++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/Translatable/Validation/RuleFactory.php b/src/Translatable/Validation/RuleFactory.php index 5dc200e2..8972913d 100644 --- a/src/Translatable/Validation/RuleFactory.php +++ b/src/Translatable/Validation/RuleFactory.php @@ -2,6 +2,7 @@ namespace Astrotomic\Translatable\Validation; +use Illuminate\Contracts\Config\Repository; use InvalidArgumentException; use Astrotomic\Translatable\Locales; @@ -30,16 +31,16 @@ class RuleFactory */ protected $locales = null; - public function __construct(int $format = self::FORMAT_ARRAY, string $prefix = '%', string $suffix = '%') + public function __construct(Repository $config, ?int $format = null, ?string $prefix = null, ?string $suffix = null) { - $this->format = $format; - $this->prefix = $prefix; - $this->suffix = $suffix; + $this->format = $format ?? $config->get('translatable.rule_factory.format'); + $this->prefix = $prefix ?? $config->get('translatable.rule_factory.prefix'); + $this->suffix = $suffix ?? $config->get('translatable.rule_factory.suffix'); } - public static function make(array $rules, int $format = self::FORMAT_ARRAY, string $prefix = '%', string $suffix = '%', ?array $locales = null): array + public static function make(array $rules, ?int $format = null, ?string $prefix = null, ?string $suffix = null, ?array $locales = null): array { - $factory = new static($format, $prefix, $suffix); + $factory = app()->make(static::class, compact('format', 'prefix', 'suffix')); $factory->setLocales($locales); diff --git a/src/config/translatable.php b/src/config/translatable.php index 6864e284..ddf71d08 100644 --- a/src/config/translatable.php +++ b/src/config/translatable.php @@ -131,4 +131,19 @@ | */ 'to_array_always_loads_translations' => true, + + /* + |-------------------------------------------------------------------------- + | Configure the default behavior of the rule factory + |-------------------------------------------------------------------------- + | The default values used to control the behavior of the RuleFactory. + | Here you can set your own default format and delimiters for + | your whole app. + * + */ + 'rule_factory' => [ + 'format' => \Astrotomic\Translatable\Validation\RuleFactory::FORMAT_ARRAY, + 'prefix' => '%', + 'suffix' => '%', + ], ]; diff --git a/tests/ValidationTest.php b/tests/ValidationTest.php index 8d330ef1..102e96db 100644 --- a/tests/ValidationTest.php +++ b/tests/ValidationTest.php @@ -130,6 +130,30 @@ public function test_format_array_it_replaces_middle_key_with_custom_regex_delim ], RuleFactory::make($rules, RuleFactory::FORMAT_ARRAY, '$', '$')); } + public function test_format_array_it_uses_config_as_default() + { + app('config')->set('translatable.rule_factory', [ + 'format' => RuleFactory::FORMAT_ARRAY, + 'prefix' => '{', + 'suffix' => '}', + ]); + + $rules = [ + 'title' => 'required', + '{content}' => 'required', + '%content%' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + '%content%' => 'required', + 'en.content' => 'required', + 'de.content' => 'required', + 'de-DE.content' => 'required', + 'de-AT.content' => 'required', + ], RuleFactory::make($rules)); + } + public function test_format_key_it_replaces_single_key() { $rules = [ @@ -178,6 +202,30 @@ public function test_format_key_it_replaces_middle_key() ], RuleFactory::make($rules, RuleFactory::FORMAT_KEY)); } + public function test_format_key_it_uses_config_as_default() + { + app('config')->set('translatable.rule_factory', [ + 'format' => RuleFactory::FORMAT_KEY, + 'prefix' => '{', + 'suffix' => '}', + ]); + + $rules = [ + 'title' => 'required', + '{content}' => 'required', + '%content%' => 'required', + ]; + + $this->assertEquals([ + 'title' => 'required', + '%content%' => 'required', + 'content:en' => 'required', + 'content:de' => 'required', + 'content:de-DE' => 'required', + 'content:de-AT' => 'required', + ], RuleFactory::make($rules)); + } + public function test_it_replaces_key_with_custom_locales() { $rules = [ From 184ef5af18e33b088f86d50791cf1a8f396bc26b Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Wed, 7 Aug 2019 11:28:04 +0200 Subject: [PATCH 7/8] fix php cs --- src/Translatable/Validation/RuleFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Translatable/Validation/RuleFactory.php b/src/Translatable/Validation/RuleFactory.php index 8972913d..e2cc6a9e 100644 --- a/src/Translatable/Validation/RuleFactory.php +++ b/src/Translatable/Validation/RuleFactory.php @@ -2,9 +2,9 @@ namespace Astrotomic\Translatable\Validation; -use Illuminate\Contracts\Config\Repository; use InvalidArgumentException; use Astrotomic\Translatable\Locales; +use Illuminate\Contracts\Config\Repository; class RuleFactory { From 4de051105817af983a8c72b8542c4aff4d8d61d3 Mon Sep 17 00:00:00 2001 From: Gummibeer Date: Wed, 7 Aug 2019 11:28:29 +0200 Subject: [PATCH 8/8] ignore phpunit cache file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a8fd3ed4..0bd2672a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor/ composer.lock coverage/ +.phpunit.result.cache