omitobisam/conditional
Conditional is a Laravel package for expressive conditional validation. It lets you apply rules only when specified conditions are met, keeping complex form request logic readable, reusable, and easy to maintain.
Install the package via Composer:
composer require omitobisam/conditional
You will land on the 2.0.0 major release. The published notes for it are thin — a 1.x→master merge, a new GitHub Actions workflow (php.yml), and the 2.x branch merge — so before you write code against it, open the compare view (1.3.0...2.0.0 on the repo) and the composer.json in the tagged release to confirm the PHP constraint and that the terminal method is still resolve(). The 1.x-era README snippets floating around the web may not match.
There is no service provider or config file to publish — this is a pure utility library.
The first place to look is the package namespace (Omitobisam\Conditional) and the README usage block for the 2.0.0 tag specifically; the entry point is a fluent builder that turns a boolean into an expressive if/else chain instead of a ternary or nested if.
Minimal first use case — replace a nested ternary in a controller or presenter:
use Omitobisam\Conditional\Conditional;
return Conditional::when($user->isAdmin())
->then(fn () => $this->adminDashboard())
->else(fn () => $this->userDashboard())
->resolve();
If that smoke test fails on 2.0.0, the failure is almost certainly a renamed entry point or terminal method rather than a logic error — check the release diff before debugging your own closures.
Guard-style branch selection. Because branches are closures, only the winning branch executes. This makes it a drop-in replacement for if/else where the branches perform side effects or expensive work:
Conditional::when($order->isPaid())
->then(fn () => $this->ship($order))
->else(fn () => $this->requestPayment($order))
->resolve();
Chaining multiple conditions. The package really earns its keep when you would otherwise nest ternaries or use elseif. Chain when() calls in priority order and finish with a single fallback:
return Conditional::when($score >= 90)
->then(fn () => 'A')
->when($score >= 80)
->then(fn () => 'B')
->when($score >= 70)
->then(fn () => 'C')
->else(fn () => 'F')
->resolve();
HTTP response builders. It is very natural in controllers returning different response types:
return Conditional::when($request->expectsJson())
->then(fn () => response()->json($data))
->else(fn () => view('dashboard', compact('data')))
->resolve();
Feature flags and config-driven behavior. Combine with config() or a feature-flag service to pick implementations. Wrap the resolution in a small named method in your service class so the fluent call stays testable and the flag source stays injectable.
API Resources / presenters. Use it inside toArray() to keep transformation logic flat instead of stacking ternaries for labels, badge colors, and visibility.
Composition tip. Keep each branch closure short; if a branch grows past a few lines, extract it into a private method. The fluent API is for readability, not for hiding multi-step logic.
Testing tip. Assert the resolved value, not the internals. Because branches are closures, a feature test against the endpoint naturally covers the correct-branch path; a unit test can call resolve() with mocked conditions directly. On 2.0.0, add one cheap assertion-level test that exercises the full when/then/else/resolve chain — it doubles as your upgrade canary if a later 2.x patch renames something.
1.3.0...2.0.0 compare, confirm the public method names, and pin the exact version ("omitobisam/conditional": "2.0.0") rather than using ^2.0 until you've validated the API surface you depend on..github/workflows/php.yml; that matrix is now the most reliable statement of which PHP versions are actually tested. Older docs mentioning 7.4 may be stale for 2.0.0 — verify against composer.json's require.php before putting this in a legacy codebase.->then($this->ship($order)) invokes ship() immediately, defeating the purpose and often causing side effects in the untaken branch. Always wrap: ->then(fn () => $this->ship($order)).resolve vs get, else vs otherwise — small packages rename terminals across majors. After upgrading to 2.0.0, grep your codebase for every Conditional:: call site and let the smoke test in Getting Started confirm the chain still reads as documented for this tag.resolve() returns whatever the taken closure returns, so mixing Response and View in a controller is fine at PHP level but confusing to consumers. Enforce a common type or Responsable interface at the boundary.when()->then()->when()->then()->else() chain, the first truthy condition wins. Put the most specific/least likely conditions first, otherwise a broad when($user) shadows everything after it.->then(...) with ->then(fn () => 'A'), 'B', etc. and dump the result — the winning branch becomes immediately visible without xdebug.App\Support\Conditional) so you can swap it for a plain match or ternary if the package stalls or the next major surprises you.resolve() return type. Consider @mixin/generics in your wrapper to keep IDE autocompletion. Re-run the analyzer after the 2.0.0 upgrade — a major bump can change inferred signatures.match is the built-in alternative. For simple equality-based branching on PHP 8, match is native, faster, and free of dependency risk. Reach for this package when you need closures, chained boolean conditions, or a Laravel-friendly fallback that reads better than nested ternaries.How can I help you explore Laravel packages today?