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

Logical Permissions Laravel Package

ordermind/logical-permissions

A Laravel package for expressive, logic-based permission checks. Define roles and permissions with AND/OR/NOT rules, then authorize users against complex conditions using a clean API. Ideal for apps needing flexible access control beyond simple role checks.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a plain PHP library, not a Laravel package — there is no service provider, facade, config file, or migration to publish. Treat it as a pure dependency and wire it into Laravel yourself.

composer require ordermind/logical-permissions-php

Where to look first:

  1. The README's logic-gate section — it documents the array shape (type + permissions keys) and the built-in gates.
  2. src/ — specifically the LogicalPermissions class (the single entry point) and the logic-gate classes. Confirm the exact method names and the custom-gate callback signature there before you write wrappers; the surface area is small enough to read in ten minutes.
  3. No Laravel docs exist, so anything you learn about integration you will be writing yourself.

First use case: the "admin OR owner, but not suspended" check that everyone writes badly inside a policy. Instead of nested ifs:

$permission = [
    'type' => 'and',
    'permissions' => [
        ['type' => 'not', 'permissions' => ['suspended']],
        ['type' => 'or', 'permissions' => ['admin', 'owner']],
    ],
];
$logicalPermissions->checkPermission($permission, $context);

The value proposition is that the permission tree is data — you can store it in config, in a database column, or return it from a policy — rather than control flow. If your rules never get deeper than two levels, plain Laravel Gate closures are simpler and you should keep them.

Implementation Patterns

Bind it as a singleton and register custom gates at boot.

// app/Providers/AppServiceProvider.php
$this->app->singleton(LogicalPermissions::class, function () {
    $lp = new LogicalPermissions();
    $lp->setDefaultLogicGate('and');

    // Custom gates: majority vote, permission-level thresholds, time windows…
    $lp->addLogicGate('at_least_two', function (array $permissions, array $context, callable $check) {
        $passed = count(array_filter($permissions, fn ($p) => $check($p, $context)));
        return $passed >= 2;
    });

    return $lp;
});

Bridge to Laravel's Gate so @can, middleware, and policies keep working. The library should be an implementation detail, not something controllers call directly:

Gate::before(fn ($user) => $user->isSuperAdmin() ? true : null);

Gate::define('edit-post', function (User $user, Post $post) {
    $tree = config("permissions.posts.edit"); // array, not code
    return app(LogicalPermissions::class)->checkPermission($tree, [
        'user' => $user,
        'post' => $post,
        'permissions' => $user->permissionFlags(), // resolves names to booleans
    ]);
});

Resolve permission names to booleans in one place. The library evaluates structure; you decide what "admin" means. A single permissionFlags() method or a PermissionContext value object that maps names to booleans keeps the tree declarative and testable — the permission array becomes a pure function of the context, which makes unit tests trivial and free of actingAs().

Store trees in config, validate them at boot. config/permissions.php returning nested arrays is the sweet spot: reviewable in PRs, cacheable with php artisan config:cache, and diffable. If you must store trees in the database (per-tenant rules, admin-editable), write a recursive validator that asserts every type is a registered gate and every leaf is a string, and run it on save — otherwise a bad row becomes a runtime exception in production.

Custom gates are the real extension point. not/and/or covers maybe 80% of cases; the remaining 20% (quorum approvals, "any two of these five roles", time-of-day restrictions, tenant-scoped overrides) is where a custom gate pays for itself versus hand-written policy methods.

Keep policies thin. A policy method that reads return $this->lp->checkPermission($tree, $ctx); is ideal. Resist the urge to mix imperative checks and tree checks in the same method — reviewers can't reason about the combination.

Gotchas and Tips

  • Zero Laravel integration means zero guardrails. No facade, no config/logical-permissions.php, no auto-discovery. Every convenience in this list is code you own and must test.
  • Be explicit about the default logic gate. If you never call the setter, an array with no type falls back to whatever the library defaults to — and mixed behaviour between "list of permissions" and "single permission" is a subtle source of bugs. Set it once at boot and document it.
  • The bypass/super-admin concept bypasses the whole tree. That is exactly what you want for super admins and exactly what you don't want for a not gate guarding a dangerous action. Verify the bypass semantics against the README before relying on it, and prefer Laravel's Gate::before for the super-admin case so the bypass is visible in one place instead of buried in the library.
  • Unknown gate types throw at check time, not at definition time. A typo in a config array is a 500 on a Tuesday. Validate trees at boot (php artisan command or a test that iterates config('permissions')) so malformed config fails CI instead of production.
  • Deep nesting is undebuggable. More than three levels and nobody can predict the outcome by reading. Add a small helper that renders a tree to an indented string, or log the context when a check fails in local/staging.
  • Never accept a permission tree from the request. The tree describes policy; letting a client post one is equivalent to letting them post an if statement. Trees come from config or validated admin input only.
  • Recursive evaluation is cheap but not free. Fine per-request; if you're evaluating hundreds of trees in a loop (e.g., listing 200 posts), compute flags once per user and hoist the context out of the loop, or memoise results per (tree, user) pair.
  • Composer/PHP constraints are yours to manage. With 4 stars and 0 dependents, expect no ecosystem pressure to keep up with PHP releases — pin the version, and budget a few hours if you ever need to patch a gate yourself (MIT licence means you can, and possibly should, vendor it).
  • The honest recommendation: adopt this only if your permission rules are genuinely combinatorial and change often enough that non-developers or config edits need to express them. For a handful of role checks, Laravel's Gate closures are better reviewed, better documented, and better supported — this library's whole value is turning access rules into data, and if you don't need that, you're paying an integration cost for nothing.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums