Skip to content

Quick start

This is the smallest working Warrant setup: one schema, one rule, one resolver, and the checks that use them. We’ll gate a Document model so a user can only view and update their own rows.

A schema declares the vocabulary for one resource — the abilities that exist and the conditions a rule may test. It doesn’t decide anything; it just teaches each condition how to emit SQL.

namespace App\Warrant;
use App\Models\Document;
use Illuminate\Contracts\Database\Query\Builder;
use Warrant\Ability;
use Warrant\Schema\Conditions\TargetedConditionContext;
use Warrant\Schema\WarrantSchema;
use Warrant\TargetedCondition;
class DocumentSchema extends WarrantSchema
{
public const model = Document::class;
#[Ability] public const VIEW = 'view';
#[Ability] public const UPDATE = 'update';
// A targeted condition narrows WHICH rows the user matches.
// The method name `isSelf` becomes the rule name `is_self`.
#[TargetedCondition]
public function isSelf(TargetedConditionContext $c): Builder
{
return $c->query->whereRaw(
'documents.user_id = ?',
[$c->user->getAuthIdentifier()],
);
}
}

Rules are plain strings in Warrant’s rule language. This one grants view and update on the user’s own rows:

if is_self they can view, update

The resolver is the glue between your access model and Warrant. At request time it returns the rules that apply to the current user for a given resource. Warrant ships no default — this small class is required.

namespace App\Warrant;
use Warrant\RuleResolutionContext;
use Warrant\RuleResolver;
use Warrant\RuleSyntaxTree\WarrantRuleSet;
class DatabaseRuleResolver implements RuleResolver
{
public function resolve(RuleResolutionContext $context): WarrantRuleSet
{
// In a real app you'd look these rules up per user/role/tenant.
// Here we return the same rule for everyone, for the documents schema.
return WarrantRuleSet::fromSyntax(
$context->schemaKey,
'if is_self they can view, update',
);
}
}

Point Warrant at the resolver and register the schema in config/warrant.php:

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

Add the trait to the model and tell it which schema governs it:

use Illuminate\Database\Eloquent\Model;
use Warrant\HasWarrantSchema;
class Document extends Model
{
use HasWarrantSchema;
public function warrantSchema(): string
{
return \App\Warrant\DocumentSchema::class;
}
}

All three trace back to that one rule:

// "Which documents can the current user update?" — a WHERE clause, paginates.
$editable = Document::query()->hasAbility('update')->paginate();
// "What can they do to each row?" — one computed column, one query.
$rows = Document::query()->selectAbilities()->get();
$rows->first()->abilities; // e.g. ['view', 'update']
// "Can they update this specific one?" — a scoped EXISTS.
Document::userHasAbilities('update', $document); // bool

That’s the whole loop. From here: