elcodi/rule-bundle
Rule Bundle is part of Elcodi, an e-commerce suite of Symfony components and bundles under MIT license. It provides a flexible rules engine for defining conditions and actions in your store. Note: read-only package; use the main elcodi/elcodi repo for issues, questions, and pull requests.
Before writing a line of code, understand what you're actually installing. elcodi/rule-bundle is a read-only Symfony bundle — a subtree split of the elcodi/elcodi monorepo. It will not boot under a Laravel kernel: it ships a Symfony Bundle class, DI extensions, compiler passes, and YAML/XML service definitions. composer require elcodi/rule-bundle in a Laravel 10/11 app will very likely fail on Symfony/Doctrine version constraints, and even if it installs, there is no service provider to register it.
So the realistic "getting started" is a spike, not an install:
composer.json): git clone https://github.com/elcodi/RuleBundle /tmp/rule-bundle.Resources/config/services.yml first. The bundle is ~90% wiring; the actual behavior lives in the elcodi/rule component (a separate package). Note the service IDs for the rule manager, factory, and repository — those are the seams you'd port.Rule, RuleGroup, Expression, and Action set of entities/interfaces. This is the part worth keeping. Everything around it is Symfony plumbing you'll discard.First use case: model a "10% off carts over $100 for logged-in customers" promotion as data, not code — a named rule, a condition group, and an action. That single scenario tells you whether the package's decomposition is richer than what you'd write yourself in a week (it usually isn't, for a v1).
If you proceed, do it as internal code you own, not as a dependency.
Extraction pattern (recommended). Fork the subtree into packages/rule-engine via a Composer path repository, strip all Symfony DI wiring, and wrap the domain classes in a Laravel service provider:
// app/Providers/RuleEngineServiceProvider.php
public function register(): void
{
$this->app->bind(RuleRepository::class, EloquentRuleRepository::class);
$this->app->singleton(RuleEvaluator::class, fn ($app) =>
new RuleEvaluator($app->make(RuleRepository::class))
);
}
Preserve the evaluation core verbatim so you can diff behavior against the original while you port.
Persistence pattern. Replace Doctrine repositories with Eloquent. The persisted model is what matters — rules, groups, and their condition/action rows:
rules (id, name, enabled, priority)rule_groups (id, rule_id, combinator: all|any)rule_conditions (id, group_id, type, operator, value)rule_actions (id, rule_id, type, payload JSON)Keep rule definitions data-driven — that is the whole point of the decomposition. Merchandisers edit rows; engineers ship condition/action types.
Evaluation workflow (the loop you'll live in):
Integration tip. Keep the engine framework-agnostic inside — plain PHP, no Illuminate or Symfony imports in the evaluation core. Your service provider and Eloquent repository are the only Laravel-aware edges. This makes it testable with plain PHPUnit and portable if you ever swap.
Condition/action registry. Follow the package's extensibility idea: register condition and action types by string key in a registry, resolved from the container. Adding a promotion type becomes "add a class + register a key," not "edit a switch."
all/any), and interaction between overlapping rules are the bug factory. Write tests that assert the final applied result, not just individual condition evaluations.How can I help you explore Laravel packages today?