diff --git a/.gitignore b/.gitignore index f5a1b63..629d623 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /_bin +/.midnite.config.json /.phpdoc /docs/generated /_data diff --git a/composer.json b/composer.json index d21ed41..4f17301 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "./vendor/bin/pest --parallel --processes=24" ], "coverage": [ - "XDEBUG_MODE=coverage ./vendor/bin/pest --parallel --processes=24 --coverage --coverage-html build/html --min=70", + "XDEBUG_MODE=coverage ./vendor/bin/pest --parallel --processes=24 --coverage --coverage-html build/html --coverage-xml build/coverage.xml --min=70", "open build/html/index.html" ], "coverageNoMin": [ diff --git a/docs/Handlers/ValidationHandler.md b/docs/Handlers/ValidationHandler.md new file mode 100644 index 0000000..584e0d7 --- /dev/null +++ b/docs/Handlers/ValidationHandler.md @@ -0,0 +1,188 @@ +# ValidationHandler Documentation + +## Introduction + +The `ValidationHandler` class is designed to streamline and enhance the validation process in Laravel applications. +While Laravel's built-in Validator class and Form Request validation are powerful tools, the `ValidationHandler` offers +a more fluent and reusable approach, especially in scenarios where you can't use Form Request validation directly in +your controller method arguments. + +This class provides a flexible and powerful way to handle form validation, offering more control over the validation +flow, redirection, and post-validation actions. It's particularly useful when you need to customize the validation +process beyond what's easily achievable with standard Laravel validation methods. + +## Key Features + +1. Flexible validation rules and messages setup +2. Seamless integration with existing FormRequest classes +3. Customizable redirection behavior +4. Support for query parameters and URL fragments +5. Input flashing and session message flashing +6. Callbacks for pass, fail, and finally scenarios +7. Automatic resolution of ValidationFactory and Request dependencies + +## Installation + +You can install the package via composer: + +```bash +composer require midnite81/core +``` + +## Usage + +### Basic Usage + +```php +use Midnite81\Core\Handlers\ValidationHandler; + +$handler = ValidationHandler::make() + ->setRules([ + 'name' => 'required|string|max:255', + 'email' => 'required|email|unique:users', + ]) + ->setRedirectRoute('form.error') + ->withFragment('error-section') + ->withFlashMessage('Please correct the errors below.') + ->onPass(function ($request) { + // Handle successful validation + }); + +$handler->validate(); +``` + +### Usage with Existing FormRequest Classes + +One of the primary strengths of the `ValidationHandler` is its ability to work seamlessly with your existing FormRequest +classes: + +```php +use App\Http\Requests\YourCustomFormRequest; +use Midnite81\Core\Handlers\ValidationHandler; + +$handler = ValidationHandler::make() + ->setFormRequest(YourCustomFormRequest::class) + ->setRedirectRoute('your.error.route') + ->withFragment('form') + ->onPass(function ($request) { + // Handle successful validation + }); + +$handler->validate(); +``` + +## Main Methods + +### setFormRequest(FormRequest|string $formRequest) + +Allows you to use an existing FormRequest class for validation rules and messages. + +### setRules(array $rules) and setMessages(array $messages) + +Manually set validation rules and error messages. + +### setRedirectUrl(string|Closure $url), setRedirectRoute(string $route, array $parameters = []), setRedirectBack() + +Configure where to redirect on validation failure. + +### withQueryParameters(array $params) and withFragment(?string $fragment) + +Add query parameters or a fragment to the redirect URL. + +### flashInput(bool $flash = true) and withFlashMessage(string $message, ?string $key = null) + +Control input flashing and set flash messages for the session. + +### onPass(Closure $callback) + +Define actions to be taken when validation passes successfully. This method is particularly useful for executing logic +that should occur immediately after successful validation, before proceeding to the next step in your application flow. + +### onFail(Closure $callback) + +Define optional actions to be taken when validation fails. It's important to note that this method is not mandatory. If +not specified, the default behavior in the `validate()` method will still throw a `ValidationException` with the +appropriate redirect URL and error messages. + +Example usage: + +```php +$handler->onFail(function ($request, $errors) { + // Custom failure handling, if needed + // Note: ValidationException will still be thrown after this callback + Log::error('Validation failed', ['errors' => $errors->toArray()]); +}); +``` + +### finally(Closure $hook) + +Define actions to be taken after validation, regardless of whether it passed or failed. + +### validate() + +This method performs the actual validation. If validation fails, it will throw a `ValidationException` with the +configured redirect URL and error messages, regardless of whether an `onFail` callback has been specified. + +## Full Implementation Example + +Here's a comprehensive example of how you might use the `ValidationHandler` in a controller, demonstrating the optional +nature of the `onFail` callback: + +```php +use Midnite81\Core\Handlers\ValidationHandler; +use Illuminate\Validation\ValidationException; + +class YourController extends Controller +{ + public function store() + { + try { + $handler = ValidationHandler::make() + ->setFormRequest(YourCustomFormRequest::class) + ->setRedirectRoute('form.error') + ->withFragment('error-section') + ->withFlashMessage('Please correct the errors below.') + ->onPass(function ($request) { + // Save the validated data + // Trigger any necessary events or jobs + }) + ->onFail(function ($request, $errors) { + // Optional: Custom failure handling + // This will be called before the ValidationException is thrown + // Unless you return false + Log::warning('Form submission failed', ['errors' => $errors->toArray()]); + }) + ->finally(function ($request, $passed) { + // Actions to perform regardless of validation outcome + Log::info('Validation attempt', ['passed' => $passed]); + }); + + $handler->validate(); + + // If we reach here, validation passed + return redirect()->route('form.success')->with('message', 'Form submitted successfully!'); + } catch (ValidationException $e) { + // The ValidationException is automatically thrown by the validate() method + // when validation fails, whether or not onFail() is used. + // The exception handling is typically managed by Laravel's exception handler, + // but you can add custom logic here if needed. + return redirect()->back()->withErrors($e->errors())->withInput(); + } + } +} +``` + +In this example, the `onFail` callback is used for optional custom handling (like logging), but the +`ValidationException` will still be thrown automatically by the `validate()` method, ensuring consistent behavior with +Laravel's built-in validation. + +## Conclusion + +The `ValidationHandler` class provides a powerful and flexible way to handle form validation in Laravel applications. By +offering a fluent interface, easy integration with existing FormRequest classes, and automatic dependency resolution, it +simplifies complex validation scenarios while keeping your code clean and maintainable. + +Whether you're working on a simple form or a complex multi-step process, the `ValidationHandler` can help streamline +your validation logic and improve the overall structure of your application. The automatic throwing of +`ValidationException` in the `validate()` method ensures consistent behavior with Laravel's standard validation, while +still allowing for custom failure handling through the optional `onFail` callback. diff --git a/readme.md b/readme.md index 77f9068..8d2b637 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,8 @@ This package contains; - [Eloquent Helpers](docs/EloquentHelpers.md) - [Entities, Requests and Responses](docs/Entities_Requests_Responses.md) - [Exceptions](docs/Exceptions.md) +- Handlers + - [ValidationHandler](docs/Handlers/ValidationHandler.md) - [Helper Functions](docs/HelperFunctions.md) - [first](docs/HelperFunctions.md#first-value) - [uuid](docs/HelperFunctions.md#uuid) diff --git a/src/Handlers/ValidationHandler.php b/src/Handlers/ValidationHandler.php new file mode 100644 index 0000000..7bc6130 --- /dev/null +++ b/src/Handlers/ValidationHandler.php @@ -0,0 +1,367 @@ +validationFactory = $validationFactory ?? app()->make(ValidationFactory::class); + $this->request = $request ?? app()->make(Request::class); + + $this->redirectBehavior = function () { + return url()->previous(); + }; + } + + /** + * Factory method to instantiate the class. + * + * @param ValidationFactory|null $validationFactory An instance of ValidationFactory. + * @param Request|null $request An instance of Request. + * @return static Returns a new instance of the class. + * + * @throws BindingResolutionException + */ + public static function make(?ValidationFactory $validationFactory = null, ?Request $request = null): static + { + return new static($validationFactory, $request); + } + + /** + * Sets the form request for the current instance. The form request can be + * an instance of FormRequest or a valid class name that extends FormRequest. + * + * @param FormRequest|string $formRequest An instance of FormRequest or + * a valid class name that extends FormRequest. + * @return self The current instance for method chaining. + * + * @throws \InvalidArgumentException If the provided class name is not a valid FormRequest + * class or if the rules method does not return validation rules. + */ + public function setFormRequest(FormRequest|string $formRequest): self + { + if (is_string($formRequest)) { + if (!class_exists($formRequest) || !is_subclass_of($formRequest, FormRequest::class)) { + throw new \InvalidArgumentException('The provided class name must be a valid FormRequest class.'); + } + $formRequest = new $formRequest(); + } + + if (!$formRequest instanceof FormRequest) { + throw new \InvalidArgumentException('The provided argument must be an instance of FormRequest or a valid class name.'); + } + + $this->formRequest = $formRequest; + $this->rules = method_exists($formRequest, 'rules') ? $formRequest->rules() : []; + $this->messages = method_exists($formRequest, 'messages') ? $formRequest->messages() : []; + + if (empty($this->rules)) { + throw new \InvalidArgumentException('The provided FormRequest class must have a rules() method that returns validation rules.'); + } + + if (method_exists($formRequest, 'errorBag')) { + $this->errorBag = $formRequest->errorBag(); + } + + return $this; + } + + /** + * Sets the validation rules for the current instance. + * + * @param array $rules An array of validation rules. + * @return self The current instance for method chaining. + */ + public function setRules(array $rules): self + { + $this->rules = $rules; + + return $this; + } + + /** + * Sets the validation messages for the current instance. + * + * @param array $messages An array of validation messages. + * @return self The current instance for method chaining. + */ + public function setMessages(array $messages): self + { + $this->messages = $messages; + + return $this; + } + + /** + * Sets the URL to which the system should redirect. + * + * @param string|Closure $url The URL or a Closure that returns the URL. + * @return self Returns the current instance for method chaining. + */ + public function setRedirectUrl(string|Closure $url): self + { + $this->redirectBehavior = $url; + + return $this; + } + + /** + * Sets the route to which the system should redirect with optional parameters. + * + * @param string $route The name of the route. + * @param array $parameters Optional parameters for the route. + * @return self Returns the current instance for method chaining. + */ + public function setRedirectRoute(string $route, array $parameters = []): self + { + $this->redirectBehavior = function () use ($route, $parameters) { + return route($route, $parameters); + }; + + return $this; + } + + /** + * Sets the redirect behavior to redirect back to the previous URL. + * + * @return self Returns the current instance for method chaining. + */ + public function setRedirectBack(): self + { + $this->redirectBehavior = function () { + return url()->previous(); + }; + + return $this; + } + + /** + * Sets the query parameters to be used. + * + * @param array $params An associative array of query parameters. + * @return self Returns the current instance for method chaining. + */ + public function withQueryParameters(array $params): self + { + $this->queryParameters = $params; + + return $this; + } + + /** + * Sets the fragment component of the URL. + * + * @param string|null $fragment The fragment or null if no fragment is to be set. + * @return self Returns the current instance for method chaining. + */ + public function withFragment(?string $fragment): self + { + $this->fragment = $fragment; + + return $this; + } + + /** + * Sets whether the input should be flashed for the next request. + * + * @param bool $flash Indicates whether the input should be flashed. Defaults to true. + * @return self Returns the current instance for method chaining. + */ + public function flashInput(bool $flash = true): self + { + $this->flashInput = $flash; + + return $this; + } + + /** + * Sets a flash message to be used in the application. + * + * @param string $message The flash message to be set. + * @param string|null $key An optional key to categorize the flash message. + * @return self Returns the current instance for method chaining. + */ + public function withFlashMessage(string $message, ?string $key = null): self + { + $this->flashMessage = $message; + if ($key !== null) { + $this->flashMessageKey = $key; + } + + return $this; + } + + /** + * Sets the callback function to be executed on pass condition. + * + * @param Closure $callback The callback function to be executed. + * @return self Returns the current instance for method chaining. + */ + public function onPass(Closure $callback): self + { + $this->passCallback = $callback; + + return $this; + } + + /** + * Sets the callback to be executed on failure. + * + * @param Closure $callback The callback function to be triggered on failure. + * @return self Returns the current instance for method chaining. + */ + public function onFail(Closure $callback): self + { + $this->failCallback = $callback; + + return $this; + } + + /** + * Registers a hook to be executed after validation. + * + * @param Closure $hook The Closure to be called after validation. + * @return self Returns the current instance for method chaining. + */ + public function finally(Closure $hook): self + { + $this->finallyCallback = $hook; + + return $this; + } + + /** + * Sets the error bag that should be used. + * + * @param string $errorBag The name of the error bag. + * @return self Returns the current instance for method chaining. + */ + public function setErrorBag(string $errorBag): self + { + $this->errorBag = $errorBag; + + return $this; + } + + /** + * Validates the current request data against the specified rules and messages. + * If validation fails, it creates a ValidationException with the appropriate + * redirect URL and error messages and throws it. + * + * @return void + */ + public function validate(): void + { + $this->authorizeFormRequest(); + + $validator = $this->validationFactory->make($this->request->all(), $this->rules, $this->messages); + + if ($validator->fails()) { + $errors = $validator->errors(); + + if ($this->failCallback) { + $shouldProceed = ($this->failCallback)($this->request, $errors); + if ($shouldProceed === false) { + return; + } + } + + $redirectUrl = $this->redirectBehavior instanceof Closure + ? ($this->redirectBehavior)($this->request, $errors) + : $this->redirectBehavior; + + if (!empty($this->queryParameters)) { + $redirectUrl = $redirectUrl . (parse_url($redirectUrl, PHP_URL_QUERY) ? '&' : '?') . http_build_query($this->queryParameters); + } + + if ($this->fragment) { + $redirectUrl .= '#' . $this->fragment; + } + + $exception = ValidationException::withMessages($errors->toArray()) + ->redirectTo($redirectUrl); + + if ($this->errorBag) { + $exception->errorBag($this->errorBag); + } + + if ($this->flashInput) { + $this->request->flash(); + } + + if ($this->flashMessage) { + session()->flash($this->flashMessageKey, $this->flashMessage); + } + + if ($this->finallyCallback) { + ($this->finallyCallback)($this->request, false); + } + + throw $exception; + } else { + if ($this->passCallback) { + ($this->passCallback)($this->request); + } + + if ($this->finallyCallback) { + ($this->finallyCallback)($this->request, true); + } + } + } + + protected function authorizeFormRequest(): void + { + if ($this->formRequest && method_exists($this->formRequest, 'authorize') && !$this->formRequest->authorize()) { + throw new AuthorizationException('This action is unauthorized.'); + } + } +} diff --git a/tests/Handlers/ValidationHandlerTest.php b/tests/Handlers/ValidationHandlerTest.php new file mode 100644 index 0000000..c116b94 --- /dev/null +++ b/tests/Handlers/ValidationHandlerTest.php @@ -0,0 +1,396 @@ +setLaravelSession($this->app['session.store']); + $this->app['request'] = $request; + $this->validationFactory = $this->app->make(ValidationFactory::class); +}); + +test('it can be instantiated', function () { + $handler = new ValidationHandler($this->validationFactory, request()); + expect($handler)->toBeInstanceOf(ValidationHandler::class); +}); + +test('it can be created using make method', function () { + $handler = ValidationHandler::make($this->validationFactory, request()); + expect($handler)->toBeInstanceOf(ValidationHandler::class); +}); + +test('it validates successfully with valid data', function () { + $request = Request::create('/', 'POST', ['name' => 'John Doe', 'email' => 'john@example.com']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules([ + 'name' => 'required|string', + 'email' => 'required|email', + ]); + + $hasErrored = false; + try { + $handler->validate(); + } catch (Exception $e) { + $hasErrored = true; + } + + expect($hasErrored)->toBeFalse(); +}); + +test('it throws ValidationException with invalid data', function () { + $request = Request::create('/', 'POST', ['name' => '', 'email' => 'not-an-email']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules([ + 'name' => 'required|string', + 'email' => 'required|email', + ]); + + expect(fn () => $handler->validate())->toThrow(ValidationException::class); +}); + +test('it uses custom error messages', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setMessages(['name.required' => 'Custom error message for name']); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->errors()['name'][0])->toBe('Custom error message for name'); + } +}); + +test('it sets redirect URL', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setRedirectUrl('/custom-error-page'); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->redirectTo)->toBe('/custom-error-page'); + } +}); + +test('it sets redirect route', function () { + Route::get('/error-page', function () { + return 'Error Page'; + })->name('error.page'); + + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setRedirectRoute('error.page', ['type' => 'validation']); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->redirectTo)->toBe(route('error.page', ['type' => 'validation'])); + } +}); + +test('it adds query parameters to redirect URL', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setRedirectUrl('/error-page') + ->withQueryParameters(['source' => 'validation_error']); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->redirectTo)->toBe('/error-page?source=validation_error'); + } +}); + +test('it adds fragment to redirect URL', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setRedirectUrl('/error-page') + ->withFragment('error-section'); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->redirectTo)->toBe('/error-page#error-section'); + } +}); + +test('it uses form request for rules and messages', function () { + $formRequest = new class extends FormRequest + { + public function rules() + { + return ['name' => 'required']; + } + + public function messages() + { + return ['name.required' => 'Name is required from form request']; + } + + public function errorBag() + { + return 'custom_error_bag'; + } + }; + + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setFormRequest($formRequest); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->errors()['name'][0])->toBe('Name is required from form request'); + expect($e->errorBag)->toBe('custom_error_bag'); + } +}); + +test('it flashes input to session when validation fails', function () { + $request = Request::create('/', 'POST', ['name' => 'John']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['email' => 'required|email']) + ->flashInput(); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect(session()->hasOldInput('name'))->toBeTrue(); + expect(session()->getOldInput('name'))->toBe('John'); + } +}); + +test('it sets flash message when validation fails', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->withFlashMessage('Validation failed', 'error'); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect(session('error'))->toBe('Validation failed'); + } +}); + +test('it executes after validation hook', function () { + $request = Request::create('/', 'POST', ['name' => 'John Doe']); + $request->setLaravelSession($this->app['session.store']); + + $hookCalled = false; + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->finally(function ($request, $passed) use (&$hookCalled) { + $hookCalled = true; + expect($passed)->toBeTrue(); + }); + + $handler->validate(); + expect($hookCalled)->toBeTrue(); +}); + +test('it executes after validation hook on failure', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $hookCalled = false; + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->finally(function ($request, $passed) use (&$hookCalled) { + $hookCalled = true; + expect($passed)->toBeFalse(); + }); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($hookCalled)->toBeTrue(); + } +}); + +test('it sets custom error bag', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setErrorBag('custom_error_bag'); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->errorBag)->toBe('custom_error_bag'); + } +}); + +test('it checks authorization for form request', function () { + $formRequest = new class extends FormRequest + { + public function rules() + { + return ['name' => 'required']; + } + + public function authorize() + { + return false; + } + }; + + $request = Request::create('/', 'POST', ['name' => 'John Doe']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setFormRequest($formRequest); + + expect(fn () => $handler->validate())->toThrow(AuthorizationException::class); +}); + +// New tests to improve coverage + +test('it sets redirect back', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->setRedirectBack(); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($e->redirectTo)->toBe(url()->previous()); + } +}); + +test('it executes onPass callback', function () { + $request = Request::create('/', 'POST', ['name' => 'John Doe']); + $request->setLaravelSession($this->app['session.store']); + + $called = false; + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->onPass(function ($request) use (&$called) { + $called = true; + }); + + $handler->validate(); + expect($called)->toBeTrue(); +}); + +test('it executes onFail callback', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $called = false; + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->onFail(function ($request, $errors) use (&$called) { + $called = true; + }); + + try { + $handler->validate(); + } catch (ValidationException $e) { + expect($called)->toBeTrue(); + } +}); + +test('it throws exception for invalid form request class name', function () { + $handler = ValidationHandler::make($this->validationFactory, request()); + expect(fn () => $handler->setFormRequest('InvalidClassName'))->toThrow(\InvalidArgumentException::class); +}); + +test('it throws exception for non-FormRequest class', function () { + $handler = ValidationHandler::make($this->validationFactory, request()); + expect(fn () => $handler->setFormRequest(\stdClass::class))->toThrow(\InvalidArgumentException::class); +}); + +test('it throws exception for form request with empty rules', function () { + $emptyRulesFormRequest = new class extends FormRequest + { + public function rules() + { + return []; + } + }; + + $handler = ValidationHandler::make($this->validationFactory, request()); + expect(fn () => $handler->setFormRequest($emptyRulesFormRequest))->toThrow(\InvalidArgumentException::class); +}); + +test('it stops validation when fail callback returns false', function () { + $request = Request::create('/', 'POST', ['name' => '']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setRules(['name' => 'required']) + ->onFail(function () { + return false; + }); + + $result = $handler->validate(); + expect($result)->toBeNull(); +}); + +test('it authorizes form request with authorized request', function () { + $authorizedFormRequest = new class extends FormRequest + { + public function authorize() + { + return true; + } + + public function rules() + { + return ['name' => 'required']; + } + }; + + $request = Request::create('/', 'POST', ['name' => 'John Doe']); + $request->setLaravelSession($this->app['session.store']); + + $handler = ValidationHandler::make($this->validationFactory, $request) + ->setFormRequest($authorizedFormRequest); + + $handler->validate(); + $this->assertTrue(true); // If we reach here, no exception was thrown +});