Skip to content

Commit

Permalink
[5.x] Update our custom string based rules to class based rules (#9785)
Browse files Browse the repository at this point in the history
Co-authored-by: Jason Varga <jason@pixelfear.com>
  • Loading branch information
jesseleite and jasonvarga authored Mar 28, 2024
1 parent 1157fd7 commit 33461ff
Show file tree
Hide file tree
Showing 38 changed files with 568 additions and 262 deletions.
10 changes: 9 additions & 1 deletion resources/js/components/field-validation/Rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,15 @@ export default [
// },
{
label: 'Unique Entry Value',
value: 'unique_entry_value:{collection},{id},{site}',
value: 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})',
},
{
label: 'Unique Term Value',
value: 'new \\Statamic\\Rules\\UniqueTermValue({taxonomy}, {id}, {site})',
},
{
label: 'Unique User Value',
value: 'new \\Statamic\\Rules\\UniqueUserValue({id})',
},
{
label: 'URL',
Expand Down
3 changes: 2 additions & 1 deletion src/Actions/DuplicateForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Statamic\Contracts\Forms\Form;
use Statamic\Facades\Form as Forms;
use Statamic\Rules\Handle;
use Statamic\Rules\UniqueFormHandle;
use Statamic\Statamic;

class DuplicateForm extends Action
Expand All @@ -31,7 +32,7 @@ protected function fieldItems()
'type' => 'slug',
'instructions' => __('statamic::messages.form_configure_handle_instructions'),
'separator' => '_',
'validate' => ['required', new Handle, 'unique_form_handle'],
'validate' => ['required', new Handle, new UniqueFormHandle],
],
];
}
Expand Down
6 changes: 6 additions & 0 deletions src/Fields/ClassRuleParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public function parse(string $rule): array
return [$key => (string) Str::of($arg)->trim('"')->replace('\\"', '"')];
} elseif (Str::startsWith($arg, "'") && Str::endsWith($arg, "'")) {
return [$key => (string) Str::of($arg)->trim("'")->replace("\\'", "'")];
} elseif ($arg === 'null') {
return [$key => null];
} elseif ($arg === 'true') {
return [$key => true];
} elseif ($arg === 'false') {
return [$key => false];
}

return [$key => $arg];
Expand Down
42 changes: 33 additions & 9 deletions src/Fields/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,43 @@ public function attributes()
private function parse($rule)
{
if (is_string($rule) && Str::startsWith($rule, 'new ')) {
[$class, $arguments] = (new ClassRuleParser)->parse($rule);

return new $class(...$arguments);
return $this->parseClassBasedRule($rule);
}

if (! is_string($rule) ||
! Str::contains($rule, '{') ||
Str::startsWith($rule, 'regex:') ||
Str::startsWith($rule, 'not_regex:')
) {
return $rule;
if (is_string($rule) && Str::contains($rule, '{') && ! Str::startsWith($rule, ['regex:', 'not_regex:'])) {
return $this->parseStringBasedRule($rule);
}

return $rule;
}

private function parseClassBasedRule($rule)
{
$rule = preg_replace_callback('/{\s*([a-zA-Z0-9_\-]+)\s*}/', function ($match) {
$value = Arr::get($this->replacements, $match[1]);

if ($value === null) {
return 'null';
}

if ($value === true) {
return 'true';
}

if ($value === false) {
return 'false';
}

return is_string($value) ? "'{$value}'" : $value;
}, $rule);

[$class, $arguments] = (new ClassRuleParser)->parse($rule);

return new $class(...$arguments);
}

private function parseStringBasedRule($rule)
{
$rule = str_replace('{this}.', $this->context['prefix'] ?? '', $rule);

return preg_replace_callback('/{\s*([a-zA-Z0-9_\-]+)\s*}/', function ($match) {
Expand Down
3 changes: 2 additions & 1 deletion src/Fieldtypes/Code.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Statamic\Fields\ArrayableString;
use Statamic\Fields\Fieldtype;
use Statamic\GraphQL\Types\CodeType;
use Statamic\Rules\CodeFieldtypeRulers;

class Code extends Fieldtype
{
Expand Down Expand Up @@ -110,7 +111,7 @@ protected function configFieldItems(): array
'key_header' => __('Columns'),
'value_header' => __('Line Style (dashed or solid)'),
'add_button' => __('Add Ruler'),
'validate' => 'code_fieldtype_rulers',
'validate' => [new CodeFieldtypeRulers],
],
],
],
Expand Down
13 changes: 6 additions & 7 deletions src/Fieldtypes/Date.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Validator;
use InvalidArgumentException;
use Statamic\Exceptions\ValidationException;
use Statamic\Facades\GraphQL;
use Statamic\Fields\Fieldtype;
use Statamic\GraphQL\Fields\DateField;
use Statamic\GraphQL\Types\DateRangeType;
use Statamic\Query\Scopes\Filters\Fields\Date as DateFilter;
use Statamic\Rules\DateFieldtype as ValidationRule;
use Statamic\Statamic;
use Statamic\Support\DateFormat;
use Statamic\Validation\DateFieldtype as ValidationRule;

class Date extends Fieldtype
{
Expand Down Expand Up @@ -366,11 +366,10 @@ public function secondsEnabled()

public function preProcessValidatable($value)
{
if ($error = (new ValidationRule($this))($value)) {
throw ValidationException::withMessages([
$this->field->handle() => $error,
]);
}
Validator::make(
[$this->field->handle() => $value],
[$this->field->handle() => [new ValidationRule($this)]],
)->validate();

if ($value === null) {
return null;
Expand Down
2 changes: 1 addition & 1 deletion src/Fieldtypes/Time.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Statamic\Fieldtypes;

use Statamic\Fields\Fieldtype;
use Statamic\Validation\TimeFieldtype as ValidationRule;
use Statamic\Rules\TimeFieldtype as ValidationRule;

class Time extends Fieldtype
{
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/CP/Assets/AssetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Http\Resources\CP\Assets\Asset as AssetResource;
use Statamic\Validation\AllowedFile;
use Statamic\Rules\AllowedFile;

class AssetsController extends CpController
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Illuminate\Http\Request;
use Statamic\Assets\FileUploader as Uploader;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Validation\AllowedFile;
use Statamic\Rules\AllowedFile;

class FilesFieldtypeController extends CpController
{
Expand Down
5 changes: 3 additions & 2 deletions src/Http/Controllers/CP/Taxonomies/TermsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Statamic\Http\Resources\CP\Taxonomies\Terms;
use Statamic\Query\Scopes\Filters\Concerns\QueriesFilters;
use Statamic\Rules\Slug;
use Statamic\Rules\UniqueTermValue;

class TermsController extends CpController
{
Expand Down Expand Up @@ -170,7 +171,7 @@ public function update(Request $request, $taxonomy, $term, $site)
'slug' => [
'required',
new Slug,
'unique_term_value:'.$taxonomy->handle().','.$term->id().','.$site->handle(),
new UniqueTermValue(taxonomy: $taxonomy->handle(), except: $term->id(), site: $site->handle()),
],
]);

Expand Down Expand Up @@ -270,7 +271,7 @@ public function store(Request $request, $taxonomy, $site)

$fields->validate([
'title' => 'required',
'slug' => 'required|unique_term_value:'.$taxonomy->handle().',null,'.$site->handle(),
'slug' => ['required', new UniqueTermValue(taxonomy: $taxonomy->handle(), site: $site->handle())],
]);

$values = $fields->process()->values()->except(['slug', 'blueprint']);
Expand Down
5 changes: 3 additions & 2 deletions src/Http/Controllers/CP/Users/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Statamic\Http\Resources\CP\Users\Users;
use Statamic\Notifications\ActivateAccount;
use Statamic\Query\Scopes\Filters\Concerns\QueriesFilters;
use Statamic\Rules\UniqueUserValue;
use Statamic\Search\Result;
use Symfony\Component\Mailer\Exception\TransportException;

Expand Down Expand Up @@ -165,7 +166,7 @@ public function store(Request $request)

$fields = $blueprint->fields()->except(['roles', 'groups'])->addValues($request->all());

$fields->validate(['email' => 'required|email|unique_user_value']);
$fields->validate(['email' => ['required', 'email', new UniqueUserValue]]);

if ($request->input('_validate_only')) {
return [];
Expand Down Expand Up @@ -277,7 +278,7 @@ public function update(Request $request, $user)

$fields
->validator()
->withRules(['email' => 'required|unique_user_value:{id}'])
->withRules(['email' => ['required', 'email', new UniqueUserValue(except: $user->id())]])
->withReplacements(['id' => $user->id()])
->validate();

Expand Down
11 changes: 5 additions & 6 deletions src/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Statamic\Exceptions\SilentFormFailureException;
use Statamic\Exceptions\UnauthorizedHttpException;
use Statamic\Facades\User;
use Statamic\Rules\UniqueUserValue;

class UserController extends Controller
{
Expand Down Expand Up @@ -63,7 +64,7 @@ public function register(Request $request)
$fields = $fields->addValues($values);

$fieldRules = $fields->validator()->withRules([
'email' => ['required', 'email', 'unique_user_value'],
'email' => ['required', 'email', new UniqueUserValue],
'password' => ['required', 'confirmed', Password::default()],
])->rules();

Expand Down Expand Up @@ -124,11 +125,9 @@ public function profile(Request $request)
try {
$fields
->validator()
->withRules([
'email' => ['required', 'email', 'unique_user_value:{id}'],
])->withReplacements([
'id' => $user->id(),
])->validate();
->withRules(['email' => ['required', 'email', new UniqueUserValue(except: $user->id())]])
->withReplacements(['id' => $user->id()])
->validate();
} catch (ValidationException $e) {
return $this->userProfileFailure($e->validator->errors());
}
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Requests/FrontendFormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
use Illuminate\Support\Traits\Localizable;
use Illuminate\Validation\ValidationException;
use Statamic\Facades\Site;
use Statamic\Rules\AllowedFile;
use Statamic\Support\Arr;
use Statamic\Validation\AllowedFile;

class FrontendFormRequest extends FormRequest
{
Expand Down
1 change: 1 addition & 0 deletions src/Providers/ExtensionServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ class ExtensionServiceProvider extends ServiceProvider
Updates\AddDefaultPreferencesToGitConfig::class,
Updates\AddConfigureFormFieldsPermission::class,
Updates\AddSitePermissions::class,
Updates\UseClassBasedStatamicUniqueRules::class,
];

public function register()
Expand Down
1 change: 0 additions & 1 deletion src/Providers/StatamicServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class StatamicServiceProvider extends AggregateServiceProvider
\Statamic\StaticCaching\ServiceProvider::class,
\Statamic\Revisions\ServiceProvider::class,
CpServiceProvider::class,
ValidationServiceProvider::class,
RouteServiceProvider::class,
BroadcastServiceProvider::class,
\Statamic\API\ServiceProvider::class,
Expand Down
29 changes: 0 additions & 29 deletions src/Providers/ValidationServiceProvider.php

This file was deleted.

19 changes: 10 additions & 9 deletions src/Validation/AllowedFile.php → src/Rules/AllowedFile.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<?php

namespace Statamic\Validation;
namespace Statamic\Rules;

use Illuminate\Contracts\Validation\InvokableRule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Http\UploadedFile;

class AllowedFile implements InvokableRule
class AllowedFile implements ValidationRule
{
private array $extensions = [
private const EXTENSIONS = [
'7z',
'aiff',
'asc',
Expand Down Expand Up @@ -108,16 +109,16 @@ class AllowedFile implements InvokableRule
'zip',
];

public function __invoke($attribute, $value, $fail): void
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (! $this->isAllowed($value)) {
$fail(__('validation.uploaded'));
if (! $this->isAllowedExtension($value)) {
$fail('statamic::validation.uploaded')->translate();
}
}

private function isAllowed(UploadedFile $file): bool
private function isAllowedExtension(UploadedFile $file): bool
{
$extensions = array_merge($this->extensions, config('statamic.assets.additional_uploadable_extensions', []));
$extensions = array_merge(static::EXTENSIONS, config('statamic.assets.additional_uploadable_extensions', []));

return in_array(trim(strtolower($file->getClientOriginalExtension())), $extensions);
}
Expand Down
18 changes: 18 additions & 0 deletions src/Rules/CodeFieldtypeRulers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Statamic\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class CodeFieldtypeRulers implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
foreach ($value as $key => $val) {
if (! is_int($key) || ! in_array($val, ['dashed', 'solid'])) {
$fail('statamic::validation.code_fieldtype_rulers')->translate();
}
}
}
}
Loading

0 comments on commit 33461ff

Please sign in to comment.