Download the PHP package enzoaccardo/ci4-adminkit-starter without Composer

On this page you can find all versions of the php package enzoaccardo/ci4-adminkit-starter. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package ci4-adminkit-starter

CI4 AdminKit Starter

Un'applicazione CodeIgniter 4 completa e pronta all'uso: pannello di amministrazione, autenticazione con secondo fattore, controllo degli accessi, layer API JWT e un sistema di builder dichiarativi che riduce liste e form a poche righe di configurazione.

PHP 8.2+ · CodeIgniter 4.7 · AdminLTE 4 · Smarty 5 · MIT

Perché esiste

Questo progetto non è nato come framework. È nato per sedimentazione.

Ogni volta che mettevo in piedi un'applicazione gestionale mi ritrovavo a riscrivere le stesse cose: il login, il reset della password, la tabella con i filtri e l'ordinamento, la paginazione, il form con gli errori sotto ogni campo, i permessi per nascondere una voce di menu, l'endpoint API con il token da rinnovare. Cambiava il dominio, mai l'impalcatura. E ogni riscrittura si portava dietro le stesse piccole imprecisioni: la colonna di ordinamento presa dalla query string senza whitelist, il filtro che perdeva lo stato cambiando pagina, il campo obbligatorio lato interfaccia ma non lato server.

Ho cominciato quindi a portarmi dietro il codice da un progetto al successivo. Prima copiando file, poi con più metodo. Quando ho aggiunto il secondo fattore per un progetto che ne aveva bisogno, l'ho scritto in modo che potesse servire anche altrove. Quando mi sono servite tabelle filtrabili in dieci schermate diverse, ho estratto la logica in trait invece di duplicarla. Quando ho dovuto descrivere il ventesimo form ho smesso di scrivere HTML e ho iniziato a dichiarare i campi, lasciando che il rendering fosse un dettaglio di implementazione.

A un certo punto il pattern era chiaro e il copia-incolla non stava più in piedi: le versioni divergevano, e un difetto corretto in un progetto restava aperto in tutti gli altri. Ho unificato allora quello che avevo sviluppato in un pacchetto iniziale, l'ho scomposto in sottopacchetti (infrastruttura, tema, secondo fattore, controllo degli accessi) e questo repository è l'applicazione che li mette insieme e li mostra al lavoro.

Il vantaggio pratico è duplice. Un progetto nuovo parte più in fretta, e parte più affidabile, perché l'impalcatura è già stata usata, corretta e testata su casi reali. Quello che resta da scrivere è la logica di business, cioè l'unica parte che davvero cambia da un progetto all'altro.

Cosa ottieni

Dopo cinque comandi hai un'applicazione che gira, con dentro:

E un utente con cui entrare subito: [email protected] / Admin1234!.

Se vieni da Laravel

Molte scelte di questo progetto assomigliano a cose che in Laravel esistono già, e non per caso: sono i pattern che negli anni si sono dimostrati comodi, riportati su CodeIgniter dove il framework lascia il posto vuoto. Se conosci Laravel ti muovi in fretta, perché i nomi e le forme sono spesso gli stessi.

In Laravel Qui
Risorse di Nova o Filament, dove dichiari i campi e il pannello si disegna Form builder e trait per le liste: dichiari sezioni, campi e colonne, il rendering è a carico del kit
$this->authorize('...') di AuthorizesRequests, Gate e Policy $this->authorize('slug') che interrompe con un 403, e $this->can('slug') che restituisce un booleano
spatie/laravel-permission per ruoli e permessi Il pacchetto ci4-adminkit-rbac, con la stessa idea di permessi come slug assegnati a ruoli
Layout e componenti Blade Layout e partial Smarty, con autoescape attivo per default
php artisan, con make:migration e i seeder php spark, con gli stessi comandi e la stessa forma
SoftDeletes e timestamp di Eloquent Gli stessi, più i campi di audit created_by, updated_by, deleted_by popolati dalla sessione
Fortify per il secondo fattore Il pacchetto ci4-adminkit-mfa, con TOTP, codici di recupero e passkey
Sanctum o Passport per le API a token JwtService e filtro jwt, con refresh a rotazione per dispositivo
Lo scheduler con una sola riga nel cron di sistema php spark tasks:run con una sola riga nel cron, ma i task sono righe di database modificabili dal pannello
Vite, integrato di serie Vite, configurato allo stesso modo
Package discovery dei service provider L'auto-discovery di CodeIgniter, su cui i quattro pacchetti si registrano da soli

La differenza di fondo resta quella tra i due framework. Laravel arriva con tutto e tu togli quello che non ti serve; CodeIgniter arriva snello e ti chiede di aggiungere. Questo starter è il mio "aggiungi", messo in forma riutilizzabile.

Dichiarare invece di scrivere

È la parte che cambia il modo di lavorare. Una schermata CRUD è di solito tre quarti di impalcatura ripetitiva e un quarto di logica applicativa. I builder invertono la proporzione: l'impalcatura la dichiari, e ti resta da scrivere soltanto ciò che è specifico del tuo dominio.

Le liste

Filtri, ordinamento e paginazione si ottengono dichiarando le colonne e i campi filtrabili. Da Admin\Users:

Il partial thead disegna le intestazioni ordinabili e la riga di filtri inline, il partial pagination il resto. La parte importante è nascosta nella firma dei metodi: applySort() accetta solo chiavi presenti in $columns e applyFilters() solo quelle dichiarate in $filterFields. La whitelist non è una raccomandazione della documentazione ma il funzionamento stesso, quindi un ?sort= arbitrario non arriva al database.

I form

Il controller descrive sezioni e campi, un unico template rende l'intero form Bootstrap. Da Admin\Users:

Quello che il builder fa da sé, e che altrimenti riscriveresti a ogni form:

I tipi disponibili sono text, email, password, number, textarea, select, multiselect, checkbox, switch, radio, date, datetime, cron, file, static, hidden e custom. Ogni famiglia ha il proprio partial, quindi aggiungere un tipo significa aggiungere un file e non allungare un switch.

I widget inclusi sono Tom Select (ricerca, tag e selezione multipla, senza jQuery), flatpickr per le date, FilePond per gli upload, un indicatore di robustezza della password e un costruttore di espressioni cron.

I campi che si parlano

Un campo può dichiarare l'effetto che ha sugli altri, senza scrivere JavaScript:

Le regole vengono normalizzate e serializzate sul tag form; uno script leggero le valuta al caricamento della pagina e a ogni cambiamento, e viene caricato solo se ci sono regole da valutare.

Una precisazione necessaria: show e require sono comportamenti dell'interfaccia. La validazione condizionale corrispondente va scritta anche lato server, e il controller di dimostrazione mostra come derivarla dalle stesse regole affects senza riscrivere la condizione.

Le tendine a cascata

Il caso classico della provincia che popola le città costa due dichiarazioni e un metodo. Il campo bersaglio dice da chi dipende e chi gli fornisce le opzioni:

nel controller esiste soltanto il metodo che restituisce i dati:

La rotta admin/<slug>/options/citta viene registrata da sola, l'incapsulamento JSON è gestito dalla classe base e l'URL del campo è costruito dal builder a partire dallo slug del controller. In modifica il valore già salvato viene riselezionato dopo il ripopolamento. Non c'è nessuna rotta, nessun URL e nessun json_encode da scrivere a mano.

Creare un record collegato senza perdere il form

Capita sempre: stai compilando un ordine e il cliente non è ancora in anagrafica. Un campo select può dichiarare un pulsante che apre in modale lo stesso form di creazione dell'entità collegata:

Nel controller servono due metodi, uno che descrive il form (riusando quello della creazione normale) e uno che salva. La rotta admin/<slug>/create/patient è automatica. Al salvataggio il nuovo record viene iniettato e selezionato nella tendina del form padre, senza ricaricare la pagina e senza perdere quello che avevi già compilato.

Tutto questo è visitabile e leggibile: la sezione admin/demo/form è una dimostrazione completa e commentata, pensata per essere cancellata quando non serve più.

La discovery delle rotte

In CodeIgniter le rotte si dichiarano al centro, in app/Config/Routes.php. Funziona, ma su un pannello con trenta sezioni quel file diventa lungo e ogni controller nuovo obbliga a tornare a modificarlo, con il rischio di toccare le rotte di qualcun altro. L'alternativa offerta dal framework è l'auto-routing, che deriva l'URL dal nome del metodo: è disattivato per default e sconsigliato, perché pubblica come rotta ogni metodo pubblico del controller, compresi quelli che non volevi raggiungibili.

La convenzione adottata qui sta in mezzo. Ogni controller dichiara le proprie rotte, ma le dichiara in modo esplicito:

AdminKit\Routing\Discovery scopre i controller di un namespace e invoca quel metodo statico. L'intero Config/Routes.php dell'applicazione si riduce a questo:

Sotto ci sono tre dettagli che fanno la differenza fra una comodità e una fonte di sorprese.

Due metodi, due livelli di esposizione. La convenzione prevede routes() e publicRoutes(). Un controller API mette in publicRoutes() login, registrazione, refresh e token ospite, che per definizione non possono pretendere un token, e in routes() tutto il resto. L'applicazione invoca publicRoutes() fuori dal gruppo protetto e routes() dentro il gruppo con il filtro jwt. La distinzione fra raggiungibile senza credenziali e protetto vive quindi nel controller, accanto alle rotte stesse, invece di dipendere dal punto in cui qualcuno le ha incollate in un file condiviso.

La risoluzione del namespace non è un glob fragile. La directory viene cercata prima nelle mappe PSR-4 registrate nell'autoloader, scegliendo il prefisso più lungo che corrisponde, così la discovery funziona anche sui namespace dei pacchetti e dei moduli e non solo su App\. Esiste poi un fallback che risolve App\ su APPPATH, perché in alcuni contesti, per esempio durante l'esecuzione dei test, l'autoloader può non avere ancora esposto le mappe. La ricerca è volutamente non ricorsiva: i controller di una sottocartella vanno scoperti con una chiamata dedicata, e questo evita che un file dimenticato in una directory annidata pubblichi rotte a tua insaputa.

adminGroup() fa una cosa in più. Crea il gruppo, con prefisso admin e filtro auth, e mentre scopre i controller registra anche le rotte di servizio del form builder, ma solo per i controller che espongono i metodi corrispondenti:

se il controller espone viene registrata
formOptions() GET admin/<slug>/options/(:segment), le opzioni delle tendine a cascata
formCreate() GET e POST su admin/<slug>/create/(:segment), il "crea nuovo" in modale

I due metodi arrivano dalla classe base, quindi per i controller del pannello la condizione è di fatto sempre soddisfatta: è il meccanismo per cui gli option provider e le modali funzionano senza scrivere una riga di routing. Lo slug è derivato dal nome della classe ed è lo stesso che il form builder usa per costruire gli URL dei campi, quindi le due parti non possono divergere perché leggono la stessa fonte.

Il risultato pratico è che per aggiungere una sezione al pannello si crea un file. Non si modifica nessun file condiviso, non si tocca la configurazione delle rotte, e l'aggiunta non può rompere le rotte di un'altra sezione.

Cosa aggiunge rispetto a CodeIgniter 4

CodeIgniter 4 è un framework deliberatamente snello: dà router, layer HTTP, model, migrazioni, validazione, filtri, sessioni, astrazione della cache, CLI e toolbar di debug. Quasi tutto quello che serve a un pannello di amministrazione non è compreso, non perché manchi qualcosa al framework ma perché non è il suo lavoro.

Questa è la lista di ciò che qui trovi già fatto e che nel framework nudo dovresti costruire.

Ambito CodeIgniter 4 di serie Qui
Autenticazione Nessuna nel core; esiste il pacchetto ufficiale Shield, a parte Login a sessione, logout, reset password via email, verifica dell'utente disattivato a ogni richiesta
Secondo fattore Assente TOTP con QR code, codici di recupero monouso, passkey WebAuthn, conferma per le azioni sensibili
Autorizzazioni Assenti Ruoli, permessi, tabella di associazione, authorize() nei controller, menu filtrato, bypass del superamministratore
Rotte Dichiarazione centralizzata, oppure auto-routing sconsigliato Dichiarazione locale al controller con scoperta automatica del namespace, più le rotte di servizio del form builder registrate da sé
Template View PHP e un parser minimale Smarty 5 con autoescape attivo, layout e partial riutilizzabili, ponte verso gli helper del framework
Form Validazione sì, costruzione del markup no Form builder dichiarativo: valori, errori per campo, required e maxlength dedotti dal model, widget, interazioni fra campi, cascate AJAX, creazione in modale
Liste Un paginatore Filtri, ordinamento e paginazione con whitelist obbligatoria delle colonne, intestazioni e filtri inline già disegnati
Campi di audit created_at, updated_at, deleted_at Anche created_by, updated_by, deleted_by, popolati dalla sessione; le migrazioni iniettano i sei campi da sé
API a token Nessun layer JWT Servizio JWT in HS256, filtro dedicato, token di accesso, refresh e ospite, rotazione del refresh per dispositivo
Rate limit Una classe Throttler, da cablare Filtro applicabile per rotta, con soglia e finestra, per esempio rateLimit:5,60
Log delle richieste Assente Filtro che registra le richieste API in ingresso, più un registro separato dei tentativi di autenticazione falliti
Impostazioni Storage chiave-valore, dal pacchetto ufficiale Servizio con priorità JSON, poi variabili d'ambiente, poi default, e la sua interfaccia nel pannello
Cache Astrazione dei driver Namespace per chiave, statistiche, invalidazione per pattern, pagina di gestione; degrada con grazia se Redis non c'è
Audit trail Assente Registro delle attività con attore, azione, entità e descrizione
Task pianificati Esecutore dal pacchetto ufficiale, task definiti nel codice Task come righe di database, modificabili dal pannello con costruttore di espressioni cron
Upload Gestione del file caricato Servizio di storage per le immagini, con la sua configurazione
Asset Nessuna pipeline Vite, più comandi che pubblicano gli asset dei pacchetti
Menu Assente Albero su database, riordino drag&drop, visibilità per permesso

Sul modello a tre livelli vale una nota, perché è una convenzione e non un'imposizione. Base definisce tabella, campi ammessi e regole di validazione, Common la logica riutilizzabile, Admin le query del pannello con le loro join. Serve a evitare che le query dell'interfaccia si mescolino alle regole del dominio, e che una join aggiunta per una schermata cambi il comportamento di tutto il resto.

Architettura

Questo repository è l'applicazione. Il codice riutilizzabile vive in quattro pacchetti separati, installati da Packagist.

Pacchetto Cosa contiene
enzoaccardo/ci4-adminkit L'infrastruttura: renderer Smarty, builder di liste e form, controller admin di base, model e migrazioni di base con i campi di audit, la discovery delle rotte, il contratto per il controllo degli accessi
enzoaccardo/ci4-adminkit-adminlte4 Il tema: layout del pannello, pagine di autenticazione, asset compilati
enzoaccardo/ci4-adminkit-mfa Il secondo fattore: TOTP, passkey, codici di recupero, il flusso completo e le sue migrazioni
enzoaccardo/ci4-adminkit-rbac Ruoli e permessi: servizio, model, migrazioni

La suddivisione risponde a una domanda pratica, cioè cosa voglio poter sostituire senza toccare il resto. Il tema è separato perché un progetto può averne uno proprio. Il secondo fattore è separato perché non tutti i progetti lo richiedono, ed è indipendente dal tema. Il controllo degli accessi usa una scoperta morbida: se il pacchetto non è installato l'applicazione continua a funzionare e le verifiche di permesso ricadono su un comportamento neutro.

Ogni pacchetto è autonomo, con le proprie migrazioni e i propri servizi, scoperti automaticamente da CodeIgniter. Aggiornarne uno non richiede di toccare l'applicazione.

Requisiti

Installazione

Il pacchetto è di tipo project, cioè lo scheletro di un'applicazione e non una libreria da montare dentro un'altra.

create-project scarica l'ultima versione stabile, esegue composer install e rimuove la cartella .git, così parti con un albero pulito e la tua storia da fare. Subito dopo, uno script di post-installazione crea il .env a partire da .env.example e lancia i tre comandi publish, quindi gli asset sono già al loro posto.

Se preferisci il clone:

Il clone da solo non scarica le dipendenze: porta giù i file versionati e nient'altro. È composer install che legge composer.lock e installa in vendor/ i quattro pacchetti e tutto il resto, e che al termine pubblica gli asset. Il .env invece resta a carico tuo, perché lo crea lo script legato alla creazione del progetto.

Da qui in avanti la strada è la stessa. Nel .env imposta il database, sotto database.default.*, e un JWT_SECRET, che deve essere una stringa casuale lunga: se resta vuoto i token non sono firmati in modo sicuro. Conviene valorizzare anche APP_SALT.

Controlla anche app.baseURL, che deve essere l'indirizzo con cui aprirai il sito, barra finale inclusa. Il valore di partenza vale per php spark serve; se usi Apache, XAMPP o Laragon va cambiato in qualcosa come http://localhost/mionuovoprogetto/public/. È l'errore che si nota per primo e si capisce per ultimo: le pagine si aprono, ma senza stile, perché CSS e JS vengono cercati sull'indirizzo sbagliato.

Migrazioni e seeder non sono automatizzabili nello script di post-installazione, perché richiedono un database già raggiungibile e configurato.

I comandi *:publish copiano in public/ gli asset che i pacchetti si portano dietro. Girano da soli dopo ogni composer install e composer update, quindi aggiornare un pacchetto ripubblica anche i suoi asset e non ti lascia con una copia vecchia in public/. Per questo i file pubblicati non sono versionati: la loro sorgente è il pacchetto, e tenerne una copia in repository significherebbe soltanto lasciarla divergere in silenzio.

Se ti serve rilanciarli a mano, per esempio dopo aver cancellato qualcosa in public/:

Se il pannello si apre senza stile, sono due le cause possibili, e si distinguono guardando in DevTools l'URL con cui viene richiesto app.css: se punta a un indirizzo dove non risponde nessuno il problema è app.baseURL nel .env, se invece risponde 404 gli asset non sono stati pubblicati e basta rilanciare i comandi qui sopra.

Entra con [email protected] e Admin1234!, poi cambia subito la password.

Per i task pianificati serve una riga nel cron di sistema:

Aggiungere una sezione al pannello

Il giro completo, per farsi un'idea del ritmo di lavoro.

  1. La migrazione. BaseMigration::createTable() aggiunge da sé i sei campi di audit, quindi descrivi soltanto i tuoi: php spark make:migration CreateArticlesTable.
  2. Il model, sui tre livelli: Base\ArticleModel con tabella, campi ammessi e $validationRules, poi Admin\ArticleModel con queryForIndex() e le sue join.
  3. Il controller. Estende AdminController, dichiara le sue rotte nel metodo statico routes() e usa i builder per lista e form, come negli esempi qui sopra. Nessun file di configurazione da toccare, la discovery lo trova.
  4. I permessi. Aggiungi gli slug, per esempio articles.view e articles.create, e chiamali con $this->authorize('articles.view').
  5. La voce di menu. Una riga in nav_items con permission_slug valorizzato, così comparirà solo a chi ha quel permesso.

Il template della lista, spesso, è l'inclusione di due partial più il corpo della tabella.

Le altre parti, in breve

Due popolazioni di utenti, distinte. Gli amministratori del pannello stanno in users e si autenticano a sessione. Gli utenti delle API stanno in app_users e si autenticano con email e password ottenendo un JWT. Sono insiemi separati di proposito, perché chi consuma le API non deve poter entrare nel pannello.

API JWT. Le rotte pubbliche, cioè login, registrazione, refresh e token ospite, hanno ciascuna il proprio rate limit; tutto il resto passa dal filtro jwt. I refresh token ruotano per dispositivo, così revocare una sessione non tocca le altre. I claim sono user_id, email e type, dove il tipo vale access, refresh o guest. I token ospite consentono l'accesso alle risorse pubbliche e vengono respinti dove serve un utente reale.

Controllo degli accessi. $this->authorize('slug') interrompe con un 403, $this->can('slug') restituisce un booleano ed è quello che serve negli endpoint JSON e per decidere se disegnare un pulsante. La visibilità del menu usa gli stessi slug delle verifiche nei controller, quindi non esiste una voce visibile che porta a un 403. Il superamministratore bypassa i controlli.

Secondo fattore. Si attiva per singolo utente. TOTP con QR code e codici di recupero monouso, passkey come secondo fattore e come conferma davanti alle azioni sensibili. Un amministratore può azzerare il secondo fattore di un altro utente, che lo riconfigurerà al login successivo.

Impostazioni, cache, log. Le impostazioni sono lette con priorità JSON, variabili d'ambiente e default, e sono modificabili dal pannello. La cache usa chiavi con namespace e sa invalidare per pattern; senza Redis lavora su file. Il log attività registra chi ha fatto cosa e su quale entità.

Asset. I sorgenti stanno in resources/admin/ e si compilano con Vite. L'output finisce in una cartella dedicata, separata dagli script statici, così una ricompilazione non cancella file che non ha generato.

Test

I test end-to-end coprono ciò che i test PHP non raggiungono: i form inviati in AJAX, le interazioni fra campi, le cascate delle tendine e le passkey, provate con un authenticator virtuale via CDP, quindi senza alcun dispositivo fisico.

Struttura

Personalizzare

Il branding del pannello si configura in app/Config/AdminLTE.php, i parametri del secondo fattore in app/Config/Mfa.php. Per un tema proprio si sostituisce il pacchetto del tema mantenendo il resto. La sezione di dimostrazione admin/demo/form e il suo controller si possono cancellare senza conseguenze, perché servono soltanto a mostrare il form builder al lavoro.

Licenza

MIT, vedi LICENSE.


All versions of ci4-adminkit-starter with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
codeigniter4/framework Version ^4.7
codeigniter4/tasks Version ^1.0
enzoaccardo/ci4-adminkit Version ^0.1
enzoaccardo/ci4-adminkit-adminlte4 Version ^0.1
enzoaccardo/ci4-adminkit-mfa Version ^0.1
enzoaccardo/ci4-adminkit-rbac Version ^0.1
firebase/php-jwt Version ^7.1
predis/predis Version ^3.4
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package enzoaccardo/ci4-adminkit-starter contains the following files

Loading the files please wait ...