Skip to content

Providing rules

Rules are data. Warrant never invents them — it asks your resolver for them at request time. This is the seam where your access-control model meets Warrant.

Implement one method. Given a context, return the WarrantRuleSet that governs this user’s access to that resource:

use Warrant\RuleResolutionContext;
use Warrant\RuleResolver;
use Warrant\RuleSyntaxTree\WarrantRuleSet;
class DatabaseRuleResolver implements RuleResolver
{
public function resolve(RuleResolutionContext $context): WarrantRuleSet
{
// $context->user — the Authenticatable being checked (nullable)
// $context->schemaKey — e.g. 'documents'
// $context->schema — the schema class string
// $context->model — the model class string, or null (capability schema)
$grants = DB::table('role_permissions')
->where('role_id', $context->user->role_id)
->where('resource', $context->schemaKey)
->pluck('rule'); // ['if is_self they can view', ...]
return WarrantRuleSet::fromSyntax(
$context->schemaKey,
$grants->implode("\n"), // rules concatenate freely
);
}
}

Store rule strings in a table, compose them from role flags, read them from JWT claims — whatever fits. Warrant only cares that you return a WarrantRuleSet.

Three ways to construct a WarrantRuleSet. The first argument is always the schema (a model instance, a schema instance, or a schema/model class string, or a plain schema-key string):

Parse a string, resolving bindings inline:

WarrantRuleSet::fromSyntax('documents', 'if is_self they can view', $bindings = []);

Build individual WarrantRules and compose them. fromRules takes a variadic list or a single array (it flattens a mix of both), accepts builders directly, and takes no bindings (the rules are already resolved):

use Warrant\RuleSyntaxTree\WarrantRule;
$own = WarrantRule::fromSyntax('if is_self they can view, update');
$noDelete = WarrantRule::fromSyntax('they cannot delete');
WarrantRuleSet::fromRules('documents', $own, $noDelete);
WarrantRuleSet::fromRules('documents', [$own, $noDelete]); // equivalent

WarrantRuleSet::build hands you a factory; each $rule() call appends a builder:

WarrantRuleSet::build('documents', function ($rule) {
$rule()->if('is_self')->theyCan('view', 'update');
$rule()->theyCannot('delete');
});

If you want the parsed rules without a rule set:

use Warrant\RuleSyntaxTree\Parsing\WarrantParser;
$rules = WarrantParser::parse('if is_self they can view', $bindings = []); // WarrantRule[]
$one = WarrantParser::parseSingleRule('they cannot delete'); // WarrantRule

When a rule’s shape depends on runtime data — a list of team ids, a feature flag, values that don’t belong in a string — the fluent builder is often clearer than assembling DSL text. WarrantRule::build() produces the same AST the parser does, and nothing is serialized to a string, so arbitrary PHP values in condition parameters survive untouched:

use Warrant\RuleSyntaxTree\WarrantRule;
$rule = WarrantRule::build()
->if('is_self')
->orIf(fn ($c) => $c->if('is_manager')->andIf('in_region'))
->theyCan('view', 'update')
->toRule();

The builder is its own topic — connectives, parenthesized groups, dynamic composition, and splicing in DSL text are all covered in The rule builder.

A schema can declare rules always merged into the rule set, regardless of what the resolver returns, by overriding implicitRules(). They’re added to every resolved rule set before compilation, so they’re validated and obey deny-overrides exactly like resolver rules — and, like every rule, they’re still evaluated against the current user via their conditions:

use Warrant\RuleSyntaxTree\WarrantRule;
class DocumentSchema extends WarrantSchema
{
protected function implicitRules(): array
{
return [
WarrantRule::fromSyntax('if is_admin they can *'),
WarrantRule::fromSyntax('if is_suspended they cannot *'),
];
}
}

Because deny-overrides is order-independent, an implicit cannot beats any resolver-supplied can — ideal for baseline guarantees like an admin escape hatch or a suspension lockout.

Warrant ships no default resolver. Configure one in config/warrant.php, plus the list of schemas:

return [
'rule_resolver' => App\Warrant\DatabaseRuleResolver::class,
'schemas' => [
App\Warrant\DocumentSchema::class,
App\Warrant\ProjectSchema::class,
],
];