Skip to content

Laravel validation + error shaping

This example focuses on validation and consistent error shapes using common Laravel patterns: Form Requests + services. The goal is to keep validation errors readable for API clients while still using Result Flow end-to-end.

Form Request

php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

final class RegisterUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'email' => ['required', 'email'],
            'password' => ['required', 'min:10'],
        ];
    }
}

Service

php
namespace App\Services;

use Maxiviper117\ResultFlow\Result;

final class UserService
{
    public function register(array $data): Result
    {
        // Domain-level validation or checks can still be done here
        if (str_ends_with($data['email'], '@blocked.test')) {
            return Result::fail([
                'type' => 'blocked',
                'message' => 'Email domain is blocked',
            ]);
        }

        // ... create user and return Result::ok($user)
        return Result::ok(['id' => 1, 'email' => $data['email']]);
    }
}

Controller

php
namespace App\Http\Controllers;

use App\Http\Requests\RegisterUserRequest;
use App\Services\UserService;
use Maxiviper117\ResultFlow\Result;

final class RegisterController
{
    public function store(RegisterUserRequest $request, UserService $users)
    {
        $result = Result::ok($request->validated())
            ->then(fn ($data, $meta) => $users->register($data))
            ->otherwise(function ($error, $meta) {
                // Normalize all failures into a consistent response shape
                if (is_array($error) && ($error['type'] ?? null) === 'blocked') {
                    return Result::fail([
                        'message' => $error['message'],
                        'code' => 'blocked_domain',
                    ], $meta);
                }

                return Result::fail([
                    'message' => 'Registration failed',
                ], $meta);
            });

        return $result->toResponse();
    }
}

Notes:

  • Form Requests keep input validation in the HTTP layer.
  • Domain-specific checks live in the service.
  • otherwise() centralizes error formatting.
  • The controller always returns toResponse() for a consistent JSON response.

Result functions used

  • ok(), then(), otherwise(), fail(), toResponse()