Sei sulla pagina 1di 26

Drupal 9 my

documentation
First Edition

My bible by Antonio Lodato

Antonio Lodato www.packt.com


Introduzione a Drupal
Per quanto riguarda il lato utente, Drupal ha tutte le funzioni standard di un sistema di
gestione dei contenuti (CMS) basato sul web:
- I visitatori possono visualizzare le informazioni pubblicate sul sito, navigare attraverso i
menu, visualizzare gli elenchi e le singole pagine, e così via.
- Gli utenti possono creare account e lasciare commenti.
- Gli amministratori possono gestire la configurazione del sito e controllare i permessi degli
utenti.
- I redattori possono creare, vedere l'anteprima e poi pubblicare il contenuto quando è pronto.
- Il contenuto può essere sindacato su RSS, dove i lettori di feed possono raccogliere nuovi
articoli man mano che vengono pubblicati.
- Con diversi temi integrati, anche l’aspetto del sito può essere facilmente cambiato.
Tuttavia, Drupal 8 ha migliorato questi temi e ha introdotto alcune capacità più potenti. Per
esempio, il supporto multilingue avanzato, la moderazione dei contenuti, la costruzione di
layout, REST API, e molte altre caratteristiche sono ora disponibili lato utente.

La versione minima di PHP richiesta per far funzionare Drupal 9 (e per installarlo tramite
Composer) è la 7.3.

Drupal utilizza la potente libreria PHP Data Objects (PDO) che è standard in PHP 7.
Questa libreria ha un livello di astrazione molto elevato che permette agli
sviluppatori di supportare numerosi database, tra cui MySQL, PostgreSQL, SQLite e
MariaDB.
Le versioni minime di database per Drupal 9 sono le seguenti:
- MySQL 5.7.8/MariaDB 10.3.7/Percona Server 5.7.8
- PostgreSQL 10 o superiore
- SQLite 3.26 o superiore

Drupal può essere eseguito sui seguenti Web Server (ma è ottimizzato per Apache, è
stato scritto proprio per Apache originariamente):
- Apache
- IIS
- Lighttpd
- Nginx

Drupal core (o nucleo), moduli e temi


Quindi l’architettura di Drupal la possiamo dividere in tre parti: core, moduli e temi.

Hooks, plugins ed eventi


Gli hooks rappresentano un concetto procedurale molto tipico di Drupal che permette
al core di Drupal e ai moduli di raccogliere (o esporre) dati da altri moduli e temi.
Concretamente, quando cerchiamo un hook il modulo o la parte del tema deve
contenere l’implementazione dell’hook che segue uno specifico formato di
denominazione, ovvero: nome_modulo_hook_name. Inoltre, ci sono anche gli hook alter
che hanno la seguente denominazione nome_modulo_hook_name_alter, praticamente
sono usati per cambiare i dati passati come riferimento all'implementazione dell'hook.
Gli hooks praticamente rappresentano il pattern Observer, ovvero ogni volta che
implementiamo un hook, quando accade qualcosa che cambia il suo stato, allora tutte
le classi che utilizzano (o osservano) quell’hooks vengono notificate del cambiamento
ed eseguono azioni.

Entità
Un’entità è praticamente un oggetto che contiene i suoi vari campi. Tutti i tipi di
entità possono avere più bundle, che sono diverse varianti dello stesso tipo di entità e
possono avere campi diversi (pur condividendo alcuni campi base).
Drupal core viene ancora fornito con il tipo di entità Node, con alcuni bundle come
Basic Page e Article nel suo profilo di installazione standard. Inoltre, viene fornito con
alcuni altri tipi di entità, come User, Comment, File, e così via.

Campi (Field)
Ci sono due tipi di campi delle entità in Drupal: campi base e campi configurabili. I
primi sono campi definiti nel codice per ogni tipo di entità, mentre i secondi sono di
solito creati e configurati nell'UI e collegati a un bundle di quel tipo di entità.
I campi possono anche essere di più tipi, a seconda dei dati che memorizzano. Si
possono avere campi stringa (o testo), campi numerici, campi data, campi e-mail e
così via. Come sviluppatori, possiamo creare i nostri tipi di campo se quelli esistenti
non sono abbastanza buoni per i nostri dati.

Menu
I menu sono gerarchici; cioè, hanno una struttura ad albero. Una voce di menu può
avere più figli, ognuno dei quali può avere i propri figli, e così via. In questo modo,
possiamo usare il sistema di menu per strutturare il nostro sito in sezioni e
sottosezioni.

Viste
Elencare contenuti e dati è sempre una capacità importante che i CMS bramano, e
questo è ciò che fa View in Drupal. E lo fa bene. Include cose come filtri, ordinamenti,
opzioni di visualizzazione e molte altre caratteristiche

Moduli (Form)
A meno che il vostro sito non abbia tre pagine e cinque paragrafi di testo, la
probabilità che abbiate bisogno di catturare l'input dell'utente attraverso qualche tipo
di modulo è molto alta.

Plugin
I plugin sono componenti di codice riutilizzabile che possono essere utilizzati e gestiti
da un sistema centrale. Tipicamente, vengono utilizzati quando un sistema gestisce
un compito in un certo modo (plugin A) ma permette ad altri moduli di fornire modi
diversi di gestire quel compito (plugin B o C).

Temi twig
Un cambiamento importante in Drupal 8 rispetto alle versioni precedenti è il
passaggio al sistema di template open source Twig (https://twig.sensiolabs.org/).
Questo rende la separazione della logica dalla presentazione molto più chiara e rende
il lavoro degli sviluppatori frontend molto più facile, per non parlare della maggiore
sicurezza. E con Drupal 9, la versione di Twig è stata finalmente aggiornata da 1 a 2.

Strumenti per sviluppare in Drupal


L’ambiente di sviluppo più flessibile è quello basato su Docker. Si può facilmente
iniziare con: https://github.com/wodby/docker4drupal. Con Docker, con pochi
comandi, si può avere un sito Drupal 9 attivo e funzionante e si può testare il codice
molto facilmente. Per quanto riguarda un editor di codice, personalmente uso
PhpStorm (come molti altri), ma sei libero di usare qualsiasi IDE tu voglia perché
Drupal stesso non richiede nulla di speciale. Usate comunque un qualche tipo di IDE
perché vi renderà la vita molto più facile.

Stile di codifica standard


https://www.drupal.org/docs/develop/standards/coding-standards

Lista dei cambiamenti di Drupal


https://www.drupal.org/list- changes/drupal

Drush
Drush (http://drupal.org/project/drush) fornisce un'interfaccia a riga di comando per
Drupal e può essere usato per eseguire compiti con poche pressioni di tasti nella
console. Durante lo sviluppo, spesso dobbiamo pulire la cache, eseguire compiti
specifici o distribuire dati a un server remoto. Drush può aiutare a realizzare compiti
come questi.

Impostazioni dello sviluppatore


Mentre si fa sviluppo locale, è utile (a volte) disabilitare cose come il caching per
essere più veloci. Per farlo, possiamo usare impostazioni locali che disabilitano il
caching, impediscono l'aggregazione di file CSS e JavaScript, e fanno altre cose simili.
Queste impostazioni si trovano all'interno del file example.settings.local.php nella
cartella /sites . Per beneficiarne, è necessario assicurarsi che siano incluse nel proprio
file settings.php principale (copiandole all'interno o includendo un file come questo).
1
Creare il tuo primo
modulo
Ora che abbiamo coperto alcuni degli aspetti introduttivi dello sviluppo dei moduli di
Drupal, è il momento di tuffarsi nel cuore di ciò che stiamo facendo: la creazione dei moduli.
Ecco alcuni degli argomenti importanti che tratteremo in questo capitolo:
- Creare un nuovo modulo per Drupal 9 - i file necessari per iniziare
- Creazione di una rotta e di un controllore
- Creazione e utilizzo di un servizio
- Creazione di un modulo
- Creare un blocco personalizzato
- Lavorare con i link
- Usare il distributore di eventi
Concretamente, in questo capitolo, creeremo un nuovo modulo personalizzato chiamato
Hello World. In questo modulo, definiremo una rotta che mappa ad un controllore e che
emette questo vecchio messaggio di programmazione. Quindi, questa sarà la nostra prima
vittoria.

Creazione del primo modulo


In questo esempio, vediamo come definire un modo che dispone di un controller che mostra
un messaggio all’utente, quindi vedremo cosa sono i servizi e come interagiscono con il
container dei servizi. Poi, creeremo un modulo in cui un amministratore sarà in grado di
sovrascrivere il messaggio mostrato sulla nostra pagina. Sarà memorizzato nella
configurazione, e modificheremo il nostro servizio per fare uso di tale configurazione. Il
punto chiave qui sarà l'uso dell'API Form. Tuttavia, parleremo anche di come memorizzare
alcuni valori di configurazione di base e aggiungere dipendenze ai nostri servizi esistenti.
Infine, vogliamo diventare un po' più flessibili. Perché gli utenti dovrebbero essere accolti
solo in una pagina specifica? Creeremo un blocco personalizzato che può essere posizionato
ovunque nel sito e mostrerà lo stesso messaggio. Vedremo anche come lavorare con i link in
modo programmatico in Drupal. Questo è un compito molto comune che ogni sviluppatore
Drupal ha bisogno di fare molto spesso. Inoltre, vedremo anche come utilizzare il
componente Event Dispatcher e, cosa più importante, come sottoscrivere gli eventi.
Creare un semplice modulo per Drupal 9 non è difficile. Hai solo bisogno di un file per
farlo riconoscere dal core di Drupal e per poterlo abilitare.
I moduli vanno all'interno della cartella /modules dell'applicazione Drupal. All'interno
della cartella /modules ci può essere una cartella /contrib, che contiene i moduli che
non sono necessari al funzionamento del core di Drupal, ma che possono aggiungere
funzionalità e a differenza dei moduli del core di Drupal possiamo disattivarli e attivarli
in base alle nostre esigenze. Poi può esserci una cartella /custom, dove mettiamo i
moduli che scriviamo su misura per la specifica applicazione. Ed è qui che metteremo il
nostro modulo personalizzato. Inizieremo creando una cartella all’interno di /modules
che rappresenta il nome del nostro modulo. All'interno di questa cartella, avremo
bisogno di creare un file informativo che descriva il nostro modulo. Questo file si
chiama nome_modulo.info.yml. Questa struttura di denominazione è importante: prima
il nome del modulo, poi info, seguito dall'estensione .yml. All'interno di questo file,
dovremo aggiungere alcune informazioni minime che descrivono il nostro modulo.
Useremo qualcosa di simile a questo:

name: First Module


description: First module
moduletype: module
core_version_requirement: ^9
package: Custom

Alcune di queste cose si spiegano da sole, ma vediamo cosa significano queste linee:

- Le prime due rappresentano il nome e la descrizione del modulo.

- La chiave type ci dice che questo è un file informativo di un modulo piuttosto che di un
tema.

- La chiave core_version_requirement specifica che questo modulo funziona con la versione 9


di Drupal, e non sarà installabile su versioni precedenti o future.

- Infine, package è il nome della categoria del modulo e sarà visualizzato nella schermata di
amministrazione dei moduli.

Questo è praticamente tutto. Il modulo può ora essere abilitato sia attraverso l'UI in
/admin/modules o tramite Drush usando il comando drush en first_module.
Module dependencies: Se il tuo modulo dipende da altri moduli, puoi specificarlo
nel suo file info in questo modo:

dependencies:
- drupal:views
- ctools:ctools

Le dipendenze dovrebbero essere nominate nel formato project:module, dove


project è il nome del progetto Drupal e module è il nome del modulo.
Configuration: Se il tuo modulo ha un modulo di configurazione generale che
centralizza le opzioni di configurazione del modulo, puoi specificare il percorso
di quel modulo nel file info. Facendo così si aggiungerà un link a quel modulo
nella pagina dell'interfaccia utente admin/moduli dove i moduli vengono
installati:
Configuration: Se il nostro modulo ha un modulo di configurazione generale
possiamo aggiungere nel file di info.yml del modulo la dipendenza nel modo
seguente:

configure: module_name.configuration_route_name

Implementazione suggerimento di aiuto del nostro


modulo (Hook di help)
Quando Drupal incontra un evento per il quale esiste un hook (un suggerimento),
cercherà in tutti i moduli le implementazioni degli hook corrispondenti. Come fa ad
individuare l’hook corrispondente ad un certo modulo, e soprattutto a quell’evento che si
è verificato? Praticamente cerca delle funzioni all’interni di tutti i moduli che sono nel
formato nome_modulo_nome_hook, dove nome_hook è praticamente il nome dell’hook
mentre il nome_modulo è il nome del modulo, ad esempio se vogliamo aggiungere un
hook di aiuto per il nostro primo modulo dobbiamo implementare una funzione
all’interno del file del modulo nome_modulo.module chiamata first_module_help(),
questo hook viene invocato quando premiamo sul pulsante di un modulo nel
pannello di amministrazione, e vengono eseguite le azioni scritte nella logica del modulo.
Le implementazioni degli hook vanno tipicamente all'interno di un file .module, quindi
creiamone uno nella nostra cartella dei moduli chiamato first_module e mettiamo un tag
PHP di apertura in cima. Quindi, possiamo avere la seguente implementazione
hook_help() all'interno (e tipicamente tutte le altre implementazioni di hook):
use Drupal\Core\Routing\RouteMatchInterface;

/**
* Implements hook_help().
*/
function first_module_help($route_name, RouteMatchInterface
$route_match) {
switch ($route_name) {
case 'help.page.first_module ':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Questo è un form di esempio.') .
'</p>';
return $output;

default:
}
}

Come potete vedere, il nome della funzione rispetta il formato precedentemente


menzionato - nome_modulo_hook_name - perché stiamo implementando
hook_help. Quindi, abbiamo sostituito hook con il nome del modulo e hook_name
con help. Inoltre, questo particolare hook prende due parametri che possiamo usare
al suo interno; anche se, nel nostro caso, ne usiamo solo uno, cioè il nome della route.
Lo scopo di questo hook è quello di fornire a Drupal del testo di aiuto su ciò che fa
questo modulo. Non implementerai sempre questo hook, ma è bene esserne a
conoscenza. Quindi, in questa implementazione, diremo a Drupal quanto segue: se
un utente sta guardando la route (ovvero la pagina) help del nostro modulo, mostra
le informazioni contenute nella variabile $output.

Figure 2.1: Pagina di Help del nostro primo modulo


Prima di andare avanti, aggiungiamo rapidamente un commento al file
nome_modulo.module per assicurarci di rispettare gli standard di codifica di Drupal.
Quindi, aggiungiamo quanto segue all'inizio del file .module:
/**
* @file
* First module file.
*/

Route (o percorso) e Controller


Il primo vero pezzo di funzionalità che abbiamo deciso di creare è una pagina Drupal
con un form semplice di recensioni (o commenti) che ci permette di visualizzare i
commenti inviati dagli altri utenti e di inviarne di nuovi. Per fare questo, abbiamo
bisogno di due cose: una route (un percorso) e un controllore. Quindi, iniziamo con la
prima.

Il percorso (o Route)
All'interno del nostro modulo, avremo bisogno di creare il nostro file di routing, che
conterrà tutte i nostri percorsi definite staticamente. Il nome di questo file sarà
first_module.routing.yml. A questo punto, presumo che abbiate capito qual è la
convenzione di denominazione dei file nella creazione di modulo Drupal. Quello che
dobbiamo scrivere nel nostro primo file di configurazione del percorso è quanto
segue:
first_module.form-recensioni:
path: '/form-recensioni'
defaults:
_controller: Drupal\first_module\Controller\
FirstModuleController::createForm
_title: Form recensioni
requirements:
_permission: 'access content'

Questa è la nostra prima definizione di un percorso. Inizia con il nome del percorso
(first_module.form-recensioni), seguito da tutte le informazioni necessarie su di essa sotto, in
un array multidimensionale formattato in formato YAML. La pratica standard è quella di far
iniziare il nome del percorso con il nome del modulo in cui si trova.
In primo luogo, abbiamo la path, che indica il percorso su cui vogliamo che questa route
lavori. Poi, abbiamo il campo defaults, che di solito contiene informazioni rilevanti per i
Controller responsabili di restituire qualcosa quando si accede a questo percorso. Nel nostro
caso, impostiamo il Controller e il metodo responsabile della restituzione della pagina, così
come il suo titolo. Infine, abbiamo una sezione requirements, che di solito ha a che fare con le
condizioni che devono essere soddisfatte perché questa rotta sia accessibile - cose come i
permessi e il formato. Nel nostro caso, richiederemo agli utenti di avere il permesso di accesso
ai contenuti, che la maggior parte dei visitatori avrà. Questo è tutto ciò di cui abbiamo
bisogno per la definizione della nostra prima rotta. Ora, avremo bisogno di creare il
controllore che vi mappi e che possa consegnare la risposta all'utente.

Variabili delle rotte


Un requisito molto comune è quello di avere dei parametri delle rotte variabili che viene
utilizzato dal Controller che gestisce la rotta, per esempio, l'ID o l'alias del percorso della
pagina che si vuole mostrare. Questi parametri possono essere aggiunti così:

path: '/form-recensioni/{param}'

Qui, {param} mapperà il suo contenuto in una variabile $param che viene passata
come parametro al Controller che gestisce questa rotta. Quindi, se l'utente va al
percorso form-recensioni/ciao, la variabile $param avrà il valore ciao e il Controllore
potrà utilizzarlo. Inoltre, Drupal è dotato di convertitori di parametri che trasformano
il parametro in qualcosa di più significativo. Per esempio, un'entità può essere
caricata automaticamente e passata al controller direttamente al posto di un ID.
Inoltre, se non viene trovata nessuna entità, la rotta si comporta come 404,
risparmiando un bel po' di righe di codice. Per ottenere questo, avremo anche bisogno
di descrivere il parametro in modo che Drupal sappia come autocaricarlo. Possiamo
farlo aggiungendo un'opzione di rotta per quel parametro:
options:
parameters:
param:
type: entity:node

Quindi, ora abbiamo mappato il parametro {param} al tipo di entità nodo. Quindi, se l'utente va
su hello/1, verrà caricato il nodo con l'ID di 1 (se esiste). Possiamo fare di meglio. Se, invece di
{param}, nominiamo il parametro {node} (il nome della macchina del tipo di entità), possiamo
evitare di dover scrivere completamente l'opzione parametri nella rotta. Drupal capirà che si
tratta di un'entità e cercherà di caricare quel nodo da solo.

Namespaces
Prima di andare avanti con il Controller che ci siamo prefissati di scrivere, analizziamo la
situazione degli spazi dei nomi in Drupal e come la struttura delle cartelle deve essere
all'interno di un modulo. Drupal usa lo standard PSR-4 per l'auto caricamento degli spazi dei
nomi. In effetti, questo significa che lo spazio dei nomi di tutte le classi del core di Drupal e
dei moduli inizia con \Drupal. Per i moduli, lo spazio dei nomi di base è
\Drupal\nome_modulo, dove nome_modulo è il nome della macchina del modulo. Questo
corrisponde quindi alla cartella /src che si trova all'interno della directory del modulo.
Quindi, essenzialmente, avremo bisogno di una cartella /src all'interno del nostro modulo per
mettere tutte le nostre classi che hanno bisogno di essere caricate automaticamente. Quindi,
possiamo andare avanti e crearla.

Il Controller
Ora che abbiamo trovato dove dobbiamo posizionare il nostro Controller, iniziamo a
creare una cartella Controller all'interno della cartella /src del nostro modulo. Anche se
non è obbligatorio, questa è una pratica standard per il posizionamento del Controller.
All'interno di questa cartella, possiamo avere il nostro primo file di classe Controller:
HelloWorldController.php.
All'interno del file, abbiamo di nuovo qualcosa di semplice (dopo i tag PHP di
apertura):

namespace Drupal\hello_world\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
* Controller for the salutation message.
*/
class HelloWorldController extends ControllerBase {
/**
* Hello World.
*
* @return array
* Our message.
*/
public function createForm() {
return [
'#markup' => $this->t('Hello World'),
];
}
}

Come prima cosa dichiariamo il namespace, abbiamo visto prima come si


definisce il namespace. Poi, definiamo la classe Controller, che estende
ControllerBase. Nella definizione della route, abbiamo fatto riferimento a un
metodo createForm() su questo Controller. Quello che fa questo metodo è
soltanto restituire un semplice codice html con il testo Hello World. Dopo che il
Controller restituisce questo array, ci sarà un EventSubscriber che prende
questo array, lo esegue attraverso il tema di Drupal, e restituisce la pagina
HTML come risposta. Il contenuto effettivo restituito dal Controller sarà
incorporato nel blocco di contenuto della pagina principale.
Ora, il nostro semplice Controller è finito. Se cancelliamo la cache e andiamo su
/form-recensioni, dovremmo incontrare una nuova pagina che restituisce la
pagina con scritto Hello World. Per fare il clean della cache ci basta utilizzare il
comando
php vendor/drush/drush/drush cr
Servizi
Essendo che vogliamo che i Controller siano molto semplici, quello che facciamo è definire la
logica come servizi, e chiamare quei servizi con i controller, tipo se nell’esempio di prima,
vogliamo stampare un messaggio diverso (e non hello world sempre), e quindi un messaggio
dinamico ad esempio, a seconda dell’ora, implementare il tutto con un Controller complica di
molto le cose per cui conviene definire un servizio e richiamarlo con il Controller, in questo
modo se vogliamo anche usare quella logica da un’altra parte, è molto più semplice
richiamare quel servizio con un Controller.

Che cos’è un servizio?


Un servizio è un oggetto che viene istanziato da un Service Container ed è usato per
gestire operazioni in modo riutilizzabile, per esempio, l’interazione con un database,
l’esecuzione di calcoli e così via. Inoltre, può utilizzare delle dipendenze (altri servizi). I
servizi sono una parte fondamentale del principio di dependency injection (DI) che è
comunemente usato nelle moderne applicazioni PHP. Questi servizi sono registrati
globalmente con il Service Container e istanziati una sola volta per richiesta, questo
significa che se vengono alterati durante una richiesta da parte di un utente, rimangono
alterati anche alla richiesta successiva da parte di quell’utente. In sostanza, sono dei
singleton. Quindi, dovreste scrivere i vostri servizi in modo tale che rimangano
immutabili, e che la maggior parte dei dati che hanno bisogno di elaborare provengano
da una dipendenza o siano passati dal client che lo usa (e non lo influenzino). Anche se
questo è il caso della maggior parte dei servizi, ce ne sono alcuni che funzionano
diversamente, nel senso che vengono ricreati ad ogni richiesta. Molte definizioni di
servizi del core di Drupal possiamo trovarli nel file services.yml che è situato nella
directory /core del nostro sito drupal. Inoltre, anche i moduli del core di Drupal hanno
definizioni di servizi per cui si potrebbe dare un’occhiata anche lì.
Il servizio HelloWorldSalutation
Ora che abbiamo un'idea generale di cosa sia un servizio, creiamone uno per vedere
tutto questo in pratica. Proviamo a fare un servizio che implementi un saluto
dinamico in base all’ora del giorno. Quindi, creiamo una classe
HelloWorldSalutation.php e la mettiamo nella cartella /src del nostro modulo.

namespace Drupal\first_module;

use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
* Prepares the salutation to the world.
*/
class HelloWorldSalutation {

use StringTranslationTrait;

/**
* Returns the salutation
*/
public function getSalutation() {
$time = new \DateTime();
if ((int) $time->format('G') >= 00 && (int) $time-
>format('G') < 12) {
return $this->t('Good morning world');
}

if ((int) $time->format('G') >= 12 && (int) $time-


>format('G') < 18) {
return $this->t('Good afternoon world');
}

if ((int) $time->format('G') >= 18) {


return $this->t('Good evening world');
}
}
}

Lo StringTranslationTrait serve per la traduzione del saluto. Il metodo


getSalutation() restituisce un saluto diverso a seconda dell'ora del giorno. La
funzione nativa di PHP per ottenere l’ora che abbiamo usato è DateTime(). Ma
potevamo usare anche il servizio di drupal per ottenere l’ora corrente, ovvero
Drupal\Component\Datetime\Time. Ora per trasformare questa classe PHP in un
servizio quello che dobbiamo fare è semplice, ovvero definire il file YAML dei servizi
del nostro modulo, ovvero il file first_module.services.yml. Questo file inizia con la
parola chiave services, sotto la quale ci saranno tutte le definizioni dei servizi del
nostro modulo. Quindi il file è il seguente:

services:
first_module.saluto:
class: Drupal\first_module\HelloWorldSalutation

Questa è la definizione del servizio più semplice che si possa avere. Gli si dà un nome
(first_module.saluto) e lo si mappa su una classe da istanziare. Una pratica standard
è far iniziare il nome del servizio con il nome del modulo. Una volta cancellata la cache, il
servizio sarà registrato sul Service Container e sarà disponibile all'uso.
Etichettare (o taggare) i servizi
Le definizioni dei servizi possono anche essere etichettate per informare il contenitore se
servono a uno scopo specifico. Per esempio, se volessimo etichettare il servizio
first_module.saluto, sarebbe qualcosa del genere:
first_module.saluto:
class: Drupal\first_module\HelloWorldSalutationtag:
- {name: tag_name}

Usare i servizi in Drupal


Ci sono essenzialmente due modi di usare i servizi: staticamente e per iniezione (injected). Il
primo è fatto con una chiamata statica al service container, mentre il secondo usa l'iniezione
delle dipendenze (Dependency Injection) per passare l'oggetto invocando il costruttore
quando richiesto (o in alcuni rari casi, invocando il metodo setter).
Staticamente, si usa la classe globale \Drupal per istanziare un servizio:

$service = \Drupal::service('hello_world.salutation');

Questo è il modo in cui usiamo i servizi nei file .module e nelle classi in cui non possiamo
usare la dependency injection.
Il modo corretto di usare i servizi è quello di iniettarli dove necessario. Certo, questo
approccio richiede un po' più di tempo ma, man mano che si procede diventerà tutto più
semplice.
Iniettare il servizio in un Controller.
Avremo bisogno di aggiungere del codice al Controller (tipicamente all'inizio della
classe, in modo da poter identificare immediatamente la presenza di questo codice
quando lo si guarda):
/**
* @var \Drupal\first_module\HelloWorldSalutation
*/
protected $salutation;

/**
* HelloWorldController constructor.
*
* @param \Drupal\first_module\HelloWorldSalutation $salutation
*/
public function construct(HelloWorldSalutation $salutation) {
$this->salutation = $salutation;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get(first_module.salutation')
);
}

Oltre a questo, assicuratevi di includere le dichiarazioni d'uso pertinenti all'inizio del file:

use Drupal\first_module\HelloWorldSalutation;
use Symfony\Component\DependencyInjection\ContainerInterface;

Quindi, per prima cosa, definiamo il costruttore del Controller, che prende il nostro
servizio come argomento e lo memorizza come proprietà. Questo è di solito il primo
metodo della classe. Attraverso il metodo create() il costruttore del Controller richiede
al Service Container il servizio o i servizi necessari, e sarà il Service Container a
restituire il giusto servizio o servizi al costruttore del Controller. Questo è di solito il
secondo metodo in una classe. È preferibile questo ordine perché è molto facile
controllare se questi metodi sono presenti.
Ora che abbiamo iniettato il servizio, possiamo usarlo per rendere il saluto del nostro controller
dinamico:

return [
'#markup' => $this->salutation->getSalutation(),
];

Ecco fatto. Ora il nostro saluto dipende dall'ora del giorno e il nostro Controller dipende
dal nostro servizio di saluto.

Form API
La nostra pagina visualizza un saluto in modo dinamico, a seconda dell'ora del
giorno. Tuttavia, ora vogliamo che un amministratore specifichi quale dovrebbe
essere il saluto, in altre parole, gli permettiamo di sovrascrivere il comportamento
predefinito del nostro saluto.
Gli ingredienti per ottenere ciò saranno i seguenti:
- Una route (una nuova pagina) che visualizza un modulo dove l'amministratore
può impostare il saluto
- Un oggetto di configurazione che memorizzerà il saluto
Nel costruire questa funzionalità, daremo anche un'occhiata a come aggiungere
una dipendenza al nostro servizio esistente. Quindi, cominciamo con la nostra
nuova route, che naturalmente va dentro il file first_module.routing.yml che
abbiamo già creato:

first_module.greeting_form:
path: '/admin/config/salutation-configuration'
defaults:
_form: Drupal\first_module\Form\SalutationConfigurationForm
_title: 'Salutation configuration'
requirements:
_permission: 'administer site configuration'
La maggior parte della definizione di questa route è la stessa che abbiamo visto prima.
C'è un cambiamento, però, nel fatto che mappa ad un form invece che ad un Controller.
Inoltre, poiché il percorso è all'interno dello spazio di amministrazione, userà il tema di
amministrazione del sito. Quello che resta da fare ora è creare la nostra classe form
all'interno della cartella /Form del nostro spazio dei nomi (una cartella standard per la
memorizzazione dei form, ma non obbligatoria). Grazie al potere dell'ereditarietà, il
nostro form è in realtà molto semplice. Quindi, di seguito abbiamo il nostro form:
namespace Drupal\first_module\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
* Configuration form definition for the salutation message.
*/
class SalutationConfigurationForm extends ConfigFormBase {

/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['first_module.custom_salutation'];
}

/**
* {@inheritdoc}
*/
public function getFormId() {
return 'salutation_configuration_form';
}

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface
$form_state) {
$config = $this->config('hello_world.custom_salutation');

$form['salutation'] = array(
'#type' => 'textfield',
'#title' => $this->t('Salutation'),
'#description' => $this->t('Please provide the salutation
you want to use.'),
'#default_value' => $config->get('salutation'),
);

return parent::buildForm($form, $form_state);


}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface
$form_state) {
$this->config('hello_world.custom_salutation')
->set('salutation', $form_state->getValue('salutation'))
->save();

parent::submitForm($form, $form_state);
}
}

Cancella la cache e navigando in admin/config/salutation-configuration visualizzerai


un semplice form di configurazione attraverso il quale puoi salvare un messaggio di
saluto personalizzato:

Figura 2.3: Form di configurazione di un saluto.


Più avanti, faremo uso di questo valore. Tuttavia, prima, parliamo un po' dei form in
generale, e poi di questo form in particolare.
Un form in Drupal è rappresentato da una classe che implementa FormInterface.
Di solito, estendiamo la classe FormBase o la classe ConfigFormBase, a seconda
del suo scopo. In questo caso, abbiamo creato un modulo di configurazione,
quindi abbiamo esteso da quest'ultima classe.

Ci sono quattro metodi principali che entrano in gioco in questa interfaccia:


- getFormId(): Restituisce un nome unico per il form, serve per identificarlo.
Quindi se poi definiamo i metodi hook, questo id ci serve per intercettare i
form.
- buildForm(): Con questo metodo dobbiamo costruire il form e restituirlo, il
form lo costruiamo con un array di definizioni di elementi del form e alcuni
metadati extra.
- validateForm(): Questo metodo viene chiamato per validare i campi del
form. Prende in input il form e un oggetto FormStateInterface che contiene i
valori inviati. È possibile contrassegnare i valori non validi sui rispettivi
elementi del form, il che significa che il modulo non viene inviato ma
aggiornato (con gli elementi sbagliati evidenziati).
- submitForm(): Questo metodo viene chiamato quando il modulo viene
inviato (se la validazione è passata senza errori). Riceve gli stessi parametri di
validateForm(). Si possono eseguire operazioni come il salvataggio dei valori
inviati.
Per approfondire i form possiamo andare alla seguente pagina, e visionare le API dei
form di Drupal:
https://api.drupal.org/api/drupal/elements/9.0.x
Da un punto di vista di dependency injection, i form possono ricevere argomenti dal
Service Container nello stesso modo in cui abbiamo iniettato il servizio salutation nel
nostro Controller. Infatti, la classe ConfigFormBase, che stiamo estendendo nel nostro
esempio, inietta il servizio config.factory perché ha bisogno di usarlo per leggere e
memorizzare valori di configurazione. Questo è il motivo per cui estendiamo da quel
form. Drupal è pieno di queste utili classi che possiamo estendere e che forniscono un
sacco di utile codice boilerplate (cioè codice riutilizzabile) che è molto comunemente
usato in tutto l'ecosistema Drupal.
Se il form che stiamo creando non riguarda operazioni di configurazione, allora ci conviene
estendere la classe FormBase, che fornisce alcuni metodi statici e implementa anche alcune
interfacce.
Nel nostro esempio non abbiamo usato il metodo validateForm(). Quest'ultimo non è
obbligatorio, ma se avessimo voluto implementarlo sarebbe potuto uscire qualcosa del
genere:
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface
$form_state) {
$salutation = $form_state->getValue('salutation');
if (strlen($salutation) > 20) {
$form_state->setErrorByName('salutation', $this->t('This
salutation is too long'));
}
}

In questo metodo di validazione, fondamentalmente controlliamo se il valore inviato per


l'elemento salutation è più lungo di 20 caratteri. Se è così, impostiamo un errore su
quell'elemento (per farlo diventare rosso di solito) e specifichiamo un messaggio di errore
sullo stato del form specifico per questo errore. Il modulo sarà quindi aggiornato e l'errore
sarà visualizzato, e il metodo del submit, in questo caso, non sarà chiamato.

Note
I messaggi di errore di validazione dei form, per impostazione predefinita,
sono stampati in cima alla pagina. Tuttavia, con il modulo Inline Form Errors
del core di Drupal, possiamo avere gli errori dei form stampati proprio sotto
agli elementi effettivi. Questo è molto meglio per l'accessibilità, così come per
la chiarezza quando si ha a che fare con form di grandi dimensioni. Nota che
l'installazione standard di Drupal 9 non ha questo modulo abilitato, quindi
dovrai abilitarlo se vuoi usarlo.

Se torniamo alla nostra classe form, vediamo anche uno strano metodo chiamato
getEditableConfigNames(). Questo è richiesto dal ConfigFormBaseTrait, che è usato nella
classe ConfigFormBase che stiamo estendendo. Quindi restituiamo un array di nomi di oggetti
di configurazione che questa form intende modificare. Questo perché ci sono due modi di
caricare gli oggetti di configurazione: uno che è un caricamento di oggetti di configurazione
modificabili e l’altro di sola lettura (immutabile). Con questo metodo, lo informiamo che
vogliamo modificare quell'oggetto di configurazione.
Come vediamo nella prima riga di buildForm(), stiamo usando il metodo config()
del tratto precedentemente menzionato per caricare il nostro oggetto di
configurazione modificabile dal Configuration factory di Drupal. Facciamo
questo per controllare il valore che è attualmente memorizzato in esso. Poi,
definiamo i nostri elementi del form (nel nostro caso, un semplice campo di
testo). Per #default_value (il valore presente nell'elemento quando l'utente
visualizza form), mettiamo quello che si trova nell'oggetto di configurazione.
Infine, alla fine del metodo, chiamiamo anche il metodo parent perché questo
fornisce il pulsante di invio del form, che per i nostri scopi è sufficiente.
L'ultimo metodo di cui abbiamo bisogno è il gestore del submit, che
fondamentalmente carica l'oggetto di configurazione modificabile, gli assegna il
valore inviato dall’utente e poi lo salva all’interno di questo oggetto di
configurazione modificabile. Infine, chiama anche il metodo parent, che invia
semplicemente un messaggio di successo all'utente sullo schermo usando il
servizio Messenger, un modo standard per mostrare all'utente un messaggio di
successo o di errore.

Alterare i form
Una cosa importante che farete come sviluppatori di form è alterare i form definiti da altri
moduli o dal core di Drupal. Possiamo cambiare i nostri moduli, e i nostri form come vogliamo,
ma come facciamo a cambiare i form scritti da altri? Niente di più facile praticamente
utilizziamo il metodo hook_form_alter(), con hook che ci basta sostituirlo con il nome del
modulo che contiene il form e possiamo alterare un qualsiasi form di un modulo
intercettandolo prima che venga elaborato per il rendering.

/**
* Implements hook_form_alter().
*/
function my_module_form_alter(&$form, \Drupal\Core\Form\
FormStateInterface $form_state, $form_id) {
if ($form_id === 'salutation_configuration_form') {
// Perform alterations.
}
}
Nel codice qui sopra, implementiamo il generico hook_form_alter(), che viene
lanciato per tutti i form quando vengono costruiti, e lo facciamo all'interno di un
modulo chiamato my_module. I primi due argomenti sono il form e lo stato del
form (lo stesso che abbiamo visto nella definizione del form), il primo è passato
per riferimento. Questo è il tipico concetto di alterazione: apportiamo modifiche a
una variabile esistente e non restituiamo nulla. Il terzo parametro è l'ID del form,
quello che abbiamo definito nel metodo getFormId() della nostra classe del form.
Controlliamo che il form sia corretto e poi possiamo fare delle modifiche al form.
Questo è, tuttavia, quasi sempre l'approccio sbagliato, perché l'hook viene sparato
per tutti i form indistintamente. Anche se in realtà non facciamo nulla per la
maggior parte di essi, è comunque una chiamata di funzione inutile, per non
parlare del fatto che se vogliamo modificare 10 form nel nostro modulo, ci saranno
ci saranno un sacco di condizionali if, ed è il prezzo che paghiamo per le funzioni
procedurali. Invece, in modo più efficiente possiamo fare così:

/**
* Implements hook_form_FORM_ID_alter().
*/
function my_module_form_salutation_configuration_form_
alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state,
$form_id) {
// Perform alterations.
}

Qui, stiamo implementando hook_form_FORM_ID_alter(), che è un hook dinamico di


alterazione in quanto il suo nome contiene l'ID effettivo del modulo che vogliamo
alterare. Così, con questo approccio, ci assicuriamo che questa funzione sia chiamata
solo quando è il momento di alterare il NOSTRO modulo. L'altro vantaggio è che se
abbiamo bisogno di modificare un altro modulo, possiamo implementare lo stesso tipo
di hook per quello e avere la nostra logica ben separata.

Custom submit handlers


Come possiamo modificare l’invio dei nostri form?
Nei form abbiamo la key #submit che contiene un array di un elemento ::submitForm
quindi ci basta accedere alla variabile $form passata per riferimento al nostro metodo di
alterazione del form, e passare il campo #submit, come di seguito:

/**
* Implements hook_form_FORM_ID_alter().
*/
function my_module_form_salutation_configuration_form_
alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state,
$form_id) {
// Perform alterations.
$form['#submit'][] = 'hello_world_salutation_configuration_
form_submit';
}

Quello che stiamo facendo in questo codice in figura, è praticamente alterare il submit
passando la nostra funzione di submit, ovvero
Questo è semplicemente il metodo submitForm() della classe del modulo. Quindi, quello che
possiamo fare è rimuovere questo elemento e aggiungere la nostra funzione, o semplicemente
aggiungere un altro elemento all'array:
hello_world_salutation_configuration_form_submit

E il la funzione di callback che abbiamo aggiunto all'array #submit è la seguente:


/**
* Custom submit handler for the form_salutation_configuration
form.
*
* @param $form
* @param \Drupal\Core\Form\FormStateInterface $form_state
*/
function my_module_salutation_configuration_form_submit(&$form,
\Drupal\Core\Form\FormStateInterface $form_state) {
// Do something when the form is submitted.
}

Quindi, la cosa bella è che si può scegliere di attaccare la propria funzione di submit
callback o sostituire quella esistente. Tenete a mente che l'ordine in cui si trovano
nell'array è l'ordine in cui vengono eseguite. Se vogliamo gestire la funzione di
validazione ci basta procedere nello stesso modo della key #submit, e usare la key
#validate al suo posto.

Rendering d ei form
Abbiamo già visto come mappare i form utilizzando le route, però ci sono casi in cui
abbiamo bisogno di renderizzare un form programmaticamente, sia all’interno di un
Controller o di un blocco, o ovunque vogliamo. Possiamo farlo utilizzando il servizio
FormBuilder. Il costruttore di form può essere iniettato usando la service key
form_builder o usato staticamente tramite la scorciatoia:

$builder = \Drupal::formBuilder();

Una volta che abbiamo il FormBuilder, possiamo costruire un form, così:


$form = $builder->getForm('Drupal\hello_world\Form\
SalutationConfigurationForm');

Nel codice sopra, $form sarà un array di rendering del form che possiamo restituire, per
esempio, all'interno di un Controller.

Dipendenze dei servizi (Service dependencies)


Precedentemente, abbiamo creato un form che permette agli amministratori di
impostare un messaggio di saluto personalizzato da mostrare nella pagina. Questo
messaggio è stato memorizzato in un oggetto di configurazione che ora possiamo
caricare nel nostro servizio HelloWorldSalutation. Quindi, facciamo proprio questo con
un processo in due fasi. Per prima cosa, dovremo modificare la definizione del nostro
servizio per dare al nostro servizio un argomento: il factory di configurazione (il
servizio responsabile del caricamento degli oggetti di configurazione). Ecco come
dovrebbe apparire ora la definizione del nostro servizio:
hello_world.salutation:
class: Drupal\hello_world\HelloWorldSalutation
arguments: ['@config.factory']

Abbiamo aggiunto quindi la key arguments, che è un array di nomi di servizi preceduti
da @. In questo caso, config.factory è il nome del servizio responsabile, che, se
controlliamo nel file core.services.yml, possiamo notare che mappa la classe
ConfigFactory di Drupal\Core\Config. Quindi, con questa modifica, alla classe
HelloWorldSalutation verrà passata un'istanza di ConfigFactory. Tutto quello che
dobbiamo fare ora è adattare la nostra classe per riceverla effettivamente:
/**
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;

/**

* HelloWorldSalutation constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_
factory
*/
public function construct(ConfigFactoryInterface $config_
factory) {
$this->configFactory = $config_factory;
}

Adesso possiamo usare il nostro oggetto di configurazione salvato nel form per
mostrare un saluto personalizzato, ci basta utilizzare la variabile
$config_factory, in cui abbiamo salvato il nostro saluto personalizzato quando
viene invocato il costruttore del servizio. Tuttavia, prima di farlo, dovremmo
anche usare la classe ConfigFactoryInterface all'inizio del file:

use Drupal\Core\Config\ConfigFactoryInterface;

Ora per mostrare il saluto personalizzato, all'inizio del metodo getSalutation()


possiamo aggiungere il seguente pezzo di codice:

$config = $this->configFactory->get('hello_world.custom_
salutation');
$salutation = $config->get('salutation');
if ($salutation !== "" && $salutation) {
return $salutation;
}

Con questa aggiunta, stiamo caricando l'oggetto di configurazione che abbiamo salvato
nel form, e da esso, richiediamo key salutation, dove, se vi ricordate, abbiamo
memorizzato il nostro messaggio. Se c'è un valore lì, lo restituiamo. Altrimenti, il codice
continuerà, e la nostra precedente logica di saluto basata sul tempo verrà applicata. Così
ora, se ricarichiamo la nostra pagina iniziale, il messaggio che abbiamo salvato attraverso
il modulo dovrebbe apparire. Se poi torniamo al modulo e rimuoviamo il messaggio, la
pagina dovrebbe tornare al saluto dinamico originale.
Diamo ora un'occhiata a come possiamo creare un blocco personalizzato che possiamo
mettere dove vogliamo e che produrrà la stessa cosa della nostra pagina.

Blocks
Blocks, since Drupal 8, are plugins. However, the blocks you create in the UI are content
entities and the placement of blocks (of both types) in the block layout are configuration
entities. So, the block system is a good example of how entities and plugins work hand in
hand in Drupal. We will talk in more detail about plugin types and entities later in the book.
So, how do we create a custom block plugin? All we need is one class, placed in the right
namespace—Drupal\module_name\Plugin\Block. In this case (with plugins), the
folder naming is important. The plugin discoverability is dependent on the plugin type
itself, and this one has the Plugin\Block namespace bit in it. But enough talk; let's
create a simple block that just renders the same as our Controller did previously, and
I will explain things along the way.

Il nostro primo plugin di blocco


So, this is our plugin class—HelloWorldSalutationBlock—which does just that:

namespace Drupal\hello_world\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\hello_world\HelloWorldSalutation;

/**
* Hello World Salutation block.
*
* @Block(
* id = "hello_world_salutation_block",
* admin_label = @Translation("Hello world salutation"),
* )
*/
class HelloWorldSalutationBlock extends BlockBase implements
ContainerFactoryPluginInterface {

/**
* The salutation service.