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

php-standard-library/option

Option type for PHP with Some/None to replace nullable values with explicit presence semantics. Helps avoid null checks, clarifies intent, and models optional data safely. Part of PHP Standard Library; see docs and contributing links.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require php-standard-library/option
    

    No additional configuration is required—just import the Option class.

  2. First Use Case: Replace Nullable Types Convert a nullable return type to an explicit Option:

    use PhpStandardLibrary\Option;
    
    function findUserByEmail(string $email): Option {
        $user = User::query()->where('email', $email)->first();
        return Option::fromNullable($user);
    }
    
    // Usage in a Laravel controller
    $userOption = findUserByEmail('user@example.com');
    if ($userOption->isSome()) {
        $user = $userOption->unwrap();
        return response()->json(['user' => $user]);
    }
    return response()->json(['error' => 'User not found'], 404);
    
  3. Where to Look First

    • Core Class: Option.php (methods like some(), none(), map(), unwrap()).
    • Laravel Integration: Focus on replacing ?Type in service methods, repositories, and controllers.
    • Testing: Use Option-aware assertions (e.g., assertTrue($option->isSome())).

Implementation Patterns

1. Service Layer Integration

Pattern: Return Option from service methods to enforce explicit handling of missing data.

class UserService {
    public function getUserById(int $id): Option {
        return Option::fromNullable(User::find($id));
    }

    public function getUserWithAddress(int $id): Option {
        return $this->getUserById($id)
            ->map(fn($user) => $user->address)
            ->filter(fn($address) => $address !== null);
    }
}

2. Repository Pattern

Pattern: Wrap Eloquent queries in Option to handle missing records.

class UserRepository {
    public function findByEmail(string $email): Option {
        return Option::fromNullable(User::where('email', $email)->first());
    }
}

3. Request Validation

Pattern: Use Option to validate optional request data.

use Illuminate\Http\Request;
use PhpStandardLibrary\Option;

public function update(Request $request, int $id) {
    $user = User::findOrFail($id);
    $newEmail = Option::fromNullable($request->input('email'));

    if ($newEmail->isSome()) {
        $user->email = $newEmail->unwrap();
    }
    $user->save();
}

4. Domain Models

Pattern: Replace nullable properties with Option in DTOs or entities.

class Order {
    private Option $shippingAddress;

    public function __construct(Option $shippingAddress) {
        $this->shippingAddress = $shippingAddress;
    }

    public function getShippingAddress(): Option {
        return $this->shippingAddress;
    }
}

5. Middleware and Pipelines

Pattern: Use Option to propagate missing data through middleware.

public function handle($request, Closure $next) {
    $authUser = Option::fromNullable(auth()->user());
    if ($authUser->isNone()) {
        return redirect()->route('login');
    }
    return $next($request);
}

6. API Responses

Pattern: Return Option-wrapped data in API responses.

public function show(User $user) {
    $address = Option::fromNullable($user->address);
    return response()->json([
        'user' => $user,
        'address' => $address->isSome() ? $address->unwrap() : null,
    ]);
}

7. Testing

Pattern: Use Option in unit tests to simulate missing data.

public function testUserNotFound() {
    $repository = new UserRepository();
    $result = $repository->findByEmail('nonexistent@example.com');
    $this->assertTrue($result->isNone());
}

Gotchas and Tips

Pitfalls

  1. Forgetting to Handle None

    • Issue: Calling unwrap() or unwrapOr() on a None value throws an exception.
    • Fix: Always check isSome() or use match() (PHP 8.0+):
      $value = $option->match(
          fn() => 'default', // None case
          fn($val) => $val    // Some case
      );
      
  2. Overusing Option

    • Issue: Applying Option to trivial cases (e.g., optional query params) adds unnecessary complexity.
    • Fix: Reserve Option for semantically meaningful absence (e.g., database lookups, API responses).
  3. Breaking Existing null Logic

    • Issue: Mixing Option and null in the same codebase can lead to confusion.
    • Fix: Gradually migrate—wrap null returns in Option::fromNullable() during transition.
  4. Performance in Hot Paths

    • Issue: Chaining map/filter on Option can be less performant than direct null checks in tight loops.
    • Fix: Use Option for readability in business logic; optimize critical paths with null checks if needed.
  5. IDE Autocompletion

    • Issue: Limited IntelliSense for Option methods (e.g., map, flatMap).
    • Fix: Use PHPDoc annotations or custom IDE plugins.

Debugging Tips

  1. Check for Unhandled None Use Option::expect() in development to fail fast:

    $user = $userOption->expect('User not found!');
    
  2. Log Option States Add a helper method to log Option values:

    Option::debug(function($value) {
        logger()->debug('Option value:', ['value' => $value]);
    });
    
  3. Static Analysis Configure PHPStan to enforce Option usage:

    # phpstan.neon
    parameters:
        level: 7
        rules:
            PhpStandardLibrary\Option\Rules\OptionTypeRule: true
    

Configuration Quirks

  1. Laravel Service Container Bind Option to a facade for consistency:

    $this->app->bind('option', function() {
        return new Option();
    });
    
  2. Eloquent Relationships Wrap relationships to avoid null leaks:

    class User extends Model {
        public function address(): Option {
            return Option::fromNullable($this->address);
        }
    }
    
  3. Blade Templates Create a helper for Option rendering:

    @if($option->isSome())
        {{ $option->unwrap() }}
    @else
        <p>No data available</p>
    @endif
    

Extension Points

  1. Custom Match Logic Extend Option with domain-specific matchers:

    class UserOption extends Option {
        public function isActive(): bool {
            return $this->match(
                fn() => false,
                fn($user) => $user->is_active
            );
        }
    }
    
  2. Integration with Collections Add Option-aware methods to Laravel Collections:

    Collection::macro('toOption', function() {
        return Option::fromNullable($this->first());
    });
    
  3. Custom Exceptions Override Option::expect() to throw domain-specific exceptions:

    Option::expect('User not found', new UserNotFoundException());
    
  4. Serialization Implement JsonSerializable for Option to support API responses:

    class Option implements JsonSerializable {
        public function jsonSerialize() {
            return $this->match(
                fn() => null,
                fn($value) => $value
            );
        }
    }
    

Laravel-Specific Tips

  1. Form Request Validation Use Option to handle optional fields:

    public function rules() {
        return [
            'email' => 'nullable|email',
            'shipping_address' => 'nullable|string',
        ];
    }
    
    public function withValidator($validator) {
        $validator->after(function($validator) {
            $shippingAddress = Option::fromNullable($this->shipping_address);
            if ($shippingAddress->isSome()) {
                // Validate address format
            }
        });
    }
    
  2. Event Handling Use Option in event listeners to handle missing data:

    public function handle(OrderCreated $event) {
        $userOption = Option::fromNullable($event->order->user);
        if ($userOption->isSome()) {
            // Send notification
        }
    }
    
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
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