my-com/laravel-conditional-actions
A Laravel package for defining conditional actions: attach rules to actions and run them only when their conditions are met. It helps keep decision logic organized, reusable, and easy to integrate into Laravel apps with a simple, expressive API for common workflows.
Treat this package as a rules engine: you define conditions (predicates over some context) and actions (side effects), persist the pairing as data, and mutate that data through an HTTP API instead of deploying code. The day-to-day value is letting ops/product change "when X and Y, do Z" behavior without a release.
Minimum steps:
composer require my-com/laravel-conditional-actions in a scratch branch and immediately check the resolved constraints in composer.lock. If you're on Laravel 9+, expect to fork and patch the service provider rather than install cleanly.php artisan migrate. Read the generated migration before running it — the schema tells you more about the package's mental model than the README does.If step 1 fails and you don't want to maintain a fork, stop here and reimplement the two-contract pattern yourself — it's a few hundred lines and you'll own the upgrade path.
Trigger from domain events, not controllers. Evaluation belongs in a listener or observer (OrderPlaced, UserUpdated), so every entry point — API, queue, console — gets the same rule behavior. Pass a well-defined context object (the model plus relevant scalars), not the whole request.
Keep actions thin and idempotent. An action should resolve a job or service and hand off. If the package retries or a rule is evaluated twice, a non-idempotent action (sending mail inline, incrementing a counter) will bite you. Prefer dispatch(new ...) and let the queue own retries.
Isolate failures per action. Wrap evaluation so one throwing action doesn't abort the rest of the chain; log the rule ID, context hash, and exception. If the package doesn't do this for you, write a thin evaluator service around it.
Version your rules as data. Seed a baseline rule set in a seeder so fresh environments and CI aren't empty. Store the rule payload as JSON with a schema version, and validate at the API boundary — an unvalidated condition config is an unhandled exception waiting for a Friday deploy.
Cache the rule set, invalidate on write. Rules are read on every evaluation and written rarely. A tagged cache keyed by the rule table, busted by the API write endpoint or a model observer, removes the query from the hot path.
Testing pattern: feature-test the API (create rule → assert persisted shape), then unit-test each action in isolation with a fake context. Use RefreshDatabase and a factory for the rule model; don't test the engine through your checkout flow.
Good fits: approval routing, discount/pricing adjustments, notification targeting, feature gating, escalation thresholds. Bad fits: invariants that must never be violated (accounting totals, authz), anything needing sub-10ms latency, anything a code reviewer must see in a diff.
vendor/, that's your signal to vendor the code into app/.RuleEvaluator interface in your own code on day one. If (when) you replace the package, only the binding changes.How can I help you explore Laravel packages today?