Skip to content

Commit

Permalink
Merge pull request #406 from amjadbanimattar/main
Browse files Browse the repository at this point in the history
Enhance Custom Rules: Introduce translatableExists and translatableUnique for Validating Translatable Attributes
  • Loading branch information
Gummibeer authored Aug 26, 2024
2 parents a375546 + 33bcf27 commit ad6369d
Show file tree
Hide file tree
Showing 11 changed files with 576 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
- [Attributes](usage/attributes.md)
- [Forms](usage/forms.md)
- [Pivot Model](usage/pivot-model.md)
- [Custom Validation Rules](usage/custom-validation-rule.md)
107 changes: 107 additions & 0 deletions docs/usage/custom-validation-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
### **Validating Unique and Exists Rule**

You can use custom rules to validate unique and exists rules for translatable attributes.

#### TranslatableUnique

Ensure that the attribute value is unique by checking its absence in the database; if the value already exists, raise a validation exception.

##### Option 1

```php
use Astrotomic\Translatable\Validation\Rules\TranslatableUnique;
...

$person = new Person(['name' => 'john doe']);
$person->save();

$data = [
'name' => 'john doe',
'email' => 'john@example.com'
];
$validator = Validator::make($data, [
'name' => ['required', new TranslatableUnique(Person::class, 'name')],
]);

```

##### Option 2

```php
use Astrotomic\Translatable\Validation\Rules\TranslatableUnique;
...

$person = new Person(['name' => 'john doe']);
$person->save();

$data = [
'name:en' => 'john doe',
'email' => 'john@example.com'
];

$validator = Validator::make($data, [
'name:en' => ['required', Rule::translatableUnique(Person::class, 'name:en')],
]);

```

##### Option 2

```php
use Illuminate\Validation\Rule;
...

$person = new Person(['name' => 'john doe']);
$person->save();

$data = [
'name:en' => 'john doe',
'email' => 'john@example.com'
];

$validator = Validator::make($data, [
'name:en' => ['required', Rule::translatableUnique(Person::class, 'name:en')],
]);

```


#### TranslatableExists

Verify if the attribute value exists by confirming its presence in the database; if the value does not exist, raise a validation exception.


##### Option 1
```php
use Astrotomic\Translatable\Validation\Rules\TranslatableExists;
...

$person = new Person(['name' => 'john doe']);
$person->save();

$data = [
'name' => 'john doe',
'email' => 'john@example.com'
];
$validator = Validator::make($data, [
'name' => ['required', new TranslatableExists(Person::class, 'name')],
]);
```

##### Option 2
```php
use Illuminate\Validation\Rule;
...

$person = new Person(['name' => 'john doe']);
$person->save();

$data = [
'name:en' => 'john doe',
'email' => 'john@example.com'
];

$validator = Validator::make($data, [
'name:en' => ['required', Rule::translatableExists(Person::class, 'name:en')],
]);
```
19 changes: 17 additions & 2 deletions src/Translatable/Contracts/Translatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ public static function disableDeleteTranslationsCascade(): void;

public static function enableDeleteTranslationsCascade(): void;

public function deleteTranslations($locales = null): void;
/**
* @param string|array<string>|null $locales
*/
public function deleteTranslations(string|array|null $locales = null): void;

public function getDefaultLocale(): ?string;

Expand All @@ -28,23 +31,35 @@ public function getTranslation(?string $locale = null, ?bool $withFallback = nul

public function getTranslationOrNew(?string $locale = null): Model;

/**
* @return array<string,array<string,mixed>>
*/
public function getTranslationsArray(): array;

public function hasTranslation(?string $locale = null): bool;

public function isTranslationAttribute(string $key): bool;

/**
* @param null|array<string> $except
*/
public function replicateWithTranslations(?array $except = null): Model;

public function setDefaultLocale(?string $locale);
public function setDefaultLocale(?string $locale): self;

public function translate(?string $locale = null, bool $withFallback = false): ?Model;

public function translateOrDefault(?string $locale = null): ?Model;

public function translateOrNew(?string $locale = null): Model;

/**
* @return HasOne<Model>
*/
public function translation(): HasOne;

/**
* @return HasMany<Model>
*/
public function translations(): HasMany;
}
9 changes: 8 additions & 1 deletion src/Translatable/Locales.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Translation\Translator as TranslatorContract;

/**
* @implements Arrayable<string, string>
* @implements ArrayAccess<string, string>
*/
class Locales implements Arrayable, ArrayAccess
{
/**
Expand All @@ -16,7 +20,7 @@ class Locales implements Arrayable, ArrayAccess
protected $config;

/**
* @var array
* @var array<string,string>
*/
protected $locales = [];

Expand All @@ -38,6 +42,9 @@ public function add(string $locale): void
$this->locales[$locale] = $locale;
}

/**
* @return array<string>
*/
public function all(): array
{
return array_values($this->locales);
Expand Down
3 changes: 2 additions & 1 deletion src/Translatable/Translatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Astrotomic\Translatable;

use Astrotomic\Translatable\Contracts\Translatable as TranslatableContract;
use Astrotomic\Translatable\Traits\Relationship;
use Astrotomic\Translatable\Traits\Scopes;
use Illuminate\Database\Eloquent\Collection;
Expand Down Expand Up @@ -314,7 +315,7 @@ public function setAttribute($key, $value)
return parent::setAttribute($key, $value);
}

public function setDefaultLocale(?string $locale)
public function setDefaultLocale(?string $locale): TranslatableContract
{
$this->defaultLocale = $locale;

Expand Down
24 changes: 20 additions & 4 deletions src/Translatable/TranslatableServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,43 @@

namespace Astrotomic\Translatable;

use Astrotomic\Translatable\Validation\Rules\TranslatableExists;
use Astrotomic\Translatable\Validation\Rules\TranslatableUnique;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rule;

class TranslatableServiceProvider extends ServiceProvider
{
public function boot()
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/translatable.php' => config_path('translatable.php'),
], 'translatable');

$this->loadTranslationsFrom(__DIR__.'/../lang', 'translatable');
$this->publishes([
__DIR__.'/../lang' => $this->app->langPath('vendor/translatable'),
], 'translatable-lang');
}

public function register()
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/../config/translatable.php', 'translatable'
__DIR__.'/../config/translatable.php',
'translatable'
);

Rule::macro('translatableUnique', function (string $model, string $field): TranslatableUnique {
return new TranslatableUnique($model, $field);
});
Rule::macro('translatableExists', function (string $model, string $field): TranslatableExists {
return new TranslatableExists($model, $field);
});

$this->registerTranslatableHelper();
}

protected function registerTranslatableHelper()
protected function registerTranslatableHelper(): void
{
$this->app->singleton('translatable.locales', Locales::class);
$this->app->singleton(Locales::class);
Expand Down
25 changes: 24 additions & 1 deletion src/Translatable/Validation/RuleFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class RuleFactory
protected $suffix;

/**
* @var null|array
* @var null|array<string>
*/
protected $locales = null;

Expand All @@ -39,6 +39,16 @@ public function __construct(Repository $config, ?int $format = null, ?string $pr
$this->suffix = $suffix ?? $config->get('translatable.rule_factory.suffix');
}

/**
* Create a set of validation rules.
*
* @param array<mixed> $rules The validation rules to be parsed.
* @param int|null $format The format to be used for parsing (e.g., 'dot' or 'bracket').
* @param string|null $prefix The prefix to be applied to each rule key.
* @param string|null $suffix The suffix to be applied to each rule key.
* @param array<string>|null $locales The locales to be used for translating rule attributes.
* @return array<string,mixed> The parsed validation rules.
*/
public static function make(array $rules, ?int $format = null, ?string $prefix = null, ?string $suffix = null, ?array $locales = null): array
{
/** @var RuleFactory $factory */
Expand All @@ -49,6 +59,13 @@ public static function make(array $rules, ?int $format = null, ?string $prefix =
return $factory->parse($rules);
}

/**
* Set the locales to be used for translating rule attributes.
*
* @param array<string>|null $locales The locales to be set. If null, all available locales will be used.
*
* @throws \InvalidArgumentException If a provided locale is not defined in the available locales.
*/
public function setLocales(?array $locales = null): self
{
/** @var Locales */
Expand All @@ -71,6 +88,12 @@ public function setLocales(?array $locales = null): self
return $this;
}

/**
* Parse the input array of rules, applying format and translation to translatable attributes.
*
* @param array<mixed> $input The input array of rules to be parsed.
* @return array<mixed> The parsed array of rules.
*/
public function parse(array $input): array
{
$rules = [];
Expand Down
Loading

0 comments on commit ad6369d

Please sign in to comment.