- How does php-standard-library/result improve error handling in Laravel compared to exceptions?
- Result treats errors as return values (e.g., `Result<Success, Failure>`) instead of throwing exceptions, enabling explicit handling in APIs, validation, and async jobs. This avoids abrupt control flow, improves testability, and lets you compose outcomes predictably (e.g., `Result::match()` for HTTP responses). It’s especially useful for expected failures like validation errors or API rate limits.
- Can I use Result with Laravel’s built-in Validator or HTTP responses?
- Yes, but you’ll need to wrap calls manually. For example, wrap `Validator::validate()` in `Result::fromCallable()` to convert validation failures into `Result::Err()`. For HTTP responses, use `Result::match()` to map success/failure to `response()` calls. The package provides no native Laravel integration yet, but custom facades or traits can bridge this gap.
- What Laravel versions and PHP versions does this package support?
- The package requires PHP 8.0+ and works with Laravel 8+ (composer.json specifies no lower bounds, but PHP 8 features like union types are leveraged). Test thoroughly with your Laravel version, as some newer features (e.g., attributes in Laravel 9+) may not be fully utilized. Check the [PHP Standard Library docs](https://php-standard-library.dev) for version-specific notes.
- How do I integrate Result into Laravel’s service container?
- Bind the Result handler in a service provider using `app->bind()` or register it as a singleton. For example: `$this->app->singleton(ResultHandler::class, fn() => new ResultHandler());`. Then inject `ResultHandler` into controllers/services or use dependency injection directly. The package plays well with Laravel’s DI, but avoid overusing it for trivial cases.
- Does Result work with Laravel queues/jobs for async error handling?
- Absolutely. Return `Result` from job handlers and use `Result::match()` to handle failures gracefully. For example, in a job’s `handle()` method, call `$this->fail($result->unwrapErr())` if the result is an error. This makes debugging async workflows easier by surfacing errors as values rather than relying on exception logs.
- How do I test code that returns Result objects in Laravel?
- Replace `expectException()` with assertions on `Result` methods. For example: `$result = $service->process($data); $this->assertTrue($result->isFailure()); $this->assertInstanceOf(CustomError::class, $result->unwrapErr())`. This makes tests more deterministic and readable, as you explicitly check for success/failure states instead of exception side effects.
- What’s the performance impact of using Result vs. exceptions in Laravel?
- Result adds minimal overhead—benchmarks show negligible differences for most use cases. However, in hot paths (e.g., high-frequency API calls), avoid deep nesting of `Result` operations. Cache repeated `Result` instances or use `Result::fromCallable()` for lazy evaluation. Profile critical paths post-adoption to confirm.
- Can I migrate from exceptions to Result gradually in a Laravel app?
- Yes, use `Result::fromThrowable()` to wrap exception-prone code. For example: `$result = Result::fromThrowable(fn() => User::create($data));`. This lets you adopt Result incrementally, starting with new features or isolated modules (e.g., API endpoints) before full migration. Existing exception-based code remains unchanged.
- Are there alternatives to php-standard-library/result for Laravel?
- Yes, consider `league/value-object` (for custom error types) or `spatie/result-object` (Laravel-specific wrapper). However, `php-standard-library/result` stands out for its lightweight design, type safety (PHP 8 union types), and functional programming patterns (e.g., `map`, `flatMap`). It’s ideal if you prefer a functional approach over Laravel’s exception-heavy ecosystem.
- How do I handle domain-specific errors with Result in Laravel?
- Extend `Result` with custom error types. For example, define `class PaymentFailed extends Error` and use `Result<Success, PaymentFailed>`. This improves type safety and IDE support (autocompletion for error cases). You can also use interfaces (e.g., `interface ApiError`) to standardize error hierarchies across your application.