diff --git a/README.md b/README.md index 48fc07e..24915b1 100644 --- a/README.md +++ b/README.md @@ -275,3 +275,39 @@ If you wish to translate the package, you may publish the language files using: ```bash php artisan vendor:publish --tag=filament-spatie-laravel-translatable-plugin-translations ``` + +## Form Validation + +You can define and use form validation rules according to the locale as follows. + +```php +public static function form(Form $form): Form +{ + return $form + ->schema([ + Forms\Components\TextInput::make('name') + ->label(__('Category Name')) + ->helperText(__('This is the name of the category.')) + ->rules(function (?Category $record) { + return [ + 'tr' => [ + 'required', + 'string', + 'max:100', + Rule::unique('tags', 'name->tr') + ->where('type', 'category') + ->ignore($record?->id, 'id') + ], + 'en' => [ + 'required', + 'string', + 'max:100', + Rule::unique('tags', 'name->en') + ->where('type', 'category') + ->ignore($record?->id, 'id') + ], + ]; + }) + ]); +} +``` diff --git a/src/Resources/Pages/Concerns/HasTranslatableValidation.php b/src/Resources/Pages/Concerns/HasTranslatableValidation.php new file mode 100644 index 0000000..8cbf546 --- /dev/null +++ b/src/Resources/Pages/Concerns/HasTranslatableValidation.php @@ -0,0 +1,27 @@ +form->getFlatComponents(), + fn ($component) => $component instanceof HasValidationRules + ); + + foreach ($components as $component) { + $rules = $component->getValidationRules(); + if (! empty($rules[$locale])) { + $component->rule($rules[$locale]); + break; + } + } + } +} diff --git a/src/Resources/Pages/CreateRecord/Concerns/Translatable.php b/src/Resources/Pages/CreateRecord/Concerns/Translatable.php index 38afb8c..c597501 100644 --- a/src/Resources/Pages/CreateRecord/Concerns/Translatable.php +++ b/src/Resources/Pages/CreateRecord/Concerns/Translatable.php @@ -3,7 +3,9 @@ namespace Filament\Resources\Pages\CreateRecord\Concerns; use Filament\Facades\Filament; +use Filament\Forms\Components\Contracts\HasValidationRules; use Filament\Resources\Concerns\HasActiveLocaleSwitcher; +use Filament\Resources\Pages\Concerns\HasTranslatableValidation; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Validation\ValidationException; @@ -12,6 +14,7 @@ trait Translatable { use HasActiveLocaleSwitcher; + use HasTranslatableValidation; protected ?string $oldActiveLocale = null; @@ -28,6 +31,30 @@ public function getTranslatableLocales(): array return static::getResource()::getTranslatableLocales(); } + public function beforeFill() + { + /** + * For the rules assigned to the locales to work properly, + * an empty form state must be created for each local. + */ + if (empty($this->otherLocaleData)) { + $components = $this->form->getFlatComponents(); + $formData = collect($components) + ->filter( + fn ($component) => $component instanceof HasValidationRules + ) + ->mapWithKeys( + fn ($component) => [$component->getName() => null] + ) + ->toArray(); + foreach ($this->getTranslatableLocales() as $locale) { + $this->otherLocaleData[$locale] = []; + + $this->otherLocaleData[$locale] = $formData; + } + } + } + protected function handleRecordCreation(array $data): Model { $record = app(static::getModel()); @@ -42,7 +69,17 @@ protected function handleRecordCreation(array $data): Model $originalData = $this->data; + /** + * Set the data for the active locale. + */ + $this->otherLocaleData[$this->activeLocale] = Arr::only($this->data, $translatableAttributes); + foreach ($this->otherLocaleData as $locale => $localeData) { + /** + * Set the validation rules for the active locale. + */ + $this->setLocaleByRules($locale); + $this->data = [ ...$this->data, ...$localeData, @@ -51,7 +88,12 @@ protected function handleRecordCreation(array $data): Model try { $this->form->validate(); } catch (ValidationException $exception) { - continue; + /** + * If the validation fails for the active locale, set the active locale + */ + $this->activeLocale = $locale; + + throw $exception; } $localeData = $this->mutateFormDataBeforeCreate($localeData); diff --git a/src/Resources/Pages/EditRecord/Concerns/Translatable.php b/src/Resources/Pages/EditRecord/Concerns/Translatable.php index a18e7dd..9eb93e2 100644 --- a/src/Resources/Pages/EditRecord/Concerns/Translatable.php +++ b/src/Resources/Pages/EditRecord/Concerns/Translatable.php @@ -5,6 +5,7 @@ use Filament\Resources\Concerns\HasActiveLocaleSwitcher; use Filament\Resources\Pages\Concerns\HasTranslatableFormWithExistingRecordData; use Filament\Resources\Pages\Concerns\HasTranslatableRecord; +use Filament\Resources\Pages\Concerns\HasTranslatableValidation; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Validation\ValidationException; @@ -14,6 +15,7 @@ trait Translatable use HasActiveLocaleSwitcher; use HasTranslatableFormWithExistingRecordData; use HasTranslatableRecord; + use HasTranslatableValidation; protected ?string $oldActiveLocale = null; @@ -34,9 +36,19 @@ protected function handleRecordUpdate(Model $record, array $data): Model $originalData = $this->data; + /** + * Set the data for the active locale. + */ + $this->otherLocaleData[$this->activeLocale] = Arr::only($this->data, $translatableAttributes); + $existingLocales = null; foreach ($this->otherLocaleData as $locale => $localeData) { + /** + * Set the locale for the validation rules. + */ + $this->setLocaleByRules($locale); + $existingLocales ??= collect($translatableAttributes) ->map(fn (string $attribute): array => array_keys($record->getTranslations($attribute))) ->flatten() @@ -51,7 +63,7 @@ protected function handleRecordUpdate(Model $record, array $data): Model try { $this->form->validate(); } catch (ValidationException $exception) { - if (! array_key_exists($locale, $existingLocales)) { + if (! in_array($locale, $existingLocales, true)) { continue; }