Asset Publisher

null Come sviluppare un modulo custom in Drupal 9

Techblog

Come sviluppare un modulo custom in Drupal 9

15 February 2023

Di Mauro Bartolommei, S2S

Cos'è un CMS

Un CMS (acronimo di Content Management System) è un applicativo per la gestione dei contenuti di un sito web, che permette al gestore dei contenuti stessi di gestirne la creazione, la modifica o l’eliminazione senza il bisogno di competenze in ambito di programmazione.

Un CMS quindi, offre molteplici vantaggi in termini di dinamicità degli aggiornamenti dei contenuti e di autonomia nella loro gestione.

La realizzazione di un sito web basato su un CMS è vantaggioso anche dal punto di vista dello sviluppo e della programmazione, in quanto gran parte del software lato server che ne gestisce i contenuti è già presente e pronto all’uso.

Dal punto di vista tecnico, possiamo definire un CMS come un’applicazione lato server, in genere appoggiata ad un Database, divisa in due parti: il back-end che si occupa dell’organizzazione dei contenuti ed il front-end che si occupa di mostrare i contenuti.

Esistono molte tipologie di CMS, alcuni a pagamento ed altri Open source (gratuiti) sviluppati da varie comunità di programmatori. Uno dei CMS Open source più famoso è Drupal.

Introduzione a Drupal

Drupal, oggi arrivato alla versione 9, è uno dei CMS più performanti, sviluppato con tecnologia PHP, è caratterizzato da una struttura estremamente flessibile che permette tramite specifiche Form per l’inserimento dei dati (CCK - Content Construction Kit) di avere un ambiente di lavoro in grado di costruire pagine in modo dinamico e che inoltre possiede un efficientissimo strumento chiamato VIEWS che tramite il CCK consente la ricerca e la visualizzazione dei dati inseriti nei moduli.

Le VIEWS (Viste), oltre a determinare la modalità di visualizzazione dei dati, consentono la ricerca degli stessi nel database e quindi la possibilità di cercare informazioni in maniera relativamente semplice.

Questa relazione perfetta tra CCK e VIEWS è una peculiarità non comune ad altri CMS che per realizzare ciò che Drupal ha già di base, necessitano di particolari estensioni (spesso di terze parti e non implementate in maniera nativa nel software).

Drupal possiede una struttura modulare che permette anche di estendere le funzionalità del CMS, attraverso l’integrazione di moduli sviluppati e aggiornati da una vasta comunità mondiale di programmatori.

In conclusione, sintetizzando possiamo dire che il funzionamento di Drupal è diviso in due fasi:

  • Inserimento dei dati attraverso Form altamente personalizzabili, realizzati dal CCK di Drupal;

  • Ricerca, elaborazione e presentazione dei dati (in base alle proprie necessità) con il module VIEWS.

La Dashboard di Amministrazione di Drupal 9

Come ogni CMS, anche in Drupal la gestione dei contenuti avviene tramite una Dashboard di Amministrazione, dalla quale sarà possibile personalizzare, settare e gestire ogni aspetto del sito web.

La Dashboard di Amministrazione è suddivisa in varie sezione raggruppate per tipologia, vediamo brevemente le tematiche più importanti racchiuse all’interno di queste sezioni.

 

Il Contenuto

Di default Drupal offre due tipologie di contenuto: Pagina Base e Articolo.

Pagina Base: è una tipologia di contenuto che solitamente viene collegata ad una voce di menù e permette di presentare dati che cambiano di rado (pagine statiche), come può essere una pagina di presentazione oppure una pagina con le informazioni per i contatti.

Articolo: è una tipologia di contenuto che viene usata nel sito quando si ha necessità di avere informazioni dinamiche, ovvero che cambiano spesso nel corso del tempo (come ad esempio il post di un blog).

E’ possibile creare tipi di Contenuto personalizzati per venire incontro a qualsiasi esigenza, specificandone le caratteristiche attraverso il “Tipo di contenuto”.

Il Tipo di Contenuto

E’ possibile specificare cosa andrà a costituire una pagina e quindi predisporne i campi (testo, immagini, select, etc..) utili alla realizzazione di una pagina personalizzata in modo da poter creare varie tipologie di Contenuto, ognuno adatto a scopi specifici.

Il Layout dei Blocchi

Permette la gestione del posizionamento dei vari elementi di blocco che costituiscono il sito web, all’interno del layout del tema di Front-end. Ad esempio, da qui è possibile stabilire in quale “Regione” del template, sarà posizionato un determinato elemento come un menù o altro (blocco).

I Menù

Oltre alla gestione del Menù di Navigazione principale, è possibile crearne di nuovi e gestirli in ogni aspetto.

Le Tassonomie

Amministra un sistema avanzato di gestione delle categorie che divide gli argomenti in “Vocabolari” (categorie) contenenti i “Termini” (sotto-categorie).

Le Viste

Views (viste) è un modulo di default e gestisce le modalità di visualizzazione per alcuni tipi di blocco e contenuti, recuperando liste di nodi e filtrandoli per particolari criteri. Detto in altri termini, fondamentalmente il modulo Views scrive una query e mostra i dati in base ai parametri forniti attraverso un’interfaccia grafica.

L’Aspetto

Permette di personalizzare l’aspetto di Drupal (sia lato front-end che back-end) attraverso i numerosi Temi grafici scaricabili dalla rete.

Alcuni temi, sono già disponibili per la configurazione al momento dell’installazione, altri invece sono prelevabili dal web o è possibile crearne di propri.

Le Persone

Tramite il sistema ACL (Access Control List) di Drupal è possibile aggiungere e gestire utenti registrati all’interno del sito specificando per ogni utente il Ruolo, i Permessi e la tipologia dei Contenuti ai quali è abilitato.

Le Estensioni (Moduli)

Drupal è un CMS che basa le proprie funzionalità sui moduli. Tramite l’aggiunta di nuovi moduli possiamo estendere le funzionalità di Drupal. Nell’installazione standard sono già forniti alcuni moduli utili alle funzionalità del CMS ma è possibile aggiungerne di nuove, installando nuove estensioni.

I moduli utilizzati devono essere abilitati (installati). E’ possibile notare che alcuni moduli sono dipendenti da altri, ovvero per poterli utilizzare è necessario installare anche i moduli dai quali dipendono.

Di solito è possibile reperire estensioni per qualsiasi tipologia di funzionalità si necessiti, ma a volte potrebbe esistere la necessità di implementarne una personalizzata.

I Moduli di Drupal

Analoghi alle Estensioni di Joomla! e i plug-in  di Worpress, i Moduli di Drupal sono un add-on per il sistema che permette di estendere la lista delle caratteristiche di base del CMS. La gestione dei moduli è raggiungibile dal Pannello di Amministrazione selezionando la voce “Gestisci” e poi la voce “Estensioni”.

Possiamo suddividere i moduli di Drupal in 3 tipologie:

  • Moduli base
    Di default Drupal è dotato di diversi moduli di base, molti dei quali già attivati al momento dell’installazione ed altri attivabili a seconda delle esigenze del sito che stiamo implementando.
     
  • Moduli Contributo - GPL
    Disponibili tramite il repository di Drupal, questi moduli sono sviluppati da terze parti e sono classificati in base alla versione compatibile con essi. Per ogni modulo è anche possibile leggere i commenti e le recensioni degli utenti che lo hanno provato. I moduli presenti nel repository sono scaricabili e utilizzabili gratuitamente (GPL: General Public Licence).
     
  • Moduli Premium
    Si tratta di moduli Drupal, sviluppati da terze parti ma di natura commerciale e quindi soggetti a pagamento per il loro utilizzo.

Ma come si fa a sviluppare un modulo custom per Drupal?

Sviluppare un modulo custom per Drupal 9

Vediamo passo passo come poter sviluppare un modo basilare che possieda un template custom all’interno del quale mostrare il titolo di un nodo e la tiplogia di contenuto di una specifica root gestita anch’essa dal modulo stesso tramite un router e un controller.

Come abbiamo anticipato, Drupal è sviluppato in linguaggio PHP pertanto sarà necessario conoscerlo per poter sviluppare e seguire questo tutorial.

Iniziamo a creare il nostro modulo custom.

Aprire la cartella Modules e creare una nuova cartella denominata “custom”.

All’interno della nuova cartella creiamo la cartella che darà anche il nome al nostro modulo custom, ad esempio “mio_modulo”.

Creiamo ora un file dal nome mio_modulo.info.yml

Indichiamo i parametri obbligatori per un modulo:

name: “Mio modulo”

type: module

core_version_requirement: ^9 (con questo indichiamo che il modulo sarà compatibile per le versioni di Drupal dalla 9 in poi).

Possiamo poi aggiungere ad esempio una descrizione, la versione, il package e le dipendenze che ci permetteranno di specificare da cosa dipende il nostro modulo.

name: 'Mio modulo'

type: module

core_version_requirement: ^9

description: "Il  mio primo modulo personalizzato"

version: 1.0

package: "mioModulo"

dependencies:

 - drupal:node

Spostandoci ora su Drupal nella schermata delle estensioni disponibili ci basterà cercare la parola “mio modulo” per vedere il nostro modulo custom disponibile e pronto all’installazione.

Selezioniamo il modulo e installiamolo.

 

Naturalmente il nostro modulo non è in grado di fare ancora niente, ma esiste e possiamo continuare ad implementarlo.

Sfruttare gli hooks di Drupal 9 per creare la pagina di aiuto del modulo

Alla pagina: https://api.drupal.org/api/drupal/core%21core.api.php/group/hooks/9.0.x sono elencate la funzioni  (hooks) del core di Drupal.

Gli hooks sono funzioni con nomi speciali che un modulo definisce e che vengono richiamate in momenti specifici per implementare dati comportamenti o manipolare i dati del database. Ogni hook ha un proprio nome e un insieme di parametri e un valore di ritorno definito e tale funzioni possono essere implementate dai nostri moduli custom.

Proviamo ad usare un hook per implementare la classica informazione di aiuto presente in ogni modulo.

Cerchiamo la funzione “hook_help” e clicchiamoci sopra per aprire la relativa pagina che descrive la funzione, i parametri accettati e quel che restituisce.

Scendiamo a fondo pagina fino all’esempio del code e copiamolo.

Torniamo quindi alla nostra cartella “mio_modulo” e creiamo un nuovo file: mio_modulo.module

Incolliamo il codice copiate e rinominiamo la funzione “hook_help” in “mio_modulo_help”.

Nel primo case dello switch impostiamo il nome del nostro modulo: “help.page.mio_modulo”.

Infine cancelliamo la parte relativa al secondo case.

<?php

/**

* Implements hook_help

*/

function mio_modulo_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {

  switch ($route_name) {

    // Main module help for the block module.

    case 'help.page.mio_modulo':

      return "<p>Questo è l'hook_help del nostro modulo</p>";

  }

}

 

Torniamo in Drupal e selezioniamo la voce “Aiuto” e nella schermata che si apre cerchiamo il nostro modulo. Selezionandolo vedremo il nostro messaggio di aiuto.

 

Continuare ad usare gli hooks di Drupal 9 per le funzioni di insert e update

Torniamo alla pagina degli hooks e cerchiamo la funzione hook_ENTITY_TYPE_insert e copiamo il codice così come fatto prima per incollarlo all’interno del file mio_modulo.module e cancelliamo tutto così:

function hook_ENTITY_TYPE_insert(Drupal\Core\Entity\EntityInterface $entity) {

}

 

Cambiamo il nome alla funzione da hook_ENTITY_TYPE_insert a “mio_modulo__ENTITY_TYPE_insert”.

Adesso dobbiamo cambiare ENTITY_TYPE nel tipo di entità che vogliamo richiamare.

Ad esempio con user se vogliamo che questa funzione sia chiamata all’entrata di un user o con un node se vogliamo interagire con un nodo.

Posso fare lo stesso ad esempio con l’hook  “hook_ENTITY_TYPE_update”.

Nel nostro esempio useremo node come ENTITY_TYPE che essendo un nodo dovrà richiamare NodeInterface dal core di Drupal.
 

<?php

use Drupal\node\NodeInterface;

/**

* Implements hook_help

*/

function mio_modulo_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {

  switch ($route_name) {

    // Main module help for the block module.

    case 'help.page.mio_modulo':

      return "<p>Questo è l'hook_help del nostro modulo</p>";

  }

}

/*

 * implements hoook_ENTITY_TYPE_update()

 *

 * @param \Drupal\node\NodeInterface $node

*/

function mio_modulo_node_update(\Drupal\node\NodeInterface $node) {

    dpm( "input: 'Drupal chiama il nostro hook_ENTITY_TYPE_update'");

}

/*

 * implements hoook_ENTITY_TYPE_insert()

 *

 * @param \Drupal\node\NodeInterface $node

*/

function mio_modulo_node_insert(\Drupal\node\NodeInterface $node) {

    dpm( "input: 'Drupal chiama il nostro hook_ENTITY_TYPE_insert'");

}

(dpm() è una funzione che stampa una variabile all’interno dell’area “messaggio della pagina”).

Torniamo a Drupal cancelliamo la cache e proviamo a creare un nodo, ad esempio una pagina base. Appena salviamo noteremo che verrà stampata la nostra frase personalizzata della funzione di insert.

Accadrà lo stesso alla modifica della pagina con l’hook di update.

 

Creare una root e il controller

In poche parole possiamo definire la root come una url con certe caratteristiche (ad esempio un titolo) e il suo controller è la classe PHP che la gestisce. Per prima cosa creiamo la nostra root. All’interno della cartella “mio_modulo” creiamo un file di nome “mio_modulo.routing.yml”.

All’interno del file dobbiamo creare la nostra rotta che deve prendere il nome dal nome del modulo:

“mio_modulo.prima_rotta”.

quindi dovremo indicare il path (nell’esempio /drupalUno è il nome del sito di esempio).

Per il momento imposteremo i permessi di accesso alla root come access content (quindi disponibile a tutti).

mio_modulo.prima_rotta:

  path: '/drupalUno/controller/pagina'

  defaults:

    _controller: ''

  requirements:

    _permission: 'access content'

 

Per impostare il controller, creiamo una nuova cartella “src” dentro la quale creeremo un’altra cartella “Controller” dove implementare la nostra classe controller.

Creiamo quindi il file “MioModuloController.php”.

 

<?php

class MioModuloController{

}

 

All’interno del controller, implementiamo una funzione dal nome “home()” nella quale istanziare un nuovo oggetto chiamato Response(). Response è un oggetto di Symfony (utilizzato da Drupal).

<?php

namespace Drupal\mio_modulo\Controller;

use Drupal\Core\Controller\ControllerBase;

use Symfony\Component\HttpFoundation\Response;

class MioModuloController extends ControllerBase{

    public function home(){

        return new Response("content: 'Questa è la risposta del controller'");

    }

}

Adesso che abbiamo creato la funzione home(), dobbiamo tornare sul file “mio_modulo.routing.yml” e specificare il nome del controller che avevamo lasciato vuoto.

mio_modulo.prima_rotta:

  path: '/drupalUno/controller/pagina'

  defaults:

    _controller: '\Drupal\mio_modulo\Controller\MioModuloController::home'

  requirements:

    _permission: 'access content'

Torniamo a Drupal e andiamo al path: nomesito/drupalUno/controller/pagina e vedremo stampata la risposta del controller.

Quello che noteremo però, sarà che la pagina mostrata è completamente bianca, priva del theme Drupal che contraddistingue le altre pagine. Vediamo quindi come risolvere questa cosa.

Migliorare il controller e mostrare la pagina di Drupal

Torniamo al file “MioModuloController.php” e modifichiamo il return con un render array con chiave #markup, come in figura

<?php

namespace Drupal\mio_modulo\Controller;

use Drupal\Core\Controller\ControllerBase;

use Symfony\Component\HttpFoundation\Response;

class MioModuloController extends ControllerBase{

    public function home(){

        return['#markup' => 'Questa è la risposta del controller'];

    }

}

Torniamo a Drupal e ricarichiamo la pagina e vedremo la risposta del controller finalmente all’interno del template Drupal.

La chiave #markup può essere sostituita anche dalla chiave #plain_text.

La differenza tra queste due chiavi è nel filtro che applica Drupal, con plain_text Drupal applica un filtro di testo puro.

 

Aggiungere parametri alla root

Possiamo fare in modo che il nostro controller funzioni anche per tutti i nodi figli del path specificato.

Nel file “mio_modulo.routing.yml” modificare il path aggiungendo una variabile, come in figura.

mio_modulo.prima_rotta:

  path: '/drupalUno/controller/pagina/{pagina}'

  defaults:

    _controller: '\Drupal\mio_modulo\Controller\MioModuloController::home'

  requirements:

    _permission: 'access content'

Torniamo quindi nel controller e passiamo al metodo home() il parametro $pagina e modifichiamo anche il testo di return usando il parametro passato.

<?php

namespace Drupal\mio_modulo\Controller;

use Drupal\Core\Controller\ControllerBase;

use Symfony\Component\HttpFoundation\Response;

class MioModuloController extends ControllerBase{

    public function home($pagina){

        return['#markup' => 'La pagina è: ' . $pagina];

    }

}

 

Torniamo in Drupal, puliamo la cache e proviamo a passare un percorso del tipo:  nomesito/drupalUno/controller/pagina/2

Vedremo che il nostro controller funzionerà anche per le pagine figlie.

Miglioriamo ancora il metodo home() sfruttando il core di Drupal e usando il NodeIterface e la sua variabile $node.

 

<?php

namespace Drupal\mio_modulo\Controller;

use Drupal\Core\Controller\ControllerBase;

use Drupal\node\NodeInterface;

use Symfony\Component\HttpFoundation\Response;

class MioModuloController extends ControllerBase{

    public function home(NodeInterface $node){

        return['#markup' => 'La pagina è: ' . $node->label()];

    }

}

Dobbiamo ora indicare al routing che la variabile è un nodo.

mio_modulo.prima_rotta:

  path: '/drupalUno/controller/pagina/{node}'

  defaults:

    _controller: '\Drupal\mio_modulo\Controller\MioModuloController::home'

  requirements:

    _permission: 'access content'

 

Torniamo in Drupal e cancelliamo la cache.

Ora se passiamo di nuovo il path  nomesito/drupalUno/controller/pagina/1 ed abbiamo almeno una pagina creata (quindi con id 1), il nostro controller stamperà il titolo della pagina. Naturalmente se ora indicheremo un path con un numero di node che non esiste, Drupal ci informerà che la pagina è inesistente.

 

Dare un po’ di formattazione al modulo

Torniamo al file: mio_modulo.module e creiamo una nuova funzione “mio_modulo_theme” implementando la funzione “hook_theme” (sempre dalla pagina: https://api.drupal.org/api/drupal/core%21core.api.php/group/hooks/9.0.x).

All’interno ritorniamo un array di array come indicato in figura creando un template di nome “modello”.

<?php

use Drupal\node\NodeInterface;

/**

* Implements hook_help

*/

function mio_modulo_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {

  switch ($route_name) {

    // Main module help for the block module.

    case 'help.page.mio_modulo':

      return "<p>Questo è l'hook_help del nostro modulo</p>";

  }

}

/*

 * implements hoook_ENTITY_TYPE_update()

 *

 * @param \Drupal\node\NodeInterface $node

*/

 

function mio_modulo_node_update(\Drupal\node\NodeInterface $node) {

    dpm( "input: 'Drupal chiama il nostro hook_ENTITY_TYPE_update'");

}

/*

 * implements hoook_ENTITY_TYPE_insert()

 *

 * @param \Drupal\node\NodeInterface $node

*/

function mio_modulo_node_insert(\Drupal\node\NodeInterface $node) {

    dpm( "input: 'Drupal chiama il nostro hook_ENTITY_TYPE_insert'");

}

/*

 * implements hoook_theme()

*/

function mio_modulo_theme($existing, $type, $theme, $path) {

  return [

    'modello' =>[

      'variables' =>[

        'etichetta' => NULL,

        'tipo' => NULL

      ]

    ]

  ];

}

Torniamo nel controller e modifichiamo il return del metodo home().

<?php

namespace Drupal\mio_modulo\Controller;

use Drupal\Core\Controller\ControllerBase;

use Drupal\node\NodeInterface;

use Symfony\Component\HttpFoundation\Response;

class MioModuloController extends ControllerBase{

    public function home(NodeInterface $node){

        return[

        '#theme' => 'modello',

        '#etichetta' => 'Questo è un titolo',

        '#tipo' => 'Pagina di base'

        ];

    }

}

        'tipo' => NULL

      ]

    ]

  ];

Al momento però non esiste ancora il template denominato “modello”, quindi dobbiamo provvedere. Creiamo allora una nuova cartella denominata “templates” all’interno della cartella “mio_modulo”.

Dentro questa nuova cartella creiamo un file “modello.html.twig” che compileremo come da immagine.

<div><h1>Etichetta: {{ etichetta }}</h1></div>

<div><p>Tipo: {{ tipo }}</p!</div>

 

Se torniamo in Drupal, cancelliamo la cache e torniamo al path:  nomesito/drupalUno/controller/pagina/1

vedremo il nostro template in azione.

Cerchiamo di rendere il tutto più dinamico, utilizzando il NodeInterface di Drupal e modifichiamo la pagina del controller come in figura:

<?php

namespace Drupal\mio_modulo\Controller;

use Drupal\Core\Controller\ControllerBase;

use Drupal\node\NodeInterface;

use Symfony\Component\HttpFoundation\Response;

class MioModuloController extends ControllerBase{

    public function home(NodeInterface $node){

        return[

        '#theme' => 'modello',

        '#etichetta' => $node->label(),

        '#tipo' => $node->bundle()

        ];

    }

}

Tornando ancora su Drupal e ripetendo quanto fatto sopra, otterremo il risultato in figura.

Conclusioni

Siamo riusciti a sviluppare un modulo molto semplice, ma che ci permette di comprendere le potenzialità nell’implementare dei moduli custom pensati per le nostre funzionalità e che si integrano perfettamente col core di Drupal. All’articolo è allegata anche la cartella “Code” con il codice completo di questo tutorial. Basterà copiare nel proprio progetto, la cartella “custom” (contenuta all’interno di “code”) per avere su Drupal il modulo disponibile all’installazione.