Skip to content

Commit

Permalink
Allows to specify rules as closure (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
nunomaduro authored Nov 20, 2023
1 parent 29abe7f commit 22b1592
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 1 deletion.
6 changes: 6 additions & 0 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ function on(Closure|array|string ...$listeners): void
function rules(mixed ...$rules): RuleOptions
{
if (count($rules) === 1 && array_key_exists(0, $rules)) {
if ($rules[0] instanceof Closure) {
CompileContext::instance()->rules = $rules[0];

return new RuleOptions;
}

$rules = $rules[0];
}

Expand Down
8 changes: 8 additions & 0 deletions src/Actions/ReturnRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Livewire\Volt\Actions;

use Closure;
use Illuminate\Container\Container;
use Livewire\Volt\CompileContext;
use Livewire\Volt\Component;
use Livewire\Volt\Contracts\Action;
Expand All @@ -13,6 +15,12 @@ class ReturnRules implements Action
*/
public function execute(CompileContext $context, Component $component, array $arguments): array
{
if ($context->rules instanceof Closure) {
return Container::getInstance()->call(
Closure::bind($context->rules, $component, $component::class),
);
}

return $context->rules;
}
}
2 changes: 1 addition & 1 deletion src/CompileContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function __construct(
public ?string $title,
public ?Closure $listeners,
public array $inlineListeners,
public array $rules,
public Closure|array $rules,
public array $messages,
public array $validationAttributes,
public ?string $paginationView,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

use Livewire\Volt\Actions;
use Livewire\Volt\CompileContext;
use Livewire\Volt\Contracts\Compiled;
use Livewire\Volt\Component;

new class extends Component implements Livewire\Volt\Contracts\FunctionalComponent
{
public static CompileContext $__context;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

public $title;

public function mount()
{
(new Actions\InitializeState)->execute(static::$__context, $this, get_defined_vars());

(new Actions\CallHook('mount'))->execute(static::$__context, $this, get_defined_vars());
}

public function save()
{
$arguments = [static::$__context, $this, func_get_args()];

return (new Actions\CallMethod('save'))->execute(...$arguments);
}

protected function rules()
{
return (new Actions\ReturnRules)->execute(static::$__context, $this, []);
}

protected function messages()
{
return (new Actions\ReturnValidationMessages)->execute(static::$__context, $this, []);
}

};
14 changes: 14 additions & 0 deletions tests/Feature/CompilerContext/RulesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
]);
});

it('may be defined using closures', function () {
$context = CompileContext::instance();

rules(fn () => ['name' => 'required|min:6', 'email' => 'nullable|email']);

expect($context->rules)->resolve()->toBe(['name' => 'required|min:6', 'email' => 'nullable|email']);
});

test('precedence', function () {
$context = CompileContext::instance();

Expand All @@ -42,4 +50,10 @@
'name' => 'second',
'email' => 'first',
]);

rules(fn () => ['name' => 'third']);

expect($context->rules)->resolve()->toBe([
'name' => 'third',
]);
});
13 changes: 13 additions & 0 deletions tests/Feature/FunctionalComponentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,19 @@
]);
});

it('can have rules', function () {
$component = Livewire::test('component-with-rules');

$component->assertSet('saved', false)
->call('save')
->assertSee('The title field is missing.')
->assertSet('saved', false)
->updateProperty('title', 'Hello')
->call('save')
->assertDontSee('The title field is missing.')
->assertSet('saved', true);
});

it('can have reactive state', function () {
$component = Livewire::test('component-with-reactive-state.todos');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
use function Livewire\Volt\{state, rules};
state(['title' => '']);
rules(fn () => ['title' => ['required', 'min:5']])
->messages(['title' => 'The title field is missing.']);
$save = function () {
$this->validate();
$this->saved = true;
}; ?>

<div>
<form wire:submit="save">
<input type="text" wire:model="title">
@error('title') <span class="error">{{ $message }}</span> @enderror

<button type="submit">Save</button>
</form>
</div>


0 comments on commit 22b1592

Please sign in to comment.