Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Ruler Bundle Laravel Package

hoathis/symfony-ruler-bundle

Symfony bundle that integrates Hoa\Ruler, a lightweight business rules engine, into Symfony applications. Define and evaluate dynamic rules with service configuration and dependency injection support for cleaner, configurable business logic.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you composer require. This bundle is archived (this 0.16.12.05 snapshot is the final state — the only post-2015 commit is a dependency bump dated Dec 2016), targets Symfony 2.7/2.8/3.0, and depends on hoa/ruler — a component from the Hoa project, which itself is discontinued. On Symfony 4+ or PHP 8, installation will likely fail dependency resolution. If you must use it, treat it as a fork-and-maintain dependency: clone it into src/ or a VCS repository, pin the Symfony/Hoa constraints yourself, and expect to patch service definitions. If you're starting fresh, symfony/expression-language is the direct functional replacement and is actively maintained.

The good news from the changelog: it is Symfony 3.0 ready (YAML definition fixes, config root fix landed for 3.0), so a Symfony 2.8/3.0-era stack is the sweet spot.

  1. Install:
    composer require hoathis/symfony-ruler-bundle
    
  2. Register the bundle in app/AppKernel.php. Note the namespace is Hoathis\SymfonyRulerBundle (the class was renamed from the original Hoa\RulerBundle — old blog posts will mislead you):
    $bundles = [
        // ...
        new Hoathis\SymfonyRulerBundle\HoathisSymfonyRulerBundle(),
    ];
    
  3. Find the service id first — it is hoathis.ruler, not hoa.ruler. A huge amount of stale documentation and StackOverflow answers reference hoa.ruler; that id does not exist in this release. The bundle's whole value proposition is that it pre-wires a Hoa\Ruler\Ruler instance for you under hoathis.ruler. Check Resources/config/services.xml for whether it's public on your Symfony version.
  4. First use case — evaluate a rule string against request/domain data. This release adds a factory method on Ruler to build a context, so you don't have to new one up manually:
    $ruler = $this->get('hoathis.ruler');
    
    $rule = 'user.age >= 18 and user.country in ("FR", "BE")';
    
    $context = $ruler->createContext();
    $context['user'] = $user; // array or object
    
    if ($ruler->assert($rule, $context)) {
        // allow
    }
    
    (If the factory helper isn't present in the exact build you have, fall back to new \Hoa\Ruler\Context() — the API is otherwise identical.)
  5. Read the upstream hoa/ruler README next — the bundle is thin glue; the DSL grammar, operators, and asserter API all live in the component. The bundle's README tells you almost nothing that the component doesn't.

Implementation Patterns

Inject, don't fetch. Wrap rule evaluation in your own service so the archived dependency is isolated to one class:

namespace AppBundle\Rules;

use Hoa\Ruler\Ruler;
use Hoa\Ruler\Context;

class RuleEvaluator
{
    private $ruler;

    public function __construct(Ruler $ruler)
    {
        $this->ruler = $ruler;
    }

    public function allows($rule, array $data)
    {
        $context = $this->ruler->createContext();
        foreach ($data as $k => $v) {
            $context[$k] = $v;
        }
        return $this->ruler->assert($rule, $context);
    }
}

Wire it with an explicit argument referencing hoathis.ruler — don't rely on autowiring, it won't work here.

Rules from the database. The classic use case is admin-editable business rules (discount eligibility, shipping gates, feature flags). Store the rule string in a column, cache the parsed model if you need throughput, and always validate the string on save by attempting an assert() against a dummy context — you want parse errors caught at authoring time, not at checkout time.

Assertions vs. plain evaluation. Ruler::assert() returns a boolean and is what you want for gates. Use the asserter to register your own domain operators:

$asserter = $this->ruler->getAsserter();
$asserter->setOperator('is_vip', function ($customer) {
    return $customer->hasRole('ROLE_VIP');
});
$asserter->setFunction('now', function () {
    return new \DateTime();
});

Register these in a compiler pass or a service that decorates the ruler, then inject that configured instance. The bundle has no config tree for custom operators — verify, but expect to do this yourself.

Lazy context values. Hoa\Ruler\Context accepts closures as values and only invokes them if the rule references them. For rules that touch expensive lookups (e.g. order.total > 100), register closures so unused branches don't query:

$context['order'] = function () use ($orderId) {
    return $this->orderRepo->find($orderId);
};

Profiler integration in dev. This release ships a WebDebugToolbar integration: when kernel.debug is enabled, the bundle swaps in a logged ruler that records evaluated rules and contexts, and exposes them in the profiler panel. Use it heavily while authoring rules — it's the fastest way to see how a stored rule was actually parsed and evaluated. Just be aware production uses the plain (unlogged) ruler, so what you see in dev is intentionally instrumented and has extra overhead.

Testing. Because the ruler is deterministic over a context, table-driven tests are the natural pattern: an array of [rule, context, expectedBool] cases, asserted against the evaluator service. This also doubles as your rule-authoring test suite for DB-stored rules.

Gotchas and Tips

  • Archived means frozen. No PHP 8 fixes, no Symfony 4+ support, no security patches. Any CVE in hoa/* or the Symfony 2/3 DI stack lands on you. Fork it, document the fork point, and put a migration ticket to Symfony ExpressionLanguage in the backlog on day one.
  • The service id is hoathis.ruler. This is the single most common integration mistake — most tutorials say hoa.ruler. If you get a "service not found" error, you likely copied a pre-rename example. The bundle namespace also changed to Hoathis\SymfonyRulerBundle, so old new Hoa\RulerBundle\HoaRulerBundle() kernels will fatal.
  • Service visibility. On Symfony 3.4+ services default to private; this bundle declares its services the old way. If get('hoathis.ruler') throws, either inject properly or check whether the service definition needs a public: true patch you'll have to maintain.
  • The debug logger is debug-only. Don't write application code that depends on LoggedRuler behavior or the profiler panel — in production you get the plain ruler. Anything you rely on for correctness must live in your own decorator, not the toolbar integration.
  • User-supplied rule strings are code. The DSL resolves function and operator names against the asserter. A whitelist-by-construction approach — separate asserter instances per rule class, each with only the operators that rule class needs — is safer than one global ruler that can call everything.
  • Silent typos. user.agee > 18 won't always fail loudly; depending on the asserter's handling of unknown identifiers it may resolve to null and evaluate false. Wrap evaluation and log parse/assert exceptions (Hoa\Ruler\Exception) rather than swallowing them.
  • Object vs. array access. Property access on objects goes through Hoa's context resolution; magic getters and ArrayAccess behave differently from plain public properties. Prefer passing arrays into the context for anything you persist, and add an integration test per entity you expose to rules.
  • Operator precedence and quoting. String literals use double quotes; comparison and in precedence follows the component grammar, not PHP's. When a stored rule starts behaving oddly, dump the parsed Hoa\Ruler\Model (or read the profiler panel) and inspect the tree before blaming the data.
  • Extension point is the asserter, not the bundle. Almost every customization question ("how do I add between?", "how do I make now injectable?") is answered by configuring Ruler::getAsserter() and using the bundled createContext() factory, not by touching the bundle. Keep a single RulerFactory service that builds and configures the instance, and you can swap the bundle out for a hand-wired hoa/ruler requirement — or for ExpressionLanguage — with one class change.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator