Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Result Laravel Package

php-standard-library/result

A lightweight Result type for PHP that represents success or failure as a value, enabling controlled error handling without exceptions. Helps you return, compose, and inspect outcomes explicitly for safer, predictable application flow.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation Add the package via Composer:

    composer require php-standard-library/result
    

    No configuration or service provider registration is needed—it’s a standalone type.

  2. First Use Case: Validation in a Laravel Controller Replace a traditional try-catch block with Result for explicit error handling:

    use PhpStandardLibrary\Result\Result;
    use Illuminate\Http\Request;
    
    public function store(Request $request) {
        $result = Result::fromCallable(function() use ($request) {
            $validated = $request->validate([
                'email' => 'required|email',
                'name' => 'required|string',
            ]);
            return User::create($validated);
        });
    
        return $result->match(
            fn($user) => response()->json($user, 201),
            fn($error) => response()->json(['error' => $error], 422)
        );
    }
    
  3. Key Methods to Know

    • Result::ok($value): Wrap a successful operation.
    • Result::fail($error): Wrap a failed operation.
    • Result::fromCallable($callable): Convert a callable that may throw into a Result.
    • $result->match($onSuccess, $onFailure): Handle success/failure cases.
    • $result->unwrap(): Get the value (throws if failure).
    • $result->unwrapErr(): Get the error (throws if success).

Implementation Patterns

1. Functional Error Handling in Laravel

Use Result to chain operations functionally, avoiding nested if-else or try-catch blocks:

use PhpStandardLibrary\Result\Result;

function processOrder(Order $order) {
    return Result::fromCallable(function() use ($order) {
        return OrderValidator::validate($order)
            ->then(fn() => PaymentService::charge($order))
            ->then(fn() => OrderRepository::save($order));
    });
}

$result = processOrder($order);
$result->match(
    fn($order) => notifyUser($order),
    fn($error) => logError($error)
);

2. API Response Handling

Standardize HTTP responses using Result:

use Illuminate\Http\Response;

public function getUser($id) {
    $result = Result::fromCallable(fn() => User::findOrFail($id));

    return $result->match(
        fn($user) => response()->json($user),
        fn($error) => response()->json(['error' => $error], 404)
    );
}

3. Domain-Specific Error Types

Extend Result with custom error types for better type safety:

class ValidationError {}
class PaymentError {}

function createUser(array $data): Result<User, ValidationError|PaymentError> {
    $validation = validate($data);
    if ($validation->isFailure()) {
        return Result::fail(new ValidationError($validation->unwrapErr()));
    }
    return Result::fromCallable(fn() => User::create($data))
        ->mapErr(fn($e) => new PaymentError($e->getMessage()));
}

4. Middleware for Result Handling

Create middleware to convert exceptions to Result::fail for consistent error handling:

public function handle($request, Closure $next) {
    try {
        return $next($request);
    } catch (Throwable $e) {
        return Result::fail($e->getMessage());
    }
}

5. Testing with Results

Simplify test assertions by treating failures as values:

public function test_user_creation_fails_with_invalid_email() {
    $result = createUser(['email' => 'invalid', 'name' => 'Test']);
    $this->assertTrue($result->isFailure());
    $this->assertInstanceOf(ValidationError::class, $result->unwrapErr());
}

6. Integration with Laravel Queues

Handle job failures explicitly using Result:

public function handle() {
    $result = Result::fromCallable(fn() => $this->processPayment());

    return $result->match(
        fn($success) => $this->notifySuccess(),
        fn($error) => $this->notifyFailure($error)
    );
}

Gotchas and Tips

Pitfalls

  1. Overusing unwrap()

    • Calling $result->unwrap() on a failure throws an exception, defeating the purpose of Result.
    • Fix: Always check $result->isOk() or use match() before unwrapping.
  2. Ignoring Errors

    • Silently discarding failures (e.g., $result->isOk() without handling the error case) can hide bugs.
    • Fix: Use match() or unwrapErr() to ensure errors are handled.
  3. Performance with fromCallable

    • Result::fromCallable() catches exceptions, which can mask unexpected errors.
    • Fix: Use it only for expected failures (e.g., validation, business logic). Reserve exceptions for true errors (e.g., DB crashes).
  4. Type Safety Gaps

    • PHP’s union types (e.g., Result<User, string>) may not work as expected in older PHP versions or IDEs.
    • Fix: Use @phpstan-type annotations for better static analysis:
      /** @phpstan-return Result<User, ValidationError> */
      function createUser(array $data) { ... }
      
  5. Middleware and Exceptions

    • Middleware that throws exceptions will bypass Result handling unless wrapped.
    • Fix: Convert exceptions to Result::fail in middleware (see Implementation Patterns).

Debugging Tips

  1. Log Results Use tap() to log intermediate results for debugging:

    $result = processOrder($order)
        ->tap(fn($r) => \Log::debug('Result state:', ['isOk' => $r->isOk()]));
    
  2. Custom Error Messages Provide descriptive error messages for failures:

    Result::fail(new ValidationError("Email is invalid: {$email}"))
    
  3. IDE Support

    • Use PHPDoc to clarify Result types:
      /**
       * @return Result<User, ValidationError>
       */
      function createUser(array $data) { ... }
      
    • Configure PHPStan/Psalm to recognize Result types for better autocompletion.
  4. Testing Failures

    • Mock Result::fail() in tests to simulate failures:
      $this->mock(Result::class)->shouldReceive('fail')->andReturn(Result::fail(new ValidationError()));
      

Extension Points

  1. Custom Result Classes Extend Result for domain-specific behavior:

    class DomainResult extends Result {
        public function toArray(): array {
            return $this->match(
                fn($value) => ['success' => true, 'data' => $value],
                fn($error) => ['success' => false, 'error' => $error->getMessage()]
            );
        }
    }
    
  2. Laravel Service Provider Bindings Bind Result to the container for dependency injection:

    $this->app->bind(Result::class, fn() => new Result());
    
  3. Integration with Laravel Validation Create a Result-aware validator:

    use Illuminate\Validation\Validator;
    
    class ResultValidator extends Validator {
        public function validateResolved(): Result {
            if ($this->fails()) {
                return Result::fail($this->errors()->first());
            }
            return Result::ok(true);
        }
    }
    
  4. Async/Await with Results Use spatie/async or Laravel Queues to handle Result in async workflows:

    Bus::dispatch(new ProcessOrderJob($order))
        ->then(fn($result) => $result->match(
            fn($order) => notifyUser($order),
            fn($error) => logError($error)
        ));
    

Config Quirks

  • No Configuration Needed: The package is dependency-free and requires no Laravel-specific setup.
  • PHP Version: Requires PHP 8.1+ for union types and named arguments. Use Result::ok()/Result::fail() for older versions.

Performance Considerations

  • Immutability: Result objects are immutable, so chaining operations (e.g., map()) creates new instances. For hot paths, consider:
    $result = Result::ok($value)->map(...)->map(...); // Creates intermediate objects
    
    • Optimization: Use Result::fromCallable() to defer computation until needed:
      $result = Result::fromCallable(fn() => expensiveOperation());
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope