- How does the Option type improve null safety in Laravel compared to using nullable types (e.g., `?User`)?
- The Option type forces explicit handling of absence (None) or presence (Some), eliminating accidental null dereferencing. Unlike nullable types, it enforces methods like `map()` or `filter()` to safely chain operations, reducing `NullPointerException`-like bugs in PHP. For example, `$user->address->city->map(...)` fails fast if any step is None, whereas `$user->address->city` silently fails with null.
- Can I use this package with Laravel Eloquent models without breaking existing queries?
- Yes, wrap Eloquent results with `Option::fromNullable($model)` to convert null returns to explicit Some/None. For raw queries, manually wrap results (e.g., `Option::fromNullable(User::find(1))`). Existing queries remain unchanged, but you’ll need to update return type hints in repositories or services to `Option<User>` instead of `?User`.
- What Laravel versions and PHP versions does this package support?
- The package requires PHP 8.1+ (for named arguments and enums) and has no Laravel-specific dependencies, so it works with Laravel 9.x, 10.x, and 11.x. It’s framework-agnostic but designed to integrate with Laravel’s Collections, Eloquent, and service containers. Check the [PHP Standard Library docs](https://php-standard-library.dev) for exact version requirements.
- How do I migrate from nullable types to Option in an existing Laravel project?
- Start with a proof of concept: refactor one domain (e.g., `User` entity) to use `Option` for nullable fields, then update 1–2 services/controllers to use methods like `map()` or `filter()`. Use `Option::fromNullable()` as an adapter for legacy null returns. Gradually replace return type hints (e.g., `function getUser(): ?User` → `function getUser(): Option<User>`) and update static analysis tools like PHPStan to enforce the change.
- Does this package work with Laravel’s Blade templates, and how do I handle Option types in views?
- Blade templates can check Option types with `@if($option->isSome())` or `@unless($option->isNone())`. For cleaner syntax, create a Blade directive or helper like `@option($option, $default, $callback)`. Avoid direct property access (e.g., `$user->address`) and use `map()` or `unwrap()` to safely extract values. Example: `@option($user->address, null, 'address')` renders the address if present.
- What’s the performance impact of using Option vs. nullable types in Laravel?
- Option introduces minimal overhead: wrapping a value in `Some` adds a tiny object instantiation cost (~1–2 microseconds), while `None` is a lightweight singleton. For most Laravel use cases (e.g., API responses, domain logic), this is negligible. Benchmark critical paths if performance is a concern, but the trade-off is safer code and explicit intent.
- How do I integrate Option with Laravel’s service container and dependency injection?
- Bind Option types in `AppServiceProvider` using `app()->bind(Option::class, fn() => new Option(...))` or resolve them directly (e.g., `new Option(Some::create($user))`). For constructors, type-hint `Option<User>` and inject `Some::create($user)` or `None::create()`. Laravel’s container will resolve the binding automatically, just like native classes.
- Are there alternatives to Option for handling optional data in Laravel?
- Alternatives include: nullable types (`?User`), `null` with `@phpstan-ignore`, or third-party libraries like `league/value-object` or `spatie/array-to-object`. Option is best for projects using functional patterns (e.g., DDD, immutable data) or where absence has semantic meaning (e.g., `User::findByEmail()`). For simple CRUD, nullable types may suffice, but Option reduces bugs and clarifies intent.
- How do I test code using Option in Laravel’s PHPUnit tests?
- Use `assertTrue($option->isSome())` or `assertTrue($option->isNone())` to verify presence/absence. For values, use `assertSame(Some::create($expected), $option)` or `assertSame(None::create(), $option)`. Mock dependencies returning `Option` with `Option::fromNullable($mockResult)`. Example: `$this->assertEquals('New York', $option->map(fn(User $u) => $u->address->city)->unwrap());`
- Will this package conflict with Laravel’s magic methods (e.g., `$user->address->city`)?
- Yes, but intentionally: Option enforces explicit chaining (e.g., `$user->address()->map(fn(User $u) => $u->city)`). To avoid conflicts, use `unwrap()` or `unwrapOr()` to extract values before magic methods. For Eloquent, prefer `Option::fromNullable($user->address)` over chaining. This trade-off prevents silent null failures but requires slight adjustments to Laravel’s magic method patterns.