astrotomic/php-conditional-proxy
A lightweight conditional proxy for PHP that lets you chain method calls on any object and execute them only when your conditions pass. Use when/unless-style checks to keep optional logic clean, fluent, and free of scattered if statements.
Install the package with Composer:
composer require astrotomic/php-conditional-proxy
There is no meaningful Laravel configuration step: this is a small, framework-agnostic utility. Look first at the ConditionalProxy class and the HasConditionalProxy trait in the package source. The core idea is simple: wrap a target object, provide a boolean condition, and every proxied method call is either forwarded to the target or silently skipped while still returning the proxy so the chain continues.
The fastest first use case is conditionally mutating a fluent builder without scattering if statements:
use Astrotomic\ConditionalProxy\ConditionalProxy;
$query = Post::query();
(new ConditionalProxy($request->user()->isAdmin(), $query))
->withTrashed()
->whereNotNull('published_at');
return $query->latest()->get();
The important point in that example is that the underlying $query is mutated when the condition is true, while the terminal get() is called on the real query object, not on the proxy. That pattern avoids most of the proxy’s sharp edges and is the best default for day-to-day Laravel work.
You can also add the package’s HasConditionalProxy trait to a class that owns fluent methods, which gives you a when($condition) entry point:
use Astrotomic\ConditionalProxy\ConditionalProxy;
$this->when($user->isAdmin())
->audit('post.deleted', $post)
->flushCache();
Mutate a shared fluent object, then call the terminal method normally. This is the safest pattern for Eloquent, the query builder, migrations, HTTP clients, and custom builders:
$query = Invoice::query();
(new ConditionalProxy($includeDrafts, $query))
->where('status', 'draft')
->orWhereNull('sent_at');
$invoices = $query->paginate();
The proxy only handles conditional calls; the real object remains available for return values, pagination, get(), first(), and so on.
Use the trait for service/action classes. If a class exposes a fluent API of its own, the trait keeps call sites clean:
class PublishPost
{
use HasConditionalProxy;
public function handle(Post $post, bool $notify): void
{
$post->publish();
$this->when($notify)
->sendNewsletter($post)
->pingSearchIndex($post);
}
}
Drive behavior from configuration and feature flags. Conditional proxies are a good fit for optional side effects where the fluent chain reads better than nested conditionals:
$this->when(config('features.audit_log'))
->record('post.published', $post)
->tag('publishing');
Combine with dependency injection at the call site. Resolve the real service from the container, then wrap only where the condition is known:
$mailer = app(NewsletterMailer::class);
(new ConditionalProxy($shouldSend, $mailer))
->to($user->email)
->subject($subject);
Use it for conditional cross-cutting calls, not for branches whose return value matters. The proxy is strongest when the called methods are fluent, mutating, or side-effect-oriented. If you need to branch on a returned value, use a normal if or Laravel’s native when() with a callback.
->get(), ->first(), ->save(), or any method that should return a meaningful value through a false-condition proxy returns the proxy rather than a result. Keep a reference to the underlying object and run terminal operations against it.__call. Add @method annotations or a @mixin annotation for the wrapped class in your own wrapper types. Without this, PHPStan, Psalm, and IDE completion will treat proxied calls as magic.Post, Builder, or another concrete class will not accept the proxy itself. Pass the real object where a concrete type is required.__call. Do not expect proxy-control methods to be forwarded to the target.when() method, for example because it extends a class or uses another trait with that name, the existing method can win over the trait method. Alias the trait method or instantiate ConditionalProxy directly.if at public boundaries. Inside a fluent chain the proxy improves readability; at the edge of a method, an explicit conditional is usually clearer and less surprising for the next developer.How can I help you explore Laravel packages today?