notiziephp.it

Migrare da spatie/laravel-permission a Redis: un playbook per la produzione

Traduzione da PHP Weekly
18 Settembre 2026 Tempo di lettura: 7 minuti
Migrare da spatie/laravel-permission a Redis: un playbook per la produzione

spatie/laravel-permission è lo standard de facto per ruoli e permessi in Laravel e, per la maggior parte delle applicazioni, è esattamente la scelta giusta. Questo articolo riguarda il caso di minoranza: i tuoi controlli di autorizzazione rappresentano una quota misurabile del carico sul database, oppure le modifiche ai permessi su larga scala continuano a innescare ricariche complete della cache. Io mantengo laravel-permissions-redis, un pacchetto che conserva l'API di Spatie ma sposta in Redis l'insieme dei permessi risolti per singolo utente. La documentazione contiene una guida alla migrazione passo passo; questo articolo ne racconta il lato produttivo: cosa verificare prima di iniziare, come sequenziare il cutover e come fare rollback.

Se non sei sicuro che la migrazione valga la pena, salta direttamente a "Quando non dovresti farlo" in fondo. Dico sul serio. Le migrazioni hanno un costo e questa ripaga solo in condizioni specifiche.

Cosa cambia davvero

Spatie mette in cache il registro globale dei permessi, ma ogni richiesta carica comunque in lazy loading le relazioni di ruoli e permessi dell'utente dal database: circa 4 query distribuite su più tabelle. Quando qualcosa cambia, l'intera cache viene invalidata e ricostruita alla richiesta successiva.

Il modello Redis-first risolve una sola volta l'intero set di permessi di ogni utente, lo memorizza come SET Redis e risponde a hasPermissionTo() con un SISMEMBER O(1). Quando cambia un ruolo o un'assegnazione, vengono riscaldati solo gli utenti coinvolti. Il database vede una sola query per richiesta (il lookup dell'utente stesso) e le modifiche ai permessi smettono di provocare valanghe di ricariche.

Due numeri onesti prima di impegnarti in qualsiasi cosa: nei miei benchmark, il tempo di esecuzione per richiesta migliora di circa 1,3-1,4x su SQLite ed è quasi identico rispetto a un database in rete. Il vantaggio non è la latenza. Il vantaggio è togliere circa il 75% delle query di autorizzazione dal database e mantenere la cache sempre calda. Se il database non è il collo di bottiglia, non lo sentirai.

Passo 0: fai prima l'audit del tuo codice

La maggior parte del dolore di una migrazione deriva da superfici API di cui non sapevi di fare uso. Fai grep prima di installare qualsiasi cosa:

grep -rn "getDirectPermissions\|getPermissionsViaRoles\|hasDirectPermission" app/
grep -rn "PermissionRegistrar" app/ tests/
grep -rn "teams\|team_id" config/permission.php

Cosa significano i risultati:

  • getDirectPermissions() / getPermissionsViaRoles(). Un tempo erano un blocco. Dalla v4.1.0 entrambi esistono con gli stessi nomi e restituiscono modelli Permission. Una differenza da conoscere: leggono le relazioni Eloquent (SQL), non la cache Redis, perché distinguere l'origine di un permesso è una questione da schermata di amministrazione, non da hot path. I controlli restano su Redis.
  • hasDirectPermission(). Non esiste. La sostituzione in una riga è $user->getDirectPermissions()->contains('name', $permission).
  • PermissionRegistrar nei test diventa il trait WithPermissions oppure un binding InMemoryPermissionRepository. Una modifica meccanica, ma mettila in conto come tempo.
  • Teams. Blocco netto. Se usi la funzionalità teams di Spatie come veri team, resta su Spatie. Se i tuoi "team" in realtà sono tenant, il multi-tenancy del pacchetto (isolamento delle chiavi Redis per tenant) potrebbe andare bene, ma valutalo come un progetto a sé stante.

Verifica anche la tua infrastruttura: ti servono Redis (phpredis o predis), PHP 8.3+ e Laravel 12 o 13. Laravel 11 si installa senza problemi ma il supporto è best-effort; la CI copre solo la 12 e la 13.

La migrazione vera e propria

Entrambi i pacchetti condividono lo stesso schema a 5 tabelle, ed è questo che rende l'operazione pratica. La versione breve:

# 1. Install alongside Spatie (they coexist fine)
composer require scabarcas/laravel-permissions-redis
# 2. Publish the config
php artisan vendor:publish --provider="Scabarcas\LaravelPermissionsRedis\PermissionsRedisServiceProvider"
# 3. See what the migration would do, without touching anything
php artisan permissions-redis:migrate-from-spatie --dry-run
# 4. Run it for real: reuses your existing tables, adds the two
#    missing columns, and warms the Redis cache
php artisan permissions-redis:migrate-from-spatie

Poi ci sono le modifiche al codice, in gran parte operazioni di trova-e-sostituisci:

  • Sostituisci use HasRoles con use HasRedisPermissions nel modello User.
  • Sostituisci gli import Spatie\Permission\Models\* con Scabarcas\LaravelPermissionsRedis\Models\*.
  • Gli alias dei middleware (role, permission, role_or_permission) e le direttive Blade mantengono gli stessi nomi. I tag di chiusura @unlessrole / @unlesspermission cambiano: @endrole / @endpermission.
  • getAllPermissions() restituisce DTO leggeri invece di modelli Eloquent. Se leggi solo ->name, non cambia nulla.
  • Rimuovi Spatie per ultimo, dopo che la tua suite di test è verde: composer remove spatie/laravel-permission.

La guida completa alla migrazione contiene la tabella completa di equivalenza dei metodi e la mappatura della configurazione. Il comando è anche sicuro da eseguire prima in staging, che è il posto in cui dovresti eseguirlo.

Il cutover in produzione

Questa è la parte che la maggior parte delle guide alla migrazione salta.

Sequenza di deploy. Rilascia la modifica al codice ed esegui il comando di migrazione nella stessa release, prima che il traffico raggiunga il nuovo codice: non copia nulla se le tabelle corrispondono, aggiunge colonne in modo idempotente e riscalda Redis. Per tabelle utenti molto grandi, riscalda tramite la coda invece di bloccare il deploy: php artisan permissions-redis:warm --queue.

Metti le operazioni sulla cache negli script con --force. permissions-redis:flush chiede conferma e, in un contesto non interattivo, alla richiesta viene risposto "no" e il comando esce con successo. Il tuo script di deploy crederà di aver fatto il flush, ma non sarà così. Dalla v4.1.0, --force salta il prompt. È proprio per questo no-op silenzioso che il flag esiste.

Verifica il cutover. php artisan about ora mostra una sezione Permissions Redis (versione, connessione, prefisso, TTL, stato del tenancy) e permissions-redis:stats mostra quante chiavi utente e ruolo sono attive. Fai un controllo a campione su alcuni utenti: $user->getPermissionNames() rispetto a quello che ti aspetti.

Piano di rollback. Questa è la proprietà migliore dello schema condiviso: le tue tabelle Spatie non vengono mai modificate in modo distruttivo (vengono aggiunte due colonne nullable, che Spatie ignora). Fare rollback significa ridistribuire la release precedente. Le chiavi Redis vivono sotto il proprio prefisso (auth: per impostazione predefinita) e possono essere eliminate senza toccare nient'altro.

Worker di lunga durata. I queue worker e Octane mantengono stato in memoria. Il pacchetto resetta le sue cache in memoria tra le richieste Octane, dietro un flag di configurazione (octane.reset_on_request). Attivalo se usi Octane; è disattivato per impostazione predefinita.

Insidie che vale la pena conoscere prima che ti trovino

  • L'isolamento dei guard è più rigoroso di quello di Spatie in un caso. Passare ID interi di ruoli o permessi li valida rispetto al guard di destinazione e scarta silenziosamente gli ID che appartengono a un altro guard. I nomi in formato stringa si comportavano già così.
  • La scadenza del TTL è gestita, ma regola il cooldown. Un cache miss innesca un riscaldamento dal database con un cooldown per utente (resolver_warm_cooldown, default 1s), così un Redis freddo dopo un riavvio non può tempestare il database. Se gestisci una concorrenza molto alta, leggi quella sezione della configurazione.
  • syncPermissions() senza argomenti sincronizza a vuoto (variadica dalla v4.1.0, come syncRoles()). Stessa semantica del passare un array vuoto, ma vale la pena saperlo se generi le chiamate dinamicamente.

Quando non dovresti farlo

  • Non usi Redis. Non aggiungere infrastruttura per questo. Spatie funziona con qualsiasi cache driver.
  • Usi la funzionalità teams. Il supporto teams di Spatie è più maturo del multi-tenancy di questo pacchetto per i pattern basati su team.
  • L'autorizzazione non è un costo misurabile. Controlla prima il query log. Se le query di autorizzazione sono rumore nelle tue metriche, la migrazione ti compra complessità, non margine.
  • Usi PHP < 8.3 o Laravel < 11. Spatie supporta versioni molto più vecchie.

Dove ti lascia tutto questo

La release v4.1.0 ha chiuso le ultime lacune dell'API che richiedevano workaround in questa migrazione (i getter per permessi diretti e quelli ereditati dai ruoli), ha aggiunto il flag --force, la sezione di about e PHP 8.5 alla matrice CI. Il changelog completo è qui.

Se esegui la migrazione e qualcosa non si comporta come dice questo playbook, è un bug nel pacchetto o nella documentazione, e io voglio saperlo: apri una issue.

Condividi su: LinkedIn

Altri articoli recenti