Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Update our custom string based rules to class based rules #9785

Merged
merged 27 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4454703
Add handling for null/true/false in custom class based rule strings i…
jesseleite Mar 25, 2024
418e291
Improve handling for class based rule string replacements.
jesseleite Mar 25, 2024
c4f6187
Move and update `UniqueEntryValue` class to `Statamic\Rules` w/ Larav…
jesseleite Mar 25, 2024
7868661
Remove `unique_entry_value` string based implementation.
jesseleite Mar 25, 2024
98c3d57
Ensure conditions builder uses new class based implementation.
jesseleite Mar 25, 2024
71e474d
In case they’re updating from older version, update script still runs.
jesseleite Mar 25, 2024
3c7cd9b
Update tests.
jesseleite Mar 25, 2024
b86212c
Move and update `AllowedFile` class to `Statamic\Rules` w/ Laravel co…
jesseleite Mar 26, 2024
2f5889a
Move and update `DateFieldtype` rule class to `Statamic\Rules` w/ Lar…
jesseleite Mar 26, 2024
760cc43
Move and update `TimeFieldtype` rule class to `Statamic\Rules` w/ Lar…
jesseleite Mar 26, 2024
46a2aba
Move and update `CodeFieldtypeRulers` rule class to `Statamic\Rules` …
jesseleite Mar 26, 2024
2698651
Revert "In case they’re updating from older version, update script st…
jesseleite Mar 26, 2024
6121716
We want a (constructor property) promotion ✊
jesseleite Mar 26, 2024
92c1ac9
Move and update `UniqueTermValue` rule class to `Statamic\Rules` w/ L…
jesseleite Mar 26, 2024
797760c
Remove `unique_term_value` string based implementation.
jesseleite Mar 26, 2024
a11f2f2
Merge branch 'master' of https:/statamic/cms into update-…
jesseleite Mar 27, 2024
4445a61
Move and update `UniqueUserValue` rule class to `Statamic\Rules` w/ L…
jesseleite Mar 27, 2024
f753300
Remove `unique_user_value` string based implementation.
jesseleite Mar 27, 2024
c3d792a
Use named args instead of passing `null`s.
jesseleite Mar 27, 2024
48234aa
Move and update `UniqueFormHandle` rule class to `Statamic\Rules` w/ …
jesseleite Mar 27, 2024
0ff4f4d
Remove `unique_form_handle` string based implementation.
jesseleite Mar 27, 2024
023294b
Don’t need this provider anymore 🎉
jesseleite Mar 27, 2024
2c4ef43
Add these rules to validation builder as well, with replacements.
jesseleite Mar 27, 2024
c0388e4
Update script.
jesseleite Mar 27, 2024
17d78e4
Delete example blueprints/fieldsets on tear down.
jesseleite Mar 27, 2024
540fa89
Nitpick.
jesseleite Mar 28, 2024
1f61918
keep it private
jasonvarga Mar 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading