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

Option Laravel Package

prewk/option

Lightweight Option type for PHP providing Some/None to avoid nulls. Adds map/flatMap/filter, unwrap with defaults, and safe chaining inspired by functional programming. Handy for Laravel and general PHP codebases where nullable values cause bugs.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require prewk/option
    

    Ensure your project uses PHP 8.1+ and has prewk/result (≥1.2.0) installed.

  2. First Use Case: Safe Null Handling Replace a common null check in a Laravel controller:

    use Prewk\Option\Option;
    
    public function showUser(Request $request, int $id)
    {
        $user = Option::fromNullable(User::find($id))
            ->map(fn($user) => UserResource::make($user))
            ->getOrElse(abort(404));
    
        return response()->json($user);
    }
    
  3. Where to Look First

    • Core Methods: Focus on Option::some(), Option::none(), map(), getOrElse(), and match().
    • Laravel Integration: Explore how to wrap Eloquent results or request inputs (see Implementation Patterns).
    • Documentation: Check the source code for method signatures and tests for examples.

Implementation Patterns

1. Replacing null Checks in Controllers

Pattern: Use Option to handle missing or failed operations in API responses.

public function updateProfile(Request $request)
{
    $profile = Option::fromNullable(Profile::find(auth()->id()))
        ->map(fn($p) => $p->update($request->validated()))
        ->match(
            fn($success) => ['status' => 'updated'],
            fn() => ['error' => 'Profile not found']
        );

    return response()->json($profile);
}

2. Functional Chaining with Eloquent

Pattern: Chain Option with Eloquent queries to avoid nested if statements.

$user = User::where('active', true)
    ->find($id)
    ->toOption() // Custom helper (see below)
    ->map(fn($u) => $u->posts()->where('published', true)->get())
    ->getOrElse([]);

// Add this helper to your `AppServiceProvider`:
if (!method_exists(Builder::class, 'toOption')) {
    Builder::macro('toOption', fn($builder) => Option::fromNullable($builder->first()));
}

3. Request Validation with Option

Pattern: Validate request inputs without isset() or null checks.

$value = Option::fromRequest('optional_field', $request)
    ->filter(fn($v) => $v !== 'invalid')
    ->map(fn($v) => (int)$v)
    ->getOrElse(0);

// Helper macro (add to `AppServiceProvider`):
Option::macro('fromRequest', function ($key, $request = null) {
    return Option::fromNullable($request?->input($key));
});

4. Middleware for Optional Dependencies

Pattern: Use Option in middleware to handle optional services.

public function handle(Request $request, Closure $next)
{
    $cache = Option::fromNullable(app('cache'))
        ->map(fn($cache) => $cache->get('key'))
        ->getOrElse(null);

    $request->merge(['cache_hit' => $cache !== null]);
    return $next($request);
}

5. Combining with prewk/result for Errors

Pattern: Use Option for success cases and Result for failures.

$result = Option::some($user)
    ->flatMap(fn($u) => UserService::processPayment($u))
    ->match(
        fn($success) => Result::ok($success),
        fn() => Result::err('User not found')
    );

6. Custom Option Extensions

Pattern: Extend Option with Laravel-specific methods.

// Add to `AppServiceProvider`:
Option::macro('toResponse', function ($default = null) {
    return $this->match(
        fn($value) => response()->json($value),
        fn() => response()->json($default, 404)
    );
});

// Usage:
$user = User::find($id)->toOption()->toResponse();

Gotchas and Tips

Pitfalls

  1. PHP 8.1+ Requirement

    • Gotcha: The package will not work on PHP <8.1 (e.g., Laravel 8 with PHP 7.4).
    • Fix: Upgrade to Laravel 9+ (PHP 8.0+) or 10+ (PHP 8.1+).
  2. Hard Dependency on prewk/result

    • Gotcha: The package requires prewk/result (≥1.2.0), even if you only use Option.
    • Fix: Install both packages explicitly:
      composer require prewk/option prewk/result
      
  3. Verbosity in Chaining

    • Gotcha: Deeply nested map()/filter() chains can become hard to read.
    • Tip: Break chains into smaller methods or use match() for complex logic:
      $result = $option->match(
          fn($value) => processValue($value),
          fn() => fallbackLogic()
      );
      
  4. Lack of Laravel-Specific Helpers

    • Gotcha: The package doesn’t include Laravel-specific macros (e.g., for Eloquent or requests).
    • Tip: Create custom macros in AppServiceProvider (see Implementation Patterns).
  5. Static Analysis Gaps

    • Gotcha: Tools like Psalm may not fully recognize Option types.
    • Tip: Use @phpstan-ignore-next-line or configure Psalm to treat Option as a custom type:
      # psalm.xml
      <type name="Prewk\Option\Option" />
      
  6. Performance Overhead

    • Gotcha: Overusing Option in performance-critical paths (e.g., loops) may add micro-overhead.
    • Tip: Benchmark with Option vs. native null checks. For hot paths, consider:
      if ($option->isSome()) {
          // Native null check for performance
          $value = $option->unwrap();
          // ...
      }
      

Debugging Tips

  1. Inspect Option Values Add a temporary __toString() method for debugging:

    Option::macro('debug', function () {
        return $this->match(
            fn($value) => "Some({$value})",
            fn() => "None"
        );
    });
    

    Usage:

    echo $option->debug(); // Output: "Some(123)" or "None"
    
  2. Common Errors

    • Cannot call method 'map' on null: You’re calling map() on Option::none(). Fix: Use flatMap() or ensure the Option is Some before chaining.
    • Unwrap panic: Calling unwrap() on Option::none(). Fix: Use unwrapOr() or getOrElse() instead.
  3. Integration with Laravel Debugbar Extend Debugbar to display Option values:

    Debugbar::extend('option', function ($option) {
        return $option->match(
            fn($value) => ['value' => $value],
            fn() => ['value' => null, 'type' => 'None']
        );
    });
    

Extension Points

  1. Custom Matchers Extend Option with domain-specific matchers:

    Option::macro('matchUser', function ($some, $none) {
        return $this->match(
            fn($user) => $some($user->name, $user->email),
            $none
        );
    });
    
  2. Laravel Collection Integration Add toOption() to Laravel Collections:

    Collection::macro('toOption', function () {
        return Option::fromNullable($this->first());
    });
    

    Usage:

    $user = User::where('active', true)->get()->toOption();
    
  3. Integration with Validation Use Option in Form Requests:

    public function rules()
    {
        return [
            'optional_field' => [
                'nullable',
                Rule::function('custom_option', function ($attribute, $value, $fail) {
                    return Option::fromNullable($value)
                        ->filter(fn($v) => $v !== 'invalid')
                        ->isSome()
                        ?: $fail('Invalid value');
                }),
            ],
        ];
    }
    
  4. **Testing

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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor