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

Laravel Permissions Redis Laravel Package

scabarcas/laravel-permissions-redis

A Laravel package that adds a Redis-backed permissions layer for fast authorization. Store and check roles and permissions with less database overhead, ideal for high-throughput apps needing quick access checks and simple integration with Laravel’s authorization ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up in five minutes.

composer require scabarcas/laravel-permissions-redis
php artisan vendor:publish --provider="Scabarcas\PermissionsRedis\PermissionsRedisServiceProvider"
php artisan migrate

Before you run anything, open composer.json for the package (or vendor/scabarcas/laravel-permissions-redis/composer.json) and confirm the Laravel/PHP constraints actually match your app. With 39 stars, 0 dependents and a single maintainer, you do not want to discover a version mismatch after you have already written @can directives across fifty Blade files.

Where to look first, in this order:

  1. config/permissions-redis.php — connection name, key prefix, TTL, and whether a DB fallback is enabled. This file dictates almost every gotcha below.
  2. The package's ServiceProvider — it tells you which contracts are bound (usually a PermissionRegistrar-style class and a repository) and therefore what you can swap out.
  3. The trait the README tells you to add to User — that is the public API surface you will actually type every day.

First use case: kill the N+1 on your authorization checks. The whole point of this package is that $user->can('posts.edit') should not hit MySQL. So the first thing to verify is that it actually does not:

// app/Models/User.php
use Scabarcas\PermissionsRedis\Traits\HasPermissions;

class User extends Authenticatable
{
    use HasPermissions;
}

Then seed a role and prove the cache is being used:

$user->assignRole('editor');

DB::enableQueryLog();
$user->hasPermissionTo('posts.edit'); // first call populates Redis
$user->hasPermissionTo('posts.publish'); // should log zero queries
dd(DB::getQueryLog());

If you see a query on the second call, the package is not doing its job for your setup — check the connection config before going further.

Implementation Patterns

Keep the database as the source of truth, Redis as a projection. The pattern that survives production is: roles/permissions/pivots live in MySQL; Redis holds a derived, per-user set of permission strings. Treat every write path as "write DB, then invalidate/rehydrate Redis." Never write permissions only into Redis — you will lose them on eviction and have no way to rebuild an audit trail.

Wrap the package behind your own authorization service. With 0 dependents and a low opportunity score, this is not a package you want leaking into 200 call sites. Bind your own interface and delegate:

// app/Contracts/Authorizer.php
interface Authorizer
{
    public function allows(User $user, string $ability): bool;
    public function flushFor(User $user): void;
}
// app/Services/RedisAuthorizer.php
class RedisAuthorizer implements Authorizer
{
    public function allows(User $user, string $ability): bool
    {
        return Cache::store('permissions')->remember(
            "perms:{$user->id}",
            now()->addHours(12),
            fn () => $user->getAllPermissions()->pluck('name')->all()
        ) && in_array($ability, ..., true);
    }

    public function flushFor(User $user): void
    {
        Cache::store('permissions')->forget("perms:{$user->id}");
    }
}

If you later swap to spatie/laravel-permission, only one class changes.

Hook invalidation into model events, not into your controllers. The failure mode is always the same: somebody runs a raw DB::table('role_user')->insert(...) in a seeder or an admin script, and the cache goes stale. Register observers on your Role/Permission models and add an explicit php artisan permissions:flush call to any script that touches the pivots directly.

// AppServiceProvider::boot()
Role::saved(fn ($role) => $role->users->each->flushPermissionCache());
Role::deleted(fn ($role) => ...);

Warm the cache after deploy, not on first request. A cold Redis plus a traffic spike is the worst-case thundering herd. Add a post-deploy step:

php artisan migrate --force
php artisan permissions:warm --chunk=500

Use it through the normal Gate, not a bespoke API. Register a Gate that delegates, so @can, $this->authorize(), policies and middleware all work unchanged:

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

Gate::define('posts.edit', fn (User $u) => app(Authorizer::class)->allows($u, 'posts.edit'));

Route middleware is then just ->middleware('can:posts.edit') — no new mental model for the team.

Multi-tenancy: namespace the key by tenant. If you run several tenants through one Redis DB, include the tenant ID in the key prefix and flush by prefix on tenant-level role changes. Do not rely on flushall.

Gotchas and Tips

Redis is a cache, so design for its absence. Every read path should degrade to a DB query rather than throwing. Wrap the cache read in a try/catch on RedisException and log; an authorization outage that 500s every page is far worse than a slow one.

php artisan migrate:fresh does not clear Redis. Your test suite will pass locally and fail in CI, or worse, pass in CI and fail in staging. Add Redis::connection('permissions')->flushdb() to your RefreshDatabase teardown, or point tests at the array store.

Key collisions with Laravel's own cache are the #1 silent bug. If the package default connection is cache and your app also caches there, a cache:clear during deploy nukes authorization state (usually harmless) but a tagged flush can also wipe unrelated keys. Give this package its own Redis database (REDIS_PERMISSIONS_DB=3) in config/database.php and its own connection name.

Watch for stale Eloquent models in whatever gets serialized. If the package serializes Permission models into Redis, a schema change (adding description, renaming a column) will deserialize into broken objects until TTL expiry. Flush on deploy whenever you touch those tables.

Super-admin bypass belongs in Gate::before, not in the package. Do not try to encode "everything" as a * permission row if the package does not document wildcard support — you will get inconsistent behaviour between @can, middleware and direct hasPermissionTo calls.

Debug with redis-cli, not with dd(). redis-cli -n 3 MONITOR | grep perms while you click through the app tells you instantly whether a request is a hit, a miss, or writing nonsense keys. SCAN 0 MATCH "perms:*" COUNT 100 is safer than KEYS on a shared instance.

Version-pin and read the diff before upgrading. A single-maintainer package with 39 stars and no dependents has no downstream ecosystem to catch regressions. "scabarcas/laravel-permissions-redis": "1.2.*" rather than ^1.2, and skim the changelog before bumping — the authorization layer is not where you want to be an early adopter.

The extension points worth knowing: the bound repository/registrar contract (swap for a DB-only implementation in tests or as an escape hatch), the cache store name in config (point it at a dedicated connection), and the trait (you can override hasPermissionTo in your User model to add logging or a feature flag). Those three cover 95% of the customisation you will ever need.

Finally, budget for the exit. Given the low adoption numbers, decide up front what "this package is abandoned" looks like for you: an Authorizer interface, a config-only dependency, and a single seeder that can rebuild every permission. Doing that on day one costs an hour; doing it after two years of @can sprawl costs a sprint.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
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