aegisora/any-of-rule-guardian

Any Of Rule Guardian provides a simple shortcut for any-of validation using aegisora/guardian and aegisora/any-of-rule.

Maintainers

Package info

github.com/Aegisora/any-of-rule-guardian

pkg:composer/aegisora/any-of-rule-guardian

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-15 13:14 UTC

This package is auto-updated.

Last update: 2026-08-15 13:16:25 UTC


README

Latest Version Total Downloads Code Coverage Badge Software License PHPStan Badge

Any Of Rule Guardian provides a simple shortcut for "any of" validation using aegisora/guardian and aegisora/any-of-rule.

It is designed for cases where you want to quickly check whether at least one of several rule contexts is valid, without manually building an AnyOfRule and a validation pipeline by hand.

This package is built on top of:

โœจ Features

  • ๐Ÿ”น Simple shortcut API for AnyOfRule
  • ๐Ÿ”น Validates that at least one of the provided rule contexts passes (logical OR)
  • ๐Ÿ”น Accepts an arbitrary number of RuleContext objects
  • ๐Ÿ”น Uses aegisora/guardian internally
  • ๐Ÿ”น Uses aegisora/any-of-rule internally
  • ๐Ÿ”น Supports a custom validation exception
  • ๐Ÿ”น Ships dedicated, typed exceptions for invalid usage
  • ๐Ÿ”น Keeps rule execution errors separated from validation errors
  • ๐Ÿ”น Fully compatible with the Aegisora ecosystem
  • ๐Ÿ”น Ready to use out of the box

๐Ÿ“ฆ Installation

composer require aegisora/any-of-rule-guardian

๐Ÿš€ Core Concept

This package wraps the common "any of" validation flow:

$guardian->check(
    RuleContextCollection::create($ruleContextA, $ruleContextB),
    AnyOfRule::create(),
    new NoneMatchedException()
);

into a dedicated shortcut class:

$anyOfRuleGuardian->check($ruleContextA, $ruleContextB, new NoneMatchedException());

Instead of manually assembling a RuleContextCollection, creating an AnyOfRule, and passing them to Guardian, you can use AnyOfRuleGuardian directly.

๐Ÿ—๏ธ Basic Usage

use Aegisora\Guardian\Guardian;
use Aegisora\Guardian\Exceptions\GuardianValidationException;
use Aegisora\RuleContract\Models\RuleContext;
use Aegisora\RuleGuardians\AnyOfRule\AnyOfRuleGuardian;

$guardian = new Guardian();

$anyOfRuleGuardian = new AnyOfRuleGuardian($guardian);

$ruleContextA = RuleContext::createFromValue($ruleA, $valueA);
$ruleContextB = RuleContext::createFromValue($ruleB, $valueB);

try {
    $anyOfRuleGuardian->check($ruleContextA, $ruleContextB);
    // at least one rule context is valid
} catch (GuardianValidationException $exception) {
    // none of the rule contexts are valid
}

Validation passes as soon as one of the provided rule contexts is valid, and fails only when all of them are invalid.

โœ… How "any of" works

You pass one or more RuleContext objects. Each context pairs a rule with the value it should validate:

// short form (aegisora/rule-contract >= 1.2.0)
$ruleContext = RuleContext::createFromValue($rule, $value);

// explicit form
$ruleContext = RuleContext::create($rule, Context::create($value));

Then check() succeeds if any of them is valid:

$anyOfRuleGuardian->check($ruleContextA);                 // one context
$anyOfRuleGuardian->check($ruleContextA, $ruleContextB);  // many contexts

At least one RuleContext is required. Calling check() with no rule context throws an invalid-usage exception.

๐Ÿงฉ Usage with Custom Exception

You may provide your own exception for validation failure. It must be the last argument.

use Aegisora\Guardian\Guardian;
use Aegisora\RuleContract\Models\RuleContext;
use Aegisora\RuleGuardians\AnyOfRule\AnyOfRuleGuardian;
use App\Exceptions\NoneMatchedException;

$guardian = new Guardian();

$anyOfRuleGuardian = new AnyOfRuleGuardian($guardian);

$anyOfRuleGuardian->check(
    RuleContext::createFromValue($ruleA, $valueA),
    RuleContext::createFromValue($ruleB, $valueB),
    new NoneMatchedException()
);

If none of the rule contexts are valid, the provided exception will be thrown instead of GuardianValidationException.

This is useful when validation errors should have domain-specific meaning.

๐Ÿงช Example in Application Service

use Aegisora\RuleContract\Models\RuleContext;
use Aegisora\RuleGuardians\AnyOfRule\AnyOfRuleGuardian;
use App\Exceptions\PaymentMethodRequiredException;

final class CheckoutService
{
    private AnyOfRuleGuardian $anyOfRuleGuardian;

    public function __construct(
        AnyOfRuleGuardian $anyOfRuleGuardian
    ) {
        $this->anyOfRuleGuardian = $anyOfRuleGuardian;
    }

    public function ensurePayable(Order $order): void
    {
        $this->anyOfRuleGuardian->check(
            RuleContext::createFromValue($this->hasStoredCardRule, $order),
            RuleContext::createFromValue($this->hasEnoughBalanceRule, $order),
            new PaymentMethodRequiredException()
        );

        // business logic for a payable order
    }
}

๐Ÿšจ Exceptions

The package raises two kinds of exceptions:

  • Validation / execution exceptions delegated to Guardian (the outcome of running the rule).
  • Usage exceptions owned by this package (the arguments passed to check() are invalid).

GuardianValidationException

Thrown when validation fails (all rule contexts are invalid) and no custom exception is provided.

The rule code for failed "any of" validation is any_of_rule.

use Aegisora\Guardian\Exceptions\GuardianValidationException;

try {
    $anyOfRuleGuardian->check($ruleContextA, $ruleContextB);
} catch (GuardianValidationException $exception) {
    echo $exception->getRuleCode(); // "any_of_rule"
}

Custom exception

When a custom exception is passed as the last argument, it is thrown instead of GuardianValidationException on validation failure.

use App\Exceptions\NoneMatchedException;

try {
    $anyOfRuleGuardian->check($ruleContextA, $ruleContextB, new NoneMatchedException());
} catch (NoneMatchedException $exception) {
    // domain-specific handling
}

GuardianExecutingRuleException

Thrown when one of the underlying rules fails to execute (raises a RuleException during validation), as opposed to simply reporting an invalid result.

use Aegisora\Guardian\Exceptions\GuardianExecutingRuleException;

try {
    $anyOfRuleGuardian->check($ruleContextA);
} catch (GuardianExecutingRuleException $exception) {
    // a rule could not be executed
}

Usage exceptions

These exceptions signal that check() was called incorrectly. They all extend the abstract base AnyOfRuleGuardianException, so you can catch the whole group at once:

use Aegisora\RuleGuardians\AnyOfRule\Exceptions\AnyOfRuleGuardianException;

try {
    $anyOfRuleGuardian->check(...$arguments);
} catch (AnyOfRuleGuardianException $exception) {
    // check() was called with invalid arguments
}
Exception Thrown when
MissingRuleContextException No RuleContext was provided (empty call, or only a Throwable).
ExceptionMustBeLastException A Throwable was passed in a non-last position, or more than one Throwable was provided.
UnexpectedArgumentException An argument is neither a RuleContext nor a Throwable (e.g. int, string, null, array, arbitrary object).

All three extend AnyOfRuleGuardianException.

๐Ÿงฉ API

AnyOfRuleGuardian::check()

/**
 * @param RuleContext|\Throwable ...$arguments
 * @throws GuardianExecutingRuleException
 * @throws GuardianValidationException
 * @throws ExceptionMustBeLastException
 * @throws UnexpectedArgumentException
 * @throws MissingRuleContextException
 * @throws \Throwable
 */
public function check(...$arguments): void

Validates that at least one of the provided rule contexts is valid.

Arguments:

  • ...$arguments โ€” one or more RuleContext objects to validate, optionally followed by a single \Throwable, as the last argument, to be thrown on failure.

The method returns void. It communicates results through exceptions only โ€” it returns nothing on success and throws on failure:

  • GuardianValidationException โ€” all rule contexts are invalid and no custom exception was provided
  • the provided custom exception โ€” all rule contexts are invalid and a custom exception was passed
  • GuardianExecutingRuleException โ€” an underlying rule failed to execute
  • MissingRuleContextException / ExceptionMustBeLastException / UnexpectedArgumentException โ€” the arguments passed to check() are invalid

Valid calls:

$anyOfRuleGuardian->check($ruleContextA);
$anyOfRuleGuardian->check($ruleContextA, $ruleContextB);
$anyOfRuleGuardian->check($ruleContextA, $ruleContextB, new NoneMatchedException());

Spreading an array of rule contexts (note: a positional argument after unpacking is not allowed, so append the exception to the unpacked list):

$anyOfRuleGuardian->check(...$ruleContexts);
$anyOfRuleGuardian->check(...[...$ruleContexts, new NoneMatchedException()]);

๐Ÿ›๏ธ Architecture

This package is a small shortcut layer over the Aegisora validation pipeline.

Flow:

  1. AnyOfRuleGuardian::check() is called with rule contexts and an optional exception
  2. The arguments are validated (typed usage exceptions are thrown on misuse)
  3. A RuleContextCollection is assembled and an AnyOfRule is created
  4. Guardian executes the rule
  5. If at least one context is valid, execution continues normally
  6. If all contexts are invalid, the custom exception or GuardianValidationException is thrown
  7. If a rule fails to execute, GuardianExecutingRuleException is thrown

Internal flow:

RuleContexts โ†’ AnyOfRuleGuardian โ†’ Guardian โ†’ AnyOfRule โ†’ Result โ†’ Exception

๐Ÿ”— Related Packages

โš–๏ธ License

This package is open-source and licensed under the MIT License. See the LICENSE for details.

๐ŸŒฑ Contributing

Contributions are welcome and greatly appreciated!. See the CONTRIBUTING for details.

๐ŸŒŸ Support

If you find this project useful, please consider giving it a star on GitHub!

It helps the project grow and motivates further development.