Il CMS che è anche un framework: entità, campi, Views, moduli, temi, hook e config management. Tutto quello che serve per capirlo e gestirlo — dalla goccia blu al deploy in produzione, senza vendere fuffa.
"Drupal è complicato" — no, è strutturato. Ha una curva d'apprendimento ripida perché ti dà in mano un modello dati serio invece di un tema con 200 plugin che litigano. La fatica iniziale è l'investimento.
Drupal è un CMS open-source (GPL) scritto in PHP, nato nel 2001. Ma la definizione onesta è un'altra: è un framework per costruire applicazioni web strutturate attorno a contenuti, con un CMS già montato sopra.
WordPress è un mobile IKEA già montato: apri la scatola, è un blog/sito, pronto in 10 minuti. Bellissimo finché ti va bene quella forma. Drupal è una falegnameria con attrezzi professionali: nessun mobile pronto, ma puoi costruire esattamente la struttura che ti serve — un portale con 12 tipi di contenuto correlati, permessi granulari, flussi editoriali, multilingua — senza incollare insieme 40 plugin che si odiano. La falegnameria richiede di imparare gli attrezzi. Ma quando il progetto è complesso, il mobile IKEA non basta più.
Drupal moderno non è più "solo PHP con qualche funzione": è costruito su componenti Symfony, gestito con Composer, con i template in Twig. Chi arriva da WordPress trova un mondo più ingegneristico e meno "clicca e vai".
Capire le versioni ti evita di costruire su qualcosa che muore tra sei mesi. Drupal ha cambiato radicalmente il modo di aggiornarsi con la versione 8, e nel 2025 ha aggiunto una novità grossa: Drupal CMS.
Da Drupal 8 in poi vale la regola semantica: una major ogni 2 anni (anni pari), una minor ogni 6 mesi con nuove funzionalità retro-compatibili. Il salto tra major moderne (8→9→10→11) è molto più facile del vecchio incubo 6→7→8.
| Versione | Stato (lug 2026) | Note |
|---|---|---|
| Drupal 7 | Morto (EOL gen 2025) | Architettura vecchia, niente Composer. Da migrare, punto. |
| Drupal 9 | EOL (nov 2023) | Ponte tra 8 e 10. Non usarlo più. |
| Drupal 10 | EOL 9 dic 2026 | Ancora diffusissimo. Se sei qui, pianifica la migrazione a 11. |
| Drupal 11 | Attuale (11.4) | Uscito lug 2024. PHP 8.3+, Symfony 7. Il target di oggi. |
| Drupal 12 | Atteso ~dic 2026 | Prossima major. Aggiornamento previsto indolore da 11. |
Storicamente Drupal "core" ti dà una scatola quasi vuota: potente ma da assemblare. Drupal CMS (lanciato a gennaio 2025) è una distribuzione ufficiale costruita su Drupal 11 pensata per marketer e non-sviluppatori: arriva già configurata, con strumenti no-code per costruire il sito senza toccare codice.
Pacchetti di configurazione applicabili con un click (blog, SEO, eventi...).
Costruzione visuale delle pagine, drag & drop.
Aggiornamenti automatici e Project Browser per installare moduli dall'admin.
Dimentica il "scarica lo zip e scompatta": Drupal moderno si installa e si gestisce con Composer. È il modo giusto, ed è ciò che ti permette di aggiornare senza impazzire.
| Componente | Consigliato |
|---|---|
| PHP | 8.3+ (con estensioni gd, pdo, mbstring, opcache) |
| Web server | Nginx o Apache |
| Database | MariaDB 10.6+ / MySQL 8+ / PostgreSQL 16+ / SQLite |
| Composer | 2.x (obbligatorio) |
| Drush | 13.x (la CLI, altamente consigliata) |
# 1. crea lo scheletro del progetto (scarica core + dipendenze)
composer create-project drupal/recommended-project miosito
cd miosito
# 2. installa Drush (la riga di comando di Drupal)
composer require drush/drush
# 3. installa il sito da CLI (invece del wizard nel browser)
./vendor/bin/drush site:install --db-url="mysql://user:pass@localhost/miodb" \
--site-name="Il Mio Sito" --account-name=admin -y
# il docroot pubblico è la cartella web/ — punta lì il vhost
# NON esporre mai l'intera cartella del progetto: vendor/ deve restare fuori
web/. Le cartelle vendor/, i file composer.json, .env e la config non devono essere raggiungibili via HTTP. Esporre l'intera cartella del progetto è l'errore che regala il tuo settings.php (con le credenziali del DB) al primo che passa.
Non installare PHP/MySQL a mano sul tuo portatile: usa uno strumento che ti dà l'ambiente pronto e replicabile.
Il più usato oggi: Docker sotto, un comando e hai Drupal in locale. Consigliato.
Alternativa a DDEV, sempre su Docker. Molto configurabile.
Massimo controllo, più lavoro. Per chi sa già cosa fa.
ddev config --project-type=drupal11, poi ddev start, poi ddev composer create drupal/recommended-project. Ti tira su PHP, il database e un URL locale con HTTPS, senza sporcare il tuo sistema. Se usi Docker, dai un'occhiata alla guida Docker per Svogliati.
Questo è il concetto che sblocca Drupal. Se capisci entità / bundle / campi, hai capito il 70% del sistema. Se non lo capisci, ti sembrerà caos. Fermati qui e leggi con calma.
Pensa a un ufficio pieno di schedari. Ogni tipo di schedario è un'entità (Contenuto, Utente, Taxonomy, Media...). Dentro un tipo di schedario ci sono cassetti diversi per categoria: sono i bundle (per il contenuto: Articolo, Pagina, Evento). Ogni scheda dentro un cassetto è una singola voce (un articolo preciso). E le righe da compilare sulla scheda (titolo, data, foto, autore) sono i campi. Costruire un sito Drupal significa in gran parte: decidere quali cassetti creare e quali righe mettere su ogni scheda.
Drupal separa nettamente due mondi, e capirlo è vitale per il deploy (cap. 11):
Un campo è configurazione; il valore che ci metti dentro è contenuto. Confondere i due è la causa n.1 di deploy andati male.
Ora che sai cosa sono, vediamo come si costruiscono in pratica dall'interfaccia: creare un tipo di contenuto, aggiungere campi e organizzare i contenuti con la tassonomia. Tutto senza scrivere codice.
Da Struttura → Tipi di contenuto (/admin/structure/types) crei un nuovo bundle di Node. Esempio: un content type "Evento". Drupal ti dà già il campo Titolo e un Body; tu aggiungi il resto.
Per ogni content type controlli tre schede fondamentali:
| Tipo di campo | Per cosa |
|---|---|
| Text (plain / formatted) | Titoli brevi o testo ricco con editor WYSIWYG |
| Number / Email / Link | Dati tipizzati con validazione automatica |
| Date | Date ed eventi (con o senza ora, con range) |
| Image / File / Media | Allegati; Media è il modo moderno e riusabile |
| Entity reference | Il più potente: collega un'entità a un'altra (un evento → un relatore) |
| Boolean / List | Sì/no, o una scelta da un elenco predefinito |
La tassonomia è il sistema di categorizzazione. Crei dei vocabolari (es. "Argomenti", "Regioni") e dentro ci metti i termini (es. "Sicurezza", "Networking"). Poi colleghi un campo entity reference ai termini dal tuo content type. Risultato: filtri, pagine per categoria, menu automatici, tutto guidato dai dati.
Content type: Evento (bundle del Node)
├─ field_titolo → Text (plain) # il titolo
├─ field_data → Date (range) # inizio/fine
├─ field_luogo → Entity reference # → entità "Sede"
├─ field_relatori → Entity reference # → entità "Persona" (multiplo)
├─ field_argomenti → Entity reference # → termini tassonomia
└─ field_locandina → Media (immagine) # la grafica
# Da questa struttura, senza codice, puoi generare:
# liste filtrabili (Views), pagine per argomento, feed, API REST.
Se il modello dati è il cuore, Views è il superpotere. È un costruttore visuale di query: crei liste, tabelle, griglie, feed, blocchi — filtrati, ordinati, paginati — senza scrivere una riga di SQL. In core da Drupal 8.
Hai presente quando in Excel metti i filtri sulle colonne e ordini una tabella? Views è quello, ma su tutto il tuo sito e con l'output che vuoi. "Dammi tutti gli Eventi futuri, dell'argomento Sicurezza, ordinati per data, 10 per pagina, mostrati come griglia di card." Componi questa richiesta con dei menù a tendina, e Views genera la query ottimizzata, il markup e la paginazione. Il 90% delle "liste" che vedi su un sito Drupal — ultimi articoli, archivio, correlati, sidebar — sono Views.
Hai i contenuti e le liste. Ora: dove finiscono sulla pagina? Drupal organizza il layout con regioni (definite dal tema) in cui piazzi blocchi, più i sistemi di menu per la navigazione.
Il tema definisce delle regioni (header, contenuto, sidebar, footer...). Un blocco è un pezzo di contenuto che assegni a una regione dalla Block layout (/admin/structure/block). Un blocco può essere: il menù principale, il logo, una View-block, un form di ricerca, o del contenuto tuo.
Drupal ha un sistema di menu nativo: crei voci, le annidi, le riordini con drag & drop. Il Main menu e il Footer menu arrivano di default. Ogni menu diventa un blocco che piazzi dove vuoi. Le voci possono puntare a nodi, viste, URL esterni.
Per un controllo più fine, Layout Builder (in core) ti fa comporre visualmente il layout di un intero content type o di un singolo nodo: righe, colonne, blocchi trascinati dentro. È il ponte tra la struttura rigida a regioni e la composizione libera delle pagine.
Uno dei punti dove Drupal umilia la concorrenza: un sistema di permessi granulare costruito su ruoli. Chi può fare cosa è deciso riga per riga, non "admin o non-admin".
Gli utenti hanno uno o più ruoli. Ai ruoli assegni i permessi (centinaia, uno per ogni azione: "creare articoli", "modificare i propri articoli", "amministrare i menu"...). Un utente può fare la somma di ciò che i suoi ruoli gli concedono. Non assegni mai permessi al singolo utente: sempre via ruolo.
Drupal core è deliberatamente essenziale. La potenza arriva dai moduli (funzionalità) e dai temi (aspetto). Sapere da dove vengono e come si installano correttamente ti salva da mille guai.
Inclusi in Drupal: Views, Media, Layout Builder, JSON:API. Attivi/disattivi.
Della community, da drupal.org. Migliaia. Si installano con Composer.
I tuoi, scritti su misura. Vivono in web/modules/custom/.
# SEMPRE con Composer — scarica il modulo e le sue dipendenze
composer require drupal/admin_toolbar drupal/pathauto drupal/token
# poi abilitalo con Drush (o dall'interfaccia /admin/modules)
drush pm:enable admin_toolbar pathauto token -y
# aggiornare un modulo
composer update drupal/pathauto -W
drush updatedb -y # applica gli update di schema al DB
# MAI scaricare lo zip e scompattarlo a mano: rompe Composer
# e rende impossibile aggiornare in sicurezza.
| Modulo | A cosa serve |
|---|---|
admin_toolbar | Rende la barra di amministrazione usabile (menù a tendina) |
pathauto + token | URL puliti automatici (/eventi/titolo invece di /node/42) |
paragraphs | Componenti di contenuto ricchi e riordinabili |
webform | Form complessi (contatti, sondaggi) senza codice |
metatag | SEO: meta tag, Open Graph, Twitter card |
search_api + Solr | Ricerca avanzata e sfaccettata |
gin (tema admin) | Backend moderno e gradevole |
Un tema controlla l'aspetto. Il frontend di riferimento moderno è Olivero (core); per l'admin, Claro o Gin. I template usano Twig, il motore di Symfony: pulito, sicuro (escaping automatico), niente PHP sparso nell'HTML.
{# override del template per il content type "evento" #}
<article class="evento">
<h2>{{ label }}</h2>
{% if content.field_data %}
<time>{{ content.field_data }}</time>
{% endif %}
<div class="corpo">{{ content.body }}</div>
</article>
{# {{ }} stampa (con escaping), {% %} logica. Mai concatenare
HTML a mano: Twig protegge da XSS di default. #}
Quando l'interfaccia non basta, scrivi un modulo custom. Drupal moderno è orientato agli oggetti e usa i pattern di Symfony: dependency injection, plugin, eventi. Ma sopravvive anche il vecchio sistema degli hook.
Un modulo è una cartella con almeno un file .info.yml che lo descrive. Da lì aggiungi routing, controller, servizi, hook.
name: 'Il Mio Modulo'
type: module
description: 'Fa qualcosa di utile'
package: Custom
core_version_requirement: '^11'
dependencies:
- drupal:node
Gli hook sono funzioni con un nome convenzionale che Drupal chiama in momenti precisi. Vuoi modificare un form? Implementi hook_form_alter(). È il modo "classico", ancora vivo per tante cose.
function miomodulo_form_alter(
&$form, $form_state, $form_id) {
if ($form_id == 'node_evento_form') {
$form['field_luogo']['#weight'] = -10;
}
}
I plugin sono il modo moderno per fornire "una cosa di un certo tipo": un blocco, un campo, un formatter, una regola di accesso. Sono classi PHP con un'annotazione/attributo che le registra. Riusabili e scopribili dal sistema.
#[Block(
id: 'saluto',
admin_label: 'Blocco Saluto',
)]
class SalutoBlock extends BlockBase {
public function build() {
return ['#markup' => 'Ciao svogliato!'];
}
}
La logica riusabile vive nei servizi (oggetti registrati nel container). Invece di richiamare funzioni globali, inietti i servizi che ti servono. È il cuore Symfony di Drupal: testabile, disaccoppiato, pulito.
// invece di funzioni globali, chiedi il servizio al container
$logger = \Drupal::service('logger.factory')->get('miomodulo');
$current = \Drupal::currentUser();
$node = \Drupal::entityTypeManager()
->getStorage('node')->load(42);
// \Drupal::service() va bene per script veloci, ma nei tuoi
// controller/plugin inietta le dipendenze dal costruttore:
// è testabile e non ti accoppia al container globale.
Come porti in produzione una modifica fatta in locale? In Drupal la configurazione è codice: si esporta in file YAML, si versiona in Git, si importa in produzione. È la funzionalità che ha reso Drupal 8+ professionale.
Immagina di poter fare una fotografia di tutta la struttura del sito — content type, campi, viste, impostazioni — e salvarla in file di testo. Cambi qualcosa in locale, rifai la foto, la metti in Git. In produzione, invece di rifare a mano gli stessi click, "sviluppi la foto" e la struttura si allinea da sola. Questo è il Configuration Management (CMI): niente più "in locale funziona ma in produzione ho dimenticato di creare quel campo".
# === IN LOCALE (dopo aver modificato la struttura) ===
drush config:export -y # scrive tutta la config in file YAML (config/sync)
git add config/ && git commit -m "Aggiunto content type Evento"
git push
# === IN PRODUZIONE (dopo il git pull) ===
composer install --no-dev # allinea core e moduli
drush config:import -y # applica la config fotografata
drush updatedb -y # update di schema DB dei moduli
drush cache:rebuild # svuota le cache
# Questi 4 comandi in produzione, in quest'ordine, sono
# il 90% dei deploy Drupal. Spesso in uno script deploy.sh.
Il flusso classico è local → staging → produzione. Il codice e la configurazione viaggiano via Git in avanti; i contenuti (che sono dati, non config) viaggiano semmai indietro (produzione → locale) con un dump del DB per lavorare su dati reali.
A volte vuoi config diversa per ambiente (moduli di debug attivi solo in locale). Si gestisce con Config Split e con override in settings.php per ambiente. Così la stessa base di config si adatta senza fork.
config:import successivo, Drupal vede che la config del DB non combacia con quella in Git e sovrascrive le tue modifiche. Regola ferrea: la struttura si cambia in locale, si esporta, si versiona. La produzione la riceve, non la genera.
Un sito Drupal si gestisce, non si "installa e dimentica". Aggiornamenti di sicurezza, cache, backup: la routine da sysadmin che tiene il sito vivo e non bucato.
Il Drupal Security Team rilascia patch il mercoledì (giorno standard degli advisory). Restare aggiornati è la singola cosa più importante: la stragrande maggioranza dei siti Drupal bucati erano semplicemente non aggiornati.
# controlla cosa è da aggiornare (core e contrib)
composer outdated "drupal/*"
drush pm:security # solo gli update DI SICUREZZA
# aggiorna core (con le dipendenze)
composer update drupal/core-recommended -W
drush updatedb -y
drush cache:rebuild
# SEMPRE: backup prima, test su staging, poi produzione.
# Drupal CMS offre gli Automatic Updates per il core.
Drupal ha un sistema di cache a livelli molto sofisticato (cache tag, contexts, BigPipe). Le basi che fanno la differenza:
drush cache:rebuild quando "qualcosa non si aggiorna"drush cr (cache:rebuild). Il 70% dei "ho fatto una modifica ma non si vede" si risolve svuotando la cache. Il restante 30% è che stavi guardando l'ambiente sbagliato.
I comandi Drush che userai ogni giorno e i termini da tenere a mente. Da tenere aperto in un tab.
drush cr # cache:rebuild (il salvavita)
drush uli # link di login one-time
drush cim / cex # config import / export
drush updb # updatedb (schema DB)
drush pmu / pm:enable # abilita/disabilita moduli
drush sql:dump # dump del database
drush status # stato del sito
drush ws # watchdog: i log
drush upwd admin # cambia password utente
/admin/content # i contenuti
/admin/structure/types # content type
/admin/structure/views # le Views
/admin/structure/block # blocchi e regioni
/admin/people # utenti
/admin/people/permissions # permessi
/admin/modules # moduli
/admin/config # configurazione
/admin/reports/status # report di stato
| Termine | In parole povere |
|---|---|
| Node | Un singolo contenuto (un articolo, una pagina). L'entità più comune. |
| Entity | Qualsiasi "cosa" gestita da Drupal: nodo, utente, termine, media... |
| Bundle | Una sotto-tipologia di entità. Per i Node = il "content type". |
| Field | Un pezzo di dato aggiunto a un bundle (data, immagine, riferimento...). |
| Content type | Un tipo di contenuto che definisci tu (Articolo, Evento, Prodotto). |
| Taxonomy | Sistema di categorie: vocabolari che contengono termini. |
| View | Una lista/tabella/griglia di contenuti costruita senza SQL. |
| Block | Un pezzo di contenuto piazzabile in una regione del tema. |
| Region | Una zona del layout definita dal tema (header, sidebar, footer). |
| Hook | Una funzione con nome convenzionale che Drupal chiama in un dato momento. |
| Plugin | Classe che fornisce "una cosa di un tipo" (blocco, campo, formatter). |
| Service | Oggetto riusabile nel container; lo inietti dove serve (DI). |
| Twig | Il motore di template (da Symfony) usato nei temi. |
| Drush | La riga di comando di Drupal: gestisci il sito da terminale. |
| Composer | Il gestore di dipendenze PHP: come installi/aggiorni Drupal e i moduli. |
| CMI / config sync | Configuration Management: la struttura esportata in YAML e versionata. |
| Recipe | Pacchetto di configurazione applicabile (novità usata da Drupal CMS). |
| Drupal CMS | Distribuzione no-code su base Drupal 11 per non-sviluppatori. |
| UID 1 | Il primo utente: bypassa ogni permesso. Da blindare. |