Guida Symfony in italiano | Aulab

GUIDE PER ASPIRANTI PROGRAMMATORI

Guida Symfony in italiano

imparerai le competenze chiave per lo sviluppo di applicazioni web con Symfony. Comincerai con i concetti fondamentali, come i framework web e il modello MVC, l’installazione di Symfony e la struttura di progetto. Imparerai a gestire controller, routing, viste con Twig, e accedere ai dati usando Doctrine ORM. Inoltre, esplorerai temi avanzati come la gestione dei form, il mapping delle relazioni, l’invio di email e l’autenticazione utente. Pronto a cominciare?

Immagine di copertina

Vuoi avviare una nuova carriera o fare un upgrade?

Trova il corso Digital & Tech più adatto a te nel nostro catalogo!

1

Introduzione a Symfony

1.1

Cos’è un framework per il web

Un framework può essere definito come un insieme strutturato e organizzato di librerie di codice, linee guida e strumenti che forniscono una base su cui gli sviluppatori possono costruire applicazioni software. In termini più formali, un framework è un ambiente di sviluppo che facilita la progettazione, l'implementazione e la manutenzione di applicazioni seguendo determinate architetture e best practices. Nel contesto dello sviluppo web, può essere visto come un insieme di componenti software che fornisce gli strumenti necessari per sviluppare applicazioni web. Questo può includere librerie per la gestione del database, template engine per la generazione di una pagina HTML, funzionalità per la gestione della sicurezza, della sessione utente e molto altro. In sostanza, un framework web è un ecosistema che permette agli sviluppatori di costruire applicazioni web in modo più efficiente e meno incline agli errori rispetto alla scrittura di codice plain. Vantaggi di un framework per il web In generale, utilizzare un framework non è indispensabile per creare una web app. Un framework può essere visto come uno strumento di supporto a disposizione dello sviluppatore che agevola e migliora la sua esperienza di sviluppo software. Possiamo riassumere i principali vantaggi forniti da un framework attraverso i seguenti punti:    Struttura e standard: uno dei principali vantaggi nell’utilizzo di un framework è la struttura standardizzata e la serie di convenzioni che impone allo sviluppatore web. Questo si traduce in molteplici benefici come aderenza alle best practices del linguaggio utilizzato e facilità di collaborazione e lavoro in team. Riutilizzo del codice: i framework offrono componenti riutilizzabili per funzionalità comuni a tutti gli applicativi web come autenticazione utente, interazione con database, gestione dei form, ecc., riducendo drasticamente il tempo e l’effort necessari per costruire software da zero. Di conseguenza, si riduce anche la quantità di codice da scrivere per "reinventare la ruota”, ovvero per scrivere funzionalità che non appartengono strettamente al problema che si vuole risolvere. Sicurezza: la sicurezza è un tema fondamentale, soprattutto in ambito web, in quanto ogni sito è potenzialmente esposto quotidianamente e costantemente a richieste da parte dei vari fruitori della rete Internet. I framework moderni solitamente includono funzionalità di sicurezza integrate, come la protezione contro SQL Injection e XSS (Cross-Site Scripting), che aiutano a proteggere l’applicazione da attacchi molto comuni nel mondo del web. Focus sulla soluzione: con tutti gli strumenti e le librerie messe a disposizione dal framework, gli sviluppatori possono concentrarsi sulla logica specifica dell’applicazione senza perdersi nei dettagli di basso livello o in problematiche comuni già affrontate da qualcun altro. Plain PHP vs Framework per il web Analizziamo un esempio di semplice codice sorgente, scritto in PHP, per affrontare i possibili problemi legati alla scrittura di codice plain senza avvalersi di alcun framework. <?php $conn = mysqli_connect("localhost", "user", "password", "db"); if (!$conn) { die("Errore connessione al DB " . mysqli_connect_error()); } ?> <html> <head> <title>Pagina di esempio</title> </head> <body> <h1>Lista clienti</h1> <?php $sql = "SELECT nome, cognome FROM clienti"; $result = mysqli_query($conn, $sql); while ($row = mysqli_fetch_array($result)) { echo $row['nome'].' '.$row['cognome'].'<br>'; } ?> </body> </html> Nelle prime righe del codice viene istanziata una connessione ad un database locale. Successivamente si passa all’apertura dei tag HTML della pagina web. Quindi viene eseguita una query SQL sul database per recuperare la lista di ipotetici clienti. Infine, i risultati della query vengono ciclati e ad ogni iterazione viene richiesta la stampa a video del nome e cognome del cliente corrente seguito da un ritorno a capo.   Questo esempio genera una pagina web molto semplice, anche un developer alle prime armi e con poca esperienza è in grado di comprenderne il funzionamento. Tale codice è anche facile da distribuire, infatti basta salvarlo all’interno di un file con estensione ".php", caricarlo su un web server e successivamente visitare la pagina da un browser. Questo approccio, ottimo per casistiche basilari, non è adatto per lo sviluppo di applicazioni web più complesse per una serie di motivi che, a breve, analizzeremo.   In generale, anche una semplice applicazione web è solitamente composta da più pagine. Quindi, cosa succede se più parti di un'applicazione hanno bisogno di utilizzare la stessa funzione, ad esempio la connessione ad un database proprio come nell’esempio precedente? Sicuramente è da evitare la duplicazione del codice: pertanto, una possibile soluzione potrebbe essere quella di condividere la logica di connessione al database in una funzione comune a più pagine.   Inoltre, all’interno dello stesso file troviamo la gestione dei tag HTML, la logica di accesso ai dati e la parte di visualizzazione degli stessi con relativa formattazione. Occuparsi contemporaneamente di tutti questi aspetti può portare ad una lunghezza eccessiva del codice sorgente grazie alla quale è facile, come sviluppatore, generare bug. Sarebbe meglio separare le varie sezioni del codice in più file sulla base, ad esempio, della tipologia di problema affrontato o della responsabilità gestita. In questo modo, più sviluppatori possono occuparsi contemporaneamente di aspetti differenti come la parte di interfaccia utente e la logica di business.   In conclusione, un framework per il web non è solo un insieme di strumenti ma è un ecosistema completo che fornisce una base solida per costruire applicazioni web robuste, sicure e manutenibili. L'uso di un framework può significativamente migliorare la qualità del codice, la collaborazione in team e la velocità di sviluppo.
1.2

Framework in PHP

Nel contesto dell'evoluzione rapida e continua del mondo della programmazione web, i framework PHP hanno giocato un ruolo cruciale, facilitando e velocizzando lo sviluppo di applicazioni web robuste e sicure.  Dopo una breve introduzione a PHP, verranno esplorati alcuni dei framework PHP più popolari analizzando brevemente le loro caratteristiche distintive.   Cos’è PHP Questa sezione ha l’obiettivo di fornire una rapida overview sul linguaggio PHP ovvero le sue origini, i suoi vantaggi e il suo ruolo nel panorama dello sviluppo web. PHP è l'acronimo di "PHP: Hypertext Preprocessor". È un linguaggio di scripting lato server che è stato progettato per lo sviluppo web, ma può anche essere utilizzato come linguaggio di programmazione generale. Quando parliamo di PHP come "linguaggio di scripting lato server", intendiamo:   linguaggio di scripting: a differenza di un linguaggio di programmazione compilato come C o il linguaggio Java, PHP è un linguaggio interpretato. Ciò significa che il codice PHP viene eseguito in tempo reale piuttosto che essere precompilato. Questo lo rende più flessibile e facile da usare per operazioni dinamiche, come la generazione di contenuti HTML in base a variabili specifiche. lato server: questo termine indica che PHP viene eseguito nel contesto di un server web, non sul computer dell'utente (chiamato anche "client"). Quando un utente richiede una pagina web che contiene codice PHP, il server esegue quel codice e invia la pagina HTML risultante al browser dell'utente. Questo permette di eseguire operazioni complesse come l'interazione con un database, la gestione delle sessioni utente e altro ancora, che non sarebbero possibili o sarebbero meno sicure se eseguite lato client.   PHP è stato creato nel 1994 da Rasmus Lerdorf, un programmatore canadese. In origine, PHP era un semplice set di script CGI in Perl utilizzato per tracciare le visite del suo curriculum online. Da allora, è cresciuto e si è evoluto fino a diventare uno dei linguaggi di programmazione più popolari al mondo. Per completare la definizione precedente, è opportuno specificare che CGI è l’acronimo di "Common Gateway Interface" ed è uno standard che permette ai server web di eseguire programmi esterni e utilizzare il loro output come contenuto web mentre Perl è un linguaggio di programmazione ad alto livello. Nel corso del tempo, PHP ha subito numerose revisioni e aggiornamenti. La versione più recente è la 8.x. Tra i punti di forza di PHP spiccano la sua capacità di integrarsi facilmente con il codice HTML e molteplici database. Si può inserire codice PHP direttamente all'interno di un documento HTML e il server lo eseguirà prima di restituire la pagina elaborata al browser dell'utente. Questo permette di creare pagine web dinamiche ovvero che variano il proprio contenuto sulla base dei dati presenti all’interno del database o sulla base delle azioni effettuate dall’utente. Il linguaggio PHP è estremamente versatile e può essere utilizzato per una vasta gamma di applicazioni web, da semplici blog fino a siti di e-commerce. Infatti, è utilizzato come linguaggio di programmazione per sistemi popolari come i CMS Drupal e WordPress o come le piattaforme di e-commerce Magento e PrestaShop.  Infine, PHP ha un ecosistema ricco e maturo, con numerosi framework che facilitano lo sviluppo di applicazioni robuste e ben strutturate.   Categorie di framework in PHP L'evoluzione dei framework PHP ha seguito una traiettoria parallela all'evoluzione del web stesso. Inizialmente, i framework PHP erano semplici e offrivano solo un set limitato di funzionalità. Successivamente, con l'aumento della complessità delle applicazioni web, i framework PHP sono diventati sempre più sofisticati introducendo funzionalità come ORM (Object-Relational Mapping), caching e autenticazione rendendo così possibile la creazione di applicazioni web più robuste e scalabili.   Possiamo suddividere i framework PHP nelle seguenti tipologie:   Full-Stack: offrono una soluzione completa per lo sviluppo di applicazioni web, dalla gestione del database alla presentazione dell'interfaccia utente. Grazie alle funzionalità e agli strumenti integrati è possibile costruire sia la parte di frontend che la parte di backend. Esempi: Laravel, Symfony, Yii Micro-Framework: sono più leggeri e focalizzati su specifiche funzionalità, come la creazione di API RESTful, microservizi o applicazioni minimaliste. Esempi: Lumen, Slim, Silex Component-Based: sono modulari e permettono di utilizzare solo i componenti necessari per il progetto offrendo una grande flessibilità. Esempi: Laminas, Aura Questa categorizzazione non è rigida e alcuni framework possono appartenere a più di una categoria. Tuttavia, è un modo utile per comprendere le caratteristiche e gli utilizzi tipici di ciascun tipo di framework. Framework PHP popolari La classificazione precedente può rivelarsi utile, ma non è l’unica attuabile! Di seguito, una breve lista dei framework PHP più popolari. Symfony Rilasciato nel 2005 Symfony è tra i framework PHP più maturi e affidabili che hanno rivoluzionato lo sviluppo delle applicazioni web. È noto per la sua modularità e la sua vasta serie di componenti riutilizzabili che possono essere utilizzati in maniera indipendente all’interno di qualsiasi progetto PHP. È spesso utilizzato in progetti aziendali e offre un alto grado di personalizzazione. Symfony è anche la base su cui altri framework e sistemi di gestione dei contenuti, come Drupal, sono costruiti. Anche il codice del core di Laravel è basato per circa il 30% su Symfony. Laravel Laravel è uno dei framework PHP più popolari grazie alla sua sintassi elegante e relativa facilità di utilizzo. Lanciato nel 2011, offre funzionalità come routing RESTful, ORM chiamato Eloquent e un potente sistema di templating chiamato Blade. Laravel è particolarmente apprezzato per la sua comunità attiva e la vasta gamma di plugin di terze parti disponibili online. CodeIgniter Pubblicato nel 2006, CodeIgniter è un framework PHP leggero e performante che aiuta gli sviluppatori a costruire rapidamente nuovi progetti. Infatti, è particolarmente utile per gli sviluppatori che vogliono un framework senza troppi requisiti di configurazione. Codeigniter è flessibile a livello strutturale, infatti incoraggia l’utilizzo del pattern MVC senza imporlo lasciando libero lo sviluppatore di seguire il modello architetturale che preferisce. Yii Yii nasce nel 2008 e nel suo sito ufficiale viene definito come veloce, sicuro ed efficiente. Incentrato sulla sicurezza, incorpora robusti meccanismi per proteggere le applicazioni da vari tipi di attacchi. Dispone anche di uno strumento di generazione automatizzata del codice, chiamato Gii, particolarmente utile per creare pagine CRUD (Create, Read, Update, Delete) in maniera visuale. Slim Slim è un micro-framework in PHP pubblicato per la prima volta nel 2010. A differenza degli altri framework visti fino ad ora, Slim dispone di meno funzionalità ma è molto utile per la creazione di API e semplici web-app. Infatti, è particolarmente adatto per la creazione di micro-servizi e API RESTful.   I framework PHP hanno rivoluzionato il modo in cui si sviluppano le applicazioni web, offrendo struttura, sicurezza e velocità nel processo di sviluppo. La scelta del framework giusto dipende da vari fattori come la complessità del progetto, le esigenze specifiche e la preferenza personale dello sviluppatore.
1.3

Cos’è Symfony

Sul sito ufficiale di Symfony si legge:   “Symfony is a set of PHP Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony.”   Da questa definizione, si capisce come Symfony non sia “solo” un semplice framework per costruire applicazioni web ma è, piuttosto, un insieme modulare di componenti che possono essere combinati assieme e in svariate forme per progettare e creare un sito web semplice, ma anche progetti complessi come piattaforme CMS o framework. Infatti, i componenti di Symfony sono le fondamenta di alcuni progetti PHP open-source utilizzati a livello internazionale come, ad esempio, Laravel e Drupal.   Dal punto di vista di uno sviluppatore web, Symfony può essere pensato come uno strumento potente e completo che offre un ambiente strutturato con funzionalità pre-implementate per costruire applicazioni web robuste e moderne. Non solo fornisce gli strumenti di base per costruire un sito web ma anche tutta una serie di componenti aggiuntivi per facilitare la gestione di compiti avanzati (API, connessione al database, sessione utente, ecc.). In questo modo, lo sviluppatore può concentrarsi sul problema che l’applicativo deve effettivamente risolvere e non sull’implementazione di funzionalità che sono trasversali alla maggior parte delle applicazioni web.   Caratteristiche principali di Symfony META DESCRIPTION: Scopri le caratteristiche distintive di Symfony, che lo rendono un framework PHP di scelta per molti sviluppatori web professionisti. Abbiamo visto che cos’è Symfony, proviamo, ora, ad elencare una lista delle sue caratteristiche principali:   Struttura ingegnerizzata: Symfony utilizza un pattern architetturale chiamato MVC (Model-View-Controller) che aiuta a separare l’applicazione in parti logiche rendendo il codice più facile da leggere e da manutenere.  Componenti di qualità: Symfony offre una vasta gamma di componenti di alta qualità per funzionalità ricorrenti in ambito web. Per esempio, il componente "Form" permette di gestire facilmente i form HTML mentre il componente "Security" fornisce strumenti avanzati per l'autenticazione e l'autorizzazione. Questi componenti sono ben documentati e mantengono un alto standard di qualità, il che li rende affidabili e robusti per lo sviluppo professionale. Community e Risorse: un altro aspetto interessante di Symfony è la sua community attiva e la grande quantità di risorse di apprendimento presenti online. Inoltre, sono disponibili molti pacchetti e plugin di terze parti che possono essere facilmente integrati in qualsiasi progetto per estendere le funzionalità. Interoperabilità e Standard: Symfony è progettato per essere conforme ai più recenti standard PHP esistenti. Questa aderenza agli standard facilita l'integrazione con altre librerie e strumenti, rendendo Symfony una scelta eccellente per progetti che richiedono un alto grado di interoperabilità. Anche sulla documentazione ufficiale di Symfony si legge “Don’t lock yourself up within Symfony! Allow yourself to build applications that precisely meets your needs!”  LTS release:  processi di rilascio ben definiti e supporto di versioni a lungo termine sono fondamentali per progetti in ambito enterprise.   In conclusione, Symfony è un framework PHP estremamente potente e flessibile che può accelerare notevolmente il processo di sviluppo web. Con una struttura ben organizzata, una vasta gamma di componenti e una community attiva, è un'opzione eccellente sia per i principianti che per gli sviluppatori php più esperti.
1.4

Come funziona Symfony

Comprendere come una richiesta effettuata da un utente sul web viene elaborata dal framework Symfony è fondamentale per poterlo utilizzare efficacemente. Per un programmatore, questa conoscenza può fare la differenza tra scrivere codice che è solo funzionale e codice che è sia efficiente che scalabile.  Prima di procedere con la spiegazione del flusso generico di una richiesta in Symfony, è necessario conoscere uno dei concetti fondamentali dell o sviluppo web ovvero HTTP.   Cos’è il protocollo HTTP META DESCRIPTION: Impara i fondamenti del protocollo HTTP e la sua funzione cruciale nel web HyperText Transfer Protocol (HTTP) è un protocollo di comunicazione stateless di livello applicativo utilizzato come fondamenta per qualsiasi distribuzione di informazioni sul World Wide Web. Nato come semplice protocollo per il trasferimento di file ipertestuali (file HTML), HTTP si è evoluto nel tempo per sostenere una sempre più vasta gamma di operazioni di comunicazione e media. Esso può essere visto non solo come un protocollo, ma anche come un modello completo per l'architettura di applicazioni web.   Al cuore di HTTP vi è il modello di interazione richiesta-risposta tra client e server. Le richieste possono assumere diverse forme, chiamate "metodi", come ad esempio GET, POST, PUT, DELETE. Ogni metodo rappresenta un tipo di operazione desiderata sulle risorse web alle quali il client è interessato. L’uso del metodo GET è associato al recupero di una risorsa, mentre POST è utilizzato per crearne una nuova. PUT e PATCH vengono impiegati per aggiornare una risorsa esistente e DELETE per rimuoverla. Questo uso semantico aiuta a mantenere un'architettura pulita e comprensibile.   Possiamo, dunque, dire che HTTP è più di un semplice protocollo per il trasferimento di dati. Esso è un ecosistema che supporta la complessità e la varietà delle moderne applicazioni web. La comprensione di tale protocollo è quindi essenziale per poter sviluppare applicazioni in ambito web.    Riassumiamo le sue caratteristiche principali:   Stateless: Ogni richiesta da parte del client al server è indipendente, il server non mantiene nessuna informazione sullo stato tra richieste successive.  Semplice: HTTP è facile da implementare e da usare. I messaggi sono in formato testuale e leggibili dall'uomo, rendendo semplice il debug. Estensibile: è possibile aggiungere nuovi header e metodi a piacere, il che significa che il protocollo può essere esteso per supportare nuove funzionalità. Connectionless: Una volta che una richiesta è conclusa, la connessione tra il client e il server viene chiusa. Tuttavia, HTTP/1.1 introduce la possibilità "keep-alive", ovvero di non terminare la connessione in automatico, per migliorare l'efficienza. Supporta diverse modalità di trasferimento: HTTP supporta il trasferimento di dati in vari modi come ad esempio multipart (per invio di allegati) e chunked (per trasferimenti in cui la risorsa viene suddivisa in più parti).   Analizziamo ora, ad alto livello, il flusso e lo scambio di messaggi tra client e server attraverso HTTP: Inizializzazione: il client, ad esempio un browser web, istanzia una connessione TCP/IP con il server web desiderato su una specifica porta, solitamente la porta 80 per HTTP e la 443 per HTTPS. Richiesta del Client: una volta stabilita la connessione, il client invia una richiesta HTTP al server. Un esempio di richiesta può essere la seguente: GET /index.php HTTP/1.1 Host: www.example.com Elaborazione del Server: il server riceve la richiesta, la analizza e la elabora. Se la risorsa richiesta è disponibile, il server la prepara per l'invio, altrimenti, restituisce un codice di errore. Risposta del Server: Il server invia una risposta al client, che può essere la risorsa richiesta (come una pagina HTML) o un messaggio di errore. Un esempio di risposta può essere la seguente: HTTP/1.1 200 OK Content-Type: text/html <HTML> ... </HTML> Chiusura della Connessione: una volta ricevuta la risposta, la connessione viene chiusa, a meno che non sia stata specificata l'opzione "keep-alive". Rendering del Browser: il browser renderizza la pagina utilizzando le informazioni ricevute, che possono includere anche ulteriori richieste per risorse come immagini, fogli di stile CSS e script JavaScript.   La comprensione del flusso dei messaggi e delle caratteristiche base di HTTP è fondamentale per qualsiasi sviluppatore che desidera capire le interazioni all’interno del web. Con questa base, è possibile spostarsi verso argomenti più avanzati come la gestione della sicurezza, l'ottimizzazione delle prestazioni e l'interazione con API RESTful.   Il flusso di una richiesta in Symfony Nel nostro caso, possiamo procedere nell’analizzare il percorso di una richiesta in Symfony. In basso è schematizzato tale flusso. Ricezione della Richiesta: tutto inizia quando il server web riceve una richiesta HTTP da un client (tipicamente un browser). Nello schema è rappresentato dal nodo HTTP Server. Il web server inoltra la richiesta al layer PHP sottostante. Symfony utilizza un componente chiamato HttpFoundation per semplificare l’interazione con le richieste e risposte HTTP. Front Controller e Kernel: la prima tappa nel framework è il "Front Controller", solitamente un file chiamato index.php. Questo file avvia il "Kernel" ovvero il nucleo dell'applicazione. Il Kernel è responsabile della gestione della richiesta e del ritorno della relativa risposta. Routing: Una volta che il Kernel è attivo, la richiesta passa attraverso il sistema di routing. Il router confronta l'URL della richiesta con l’insieme dei percorsi che sono stati censiti all’interno del file di configurazione di routing. Una volta individuato il percorso corretto, il router determina quale Controller e quale metodo di tale Controller deve essere invocato. Controller e Azione: nella fase successiva a quella di routing, il Kernel chiama il Controller designato e il relativo metodo (spesso chiamato "Azione"). Un controller è essenzialmente una classe PHP che contiene la logica di business per gestire una particolare richiesta. La funzione del Controller è quella di preparare i dati necessari per la vista e poi restituire una risposta, solitamente tramite un template. Model e Database: all'interno del Controller, spesso si ha la necessità di accedere ai dati presenti sul database per recuperare ed elaborare le informazioni richieste. Qui entra in gioco il "Model". Utilizzando il componente ORM (Object-Relational Mapping) di Symfony, chiamato Doctrine, è possibile interagire con il database in modo molto più intuitivo, lavorando con oggetti ed eseguendo query tramite metodi specifici piuttosto che eseguire query SQL “raw”. Creazione della Risposta: a questo punto, il Controller ha elaborato i dati ed eseguito la logica di business necessaria, pertanto si può procedere con la generazione di una “Risposta”. Questa può essere una pagina HTML, un file JSON, un media o qualsiasi altro tipo di contenuto web. Invio della Risposta: infine, la risposta viene inviata al client attraverso il server web. Da qui, il browser del client elabora la risposta e la fa visualizzare all'utente. Comprendere il flusso di una richiesta attraverso Symfony ci permette di scrivere codice più pulito e manutenibile. Inoltre, ci permette di risolvere eventuali bug più efficacemente avendo una visione completa dei componenti in gioco e delle interazioni tra di essi.
1.5

Il Pattern MVC

Uno dei concetti fondamentali che ogni programmatore dovrebbe conoscere per sfruttare a pieno le potenzialità della programmazione in Symfony è il pattern MVC o Model-View-Controller. Ma prima di approfondire nel dettaglio questo pattern, è utile fare un passo indietro per spiegare cos’è un Design Pattern.   Cos'è un Design Pattern? META DESCRIPTION: Introduzione ai design pattern, elementi cruciali per strutturare codice efficiente, manutenibile e riutilizzabile. Nell'ingegneria del software, un design pattern è una soluzione generale e riusabile ad un problema comune che si verifica in contesti specifici durante la progettazione del software.  Un design pattern non è uno snippet di codice da riutilizzare all’interno del proprio progetto. Piuttosto, è un modello o una serie di linee guida che aiutano a strutturare il codice in modo da renderlo più efficiente, manutenibile e, in molti casi, riutilizzabile.  Il concetto di design pattern è stato reso popolare dal libro "Design Patterns: Elements of Reusable Object-Oriented Software" di Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, noti anche come la "Gang of Four" (GoF). I design pattern possono essere categorizzati in vari modi, ma una delle classificazioni più comuni li divide in tre tipi principali: Creazionali: trattano i meccanismi di creazione degli oggetti. Alcuni esempi sono Singleton, Factory Method e Abstract Factory. Strutturali: riguardano la composizione di classi o oggetti per formare strutture più complesse mantenendole, al tempo stesso, flessibili ed efficienti. Alcuni esempi sono Adapter, Decorator e Composite. Comportamentali: si occupano dell’assegnazione delle responsabilità tra gli oggetti e delle relative interazioni. Alcuni esempi sono Observer, Strategy e Command.   A supporto di questa categorizzazione “classica”, nel corso del tempo, sono stati affiancati nuovi pattern e quindi nuove tipologie di pattern. Ad esempio, i pattern architetturali sono modelli di progettazione su larga scala che riguardano l’organizzazione globale del sistema. Uno di essi è proprio MVC.   Che Cos’è il pattern MVC? MVC è l’acronimo di Model-View-Controller. È un design pattern architetturale largamente utilizzato nello sviluppo web. Permette di separare un'applicazione in tre componenti logici interconnessi ma allo stesso tempo indipendenti. Questa suddivisione aiuta a isolare in maniera chiara la logica di business, l'interfaccia utente, le interazioni utente e la gestione del dato rendendo il codice più facile da gestire e da estendere. L’applicazione web risulta, quindi, divisa in tre parti fondamentali:   Model (M): Il Model si occupa della gestione del dato. Ovvero, logiche di business del dominio applicativo, regole di manipolazione del dato e interazioni con la fonte dati (solitamente un database). View (V): La View è responsabile della rappresentazione grafica dei dati e dell'interfaccia utente. È il componente che l'utente vede e con cui interagisce direttamente. La View riceve dati dal Model e li presenta in un formato che è comprensibile e interagibile per l'utente. Controller (C): Il Controller fa da ponte tra il Model e la View. Gestisce le interazioni utente, elabora le richieste e aggiorna il Model o la View. In altre parole, riceve l'input dall'utente attraverso la View, elabora queste informazioni (eventualmente tramite l’ausilio del Model) e aggiorna la View per riflettere il cambiamento di stato.   In basso è illustrato il pattern MVC come discusso nell’elenco precedente.   Perché il pattern MVC è importante in Symfony? Symfony implementa il pattern MVC in modo molto efficiente. La struttura di directory di un progetto Symfony segue strettamente il pattern MVC con cartelle dedicate per ciascuno dei tre componenti. Questo non solo rende il codice estremamente organizzato, ma facilita anche la scalabilità e la manutenzione. Riassumendo alcuni dei vantaggi offerti dall’adozione del pattern MVC in Symfony: Separazione dei compiti: rende più facile dividere il lavoro in un team di sviluppo. Ogni persona si può concentrare su un componente differente.  Manutenibilità: con una netta divisione tra logica di business, interfaccia utente e controllo dell'applicazione, diventa più semplice apportare modifiche e aggiornamenti senza influenzare altri aspetti del sistema. Riusabilità: è possibile riutilizzare i Model e i Controller con View differenti, o condividere una View tra più Model, rendendo il codice modulare. Testabilità: la separazione dei compiti facilita anche la fase di testing. Si può testare la logica del Model separatamente dalla logica dell'interfaccia utente, rendendo l'intero processo di sviluppo più robusto. Vediamo ora come ciascuno dei componenti del pattern MVC si manifesta in Symfony. Model in Symfony Symfony utilizza Doctrine come ORM (Object-Relational Mapping) per rappresentare e manipolare i dati. In tale ambito, le “entità” Doctrine agiscono come il "Modello", che incapsula la logica di business e le interazioni con il database. È possibile creare, leggere, aggiornare e cancellare record nel database utilizzando il Model associato ad una determinata tabella. View in Symfony In Symfony, la View  è principalmente gestita attraverso un template engine chiamato Twig. Esso permette di separare la logica di presentazione dal codice PHP rendendo più facile progettare e manutenere le interfacce utente. Le viste in Twig possono includere variabili e logica per la presentazione, che vengono compilate in codice PHP puro per la visualizzazione. Controller in Symfony I controller in Symfony sono classi PHP che gestiscono le richieste HTTP e contengono la logica necessaria per collegare il Model e la View. In un'applicazione Symfony tipica, un controller riceve una richiesta HTTP, interagisce con il Model per manipolare i dati e prepara la risposta da inviare al client, spesso utilizzando una View per formattare le informazioni ritornate.

2

Preparazione dell’ambiente di sviluppo

2.1

Server locale con XAMPP

XAMPP è un software multipiattaforma libero e gratuito distribuito da Apache che offre un ambiente di sviluppo server facile da usare e configurare tramite tecnologie open-source.  Il nome "XAMPP" è un acronimo che sta per:  X: cross-platform, infatti XAMPP è disponibile per vari sistemi operativi. A: Apache HTTP, il web server. M: MariaDB, il DBMS (Database Management System). P: PHP, il linguaggio di scripting lato server. P: Perl, un altro linguaggio di scripting lato server. XAMPP facilita lo sviluppo e il test di applicazioni web in ambiente locale senza la necessità di configurare un web server ed un DBMS separatamente in quanto tutti i componenti necessari sono già inclusi nel pacchetto. Componenti di XAMPP Fatta esclusione per la prima lettera dell’acronimo, le altre indicano i diversi componenti di cui XAMPP è composto, che ci permettono di creare un web hosting locale che simula il comportamento di un web hosting remoto. Vediamoli più nel dettaglio: Apache: un web server popolare e affidabile che si occupa della gestione delle richieste HTTP e del trasferimento di pagine web. MariaDB: un DBMS open source utilizzato per creare, gestire e manipolare database relazionali. Essendo un fork di MySql è praticamente intercambiabile con esso. PHP: il linguaggio di scripting lato server utilizzato da Symfony. Perl: un linguaggio di programmazione ad alto livello spesso utilizzato per lo sviluppo web.ù Perché utilizzare XAMPP I motivi principali per utilizzare XAMPP sono i seguenti:   Facilità di Installazione: XAMPP offre un processo di installazione semplice e veloce permettendo agli sviluppatori di configurare rapidamente un ambiente di sviluppo web in locale. Ambiente completo: XAMPP include tutti gli strumenti essenziali di cui uno sviluppatore ha bisogno per iniziare a lavorare su un progetto web, eliminando la necessità di installare e configurare singolarmente ogni componente. Community e Supporto: Essendo una soluzione popolare, XAMPP ha una grande community di sviluppatori e una vasta gamma di risorse educative e tutorial disponibili online. Portabilità: XAMPP dispone anche di una versione “portable” per installazione su pendrive USB permettendo di trasportare il proprio ambiente di sviluppo anche su altre macchine.
2.2

Come installare XAMPP

In questa sezione verrà illustrata la procedura di installazione di XAMPP sulle varie piattaforme. Per scaricare i pacchetti di installazione fare riferimento al sito ufficiale Come installare XAMPP su Windows Download: Scaricare l'installer di XAMPP dal sito ufficiale. Installazione: Eseguire l'installer e seguire le istruzioni visualizzate. Durante l'installazione verrà chiesto quali componenti installare. Selezionare Apache HTTP, MySQL, PHP e phpMyAdmin. Verifica: Una volta completata l'installazione, avviare il pannello di controllo XAMPP e verificare che Apache HTTP e MySQL siano in esecuzione.   Come installare XAMPP su Linux Download: Scaricare lo script di installazione di XAMPP dal sito ufficiale. Permessi di Esecuzione: Rendere lo script eseguibile con il comando chmod +x xampp-linux-x64-8.x.x-x-installer.run Installazione: Avviare l'installazione con il comando sudo ./xampp-linux-x64-8.x.x-x-installer.run e seguire le istruzioni visualizzate. Verifica: Dopo l'installazione, verificare che Apache HTTP e MySQL siano in esecuzione utilizzando il pannello di controllo XAMPP. Come installare XAMPP su MacOS Download: Scaricare il file in formato dmg di XAMPP dal sito ufficiale.. Installazione: Aprire il file appena scaricato e trascinare l’icona XAMPP nella cartella Applicazioni. Verifica: Avviare XAMPP attraverso il Launchpad e verificare che Apache HTTP e MySQL siano in esecuzione. Screenshot del pannello di controllo di XAMPP:   Una volta avviato XAMPP e assicuratisi che i servizi Apache HTTP e MySQL siano in esecuzione è possibile visitare l’indirizzo http://localhost tramite browser. Se tutto è andato a buon fine, apparirà la pagina di benvenuto di XAMPP come nell’immagine in basso.
2.3

Come configurare XAMPP

La fase successiva a quella di installazione è la configurazione dell’ambiente di sviluppo per soddisfare le esigenze specifiche del progetto sul quale si vuole lavorare. Di seguito verranno analizzate le configurazioni principali applicabili ai componenti di XAMPP. Come configurare Apache HTTP Apache HTTP è il web server utilizzato da XAMPP e la sua configurazione è fondamentale per lo sviluppo web. Ecco come configurarlo: File di configurazione: Il file di configurazione principale di Apache si chiama http.conf e, solitamente, si trova all’interno della directory apache della root di XAMPP. Per modificare le configurazioni del web server è sufficiente utilizzare un editor di testo. Porta in ascolto: il valore predefinito della porta sulla quale è in ascolto Apache è 80. Se necessario, è possibile modificare tale parametro (es. è presente un altro web server o servizio sulla stessa porta). Document Root: identifica il path dove sono memorizzati i file del sito web locale. Con Apache è possibile anche gestire più progetti all'interno dello stesso ambiente di sviluppo. I virtual host, infatti, permettono di ospitare più siti web su un singolo web server, con ciascun sito che ha il proprio nome di dominio e directory. Ecco come configurare un virtual host in XAMPP: File di configurazione: aprire il file /apache/conf/extra/httpd-vhosts.conf partendo dalla directory principale di XAMPP con un editor di testo. Creazione di un Virtual Host: aggiungere un nuovo blocco <VirtualHost> all’interno del file di configurazione per definire un nuovo virtual host. Specificare il DocumentRoot (la directory dove risiedono i file del sito web) ed il ServerName (il nome di dominio locale da utilizzare). Di seguito un esempio per configurare un virtual host per il dominio “progettodiesempio.local”: <VirtualHost *:80> DocumentRoot "C:/xampp/htdocs/progetto_di_esempio" ServerName progettodiesempio.local </VirtualHost> Modifica del file hosts: per fare in modo che il sistema locale riconosca il nuovo nome di dominio, è necessario aggiungere una nuova voce al file hosts. All’interno di questo file sono presenti le mappature tra indirizzi IP e nomi di dominio. Su Windows, il file si trova in C:\Windows\System32\drivers\etc\hosts. Su Linux e macOS, è localizzato in /etc/hosts. Per collegarci all’esempio precedente possiamo aggiungere all’interno del file hosts una riga di testo come la seguente: 127.0.0.1 progettodiesempio.local Riavvio del servizio Apache: dopo aver effettuato le modifiche, riavviare il server Apache attraverso il pannello di controllo XAMPP per applicare le nuove configurazioni. Verifica della configurazione: per verificare che il nuovo virtual host sia stato configurato correttamente, aprire il browser web e visitare http://progettodiesempio.local. Dovrebbe comparire il sito web ospitato localmente nella cartella indicata all’interno della configurazione del virtual host.Utilizzando i virtual host, è possibile simulare un ambiente di hosting molto vicino a quello di un server live, facilitando lo sviluppo e il testing delle applicazioni web. Come configurare MySQL MySQL (o MariaDB in XAMPP) è il DBMS di XAMPP. Ecco come configurarlo: File di configurazione: il file di configurazione principale si trova in mysql/bin/my.ini all’interno della directory principale di XAMPP. Tramite questo file è possibile gestire tutte le configurazioni del database. Password utente root: l’utente root è quello che detiene i più alti privilegi di accesso al database e permette la creazione dei vari database.   Come Configurare PHP PHP è il linguaggio di scripting lato server utilizzato per creare applicazioni web dinamiche. Ecco come configurare PHP in XAMPP: File di configurazione: il file php.ini contiene tutte le configurazioni di PHP. Si trova nella cartella php sotto la root di XAMPP. Estensioni: in base alle necessità è possibile abilitare e disabilitare le estensioni di PHP. Per quanto riguarda Symfony, ci sarà la necessità di abilitare una serie di estensioni. Log: è possibile modificare le impostazioni di log degli errori per facilitare il debugging durante lo sviluppo.
2.4

Gestione del Database con phpMyAdmin

Oltre ai componenti principali che abbiamo visto nelle sezioni precedenti, XAMPP mette a disposizione uno strumento per la gestione del database chiamato phpMyAdmin. Questo strumento permette di amministrare i database MySQL direttamente dal browser attraverso una comoda interfaccia grafica; phpMyAdmin facilita anche l'importazione e l'esportazione di database, permettendo di trasferire facilmente i dati tra diversi ambienti di sviluppo. Per accedere a phpMyAdmin assicurati di avere avviato i servizi Apache e MySQL tramite pannello di controllo di XAMPP. Successivamente, apri il browser e visita l’indirizzo http://localhost/phpmyadmin per accedere all’interfaccia di amministrazione. La pagina risultante dovrebbe essere la seguente: Come si nota dallo screenshot, sulla barra sinistra sono presenti i vari database che è possibile gestire. Sulla destra, invece, sono presenti dei tab per eseguire determinate azioni sui vari database. Dal tab “Database”, ad esempio, è possibile creare un nuovo database.
2.5

Che cos’è Composer e il suo ruolo in Symfony

La gestione delle dipendenze è un aspetto cruciale nello sviluppo di applicazioni software. Le applicazioni moderne spesso dipendono da una serie di librerie e pacchetti esterni e tener traccia di queste dipendenze e delle loro versioni può diventare rapidamente complicato. Qui entra in gioco Composer, indispensabile per tutti gli sviluppatori PHP, fornendo uno strumento robusto e flessibile per la gestione delle dipendenze in modo efficiente. Tramite l’ausilio di Composer, è possibile dichiarare le librerie necessarie al progetto senza preoccuparsi di doverle scaricare e installare manualmente assicurandosi di utilizzare le versioni corrette. I benefici che ne derivano sono riduzione del tempo necessario per il processo di installazione delle dipendenze e riduzione di conflitti legati alla compatibilità tra versioni dei vari pacchetti. Nel contesto di Symfony, Composer assume un ruolo ancora più centrale. Symfony è costruito attorno a una serie di componenti riutilizzabili e Composer è lo strumento che ne facilita la gestione, che si tratti di installare un bundle Symfony da un repository esterno o di gestire le versioni dei componenti di Symfony stesso. Oltre alla gestione delle dipendenze, Composer offre una serie di funzionalità, tra cui: Autoloading: Composer fornisce un autoloader PSR-4, facilitando il caricamento automatico delle classi senza la necessità di includere manualmente i file. Scripts: è possibile definire script personalizzati per l'automazione di una serie di task di sviluppo. Optimizations: Composer offre una serie di ottimizzazioni per migliorare le prestazioni, tra cui l'ottimizzazione dell'autoloader.
2.6

Come installare e come configurare Composer

Prima di poter iniziare a sfruttare tutti i benefici di Composer, è necessario installarlo e configurarlo correttamente nel proprio ambiente di sviluppo. Per un dettaglio completo e aggiornato della procedura di installazione fare riferimento alla documentazione ufficiale di Composer. Come installare Composer su Windows Per installare Composer su Windows seguire i seguenti passaggi: Download: scaricare l'installer di Composer dal sito ufficiale https://getcomposer.org/Composer-Setup.exe. Installazione: eseguire il file scaricato seguendo le istruzione dell'installer. Durante l’installazione verrà richiesto di specificare il percorso di PHP. Verifica: terminata l’installazione è necessario verificare l’esito tramite il seguente comando da lanciare su prompt: composer --version L’output del comando, se tutto è andato a buon fine, sarà la versione di Composer appena installata. Come installare Composer su Linux/ Unix/ macOS Download: aprire un terminale ed eseguire il seguente comando per scaricare l'installer di Composer: curl -sS https://getcomposer.org/installer | php Installazione: una volta scaricato, spostare l'eseguibile di Composer in una directory che fa parte di PATH per poterlo eseguire a livello globale. Ad esempio: mv composer.phar /usr/local/bin/composer Verifica: per verificare che Composer sia stato installato correttamente eseguire il seguente comando: composer --version
2.7

Gestione delle dipendenze con Composer

La gestione delle dipendenze è una delle funzionalità principali di Composer, permettendo agli sviluppatori php di definire, installare e mantenere aggiornate le librerie e gli strumenti necessari per un progetto PHP. Il file composer.json Il nucleo centrale di ogni progetto orchestrato da Composer è rappresentato dal file composer.json. All’interno di questo file json sono contenute le informazioni necessarie alla definizione delle dipendenze del progetto. Ecco una panoramica dei nodi più importanti:   require: elenco delle librerie da utilizzare nel progetto con le relative versioni. require-dev: elenco librerie necessarie solo per la fase di sviluppo, come ad esempio gli strumenti di testing. autoload: regole per l'autoloading delle classi del progetto. scripts: script custom Come installare le dipendenze con Composer Dopo aver definito le dipendenze nel file composer.json, il passo successivo è installarle. Ecco come:   Inizializzazione del progetto: per inizializzare un nuovo progetto si può usare il comando composer init che andrà a generare un nuovo file composer.json attraverso una serie di prompt interattivi. Aggiunta delle dipendenze: è possibile aggiungere nuove dipendenze al progetto con il comando composer require. Installazione: tramite il comando composer install verranno installate tutte le dipendenze definite nel file composer.json. Come aggiornare le dipendenze con Composer Mantenere le dipendenze aggiornate è fondamentale per la sicurezza e la stabilità del progetto. Ecco come è possibile farlo: Aggiornamento singolo: per aggiornare una singola dipendenza usare il comando composer update vendor/package. Aggiornamento completo: per aggiornare tutte le dipendenze alla loro ultima versione compatibile con il selettore di versione indicato usare il comando composer update. Gestione delle versioni con Composer Definire correttamente le versioni delle dipendenze è fondamentale per prevenire conflitti e mantenere la compatibilità. Composer supporta una varietà di vincoli di versione, tra cui: Versioni specifiche: ad esempio 1.0.3. Range di versioni: ad esempio >=2.1 <3.0. Livello di stabilità: è possibile specificare il livello di stabilità accettato usando flag come @dev @beta ecc ad esempio 1.0.*@beta. Operatore Tilde: più flessibile rispetto alla specifica di una versione fissa ma abbastanza restrittivo per evitare aggiornamenti che aggiungono cambiamenti non compatibili.  ~1.0 equivale a >=1.0 <2.0: accetta qualsiasi versione che inizia con 1. (1.0, 1.1, 1.2, ..., 1.9.9). ~1.0.0 equivale a >=1.0.0 <1.1.0: accetta qualsiasi versione che inizia con 1.0. (1.0.0, 1.0.1, 1.0.2, ..., 1.0.9). In sostanza, l'ultimo numero specificato nel vincolo tilde può aumentare, mentre gli altri no. Operatore Caret: ancora più flessibile dell’operatore tilde in quanto permette di accettare anche aggiornamenti minori che sono retrocompatibili con la versione attuale. Alcuni esempi: ^1.0.0 equivale a >=1.0.0 <2.0.0: accetta qualsiasi versione che inizia con 1. (1.0.0, 1.1.0, 1.2.0, ..., 1.9.9), ma non 2.0.0. ^0.1.0 equivale a >=0.1.0 <0.2.0: accetta qualsiasi versione che inizia con 0.1. (0.1.0, 0.1.1, 0.1.2, ..., 0.1.9), ma non 0.2.0. ^0.0.1 equivale a >=0.0.1 <0.0.2: accetta solo patch per la versione 0.0.1. Come si nota dagli esempi precedenti, permette di aggiornare il pacchetto a qualsiasi nuova versione che non modifica il primo numero non-zero della versione attuale. Esempio di file composer.json Per avere una visione più concreta di come è strutturato un file composer.json, prendiamo in considerazione il seguente esempio, che potrebbe essere utilizzato come base di in un progetto in Symfony: { "name": "me/myproject", "type": "project", "license": "MIT", "description": "Progetto esempio Symfony", "authors": [ { "name": "Nome Autore", "email": "[email protected]" } ], "require": { "php": ">=8.1", "doctrine/orm": "^2.11", "symfony/framework-bundle": "^6.3", "symfony/console": "^6.3", "symfony/yaml": "^6.3" }, "require-dev": { "symfony/browser-kit": "^6.3", "symfony/phpunit-bridge": "^6.3", "symfony/debug-bundle": "^6.3", }, "autoload": { "psr-4": { "App\\": "src/" } }, "autoload-dev": { "psr-4": { "App\\Tests\\": "tests/" } }, "config": { "preferred-install": { "*": "dist" }, "sort-packages": true } } In questo esempio: name: identifica univocamente il progetto. type: specifica il tipo di progetto. license: definisce la licenza sotto cui è distribuito il progetto. description: fornisce una breve descrizione del progetto. authors: elenco degli autori del progetto. require: elenco delle dipendenze necessarie per il progetto. require-dev: dipendenze necessarie solo per lo sviluppo.
2.8

Autoloading con Composer

L'autoloading è una funzionalità che permette di caricare automaticamente classi e file all’interno del progetto PHP, eliminando la necessità di includere manualmente i file con istruzioni include o require. Composer fornisce un potente sistema di autoloading che aderisce agli standard PSR PHP. In questa sezione, esploreremo come configurare l'autoloading con Composer. Standard PSR Gli standard PSR (PHP Standard Recommendation) sono una serie di raccomandazioni che definiscono come scrivere codice PHP in modo interoperabile. Composer supporta vari standard PSR per l'autoloading, tra cui PSR-4, standard di autoloading che permette di definire uno schema di nomi di classi flessibile e intuitivo.   Come configurare l'autoloading con Composer Per configurare l'autoloading nel progetto, è necessario aggiungere una sezione autoload al file composer.json. Ecco come fare:   PSR-4: per configurare un autoloading PSR-4, è necessario definire uno spazio dei nomi e il percorso della directory dove sono situati i file delle classi. Ad esempio: "autoload": { "psr-4": { "App\\": "src/" } } Classmap: se il progetto non segue gli standard PSR, si può utilizzare l'autoloading basato su classmap, che mappa ogni classe a un file specifico. Ad esempio: "autoload": { "classmap": [ "src/", "lib/" ] } Come generare l'autoloader con Composer Dopo aver configurato l'autoloading nel file composer.json, per generalo è sufficiente eseguire il comando: composer dump-autoload Tale comando genera il file vendor/autoload.php da includere all’interno degli script PHP per abilitare l'autoloading. In ambiente di produzione, è consigliabile ottimizzare l'autoloader per migliorare le prestazioni tramite il comando: composer dump-autoload --optimize Questo comando genera un autoloader più performante che permette di ridurre il tempo di caricamento delle classi. In un progetto Symfony, l'autoloader di Composer è integrato nativamente, facilitando il caricamento automatico delle classi e dei file necessari. Nel file “public/index.php” è presente, infatti, una riga che include l’autolader: require __DIR__ . '/vendor/autoload.php';
2.9

Scripts e Comandi Personalizzati con Composer

Composer non solo facilita la gestione delle dipendenze e l'autoloading, ma offre anche la possibilità di definire e eseguire script personalizzati permettendo di automatizzare una serie di task di sviluppo. Definizione degli Script con Composer Gli script vanno definiti all’interno della sezione scripts del file composer.json. Questi possono essere semplici comandi shell o riferimenti a classi PHP. In basso un esempio di sezione scripts del file composer.json: "scripts": { "test": "phpunit", "start": "php -S localhost:8000 -t public", "cache-clear": [ "@php bin/console cache:clear", "@php bin/console cache:warmup" ] } In questo esempio: test: esegue PHPUnit per eseguire i test di unità definiti per il progetto. start: avvia il server PHP incorporato. cache-clear: esegue due comandi in serie per resettare la cache. Esecuzione degli Script con Composer Gli script possono essere eseguiti usando il comando composer run-script. Ad esempio, per eseguire lo script “start” definito nell’esempio, utilizzare il comando: composer run-script start Comandi Personalizzati con Composer Composer permette anche di creare comandi personalizzati per estendere le funzionalità e per creare workflow custom. Per creare un comando personalizzato, come primo step è necessario creare una classe PHP che implementa l'interfaccia Composer\Command\Command. In basso un esempio: namespace MyVendor; use Composer\Command\BaseCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CustomCommand extends BaseCommand { protected function configure(): void { $this->setName('my-custom-command'); } protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Comando custom eseguito con successo :)'); return 0; } } Creato il comando, è necessario registrarlo in composer.json: "autoload": { "psr-4": { "MyVendor\\": "src/" } }, "scripts": { "custom-command": "MyVendor\\CustomCommand " } Infine, per eseguire il comando: composer custom-command Come si nota nell’esempio, la classe creata include due metodi che fanno parte dei componenti di Symfony Console.
2.10

Come configurare Visual Studio Code

Ti avevamo già parlato di Visual Studio Code nell'ambito della nostra guida Bootstrap in Italiano. Lo rincontriamo, in questa sede, perchè prima di addentrarsi nello sviluppo web con Symfony, è necessario completare il nostro ambiente di sviluppo con un editor di codice. Visual Studio Code, sviluppato da Microsoft, è uno degli editor di codice multipiattaforma più popolari che offre una vasta gamma di funzionalità, tra cui il supporto a svariati linguaggi di programmazione ed una ricca collezione di estensioni che migliorano e agevolano lo sviluppo software. Vediamo come installarlo e come configurarlo.   Come Installare Visual Studio Code Per iniziare ad utilizzare VS Code è sufficiente seguire questi semplici passaggi:   Visitare il sito web ufficiale: visitare il sito ufficiale di Visual Studio Code e scegliere la versione adatta al proprio sistema operativo (disponibile per Windows, Linux e macOS). Scaricare l'installer: cliccare sul pulsante di download e attendere il completamento. Installazione: Una volta scaricato il file, avviarlo e seguire le istruzioni fornite dall'installer. Durante il processo di installazione, è possibile scegliere di aggiungere VS Code alla variabile di sistema PATH. Questa opzione permette di avviare l'editor da riga di comando. Terminata l’installazione, all’avvio dell’IDE VS Code si presenterà una schermata simile a quella in basso. VS Code presenta una barra laterale sinistra che facilita l'accesso rapido a vari strumenti come l'esplora risorse per la navigazione dei file e delle cartelle, il cerca per effettuare ricerche e sostituzioni all’interno dei vari file del progetto, il controllo del codice sorgente per repository git e l’accesso al marketplace per l’installazione di nuove estensioni. VS Code, inoltre, permette di personalizzare l'ambiente di sviluppo scegliendo tra diversi temi, layout e configurazioni.  Come si può notare, Visual Studio Code dispone di base di una serie di funzionalità che lo rendono un potente editor di codice. Per renderlo ancora più potente è possibile aggiungere nuove funzionalità tramite le estensioni che sono presenti all’interno del suo marketplace. Nella sezione successiva, esploreremo alcune delle estensioni più utili per lo sviluppo web in Symfony.   Estensioni di Visual Studio Code per lo sviluppo in Symfony META DESCRIPTION: Esplora le estensioni essenziali di VS Code per Symfony, migliorando la produttività e l'efficienza nello sviluppo. Di seguito, una lista di estensioni che possono essere definite “essenziali” per uno sviluppo Symfony efficiente e produttivo. Per installare un’estensione è necessario aprire il pannello delle estensioni dalla barra laterale sinistra e digitare nella relativa barra di ricerca il nome dell’estensione desiderata. Una volta individuata, basterà premere sulla voce “Install” per installarla.   PHP Intelephense PHP Intelephense aggiunge a VS Code una serie di funzionalità avanzate per facilitare la vita agli sviluppatori PHP, tra cui: Code completion (IntelliSense): fornisce suggerimenti intelligenti per funzioni e metodi mentre viene scritto il codice mostrando la firma e, quindi, i parametri richiesti. Suggerisce automaticamente variabili e metodi basandosi sullo scope corrente del codice. Go to definition: permette di saltare rapidamente alle definizioni di classi, metodi e funzioni. Find references: aiuta a trovare tutti i riferimenti a una specifica classe, metodo o funzione. Formattazione: fornisce strumenti per la formattazione automatica del codice, aiutando a mantenere un codice più facilmente leggibile. Auto Close Tags L'estensione Auto Close Tag è molto utile quando si lavora con file HTML, offrendo funzionalità come: Chiusura automatica dei tag: una volta inserito un tag di apertura, l’estensione inserisce automaticamente quello di chiusura, facendo risparmiare tempo e riducendo gli errori. Supporto per molteplici linguaggi: supporta svariati linguaggi, inclusi il linguaggio HTML, Twig e il linguaggio PHP. Una volta installata, è necessario accedere alle impostazioni dell’estensione e modificare l’array auto-close-tag.activationOnLanguage aggiungendo “twig” e verificando la presenza di “html” e “php”. In questo modo, l’estensione si attiverà su tutti i linguaggi che useremo per sviluppare in Symfony.   PHP Namespace Resolver Questa estensione facilita l'importazione e l'organizzazione dei namespace, offrendo funzionalità come: Importazione automatica: facilita l’importazione automatica dei namespace mancanti all’interno del file PHP che si sta modificando. Ricerca intelligente: cerca e importa i namespace corretti sulla base delle classi utilizzate all’interno del codice. Organizzazione dei namespace: aiuta a mantenere i file PHP ordinati organizzando i namespace in modo logico. Twig L'estensione Twig aggiunge un supporto avanzato per il template engine Twig in VS Code, con funzionalità come: Highlight della sintassi: migliora la leggibilità del codice Twig evidenziando diversi elementi sintattici con colori distinti. Autocompletamento: fornisce suggerimenti mentre viene scritto codice Twig, aiutando a scrivere codice più velocemente e con meno errori. Formattazione: aiuta a formattare il codice con strumenti automatici rendendolo più leggibile e facile da manutenere.
2.11

Ambiente di sviluppo Symfony remoto (Editor online Symfony)

Come visto nelle sezioni precedenti di questa guida, un ambiente di sviluppo locale richiede l’installazione e la configurazione sulla propria macchina di tutti gli strumenti e linguaggi necessari. Questo permette di simulare un hosting reale su un pc senza pagare un servizio online e non necessitando, potenzialmente, di una connessione Internet e svincola lo sviluppatore da qualsiasi tipo di latenza (connessione al database remoto, navigazione delle pagine della web app, ecc.). Inoltre, le performance, essendo legate alla potenza della macchina locale, dovrebbero essere migliori rispetto ad un hosting online, riducendo i tempi legati ai test e al debug. Gli svantaggi di questa soluzione risiedono principalmente nel processo di configurazione dell’ambiente stesso, soprattutto per i meno esperti, e nella portabilità del progetto su un’altra macchina. Per questi motivi, è giusto approfondire anche gli ambienti di sviluppo online. Tali servizi web-based forniscono tutti gli strumenti necessari per scrivere ed eseguire applicazioni attraverso un semplice browser web. In questo modo è possibile lavorare al proprio progetto senza la necessità di una postazione fissa. Di contro, è richiesta una connessione internet e, solitamente, questi servizi prevedono un abbonamento a pagamento per accedere a funzionalità avanzate. In questa sezione citeremo uno di questi servizi, chiamato Php Sandbox, che permette di configurare un ambiente di sviluppo online per progetti PHP.  Che cos'è PHP Sandbox? Partiamo dalla tagline del sito ufficiale: “Start building your next PHP app in seconds. Quickly prototype or share PHP Projects, without setting up a local environment.” L’obiettivo di phpsandbox è chiaro: semplificare e velocizzare il processo di sviluppo di progetti PHP rimuovendo la complessità della configurazione di un ambiente di lavoro locale sfruttando i vantaggi di un ambiente di lavoro remoto. Le funzionalità principali sono: Ambiente online: ambiente di lavoro totalmente online alla pari di quello locale ma senza la necessità di effettuare alcuna installazione o configurazione di tool e sistemi. Editor del codice integrato: è possibile scrivere il codice sorgente del progetto direttamente all’interno del browser. Supporto per Composer: si ha accesso a tutti i package Composer con la possibilità di gestire tutte le dipendenze del progetto. Supporto semplificato per operazioni come install, update e delete dei pacchetti. Terminale integrato Supporto per framework PHP popolari come Symfony e Laravel Possibilità di selezionare una determinata versione di PHP Preview dell’applicazione online tramite URL pubblico Condivisione del progetto: condivisione tramite link e possibilità di fork per clonare il progetto.   Se vuoi scoprire come configurare questo ambiente di lavoro per utilizzarlo, abbiamo preparato per te un tutorial su come usare PHP Sandbox

3

L’ecosistema Symfony

3.1

Come installare Symfony

Terminate le premesse introduttive, entriamo finalmente nel vivo della nostra guida! Requisiti di sistema per installare Symfony Prima di procedere con la creazione di un progetto Symfony è necessario verificare i requisiti di base. Avendo installato XAMPP, abbiamo a già a disposizione la versione di PHP corretta, ovvero maggiore o uguale alla 8.1. Per verificare, avviare Apache dal pannello di controllo di XAMPP e visitare la pagina http://localhost/dashboard/phpinfo.php. Oltre alla versione, è necessario verificare che siano abilitate le seguenti estensioni PHP: Ctype iconv PCRE Session SimpleXML Tokenizer Le estensioni sopra elencate dovrebbero già essere abilitate di default sul sistema. Per verificarlo, è sufficiente ricercare per nome estensione nella schermata phpinfo precedente. Ad esempio, ricercando l’estensione PCRE dovrebbe presentarsi una schermata come la seguente (notare l’evidenziazione in verde): In generale, per abilitare le estensioni che non lo sono già di default o che non fanno parte del core di PHP, accedere in modifica al file php.ini tramite pannello di controllo di XAMPP: Ricercare l’estensione tramite la chiave di ricerca extension={nome estensione}. Ad esempio, per verificare che sia abilitata l’estensione intl ricercare extension=intl: Dalla schermata precedente, si nota che tale estensione non è abilitata in quanto commentata tramite il carattere ; (punto e virgola). Per abilitarla è sufficiente rimuovere il carattere ; da inizio riga, salvare e riavviare il servizio Apache dal pannello di controllo di XAMPP. Altro requisito fondamentale per procedere con la creazione di un nuovo progetto Symfony è il gestore di dipendenze Composer, già approfondito nel capitolo relativo alla configurazione dell’ambiente di sviluppo.   Come creare un progetto Symfony con XAMPP Per creare un nuovo progetto Symfony, aprire il terminale, posizionarsi nella directory che si preferisce e lanciare il seguente comando composer create-project symfony/skeleton:"6.3.*" symfony-test sostituendo symfony-test con il nome del proprio progetto. Il comando si occuperà di creare un nuova cartella chiamata “symfony-test”, scaricare le dipendenze base di Symfony e generare le cartelle di default necessarie per iniziare a sviluppare il progetto web. Una volta creata la struttura base del progetto, possiamo configurare un virtual host per navigare le pagine web del sito. Aprire in modifica il file httpd-vhosts.conf localizzato solitamente in C:\xampp\apache\conf\extra\ su Windows, /Applications/XAMPP/xamppfiles/etc/extra/ su macOS e /etc/httpd/conf/extra/ su Linux. Quindi, aggiungere la seguente configurazione alla fine del file: <VirtualHost *:80> DocumentRoot "C:\percorso\progetto\symfony\public" ServerName symfony-test.local <Directory "C:\percorso\progetto\symfony\public"> AllowOverride None Require all granted FallbackResource /index.php </Directory> </VirtualHost> sostituendo correttamente il percorso con quello relativo alla cartella del progetto sul proprio sistema. Da notare come, ad essere esposta sul web server sia solo la cartella public. Il dominio assegnato per questo virtual host è symfony-test.local. Il passo successivo è quello di registrare il dominio scelto all’interno del file hosts: 127.0.0.1 symfony-test.local in questo modo, si associa l’indirizzo locale (127.0.0.1) al dominio symfony-test.local. A questo punto, non resta che riavviare il servizio Apache tramite il pannello di controllo XAMPP e visitare con il browser l’indirizzo symfony-test.local: Congratulazioni! Hai installato Symfony! Vediamo, a seguire, come si presenterà la cartella del progetto appena creato.
3.2

Struttura delle cartelle di un progetto Symfony

Una volta effettuata l’installazione di Symfony, la cartella del progetto si presenterà come nella figura in basso. Come puoi notare dall'immagine, ogni progetto Symfony è composto da sei cartelle principali (che approfondiremo a breve): bin config public src var vendor Nella route principale, invece, troviamo i seguenti file: .env: in questo file vengono definite tutte le variabili d’ambiente da passare all’applicazione. Possono essere presenti parametri di configurazione, stringhe di connessione al database e credenziali. composer.json: come già visto in precedenza, in questo file json vengono definiti i requisiti e le dipendenze del progetto. composer.lock: questo file è generato da Composer e non deve mai essere modificato manualmente. Contiene un elenco di tutte le dipendenze installate e della relativa versione. symfony.lock: questo file è gestito da Symfony Flex, come vedremo più avanti nella guida, e anche lui non deve mai essere modificato manualmente. Vediamo, ora, le singole cartelle un po’ più nel dettaglio.   La cartella bin in un progetto Symfony All’interno della cartella bin è presente lo script console. La console è la CLI (command line interface) con la quale è possibile effettuare varie operazioni sul progetto, come la pulizia della cache e la creazione di codice boilerplate. Per accedere alla console è sufficiente un terminale. La cartella config in un progetto Symfony All’interno della cartella config sono presenti tutte le configurazioni del progetto. Generalmente, la maggior parte dei file di configurazione saranno in formato yaml. Ecco una rapida overview di quello che è presente in “config”: packages/: questa sottodirectory contiene una serie di file YAML che definiscono la configurazione dei vari bundle e componenti utilizzati nell'applicazione. services.yaml: qui vengono definiti i servizi utilizzati nell'applicazione. routes.yaml: questo file è dove vengono definite le rotte dell'applicazione, tramite la mappatura dei Controller agli URL desiderati. La cartella public in un progetto Symfony La cartella public contiene l’entry point dell’applicazione ovvero il file index.php. In questa cartella, inoltre, possono essere inseriti tutti gli asset del sito come immagini, file CSS, file JavaScript e qualsiasi altro tipo di risorsa che si vuole rendere disponibile pubblicamente su Internet. Solitamente, la cartella “public” deve essere l’unica cartella accessibile pubblicamente dai visitatori del sito web. Tutte le altre cartelle devono essere private e non esposte pubblicamente su Internet, in quanto contengono la logica di business e le impostazioni del progetto. La cartella src in un progetto Symfony La cartella src (source) è il posto in cui è presente la logica di business del progetto, quindi conterrà i file PHP relativi a Controller, Model, ecc. La cartella var in un progetto Symfony “var” è una cartella interna di Symfony che ingloba a sua volta due cartelle chiamate “cache” e “log”. La cartella cache ospita tutti i file e le impostazioni generate durante l’avvio e l’esecuzione della web app per permettere agli utenti del sito un’esperienza di utilizzo migliore ed una navigazione più veloce, riducendo il tempo necessario per generare le risposte. Nella cartella log, invece, vengono tracciate tutte le eccezioni, messaggi di errore e richieste al server che si verificano durante l’esecuzione dell’applicazione e sono utili per effettuare debug e ricerche su eventi specifici. La cartella vendor in un progetto Symfony La cartella “vendor” contiene tutti i file generati durante l’installazione di Symfony e non deve essere modificata. All’interno sono presenti tutti i componenti di Symfony e librerie di terze parti installate. Questa cartella viene gestita da Composer.
3.3

Come utilizzare Symfony Console

Symfony Console è un potente strumento, messo a disposizione dal framework Symfony, con interfaccia a riga di comando per eseguire operazioni di varia natura sul progetto. Tramite console è possibile recuperare informazioni sul progetto, effettuare debug, gestire le dipendenze, automatizzare compiti ripetitivi come la pulizia della cache e generare codice per la creazione di nuovi Controller, Entity, ecc.  Per accedere alla console è sufficiente utilizzare un terminale come, ad esempio, quello integrato in Visual Studio Code. Dopo aver installato Symfony, posizionarsi nella cartella del progetto e lanciare il comando: php /bin/console Tramite questo comando, chiediamo a PHP di eseguire il file “console” presente all’interno delle cartella “bin”. L’output di questo comando illustra l’utilizzo base di Symfony Console elencando tutti i comandi disponibili. Come si nota dalla sezione Usage, per ogni comando è possibile definire una lista di opzioni e argomenti. Vediamone un esempio.   Esempio: come svuotare la cache con Symfony Console Se volessimo, ad esempio, richiedere le istruzioni per l’utilizzo del comando cache:clear, utile per rimuovere la cache, sarà sufficiente digitare sul terminale il nome del comando con l’opzione help: php bin/console cache:clear --help Symfony Console visualizzerà la schermata seguente: Analizziamo le sezioni della schermata precedente: Description: la descrizione del comando per il quale è stato richiesto aiuto. Usage: come utilizzare il comando da CLI Options: la lista di opzioni che possono essere fornite in input al comando Help: un esempio pratico di utilizzo del comando. In questo caso specifico, l’esempio propone la pulizia della cache sull’ambiente dev ovvero di sviluppo. Proviamo, ora, a lanciare il comando senza alcuna opzione, con l’obiettivo di svuotare la cache: php bin/console cache:clear Il risultato sarà il seguente: Svuotare la cache è utile per aggiornare lo stato dell’applicazione dopo che sono state effettuate modifiche sui file.   Informazioni sul sistema con Symfony Console Un altro comando utile è il comando about che ci permette di avere informazioni sul progetto e sul sistema. Per eseguirlo, basta lanciare il seguento comando php bin/console about Riceveremo in output tutte le informazioni del progetto, come la versione di Symfony, e le informazioni sul sistema, come la versione di PHP utilizzata. La lista dei comandi presenti in Symfony Console aumenta sulla base dei componenti che vengono installati. Nel momento in cui aggiungeremo Doctrine ORM, ad esempio, avremo a disposizione una serie di comandi per interagire con il database.
3.4

Come utilizzare Symfony Flex

Che cos'è Symfony Flex? Symfony Flex è un plugin Composer che possiamo utilizzare per gestire le dipendenze nel nostro progetto e per automatizzare molte delle attività più comuni che si eseguono durante lo sviluppo di un’applicazione Symfony. Flex è presente nell’installazione base avviata tramite Symfony installer. Per aggiungerlo manualmente è possibile farlo tramite il comando: composer require symfony/flex Dopo l’installazione, Flex inizierà automaticamente a gestire le Recipe.   Cosa sono le Recipe in Symfony? Symfony Flex è basato sul concetto di Recipe (letteralmente ricette), ovvero un insieme di istruzioni automatizzate che vengono eseguite quando viene aggiunto, aggiornato o rimosso un pacchetto ad una applicazione Symfony. Le recipe possono effettuare le seguenti operazioni sul progetto dove vengono lanciate:   Creare e modificare file e cartelle Aggiungere nuove configurazioni Aggiungere variabili d’ambiente Le recipe sono sono reperibili al link recipe ufficiali Symfony. Oltre a quelle ufficiali, sviluppate dal team di Symfony, sono disponibili anche quelle della community (e, quindi, sviluppate da terzi), al link recipe della community Symfony.   Struttura di una Recipe in Symfony Ogni recipe è strutturata seguendo un insieme di cartelle e file ben definiti: nome-recipe/ ├── config/ │ └── packages/ │ └── nome_pacchetto.yaml ├── src/ │ └── ... ├── templates/ │ └── ... ├── public/ │ └── ... ├── manifest.json └── README config/: contiene le configurazioni specifiche del pacchetto. Le configurazioni sono di solito definite in file YAML all'interno della sottocartella packages/. src/: può contenere file con codice sorgente da aggiungere al progetto durante l'installazione del pacchetto. templates/: contiene template Twig che possono essere utilizzati o estesi all’interno del progetto. public/: può contenere asset pubblici, come CSS, JavaScript e immagini, che vengono utilizzati dal pacchetto. manifest.json: questo è il file centrale della recipe. All’interno di esso sono definite una serie di istruzioni che Flex seguirà durante l'installazione del pacchetto. Ad esempio: copiare file e cartelle dalla recipe al progetto. eseguire script durante l'installazione. aggiungere variabili d'ambiente al file .env del progetto. aggiungere nuove configurazioni ai file già presenti nel progetto o aggiungere nuovi file di configurazione README: contiene istruzioni e informazioni sul pacchetto, fornendo una guida su come utilizzare il pacchetto una volta installato. Utilizzo di Symfony Flex Symfony Flex utilizza il file symfony.lock presente all’interno della root del progetto per gestire le versioni delle librerie ma questo passaggio è trasparente allo sviluppatore php che continuerà ad utilizzare Composer per l’aggiunta di nuove librerie. Infatti, Flex è controllato da tre semplici comandi. require: per installare un nuovo pacchetto è sufficiente usare il comando composer require seguito dal nome del pacchetto: composer require nome/pacchetto update: per aggiornare una dipendenza alla sua ultima versione usare il comando composer update seguito dal nome del pacchetto: composer update nome/pacchetto remove: infine, per rimuovere un pacchetto, usare il comando composer remove seguito dal nome del pacchetto: composer remove nome/pacchetto Come installare Twig con Symfony Flex Grazie all’ausilio di Symfony Flex è possibile installare e configurare Twig, il template engine per la creazione delle interfacce web, con un solo comando: composer require twig Il package twig per il quale è stata richiesta l’installazione, non fa riferimento al package Composer ma è un alias di Flex che punta a symfony/twig-bundle. Flex si occuperà, per prima cosa, di risolvere gli alias per Composer. Successivamente, installerà la recipe del pacchetto richiesto.   In particolare, la recipe di Twig, si occupa di: abilitare automaticamente la nuova funzionalità in config/bundles.php aggiungere in config/packages il file twig.yaml contenente la configurazione di default di Twig aggiungere in config/packages/test il file twig.yaml contenente alcune opzioni di configurazione da utilizzare durante i test creare la cartella templates ed all’interno il file di layout base.html.twig   Il codice sorgente della recipe è disponibile sul repository Github di Symfony relativo alle recipe ufficiali La struttura di cartelle, per twig-bundle è la seguente: Il file manifest.json contiene: { "bundles": { "Symfony\\Bundle\\TwigBundle\\TwigBundle": ["all"] }, "copy-from-recipe": { "config/": "%CONFIG_DIR%/", "templates/": "templates/" }, "conflict": { "symfony/framework-bundle": "<5.3" } } Il nodo bundles indica quali bundles attivare all’interno in Symfony e, quindi, all’interno del file config/bundles.php. Il nodo copy-from-recipe indica quali cartelle dovranno essere copiate dalla recipe e dove all’interno del progetto. In questo caso specifico, la cartella config verrà copiata in %CONFIG_DIR%/ ovvero la cartella config del progetto e templates in templates andando a creare una nuova cartella nella root del progetto.

4

Controller, Routing e Views in Symfony

4.1

Controller in Symfony

Seguendo la suddivisione proposta dal pattern MVC, possiamo pensare al controller come un blocco logico che ha il compito di reagire agli input ricevuti con un determinato comportamento, avvalendosi eventualmente del supporto del blocco model per l’accesso ai dati, restituendo in output le informazioni utili per la presentazione al blocco view. Formalmente, possiamo dire che un controller è una funzione PHP che riceve le informazioni da un oggetto di tipo Request, esegue una serie di elaborazioni ed infine crea e restituisce un oggetto di tipo Response. Request e Response sono due classi di Symfony aventi proprietà e metodi pubblici che facilitano l’accesso ai dati della richiesta e la generazione della risposta incapsulando i dati elaborati. Esse appartengono al componente Symfony HttpFoundation che definisce un layer orientato agli oggetti per HTTP. Infatti, nel linguaggio PHP ogni richiesta è gestita all’interno di variabili globali come $_GET, $_POST e $_COOKIE mentre la risposta può essere generata con funzioni come php echo e header(). HttpFoundation sostituisce le variabili globali e le funzioni PHP precedentemente citate da una gerarchia di classi che facilitano la manipolazione e l’elaborazione delle informazioni legate al flusso HTTP. Nel caso di una web-application, ad esempio, l’utente potrebbe richiedere l’accesso al sistema tramite form di login o l’accesso ad una risorsa specifica come l’articolo di un blog. Solitamente, il controller restituisce una pagina HTML ma è anche possibile restituire un file JSON, XML, un redirect o un errore 404. In basso, la schematizzazione ad alto livello del meccanismo di richiesta-risposta gestito da un controller:   Come creare un Controller in Symfony I controller in Symfony sono solitamente dei metodi raggruppati in classi PHP. Ogni gruppo di metodi è legato da una determinata responsabilità, come può essere la gestione di una risorsa quale l’articolo di un blog. In questi casi, parliamo di CRUD (Create, Read, Update e Delete) ovvero di un insieme di metodi che permettono la creazione, lettura, modifica ed eliminazione di un articolo. La cartella src/controller è il posto in cui devono essere inseriti tutti i file PHP relativi ai controller. La creazione di un classe controller può essere fatta creando manualmente un file, oppure ci si può avvalere del componente MakerBundle di Symfony per la generazione automatica. Per installare MakerBundle è sufficiente lanciare da terminale il seguente comando: composer require --dev symfony/maker-bundle Come già visto in precedenza, Symfony Flex si occuperà di installare il bundle e di configurarlo al posto nostro. Per generare un controller utilizzare il comando: php bin/console make:controller La console ci chiederà il nome da assegnare al nuovo controller. Il suffisso Controller viene aggiunto in automatico dal comando nel caso in cui non venisse inserito dall’utente. Questa è una convenzione utilizzata da Symfony per far riconoscere allo sviluppatore in maniera semplice e veloce il tipo di file direttamente dal nome. Come si nota dall’output della console, oltre alla creazione del controller, il comando si è occupato anche di creare una cartella hello sotto templates generando all’interno il file Twig index.html.twig. Questo è avvenuto perchè sul progetto è già installato il modulo twig. In caso contrario, verrebbe generato soltanto il file relativo al controller. Analizziamo il codice del Controller appena generato: <?php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class HelloController extends AbstractController { #[Route('/hello', name: 'app_hello')] public function index(): Response { return $this->render('hello/index.html.twig', [ 'controller_name' => 'HelloController', ]); } } Il Controller ha la struttura di un classico file PHP con all’inizio il tag di apertura <?php. Successivamente è presente il namespace della classe. Notiamo che il namespace è App. In generale, tutto il codice presente sotto la cartella src del progetto fa riferimento ad App. Questa configurazione è stata effettuata in automatico durante l’installazione ed è possibile visualizzarla all’interno del file composer.json, come mostrato dallo snippet seguente: "autoload": { "psr-4": { "App\\": "src/" } }, HelloController estende la classe AbstractController di Symfony dalla quale eredita una serie di metodi utili per le operazioni che vengono eseguite comunemente nei controller come il rendering delle view, l’accesso ai service e la generazione delle risposte. L’unica funzione contenuta all’interno della classe HelloController è index(). Tale funzione si occupa di effettuare il render della view hello/index.html.twig. Sopra alla definizione della funzione è presente la route ovvero il path della richiesta sul quale si attiverà il controller creato. Per verificare che l’associazione tra path e controller sia stata registrata in Symfony è possibile utilizzare un utile comando della console: php bin/console debug:router L’output del comando contiene l’elenco delle route presenti nel progetto. Nel nostro caso, notiamo come al percorso /hello sia associato app_hello che fa riferimento al metodo index() definito all’interno della classe HelloController. Aprendo in un browser l’indirizzo symfony-test.local/hello avremo in output la seguente schermata.   Ora proviamo a riadattare lo schema relativo al flusso di Symfony, analizzato poc'anzi, alla richiesta che ci ha portati a visualizzare la pagina di “Hello” appena creata Come si nota, Symfony, una volta intercettata la Request, ricerca il controller associato al path /hello, ovvero HelloController->index(), ed elabora la pagina HTML tramite il template /hello/index.html.twig da restituire come Response. Rispetto allo schema completo, in questo flusso, non è presente il blocco “Model”, concetto che approfondiremo più avanti.   Gestione degli errori in Symfony I controller, come detto in precedenza, possono restituire risposte di svariato tipo. Durante l’elaborazione di una richiesta, può capitare di dover gestire delle eccezioni legate a problematiche come risorsa non trovata (404) o errore generico del server (500).   Modifichiamo il codice della classe controller HelloController aggiungendo una nuova function che, a scopo dimostrativo, genera un’eccezione di tipo NotFoundHTTPException. #[Route('/hello-404', name: 'app_hello_404')] public function notFound(): Response { throw $this->createNotFoundException('Ops! Risorsa non trovata'); } Accedendo tramite browser all’indirizzo symfony-test.local/hello-404 vedremo la pagina di errore: In Symfony, tutti gli errori sono trattati come eccezioni, siano essi 404 o errori fatali. In ambiente di sviluppo, Symfony, per aiutarci a scovare il problema che ha generato l’eccezione, genera una pagina speciale (come quella precedente) in cui sono presenti molte informazioni utili per il debug. Questa pagina contiene una serie di informazioni sensibili riguardo il sistema e il progetto e può essere utilizzata da utenti malevoli per scoprire exploit e danneggiare il sito. In ambiente di produzione, infatti, Symfony sostituisce tale pagina con una minimale di errore generico che può essere personalizzata a proprio piacimento.
4.2

Routing in Symfony

Il routing è uno degli aspetti fondamentali di qualsiasi web framework moderno in quanto permette di mappare URI specifici a determinate azioni all’interno dell’applicazione. In sostanza, il routing determina come l’applicazione risponde ad ogni singola richiesta HTTP. Grazie alla configurazione del routing in Symfony, quando l’applicazione riceve una richiesta è in grado di invocare il metodo corretto di una determinata classe controller per generare una risposta. Come configurare il routing in Simfony Le rotte possono essere configurate in vari formati, ovvero YAML, XML, PHP o tramite PHP Attributes. Tutti questi formati sono intercambiabili tra di loro in quanto offrono le stesse funzionalità e le stesse performance. Le best practices di Symfony consigliano di utilizzare gli attributes in quanto, come già visto nel controller HelloController, sono definite assieme alla funzione controller, nello stesso file, evitando così di dover gestire più file in formati e cartelle differenti. Di seguito, una overview sulla configurazione delle route nei vari formati possibili. Successivamente, ci concentreremo solo sull’utilizzo dei PHP Attributes.   Configurare una route con PHP Attributes Gli attributes in PHP sono una caratteristica introdotta con PHP 8.0. Essi forniscono un modo per aggiungere meta-dati a classi, metodi, proprietà e parametri, simili a quelle che vengono chiamate annotations in altri linguaggi di programmazione. Nel caso delle route, prendiamo l’esempio del nostro HelloController in cui abbiamo definito il path /hello class HelloController extends AbstractController { #[Route('/hello', name: 'app_hello')] public function index(): Response { return $this->render('hello/index.html.twig', [ 'controller_name' => 'HelloController', ]); } Questa configurazione definisce una route chiamata app_hello che mappa l’URI /hello al metodo index() della classe HelloController.   Configurare una route con YAML La stessa route può essere configurata tramite YAML. Per farlo, è necessario creare un file nella cartella config chiamato routes.yaml. # config/routes.yaml app_hello: path: /hello controller: App\Controller\HelloController::index Configurare una route con XML  Utilizzando il formato XML, la route può essere configurata nel seguente modo: <!-- config/routes.xml --> <?xml version="1.0" encoding="UTF-8" ?> <routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> <route id="app_hello" path="/hello" controller="App\Controller\HelloController::index"/> </routes> Nb. Il file routes.xml deve essere creato in config.   Configurare una route con PHP Per configurare le route tramite PHP è necessario creare il file routes.php nella directory config: // config/routes.php use App\Controller\HelloController; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return function (RoutingConfigurator $routes): void { $routes->add('app_hello', '/hello) ->controller([HelloController::class, 'index']) ; }; Specializzare le route con le opzioni methods e condition La route app_hello precedentemente definita non specifica alcun metodo HTTP. In questa casistica, Symfony permette di contattare tale route con qualsiasi metodo come GET e POST. Utilizzando l’opzione methods è possibile limitare l’accesso alla route ad uno o più metodi HTTP.   Supponiamo di voler abilitare la route /hello solo per le chiamate GET e HEAD. Il codice verrà modificato nel seguento modo: #[Route('/hello', name: 'app_hello', methods: ['GET', 'HEAD'])] Un’altra opzione che è possibile utilizzare nella configurazione delle route è condition. Essa permette di abilitare la route solo quando si verificano determinate condizioni logiche. La sintassi delle espressioni deve seguire quella del componente ExpressionLanguage di Symfony.   Supponiamo, ad esempio, di voler attivare il path /hello solo per le chiamate GET e che contengono nell’header della richiesta un attributo chiamato “APPLICATION-KEY”: #[Route( '/hello, name: 'app_hello', condition: "context.getMethod() == 'GET' and request.headers.has('APPLICATION-KEY')" )] Parametri delle route in Symfony Fino a questo momento, abbiamo visto come associare un path statico ad una route. Spesso, però, si ha la necessità di ricevere in input dei valori da parte del client per poter eseguire delle elaborazioni specifiche. Ad esempio, in un blog, per accedere al contenuto di articolo potremmo pensare di creare una route /articolo. Tale route è uguale per ogni articolo. Per specificare a quale articolo si è interessati, è necessario introdurre il concetto di parametri dinamici delle route. Infatti, un parametro dinamico è un valore variabile che può essere fornito ad una route.   In Symfony, ad una route possono essere associati uno o più parametri. Per farlo, è sufficiente usare la seguente sintassi: /path/{nome del parametro} Nel caso dell’articolo di un blog, la definizione della route potrebbe essere la seguente: /articolo/{id} {id} è il parametro dinamico che rappresenta l’id di un articolo. Quando un utente visita, ad esempio, l’URL /articolo/43, il valore 43 viene fornito al controller all’interno della variabile id. In questo modo, il controller può richiedere al database l’articolo tramite il suo identificativo univoco. Esempio di una route con parametro A scopo dimostrativo, creiamo un nuovo metodo all’interno della solita classe HelloController: #[Route('/hello/random/{max}', name: 'app_hello_random')] public function randomNumber(int $max): Response { $number = random_int(0, $max); return new Response("Numero casuale: $number"); } La route sopra definita /hello/random/{max} prevede un parametro dinamico chiamato max. Il metodo controller randomNumber associato alla route genera e fornisce in output un numero casuale che va da 0 fino al valore del parametro ricevuto in input. Accedendo alla URL symfony-test.local/hello/random/100 otterremo un numero casuale tra 0 e 100: La funzione randomNumber ha utilizzato correttamente il parametro ricevuto. Se, però, al posto di un parametro numerico, inseriamo un valore di tipo stringa, otteniamo la seguente schermata: Symfony ha risposto con un errore del server (500 Internal Server Error) perchè randomNumber ha specificato nella firma del metodo un parametro id di tipo int, mentre noi abbiamo fornito un valore testuale.
4.3

Come validare le route in Symfony

Come abbiamo visto nel capitolo precedente, i valori passati come parametri alle route potrebbero essere non validi o addirittura non sicuri. Qui entra in gioco la validazione dei parametri delle route, essenziale per diverse ragioni:   integrità dei dati: verificare che i valori ricevuti siano nel formato corretto e non causino errori nell’applicazione sicurezza: prevenire attacchi come SQL injection user experience: fornire all’utente sempre dei feedback sensati quando inserisce dati non validi o esegue azioni non consentite   Come validare le route in Symfony? Semplice! Per validare i parametri è possibile utilizzare l’opzione requirements all’interno della definizione della route. I requirements sono composti da espressioni regolari che specificano per ogni singolo parametro quali sono i requisiti da soddisfare.    Per specificare nell’esempio precedente che il parametro max deve essere di tipo numerico, possiamo aggiungere l’opzione requirements alla definizione della route in questo modo: #[Route('/hello/random/{max}', name: 'app_hello_random', requirements: ['max' => '\d+'])] Come si nota, al parametro max è stata associata l’espressione regolare ‘\d+’ che accetta solo valori numerici. Se l’URL contiene un valore non numerico per il parametro max, Symfony non valuterà la route. Proviamo a ricaricare l’URL symfony-test.local/hello/random/ciao Come risultato non otterremo più un errore 500 ma un 404 not found in quanto la route con parametro testuale non è riconosciuta come registrata sul sistema.   L’opzione requirements può essere utilizzata inline per rendere la route più compatta. La sintassi da applicare ad ogni parametro è {nome del parametro<requirements>} La route precedente può essere modificata come segue: #[Route('/hello/random/{max<\d+>}', name: 'app_hello_random')] Debug delle route in Symfony Una tipica web application sarà composta da un numero importante di route. Per semplificare la gestione ed il debug delle route presenti nell’applicazione, Symfony mette a disposizione un utile comando della console, che abbiamo già accennato brevemente nella sezione relativa ai controller: php bin/console debug:router Il comando debug fornisce in output l’elenco di tutte le route nello stesso ordine in cui vengono valutate da Symfony. Per ogni route vengono fornite le seguenti informazioni:   Name: il nome univoco assegnato alla route Method: il metodo HTTP (o i metodi) da utilizzare per accedere alla route Scheme: il protocollo utilizzato per la route, ovvero http o https Host: indica l'host (o gli host) su cui la rotta è valida. Di default, la rotta è valida per qualsiasi host, ma Symfony permette di definire rotte specifiche per determinati domini o sottodomini. Path: il percorso sul quale è mappata la route   Lanciando il comando da terminale, possiamo visualizzare le route create nella classe controller HelloController che stiamo utilizzando in questa guida. Per ogni singola route, inoltre, è possibile accedere a dettagli specifici. Ad esempio, esploriamo nel dettaglio la route app_hello_random: php bin/console debug:router app_hello_random In output avremo: Oltre alle informazioni già ottenute con la lista delle route, con l’analisi di una singola route otteniamo anche delle utili informazioni riguardo la classe controller ed il metodo associati alla rotta, requisiti sui parametri ed eventuali opzioni aggiuntive configurate per la route.
4.4

View in Symfony

In Symfony, come in molti altri framework MVC (Model-View-Controller), le View sono responsabili della presentazione dei dati all'utente e sono anche i componenti con cui l’utente generalmente interagisce. Le view trasformano i dati forniti dal controller in un formato leggibile, solitamente un file HTML, che può essere visualizzato nel browser dall'utente.  Symfony utilizza un motore di template chiamato "Twig" per gestire le view, che approfondiremo più avanti.   La cartella del progetto preposta alla gestione dei template delle view è templates/. All'interno di questa cartella, i template possono essere ulteriormente organizzati in sottocartelle in base alla loro funzione o al modulo dell'applicazione a cui appartengono. In Symfony, la comunicazione tra il controller e la view è fondamentale per visualizzare dati dinamici e personalizzare l'output per l'utente. Il metodo principale utilizzato per passare dati dal controller alla view è render(). Questo metodo accetta due argomenti principali: il primo è obbligatorio ed è il percorso del template Twig da renderizzare Il secondo è opzionale ed è un array associativo (coppie chiave-valore) di variabili che possono essere utilizzate all'interno del template.   Effettivamente, se riprendiamo il codice del controller associato alla route hello, generato con il comando make #[Route('/hello', name: 'app_hello', methods: ['GET', 'HEAD'])] public function index(): Response { return $this->render('hello/index.html.twig', [ 'controller_name' => 'HelloController', ]); } notiamo l’invocazione del metodo render. A tale metodo vengono passati il nome del template da utilizzare hello/index.html.twig ed un array con all’interno la chiave controller_name associata al valore di tipo stringa HelloController. Nel template Twig viene utilizzata la variabile controller_name per stamparla a video come mostrato nello snippet seguente: <div class="example-wrapper"> <h1>Hello {{ controller_name }}! ✅</h1> Le variabili passate ad un template possono essere stringhe, array o oggetti.
4.5

I template con Twig

Che cos'è Twig? Twig è un template engine flessibile e potente per il linguaggio PHP. È stato progettato per essere sia veloce che sicuro, offrendo una sintassi chiara e concisa. Twig separa la logica di presentazione dalla logica applicativa permettendo agli sviluppatori php e ai web designer di lavorare in parallelo e in maniera indipendente.   Caratteristiche principali di Twig: Facile: Twig ha una sintassi specifica per i template che è sia leggibile che espressiva. Questo rende i template facili da scrivere e da comprendere anche per i web designer. Estensibile: Twig è altamente estensibile. Gli sviluppatori possono aggiungere funzionalità personalizzate attraverso estensioni, permettendo a Twig di adattarsi a una vasta gamma di esigenze e contesti. Sicuro: una delle principali preoccupazioni quando si lavora con i template è la sicurezza. Twig offre meccanismi come l'escaping automatico per prevenire attacchi di tipo script injection. Veloce: Twig compila i template in codice PHP plain, il che significa che, una volta compilati, i template sono estremamente veloci e efficienti. Separazione delle responsabilità: Twig promuove una chiara separazione tra la logica di presentazione e la logica applicativa. Questo non solo rende il codice più pulito e manutenibile, ma facilita anche la collaborazione tra sviluppatori e designer. Ben documentato: Ogni caratteristica di Twig è documentata e disponibile sul sito ufficiale    Twig rappresenta una soluzione potente per la gestione dei template in PHP. Con la sua sintassi chiara, le prestazioni ottimizzate e l'attenzione alla sicurezza, Twig offre agli sviluppatori uno strumento robusto e flessibile per la creazione di view dinamiche.   Come utilizzare Twig I file dei template Twig sono localizzati di default all’interno della cartella templates/. Tale configurazione è definita all’interno del file di twig.yaml:   twig: default_path: '%kernel.project_dir%/templates' Gli elementi di Twig sono separati dal resto del template tramite l’utilizzo dei delimitatori:   {{ ... }} utilizzato per stampare il contenuto di una variabile o il risulato di una espressione {% ... %} utilizzato per racchiudere logica come strutture condizionali e cicli {# ... #} utilizzato per l’inclusione di commenti all’interno del template. Tali commenti, non verranno renderizzati nell’output finale.   Symfony raccomanda le seguenti convenzioni per il naming di file e cartelle relative ai template: utilizzare lo stile snake case (underscore _ al posto degli spazi) per i nomi di file e cartelle. Esempio: blog/lista_articoli.html.twig definire due estensioni per i nomi dei file dove la prima è l’estensione che il template dovrà generare (es. HTML) e la seconda è twig. Esempio: index.html.twig Variabili in Twig Per accedere ad una variabile in Twig è sufficiente utilizzare la sintassi {{ nome_variabile }} Se una variabile è un oggetto si può accedere ai suoi attributi utilizzando la dot notation ‘.’. In caso di array, invece, si possono utilizzare le parentesi quadre ‘[]’ {{ prodotto.nome }} {{ prodotti[0].nome }} In Twig è anche possibile definire una variabile lato template utilizzando la sintassi {% set nome_variabile = "Valore" %} Ad esempio, per definire una variabile tipo_utente con dentro il valore stringa admin è sufficiente l’istruzione seguente {% set tipo_utente = "admin" %} Strutture di controllo in Twig Il costrutto if in Twig Il costrutto if in Twig è simile a quello presente nel linguaggio PHP. Per valutare una variabile contenente un valore booleano, consideriamo questo semplice esempio: {% if autenticato == false %} <p>Accedi al sistema per proseguire.</p> {% endif %} In Twig è possibile anche valutare se un array ha almeno un elemento: {% if utenti %} . . . {% endif %} Per valutare più rami condizionali e creare costrutti if più complessi è possibile usare elseif ed else, proprio come in PHP: {% if carburante > 30 } Hai il pieno di carburante {% elseif carburante > 15} Hai più di metà pieno di carburante {% elseif carburante > 5} Rimangono solo {{ carburante }} litri di carburante {% else %} Sei in riserva! {% endif %} Il costrutto for in Twig Con for è possibile ciclare gli elementi di un array. Ad esempio, per stampare il nome di ogni prodotto contenuto nell’array prodotti è sufficiente scrivere le seguenti istruzioni: {% for prodotto in prodotti %} {{ prodotto.nome }} {% endfor %} All’interno di ogni blocco iterativo è possibile accedere ad una serie di variabili speciali:   loop.index: l’indice dell’elemento corrente (partendo da 1) loop.index0: l’indice dell’elemento corrente (partendo da 0) loop.revindex : il numero di iterazioni rimanenti (partendo da 1) loop.revindex0: il numero di iterazioni rimanenti (partendo da 0) loop.first: true se è la prima iterazione loop.last: true se è l’ultima iterazione loop.length: il numero totale degli elementi nell’array loop.parent: presente solo all’interno di un ciclo annidato. Fornisce l’accesso all’oggetto loop del ciclo superiore.   Con Twig è possibile iterare, oltre che sui valori, anche sulle chiavi dell’array: {% for key, prodotto in prodotti %} {{ key }}: {{ prodotto.nome }} {% endfor %} Filtri in Twig I filtri in Twig permettono di modificare e formattare i valori delle variabili prima di visualizzarle. La sintassi per l’utilizzo di un filtro è la seguente {{ variabile|filtro }} Affianco alla variabile si aggiunge il simbolo “|” chiamato pipe e, successivamente, si indica il filtro che si vuole utilizzare. I filtri possono anche essere concatenati per applicare più trasformazioni in sequenza.   Sul sito ufficiale è consultabile la lista completa dei filtri. Di seguito analizzeremo alcuni dei filtri più comuni disponibili in Twig.   Il filtro upper in Twig Il filtro upper in Twig converte una stringa in maiuscolo {{ 'hello'|upper }} Risultato: HELLO   Il filtro lower in Twig Il filtro lower in Twig converte una stringa in minuscolo {{ 'HELLO'|lower }} Risultato: hello Il filtro date in Twig Il filtro date in Twig formatta una data in un formato specifico {{ data_da_formattare|date('d/m/Y') }} Risultato: 11/10/2023 Il filtro length in Twig Il filtro length in Twig restituisce la lunghezza di una stringa o di un array {{ 'hello'|length }} Risultato: 5 Il filtro slice in Twig Il filtro slice in Twig estrae una porzione di stringa o array {{ 'hello world'|slice(0,5) }} Risultato: hello Funzioni in Twig Oltre ai filtri, Twig offre una serie di funzioni che possono essere utilizzate per eseguire operazioni specifiche all'interno dei template. Le funzioni sono simili ai filtri, ma vengono invocate in modo diverso e spesso restituiscono valori piuttosto che trasformare dati esistenti. Per invocare una funzione è sufficiente utilizzare una sintassi simile a quella delle funzioni in molti linguaggi di programmazione: {{ nome_funzione(argomento1, argomento2, ...) }} Analogamente a quanto detto per i filtri, anche la lista completa delle funzioni in Twig è consultabile sul sito ufficiale.  Di seguito analizzeremo alcune funzioni presenti in Twig. La funzione range in Twig La funzione range in Twig restituisce una lista contenente una progressione aritmetica di numeri interi {% for i in range(1, 5) %} {{ i }}, {% endfor %} Risultato: 12345 La funzione dump in Twig La funzione dump in Twig è molto utile in fase di debug, permette di stampare informazioni su una specifica variabile {{ dump(prodotto) }} Risultato array:3 [ "nome" => "Pizza Margherita" ... ] La funzione date in Twig La funzione date in Twig permette di estrarre e manipolare la data corrente e di convertire una stringa in una data per effettuare comparazioni. {% if date(prodotto.data_creazione) < date('-3days') %} . . . {% endif %} Nell’esempio precedente, viene confrontata la data di creazione di un prodotto con la data di tre giorni fa.

5

Accesso ai dati in Symfony

5.1

Come configurare il database con Doctrine ORM

Symfony mette a disposizione dello sviluppatore una serie di strumenti che permettono di utilizzare e semplificare la gestione di un database. Questi tool, racchiusi all’interno del bundle Doctrine ORM, sono in grado di supportare sia database relazionali come MySQL che database NoSQL come MongoDB. Prima di procedere con la configurazione di Doctrine ORM, è opportuno spiegare cosa significa ORM. Cos’è un ORM META DESCRIPTION: Scopri cos'è un ORM, il suo ruolo nella mappatura object-relational e come facilita la gestione dei dati nelle applicazioni web. ORM sta per Object-Relational Mapping ed è una tecnica di programmazione che consente di interagire con i database, come ad esempio MySQL o PostgreSQL, utilizzando oggetti. In questo modo, viene ridotta drasticamente la quantità di istruzioni SQL necessarie per eseguire tutte le operazioni sui dati richieste dal progetto. Infatti, l'ORM permette di lavorare con il database utilizzando il paradigma della programmazione orientata agli oggetti. L'idea di base dietro un ORM è quella di mappare gli oggetti alle tabelle del database e viceversa, come mostrato nello schema seguente. Questo meccanismo permette agli sviluppatori di concentrarsi sulla logica dell'applicazione piuttosto che sulle specifiche del database. I principali vantaggi derivanti dall’utilizzo di uno strato ORM sono i seguenti:   Astrazione del database: non è necessario scrivere codice SQL specifico per un determinato DBMS. L'ORM si occupa di generare istruzioni SQL per il database che si sta utilizzando. Sicurezza: L'ORM fornisce protezione integrata contro le vulnerabilità più comuni come SQL injection. Manutenibilità: il codice è più pulito, più comprensibile e più facile da manutenere. Riusabilità: la separazione tra logica di business e logica di accesso ai dati favorisce il riutilizzo del codice in più componenti. Ricordiamo che, se dovessi aver necessità di rispolverare i concetti inerenti a database e al linguaggio SQL, potrai consultare,i tra le nostre risorse online, la nostra guida SQL in italiano.   Come installare Doctrine ORM Procediamo all’installazione di Doctrine tramite il Symfony pack orm: composer require symfony/orm-pack Il comando lanciato si è occupato di effettuare l’installazione e la configurazione del bundle Doctrine ORM per Symfony. Per configurare la connessione al database è necessario accedere al file .env della root del progetto, identificare la variabile DATABASE_URL riferita al proprio DBMS e decommentare. Nel caso di XAMPP, la riga di interesse è la seguente DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" Sostituire app (username), !ChangeMe! (password), 127.0.0.1 (indirizzo), 3306 (porta) e app (nome) e il parametro serverVersion con le impostazioni del proprio server dbms. Per recuperare le informazioni sul server, possiamo avvalerci del supporto di phpmyadmin. Per prima cosa, accedere al pannello di controllo di XAMPP e avviare il servizio MySql. Dal pannello di controllo è già visibile l’informazione sulla porta dove è in ascolto il database. Premere su “Admin” (evidenziato in giallo) per accedere a phpmyadmin. Si presenterà la seguente schermata: Nella parte destra dell’interfaccia web, all’interno della sezione “Server del Database”, sono disponibili le informazioni sul nostro DBMS. La prima riga contiene indirizzo (127.0.0.1) e porta (33306) del database.    Successivamente, è indicata la tipologia (MariaDB) e, più in basso, è possibile reperire la versione del DBMS (10.4.28-MariaDB). Aggiorniamo, quindi, la variabile DATABASE_URL all’interno del file .env con le informazioni appena recuperate: DATABASE_URL="mysql://root:[email protected]:33306/symfonytest?serverVersion=10.4.28-MariaDB&charset=utf8mb4" A questo punto, occorre solo Inserire le proprie credenziali e scegliere il nome del database per il progetto, in questo caso symfonytest. Ulteriori configurazioni di Doctrine sono presenti in /config/packages/doctrine.yaml: Osserviamo il nostro codice. Alla riga 3 notiamo come l’URL per la connessione al database venga recuperato dal file .env appena modificato. Un altro passaggio interessante si trova alla riga 21, dove viene definito il prefisso che identifica il namespace dove devono essere collocate le Entity da mappare. Tutte le altre configurazioni possono essere approfondite sulla documentazione ufficiale. Come creare un database in Symfony Completata la configurazione di Doctrine, si può procedere con la creazione del database. Se il database specificato in DATABASE_URL di .env non esiste ancora, è possibile crearlo direttamente da console di Symfony utilizzando il comando: php bin/console doctrine:database:create Riceviamo il seguente messaggio in output   Accedendo nuovamente a phpMyAdmin si può notare come, effettivamente, sia stato creato il nuovo database Il nuovo database risulta senza alcuna tabella definita, vedremo più avanti come creare e mappare nuove tabelle tramite l’utilizzo delle Entity.
5.2

Come generare una Entity Class in Symfony

Con il servizio del database in esecuzione e la connessione configurata, l’applicazione Symfony è in grado di interagire con le tabelle che andremo a creare. Come visto in precedenza, ogni tabella viene mappata in una classe PHP chiamata Entity, grazie all’ausilio di Doctrine ORM. Nello specifico, la mappatura avviene seguendo la logica rappresentata nello schema in basso. La tabella viene rappresentata da una classe Entity Le righe della tabella vengono mappate con le istanze della classe Entity Le colonne di una riga specifica sono mappate con gli attributi dell’istanza della classe Entity che fa riferimento a quella riga Tutte le Entity devono essere localizzate all’interno della cartella /src/Entity. Essendo classi PHP, possono essere create manualmente. Symfony, per semplificare la creazione di una nuova entity, ci viene in aiuto con il bundle Maker. Per generare una nuova classe, basta lanciare il comando della console. php bin/console make:entity NomeEntity Questo comando permette di aggiungere in maniera interattiva le proprietà (che verranno mappate come colonne) della nuova classe, facendo scegliere per ogni proprietà nome, tipo di dato e se può contenere valori nulli. Per lo scopo di questa guida, andremo a creare una classe “Prodotto” che conterrà le proprietà “nome”, “prezzo” e “disponibile”. Completata l’interazione con il comando, all’interno della cartella /src/Entity troveremo la nuova classe Prodotto.php <?php namespace App\Entity; use App\Repository\ProdottoRepository; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: ProdottoRepository::class)] class Prodotto { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 255)] private ?string $nome = null; #[ORM\Column] private ?float $prezzo = null; #[ORM\Column] private ?bool $disponibile = null; public function getId(): ?int { return $this->id; } public function getNome(): ?string { return $this->nome; } public function setNome(string $nome): static { $this->nome = $nome; return $this; } public function getPrezzo(): ?float { return $this->prezzo; } public function setPrezzo(float $prezzo): static { $this->prezzo = $prezzo; return $this; } public function isDisponibile(): ?bool { return $this->disponibile; } public function setDisponibile(bool $disponibile): static { $this->disponibile = $disponibile; return $this; } } Doctrine ha aggiunto in automatico una proprietà “id” che rappresenta l’identificativo univoco di un record all’interno della tabella. Dopo l’elenco delle proprietà, inoltre, ha generato i metodi chiamati getter e setter relativi alle operazioni di lettura e scrittura delle proprietà di una specifica istanza della classe Prodotto. La classe generata appare come un normale file PHP, con proprietà e metodi. La differenza risiede nei PHP Attributes specifici di Doctrine utili per definire le modalità di mapping della tabella e le caratteristiche specifiche degli attributi lato database.   L’annotazione ORM\Entity è fondamentale per marcare una classe PHP come Entity e per definire la classe Repository collegata (approfondiremo in seguito questo passaggio). L’annotazione ORM\Column, invece, permette di mappare una proprietà della classe su una colonna della tabella. A @ORM\Column possono essere passati una serie di attributi opzionali come nel caso della proprietà “nome” alla quale è stata assegnata una lunghezza massima di 255 caratteri: #[ORM\Column(length: 255)] private ?string $nome = null; Prendiamo, ora, in considerazione la definizione dell’attributo “id”: #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; Notiamo i seguenti PHP Attributes: ORM\Id: serve per marcare l’attributo come l’identificatore univoco dell’entità ovvero la chiave primaria all’interno del database ORM\GeneratedValue: specifica la strategia da utilizzare per generare l’identificatore univoco. Se non viene fornita alcuna opzione, viene usata quella di default del dbms utilizzato.   Puoi consultare la lista completa di tutti gli Attributes presenti in Doctrine ORM sul sito ufficiale.   Una volta che la Entity è configurata, è necessario riportare le modifiche anche sul database per creare la relativa tabella. Nel nostro caso, verrà creata la tabella prodotto associata all’Entity Prodotto. Per farlo è sufficiente lanciare il comando php bin/console make:migration Approfondiremo più avanti, con un capitolo dedicato, cos’è una Migration.   Come creare un nuovo record in un database in Symfony La classe Entity appena creata ci permette di effettuare operazioni sui dati presenti in tabella tramite la mappatura e le funzionalità messe a disposizione da Doctrine. Per procedere con la creazione di un oggetto di tipo Prodotto e la relativa scrittura su database come nuovo record, andiamo a creare un controller chiamato ProdottoController e aggiungiamo il seguente metodo: #[Route('/prodotto/crea', name: 'prodotto_crea')] public function creaProdotto(EntityManagerInterface $entityManager): Response { $pizzaMargherita = new Prodotto(); $pizzaMargherita->setNome('Pizza Margherita'); $pizzaMargherita->setPrezzo(7); $pizzaMargherita->setDisponibile(true); $pizzaMargherita->setDescrizione('Un classico intramontabile. Pomodoro, mozzarella, basilico'); $entityManager->persist($pizzaMargherita); $entityManager->flush(); dd($pizzaMargherita); } Per testare il funzionamento, non avendo il template modificato per la visualizzazione dei dati derivanti dal database, utilizziamo la funzione dd() (dump and die) che ci permette di stampare a video l’oggetto che andremo a creare. Accediamo alla route appena creata symfony-test.local/prodotto/crea per testare il funzionamento: L’output del metodo appena eseguito è l’oggetto di tipo Prodotto che abbiamo configurato. Notiamo come ora sia presente anche la proprietà id, contenente l’identificativo univoco memorizzato sul database. Come ulteriore verifica della creazione del record sul database all’interno della tabella prodotto collegata alla Entity Prodotto, è sufficiente accedere a phpMyAdmin: Analizzando il codice del metodo creaProdotto possiamo notare delle differenze rispetto a quanto visto fino ad ora. Notiamo come sia presente un nuovo componente chiamato EntityManager. Esso è il punto centrale di accesso alle funzionalità ORM offerte da Doctrine facendo da intermediario tra le Entity e il database. Tramite un’interfaccia orientata agli oggetti, incapsula la logica necessaria per gestire le entità e interagire con il database. Per tale motivo, EntityManager viene “iniettato” come servizio alla riga 16. Dalla riga 18 alla riga 22, invece, viene creato un oggetto di tipo “Prodotto” e grazie ai setter precedentemente definiti, vengono popolati gli attributi nome, prezzo, disponibile e descrizione. Le righe 24 e 26 richiamano due metodi dell’EntityManager:   persist: con il quale si richiede a Doctrine di gestire come entità l’oggetto che gli viene passato come argomento  flush: si occupa di eseguire la query SQL al posto nostro. In questo caso specifico esegue una query di INSERT creando un nuovo record nella tabella prodotto.  
5.3

Migrazione dei dati in Symfony

Una migrazione è essenzialmente una versione dello schema del database che contiene un set di modifiche correlate (come l’aggiunta di una nuova tabella, la modifica di una colonna esistente, l’aggiunta di una chiave primaria, ecc.) I principali vantaggi delle migrations sono i seguenti:   Versionamento: le migrations permettono di versionare le modifiche apportate allo schema del database nel corso tempo, tenendo traccia di tutti i cambiamenti avvenuti sulle singole tabelle. In questo modo è più facile gestire e distribuire le modifiche sui diversi ambienti. Collaborazione: se si lavora in team, le migrazioni sono un ottimo strumento per assicurare che tutti gli sviluppatori php siano allineati allo stesso schema del database. Rollback: le migrazioni permettono di eseguire il rollback di tutte le modifiche apportate da una determinata versione, ovvero di tornare allo stato precedente. Questo, ovviamente, risulta molto utile nel caso in cui si introducono bug con una nuova versione dello schema del database.   Dopo aver apportato modifiche alle classi Entity è possibile generare una nuova migrazione con il comando php bin/console make:migration Tale comando, esegue un’analisi di tutte le classi Entity per determinare le differenze tra il mapping corrente e lo schema del database. Il risultato di questa analisi genererà una nuova classe di migration con le modifiche necessarie I file delle migrations sono memorizzati nella cartella  /migrations. Accedendo al file appena generato notiamo che la classe estende AbstractMigration e la presenza dei metodi up e down. Vediamoli final class Version20230926094844 extends AbstractMigration { public function getDescription(): string { return ''; } public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs $this->addSql('CREATE TABLE prodotto (id INT AUTO_INCREMENT NOT NULL, nome VARCHAR(255) NOT NULL, prezzo DOUBLE PRECISION NOT NULL, disponibile TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('DROP TABLE prodotto'); } } il metodo up: definisce le modifiche da apportare allo schema del database. In questo caso è presente la query SQL per la creazione della tabella “prodotto” che abbiamo definito attraverso la Entity class “Prodotto”. il metodo down: definisce come annullare le modifiche apportate dalla migrazione. Nel caso corrente troviamo la query SQL di drop per eliminare la tabella prodotto. Lanciamo, quindi, il comando per applicare le modifiche al database: php bin/console doctrine:migrations:migrate Nel nostro caso, andrà a creare sul database la tabella prodotto associata alla Entity Prodotto.  Accedendo a phpMyAdmin possiamo vedere che sono state create le due tabelle doctrine_migration_versions e prodotto. La tabella ‘prodotto’ dispone di tutte le colonne definite nella classe Entity con le relative specifiche. Per gestire le migrazioni e avere una panoramica di esse, Doctrine mette a disposizione il seguente comando php bin/console doctrine:migrations:status Questo comando offre in output una serie di informazioni riguardanti le migrazioni eseguite e le versioni corrente e passata Abbiamo visto come creare una tabella tramite migration. Ora, proviamo ad aggiungere una nuova proprietà sulla nostra Entity Prodotto e ad aggiornare lo schema del database. Possiamo farlo direttamente modificando il file PHP della classe oppure avvalendoci del comando make. Una volta aggiunto il campo, creiamo una nuova migration e lanciamola. A scopo esemplificativo, andremo ad aggiungere un nuovo attributo chiamato “descrizione”. public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE prodotto ADD descrizione VARCHAR(255) DEFAULT NULL'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE prodotto DROP descrizione'); } I metodi up e down della nuova versione contengono le query per aggiungere e rimuovere la colonna descrizione dalla tabella prodotto.   Proviamo ad effettuare il rollback dell’ultima migration, ovvero a riportare lo schema del database a prima dell’aggiunta della colonna descrizione all’interno della tabella prodotto. php bin/console doctrine:migrations:migrate prev L’output del comando sarà il seguente Come si nota, lo schema è stato riportato con successo alla versione precedente senza dover andare ad agire direttamente sul database.
5.4

Repository con Doctrine in Symfony

Come visto nella classe Prodotto, all’interno dell’attributes ORM\Entity è definita la classe Repository collegata. In generale, con il termine Repository si fa riferimento ad un design pattern che permette di isolare e astrarre il layer di accesso ai dati dal resto dell’applicazione. In Symfony, viene implementato attraverso Doctrine.  Le classi PHP inerenti i Repository sono localizzate all’interno della cartella src/Repository. Nel caso della Entity Prodotto facciamo riferimento alla classe ProdottoRepository generata da MakerBundle: class ProdottoRepository extends ServiceEntityRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Prodotto::class); } } Ogni classe Repository in Symfony estende ServiceEntityRepository per avere accesso ad una serie di metodi e funzionalità predefinite per la gestione dei dati che facilitano l’interazione con il database. Tra i metodi più comuni troviamo: find: recupera un'entità tramite il suo identificatore univoco. findAll: recupera tutte le entità di un determinato tipo. findBy: recupera tutte le entità che soddisfano determinate condizioni; è possibile ordinare i risultati, limitare il numero di entità restituite e specificare un offset per la paginazione. findOneBy: recupera la prima entità che soddisfa determinate condizioni; è possibile ordinare i risultati per ottenere un’entità specifica. Tutti i metodi elencati permettono di eseguire interrogazioni al database senza bisogno di dover scrivere query SQL.   Come interrogare il database con i Repository di Doctrine Proviamo, ora, ad interagire con il database tramite l’ausilio dei Repository di Doctrine. Metodo findAll Come primo test, recuperiamo tutti i record presenti nella tabella prodotto. Per farlo, è sufficiente iniettare ProdottoRepository all’interno del metodo index del controller ProdottoController e utilizzare il metodo findAll: public function index(ProdottoRepository $prodottoRepository): Response { dd($prodottoRepository->findAll()); } Accediamo, quindi, alla pagina symfony-test.local/prodotto per visualizzare tutti i record recuperati e mappati all’interno di oggetti Entity di tipo Prodotto: Metodo find Per accedere ad una singola entità, viene in aiuto il metodo find che accetta come argomento l’id del record desiderato. Nella schermata precedente, abbiamo visto che tra gli attributi di ogni oggetto è presente anche id, che fa riferimento alla chiave primaria del record specifico sulla tabella prodotto. Per recuperare, ad esempio, il prodotto con nome “Tè alla pesca” è sufficiente modificare il metodo del controller come segue: public function index(ProdottoRepository $prodottoRepository): Response { dd($prodottoRepository->find(3)); } In output riceviamo la stampa dell’entità richiesta. Con l’utilizzo pratico dei metodi forniti dalla classe Repository, tutta la parte teorica discussa precedentemente sugli ORM appare molto più chiara. Con la funzione find abbiamo richiesto, tramite un’interfaccia orientata agli oggetti, i dati di un record specifico presente su una tabella specifica. Doctrine si è occupato di eseguire la query SQL per recuperare i dati di un record da una tabella (nel nostro caso prodotto) e di mappare il risultato su una entità ovvero di creare un’istanza di una classe Entity (nel nostro caso Prodotto). Metodi findOneBy / findBy Per ricercare un record per un attributo (o una serie di attributi) differente da id, abbiamo a disposizione il metodo findOneBy. Tale metodo accetta come parametri un array associativo composto da coppie di nome attributo e valore da utilizzare per la ricerca su quell’attributo. Ad esempio, per cercare un prodotto per nome possiamo usare $prodottoRepository->findOneBy(['nome' => 'Pizza Margherita']) Il metodo findBy è simile a findOneBy, con la differenza che restituirà in output tutti i risultati della ricerca effettuata sulla base dei criteri forniti in input.   Interrogazioni con QueryBuilder Oltre ai metodi visti precedentemente, Doctrine mette a disposizione un potente strumento chiamato QueryBuilder che offre una modalità orientata agli oggetti per creare query più articolate e complesse.   I metodi più comuni di QueryBuilder sono i seguenti: select: definisce le colonne da selezionare dalla tabella from: specifica da quale tabella o entità recuperare i dati where: aggiunge una clausola WHERE alla query per filtrare i risultati setParameter: imposta un parametro che può essere utilizzato nella query orderBy: ordina i risultati della query in base ad una specifica colonna e verso (ascendente o discendente) Proviamo ad utilizzare il QueryBuilder nel nostro repository ProdottoRepository per recuperare tutti i prodotti che hanno un prezzo superiore ad un valore che verrà passato in input al controller. Aggiungiamo il seguente metodo in ProdottoRepository: public function findAllWithPrezzoGreaterThan(float $prezzo): array { return $this->createQueryBuilder('p') ->where('p.prezzo > :prezzo') ->setParameter('prezzo', $prezzo) ->orderBy('p.prezzo', 'ASC') ->getQuery() ->getResult(); } Nello snippet precedente, si vede come siano stati utilizzati i metodi   where: è stato specificato che l’attributo prezzo deve essere maggiore di un parametro :prezzo setParameter: contiene il parametro da passare alla clausola where orderBy: è stato richiesto di ordinare i risultati per prezzo dal più economico al più costoso getQuery: genera la query SQL da lanciare sul database getResult: esegue la query e recupera i risultati   Nel controller andiamo a creare, quindi, un metodo che riceve in input un valore numerico da utilizzare per la query e ritorna in output il risultato dell’interrogazione al database: #[Route('/prodotto/prezzo-gt/{prezzo<\d+>}', name: 'prodotto_with_prezzo_greater_than')] public function findProdottiPrezzo(ProdottoRepository $prodottoRepository, float $prezzo): Response { dd($prodottoRepository->findAllWithPrezzoGreaterThan($prezzo)); } Richiamando la route con il parametro 5 riceviamo correttamente i due prodotti che hanno prezzo superiore a 5: Utilizzando come parametro il valore 10, invece, ne riceviamo solo uno:   Analizzare le query SQL con Profiler Per visualizzare le query SQL che effettivamente vengono lanciate da Doctrine, possiamo utilizzare un utile strumento di sviluppo chiamato Profiler. Questo strumento fornito da Symfony, raccoglie informazioni dettagliate sulle prestazioni e sul comportamento dell’applicazione durante l’esecuzione delle varie richieste. Per installare Profiler basterà lanciare il seguente comando Composer da terminale: composer require --dev symfony/profiler-pack Eseguiamo di nuovo la richiesta sulla route /prodotto/prezzo-gt/10 e apriamo l’interfaccia di Profiler tramite browser all’indirizzo http://symfony-test.local/_profiler. Dovrebbero essere presenti le ultime richieste effettuate sull’applicazione Symfony. Selezioniamo, quindi, quella appena eseguita e accediamo dal menù laterale alla sezione “Doctrine”. Ecco a nostra disposizione tutte le informazioni dettagliate sull’interazione con il database per la richiesta. Notiamo come la query creata con QueryBuilder sia stata effettivamente tradotta in query SQL: SELECT p0_.id AS id_0, p0_.nome AS nome_1, p0_.prezzo AS prezzo_2, p0_.disponibile AS disponibile_3, p0_.descrizione AS descrizione_4 FROM prodotto p0_ WHERE p0_.prezzo > ? ORDER BY p0_.prezzo ASC

6

Approfondimenti su Symfony

6.1

Form in Symfony

I form sono componenti essenziali nelle applicazioni web. Tramite un’interfaccia interattiva, facilitano la raccolta di dati dagli utenti da inviare al server per l’elaborazione. Ogni form, solitamente, gestisce un set di informazioni correlate come possono essere quelle relative alla registrazione di un utente, la ricerca di una risorsa o l’invio di un feedback.   Un form, tipicamente, è composto da una serie di campi di input che abilitano l’utente all’inserimento di dati: text input: permette l’inserimento di testo libero password: simile al text input ma con il testo inserito nascosto per privacy checkbox: permette di selezionare più valori tra quelli disponibili radio button: permette la selezione di una sola opzione tra quelle disponibili select box: fornisce una lista di opzioni da un menù a tendina. Può essere configurato per permettere la selezione di una o più opzioni upload: permette il caricamente di uno o più file   Oltre ai campi di input, i form dispongono dei seguenti pulsanti: submit: utile per l’invio dei dati del form presenti nei campi di input al server per l’elaborazione reset: ripristina il valore di tutti i campi di input presenti nel form ai loro valori predefiniti Come creare un form in Symfony La creazione e l’elaborazione di form HTML si compone di un set di task ripetitivi e di complessità variabile. Solitamente, per far funzionare correttamente un form si ha la necessità di configurare l’interfaccia per la visualizzazione dei campi di input, abilitare il form per l’invio dei dati, validare i dati ricevuti lato server, mappare i dati del form all’interno di oggetti e fornire feedback adeguati all’utente. Le attività citate possono essere gestite dal componente Form di Symfony, in grado di fornire tutte le funzionalità necessarie sia per scenari basilari che per scenari più complessi. Installiamo, quindi, il componente Form grazie all’ausilio di Symfony Flex: composer require symfony/form I form possono essere creati direttamente nei metodi delle classi controller che estendono AbstractController oppure, separatamente, in una classe dedicata. Le linee guida di Symfony raccomandano di inserire meno logica possibile all’interno dei Controller e, quindi, opteremo per la seconda opzione. Inoltre, separando il Controller dal form, quest’ultimo potrà essere riutilizzato anche in altri Controller o service.   Il workflow raccomandato da Symfony per la gestione dei form è il seguente:   Creare il form con una classe dedicata Renderizzare il form in un template Twig in modo che l’utente possa compilarlo e inviarlo Validare i dati inviati, trasformarli in variabili PHP e/o oggetti ed elaborarli Procediamo, quindi, col creare una nuova classe per gestire il form di aggiunta di un nuovo prodotto tramite l’ausilio del comando make: php bin/console make:form Il comando richiede di inserire in maniera interattiva il nome della classe e l’Entity collegata. Inseriamo ProdottoType per il nome del form e Prodotto per l’Entity da collegare. Tutti i form sono localizzati all’interno delle cartella src/Form. Apriamo il file appena generato: <?php namespace App\Form; use App\Entity\Prodotto; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class ProdottoType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('nome') ->add('prezzo') ->add('disponibile') ->add('descrizione') ; } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => Prodotto::class, ]); } } Nella classe sono stati aggiunti in automatico i campi relativi all’entità che è stata collegata attraverso il metodo add all’interno di buildForm. Più in basso, invece, è presente il metodo configureOptions all’interno del quale viene definita l’Entity associata al form. Sono necessarie delle modifiche per configurare correttamente il form. Modifichiamo il codice precedente con il seguente: public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('nome', TextType::class, ['label' => 'Nome']) ->add('prezzo', NumberType::class, [ 'label' => 'Prezzo', 'rounding_mode' => NumberFormatter::ROUND_HALFUP, 'scale' => 2 ]) ->add('disponibile', CheckboxType::class, [ 'label' => 'Disponibile', 'required' => false ]) ->add('descrizione', TextType::class, [ 'label' => 'Descrizione', 'required' => false ]) ->add('submit', SubmitType::class, ['label' => 'Crea il prodotto!']); } Ad ogni campo sono state aggiunte delle opzioni per poter gestire in maniera più accurata i dettagli dell’input specifico. Notiamo come sono state modificate le invocazioni al metodo add del FormBuilder. La firma di tale funzione è la seguente: public function add(string|FormBuilderInterface $child, string $type = null, array $options = []): static; Nel nostro caso, nel primo parametro abbiamo inserito il nome dell’attributo della Entity, nel secondo parametro la tipologia di campo e nel terzo parametro un array di opzioni.  La lista completa dei tipi di campo gestibili tramite Form di Symfony è disponibile sulla documentazione ufficiale di Symfony .  Nella lista delle opzioni, nei vari campi abbiamo aggiunto label per specificare l’etichetta da mostrare sull’interfaccia web e required per specificare se il campo deve essere obbligatorio o meno. Nel campo prezzo, a differenza degli altri, abbiamo aggiunto anche rounding_mode per gestire l'arrotondamento e scale per limitare le cifre decimali a due cifre. Infine, nell’ultimo metodo add è presente la definizione del pulsante submit per l’invio dei dati del form al server.   Aggiungiamo, ora, un controller per poter gestire il form all’interno della classe ProdottoController: #[Route('/prodotto/crea', name: 'prodotto_crea')] public function creaProdotto(): Response { $form = $this->createForm(ProdottoType::class, new Prodotto()); return $this->render('prodotto/crea.html.twig', [ 'form' => $form ]); } Il controller invoca il metodo createForm passando il form ProdottoType e una nuova istanza dell’Entity Prodotto. Il risultato di questa operazione viene fornita al template all’interno della variabile form. Il pezzo mancante per poter provare il funzionamento del form è il template da renderizzare, come si nota dall’istruzione di return del metodo appena creato. Creiamo il template crea.html.twig in src/templates/prodotto e aggiungiamo le seguenti istruzioni: {% extends 'base.html.twig' %} {% block title %}Crea nuovo prodotto{% endblock %} {% block body %} <h1>Crea nuovo prodotto</h1> {{ form(form) }} {% endblock %} La funzione form() si occupa di stampare a video tutti i campi passati dal controller sotto forma di input HTML e racchiuderli all’interno del tag <form>. Di default, il metodo di invio dei dati è POST e il target URL del form è quello della pagina corrente. Ora che sono collegati form, entity, controller e template possiamo accedere alla route prodotto/crea per visualizzare il risultato: Symfony si è occupato di creare il form HTML sulla base delle specifiche che gli abbiamo fornito tramite la classe ProdottoType. Proviamo ad analizzare il codice HTML generato per la pagina. Per farlo, è sufficiente utilizzare la console sviluppatori del browser premendo tasto destro sulla pagina e scegliendo la voce “Esamina” o “Ispeziona” a seconda del browser utilizzato. Come si nota dall’ immagine precedente, Symfony ha generato il tag <form> e racchiuso al suo iinterno i vari campi. Per il campo Nome, ad esempio, ha generato un div html con all’interno una label e un input text.   Come gestire l’invio dei dati di un form in Symfony Una volta configurato il form e renderizzato, è necessario gestire i valori che verranno inseriti dall’utente. Proviamo ad inserire dei dati e inviare il form tramite il pulsante “Crea il prodotto!”. La pagina viene ricaricata e i campi vengono ripristinati ai valori di default, ovvero senza alcun carattere inserito. Questo accade perché non stiamo ancora gestendo il sumbit. I dati inseriti, però, sono stati inviati al server. Analizziamo la richiesta appena effettuata tramite Profiler:   Nella sezione POST Parameters notiamo che, effettivamente, nella richiesta sono presenti i dati che abbiamo inserito. Adeguiamo il controller e analizziamo il codice: #[Route('/prodotto/crea', name: 'prodotto_crea')] public function creaProdotto(Request $request): Response { $form = $this->createForm(ProdottoType::class, new Prodotto()); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $prodotto = $form->getData(); dd($prodotto); } return $this->render('prodotto/crea.html.twig', [ 'form' => $form ]); } Nel metodo è stato aggiunto il parametro request di tipo Request (HttpFoundation) per poter accedere ai dati della richiesta. Successivamente, sono presenti le istruzioni per la gestione del form, che ramificano il percorso della richiesta nel seguente modo:   La prima volta che viene caricata la pagina, $form->isSubmitted() ritorna false in quanto il form non è ancora stato inviato. Viene saltato il blocco condizionale e il form viene renderizzato. Quando l’utente invia il form, handleRequest() aggiorna l'oggetto di tipo Prodotto passato in fase di creazione del form e lo valida, ovvero, nel nostro caso, verifica che i campi obbligatori siano stati compilati e che il campo prezzo sia effettivamente di tipo numerico. isValid() restituisce true: è possibile effettuare delle azioni specifiche come memorizzare l’oggetto nel database, mostrare un messaggio all’utente. Nel nostro caso, stampa a video l’oggetto con le proprietà aggiornate. isValid() restituisce false: il form viene renderizzato nuovamente ma, questa volta, verranno visualizzati i messaggi di errore relativi alla validazione dei vari campi   Inseriamo un valore di tipo testuale all’interno del campo prezzo per ricadere nel ramo isValid() == false. In output avremo il messaggio di errore relativo al campo prezzo e la risposta avrà codice di stato 422.   Correggendo l’errore e aggiornando il campo prezzo con un valore numerico, riceveremo in output il contenuto dell’oggetto Prodotto gestito dal form:   Notiamo come l’attributo id è impostato a null in quanto tale oggetto non è ancora gestito da Doctrine e, quindi, non è collegato ad un record specifico sul database.   Validazione dei dati di un form in Symfony La validazione dei dati di un form composer require symfony/validator è un aspetto cruciale nello sviluppo di applicazioni web. Assicura che i dati inviati dagli utenti siano accurati, completi e conformi alle aspettative e ai requisiti del sistema. In Symfony, la validazione dei dati di un form è facilitata da un robusto sistema di vincoli e validatori. Tali vincoli chiamati constraints sono set di regole da applicare ad ogni singolo campo presente nel form.   Prima di procedere con l’aggiunta di constraint assicurati di avere installato il supporto alla validazione con il comando: composer require symfony/validator Le regole di validazione possono essere applicate direttamente nelle classe Entity tramite PHP Attributes. A scopo esemplificativo, aggiungiamo delle constraint a Prodotto per l’attributo nome: use Symfony\Component\Validator\Constraints as Assert; // code . . . #[Assert\Length( min: 3, minMessage: 'Il nome del prodotto deve contenere almeno {{ limit }} caratteri', )] #[ORM\Column(length: 255)] private ?string $nome = null; Nello snippet precedente è presente la constraint Length con due opzioni: min: lunghezza minima. La validazione fallisce se il valore inserito non raggiunge un minimo di lunghezza impostato minMessage: modifica il messaggio di errore di default che viene visualizzato in caso di fallimento nella validazione della lunghezza minima del valore inserito In generale, Length è utile per aggiungere regole al controllo della lunghezza minima e massima del valore inserito. Nel nostro caso, è utilizzato solo per gestire la lunghezza minima.   Accediamo dal browser alla route /prodotto/crea e compiliamo il form inserendo all’interno del campo nome un testo con due caratteri. Inviamo il form per verificare se le regole di validazione aggiunte sono state recepite dal sistema:     Come si nota dalla schermata precedente, Symfony ha validato il form e rilevato un errore sulla lunghezza minima prevista per il campo nome. Ma non è tutto! Puoi trovare l’intera lista di constraints supportate da Symfony nella documentazione ufficiale. Nella maggior parte dei casi, come nell’esempio precedente, il form è strettamente collegato ad un oggetto e i campi del form riflettono le proprietà di tale oggetto. In casi particolari, invece, i form non sono collegati ad una classe. Per gestire tale casistica, Symfony permette l’utilizzo delle constraints all’interno delle opzioni passate al metodo add di una istanza della classe FormBuilderInterface.   Adattiamo la validazione precedente effettuata sull’input nome all’interno del metodo buildForm di \Form\ProdottoType.php   use Symfony\Component\Validator\Constraints\Length; // code . . . $builder ->add( 'nome', TextType::class, [ 'label' => 'Nome', 'constraints' => [ new Length([ 'min' => 3, 'minMessage' => 'Il nome del prodotto deve contenere almeno {{ limit }} caratteri' ]), ], ] ) Utilizzando la constraint Length, otterremmo lo stesso risultato della validazione inserita all’interno della Entity Prodotto.
6.2

Mapping delle relazioni con Doctrine ORM: come mappare le relazioni in Symfony

Il mapping delle relazioni in Doctrine è un concetto fondamentale che facilita la rappresentazione e la gestione dei dati in applicazioni Symfony. Come già affrontato, il "mapping" permette la creazione di collegamenti e associazioni tra le classi PHP, ovvero le Entity, e le corrispondenti tabelle del database. È un processo che consente di tradurre e sincronizzare gli oggetti dell'applicazione con i dati memorizzati all’interno di un database. Relazioni tra tabelle in Symfony Nel contesto dei database, le relazioni sono connessioni logiche stabilite tra diverse tabelle. Queste relazioni sono fondamentali per organizzare e strutturare i dati in modo efficiente, garantendo integrità e coerenza dei dati. Le relazioni tra le tabelle sono categorizzate principalmente in tre tipi:   Uno-a-Uno (1:1): In questo tipo di relazione, un record in una tabella è associato a un solo record in un'altra tabella. Uno-a-Molti (1:N): Qui, un record in una tabella può essere associato a più record in un'altra tabella. Molti-a-Molti (M:N): In questa relazione, più record in una tabella possono essere associati a più record in un'altra tabella. Di seguito, un esempio di relazioni tra tabelle nell’ambito di un tipo sistema di e-commerce:   Esempio 1:1: un ordine ha una spedizione associata ed una spedizione fa riferimento ad un solo ordine. Esempio 1:N: una categoria può contenere più prodotti ma un prodotto può appartenere ad una sola categoria.  Esempio M:N: un ordine può contenere più prodotti e un prodotto può essere aggiunto a più ordini.   Nelle sezioni successive andremo a sviluppare gli esempi elencati precedentemente all’interno del nostro progetto Symfony di test. Pronto? Cominciamo!   Mapping 1:1 in Symfony: relazione Uno-a-Uno   Per creare le entità Ordine e Spedizione utilizziamo, come al solito, il comando make. In Ordine aggiungiamo le proprietà stato, per tracciare lo stato dell’ordine, e prezzoTotale. In Spedizione, invece, prevediamo le proprietà corriere, trackingNumber e dataConsegnaStimata. Il passaggio successivo è quello di mettere in relazione le due entità. Accediamo in modifica ad Ordine, sempre con l’aiuto del comando make, e aggiungiamo un campo di tipo relation per mappare la relazione con Spedizione.   Come si nota, il comando richiede una serie di informazioni per creare correttamente la relazione desiderata tra le entità: per prima cosa viene chiesto quale è l’entità con la quale relazionare Ordine. Digitiamo “Spedizione” Il passo successivo è la selezione della tipologia di relazione tra le entità. Symfony ci propone le varie opzioni in una tabella riepilogativa spiegando anche quali sono le caratteristiche di ognuna. Nel nostro caso digitiamo “OneToOne”. La nuova proprietà spedizione in Ordine può essere null in quanto, se l’ordine è ancora in fase di validazione, la spedizione non sarà stata creata ed associata. Possiamo scegliere se aggiungere o meno in Spedizione una proprietà per accedere alla relativa istanza di Ordine. Abbiamo digitato “yes”. Successivamente, ci viene richiesto il nome di tale proprietà che chiameremo ordine.   Conclusa l’esecuzione di questo comando, avremo le entità Ordine e Spedizione aggiornate e collegate da una relazione di tipo 1 a 1. Di seguito, il codice sorgente delle classi Ordine e Spedizione (sono stati rimossi per brevità proprietà e metodi non inerenti la relazione creata). /src/Entity/Ordine.php <?php // imports . . . #[ORM\Entity(repositoryClass: OrdineRepository::class)] class Ordine { // proprietà . . . #[ORM\OneToOne(inversedBy: 'ordine', cascade: ['persist', 'remove'])] private ?Spedizione $spedizione = null; // getter e setter . . . public function getSpedizione(): ?Spedizione { return $this->spedizione; } public function setSpedizione(?Spedizione $spedizione): static { $this->spedizione = $spedizione; return $this; } } /src/Entity/Spedizione.php <?php // imports . . . #[ORM\Entity(repositoryClass: SpedizioneRepository::class)] class Spedizione { // proprietà . . . #[ORM\OneToOne(mappedBy: 'spedizione', cascade: ['persist', 'remove'])] private ?Ordine $ordine = null; // getter e setter . . . public function getOrdine(): ?Ordine { return $this->ordine; } public function setOrdine(?Ordine $ordine): static { // unset the owning side of the relation if necessary if ($ordine === null && $this->ordine !== null) { $this->ordine->setSpedizione(null); } // set the owning side of the relation if necessary if ($ordine !== null && $ordine->getSpedizione() !== $this) { $ordine->setSpedizione($this); } $this->ordine = $ordine; return $this; } } Notiamo l’utilizzo del PHP attribute ORM\OneToOne in entrambe le classi: // Ordine #[ORM\OneToOne(inversedBy: 'ordine', cascade: ['persist', 'remove'])] private ?Spedizione $spedizione = null; // Spedizione #[ORM\OneToOne(mappedBy: 'spedizione', cascade: ['persist', 'remove'])] private ?Ordine $ordine = null; Tale sintassi è fondamentale per definire il mapping tra le Entity. All’interno di esse notiamo le opzioni inversedBy e mappedBy. In Doctrine, sono utilizzate per specificare e configurare le relazioni bidirezionali tra le entità. Sono essenziali per definire chiaramente la proprietà che mappa e gestisce la relazione tra le due parti coinvolte.   mappedBy viene utilizzato nella classe dell'entità che è il lato "inverso" della relazione, indica che la relazione è già stata mappata dall'altra entità. L'entità con mappedBy non è responsabile per la gestione della relazione ovvero non viene utilizzata per fare modifiche dirette alla relazione, come l'aggiunta o la rimozione di elementi;   inversedBy viene utilizzato nella classe dell'entità che è il lato "proprietario" della relazione. L'entità con inversedBy è responsabile per la gestione della relazione. Le modifiche alla relazione (come l'aggiunta o la rimozione di elementi) devono essere effettuate da questo lato per essere riconosciute e applicate anche al database. Notiamo, infine, la presenza dell’opzione cascade. Essa è utile per definire come le operazioni effettuate su un’entità debbano riflettersi sulle entità correlate.   Mapping 1:N in Symfony: relazione Uno-a-Molti  Per implementare una relazione 1:N consideriamo l’esempio Categoria-Prodotto:   Ogni prodotto appartiene ad una categoria specifica. A quella stessa categoria, però, possono afferire anche altri prodotti. Utilizziamo il comando make per creare la Entity Categoria e generare la relazione.   Abbiamo scelto una relazione “OneToMany” in quanto, come da schermata: ogni Categoria può essere relazionata con più oggetti di tipo Prodotto. Ogni Prodotto può avere una sola Categoria. Il comando andrà ad aggiungere anche una nuova proprietà in Prodotto per gestire la Categoria relazionata ad una specifica istanza.   Lato Entity Categoria, è stata aggiunta in automatico la proprietà prodotti che definisce la relazione con una Collection di Prodotto: #[ORM\OneToMany(mappedBy: 'categoria', targetEntity: Prodotto::class)] private Collection $prodotti; Dal lato di Prodotto ritroviamo la mappatura inversa: #[ORM\ManyToOne(inversedBy: 'prodotti')] private ?Categoria $categoria = null; Mapping M:N in Symfony: relazione Molti-a-Molti Ora è il momento di implementare l’ultimo esempio, quello relativo alle entità Prodotto e Ordine legate da una relazione di molti a molti (M:N).   Con il comando make andiamo a richiedere le modifiche alla classe Ordine. Aggiungiamo un nuovo campo chiamato prodotti di tipo relation.   La relazione scelta è “ManyToMany”. All’interno di Ordine è stato aggiunto in automatico il mapping della relazione: #[ORM\ManyToMany(targetEntity: Prodotto::class, inversedBy: 'ordini')] private Collection $prodotti; e i metodi per la gestione dei prodotti associati ad un ordine specifico: /** * @return Collection<int, Prodotto> */ public function getProdotti(): Collection { return $this->prodotti; } public function addProdotti(Prodotto $prodotti): static { if (!$this->prodotti->contains($prodotti)) { $this->prodotti->add($prodotti); } return $this; } public function removeProdotti(Prodotto $prodotti): static { $this->prodotti->removeElement($prodotti); return $this; } Stessa cosa è stata fatta dal lato dell’entità Prodotto che avrà come targetEntity la classe Ordine. Tramite la relazione appena creata, attraverso un’istanza di Ordine è possibile accedere a tutti i dettagli dei prodotti associati direttamente dall’oggetto stesso attraverso il metodo getProdotti().
6.3

Invio Mail in Symfony: come inviare mail in Symfony

L'invio di email è una funzione comune nelle applicazioni web per notifiche, conferme, newsletter e altro. Symfony fornisce componenti potenti e flessibili per gestire l'invio di email in modo efficiente e sicuro. Uno tra questi è Symfony Mailer. Installazione e configurazione di Symfony Mailer Symfony Mailer è un componente del framework PHP Symfony che fornisce una solida infrastruttura per l'invio di email in applicazioni web. Iniziamo, quindi, con l’installazione di Symfony Mailer: composer require symfony/mailer Le e-mail vengono consegnate attraverso un canale chiamato “transport”. Con la configurazione base di Mailer è possibile utilizzare il protocollo SMTP. Per farlo è sufficiente configurare il DNS all’interno del file .env: MAILER_DSN=smtp://user:[email protected]:port sostituiamo, poi, i parametri con i valori che fanno riferimento all’account di posta che vogliamo utilizzare. Per utilizzare servizi di terze parti come Mailgun o SendGrid, è possibile installare un package specifico per ognuno di essi; di seguito, una tabella con i vari comandi per l’installazione di ciascun servizio.   Servizio Comando per l’installazione Amazon SES composer require symfony/amazon-mailer Infobip composer require symfony/infobip-mailer Mailchimp Mandrill composer require symfony/mailchimp-mailer Mailgun composer require symfony/mailgun-mailer Mailjet composer require symfony/mailjet-mailer MailPace composer require symfony/mail-pace-mailer MailerSend composer require symfony/mailer-send-mailer Postmark composer require symfony/postmark-mailer SendGrid composer require symfony/sendgrid-mailer Sendinblue composer require symfony/sendinblue-mailer   Come inviare una e-mail in Symfony Una volta installato Symfony Mailer, entriamo nel vivo e scopriamo come inviare un’e-mail in Symfony, con esempi pratici. Come primo step creiamo il controller che ci servirà per gestire la richiesta di invio e-mail. php bin/console make:controller MailController Creiamo una route /mail collegata al metodo sendEmail: #[Route('/mail', name: 'app_send_mail')] public function sendEmail(MailerInterface $mailer): Response { $email = (new Email()) ->from('[email protected]') ->to('[email protected]') ->subject('E-mail da Symfony!') ->text('Hello world'); $mailer->send($email); return new Response('e-mail inviata'); } sendEmail utilizza MailerInterface per poter definire i parametri della e-mail from, to, subject e text e il metodo send per effettuare l’invio. Il messaggio verrà inviato immediatamente attraverso il transport configurato, nel nostro esempio SMTP.   Con il browser richiediamo l’URL symfony-test.local/mail per innescare l’invio della e-mail attraverso il Controller sendEmail   Il controller ci ha risposto con la Response che abbiamo impostato ovvero il testo “e-mail inviata”. Verifichiamo cosa è avvenuto effettivamente grazie a Symfony Profiler:   Accediamo alla sezione Email della richiesta per notare come, effettivamente, lo stato di invio è Sent e il transport utilizzato è SMTP. In basso, è presente anche il contenuto della e-mail inviata, in questo caso il testo “Hello world”.   Utilizzare i template Twig per le e-mail E se ti dicessimo che le e-mail da inviare possono essere personalizzate? Questo è possibile attraverso i template di Twig. In questo modo è possibile utilizzare linguaggio HTML e CSS e accedere a delle variabili del context dell'oggetto TemplatedEmail. /templates/email/test.html.twig <style> h1 { color: yellow; background-color: red; } </style> <h1>Ciao {{ email.to[0].address }}!</h1> <p> Il tuo ordine {{ numero_ordine }} è stato spedito. </p> <p> La consegna è prevista tra {{ giorni_consegna }} giorni. </p> Modifichiamo il controller per utilizzare il template appena creato. Invece dell’oggetto Email utilizzato in precedenza, questa volta è necessario istanziare la classe TemplatedEmail. Questa classe estende Email aggiungendo dei metodi per gestire i template Twig: #[Route('/mail', name: 'app_send_mail')] public function sendEmail(MailerInterface $mailer): Response { $email = (new TemplatedEmail()) ->from('[email protected]') ->to('[email protected]') ->subject('E-mail da Symfony!') ->htmlTemplate('emails/test.html.twig') ->context([ 'numero_ordine' => 'AX34281', 'giorni_consegna' => 5 ]); $mailer->send($email); return new Response('e-mail inviata'); } Il template Twig ha accesso ad ogni parametro che è passato all’interno di context(), un metodo della classe TemplatedEmail, e ad una variabile speciale chiamata email con i dettagli di mittenti e destinatari previsti. Risultato:
6.4

Come implementare l’autenticazione utente in Symfony

L'autenticazione utente è un processo critico in qualsiasi applicazione web che gestisce dati sensibili o che fornisce contenuti personalizzati. Si tratta del meccanismo attraverso il quale un sistema verifica l'identità di un utente per assicurarsi che sia effettivamente chi sostiene di essere. Questo processo è fondamentale per garantire la sicurezza, la privacy e l'integrità dei dati. In Symfony, il processo di autenticazione è supportato da una serie di tool e utility che ne semplificano l’implementazione garantendo, in ogni caso, un alto tasso di personalizzazione. Partiamo dall’installazione del pacchetto security di Symfony che fornisce tutte le funzionalità di autenticazione e autorizzazione utili alla sicurezza di una web-application: composer require symfony/security-bundle Symfony Flex si occuperà di creare un file di configurazione chiamato security.yaml.   L’entità Utente in Symfony Ora procediamo con la creazione della Entity Utente. Questa è una classe speciale che implementa la classe UserInterface del componente security di Symfony. Per crearla, facciamoci aiutare da MakerBundle: php bin/console make:user Come puoi vedere, la console richiede il nome della classe (di default User), se i dati degli utenti vogliono essere memorizzati sul database tramite il mapping di Doctrine, la proprietà che dovrà essere l’identificativo univoco di ogni utente e, infine, se la password dovrà essere gestita da Symfony o da un sistema terzo.  Ecco il codice sorgente della classe Utente appena generata: <?php namespace App\Entity; use App\Repository\UtenteRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; #[ORM\Entity(repositoryClass: UtenteRepository::class)] class Utente implements UserInterface, PasswordAuthenticatedUserInterface { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 180, unique: true)] private ?string $email = null; #[ORM\Column] private array $roles = []; /** * @var string The hashed password */ #[ORM\Column] private ?string $password = null; public function getId(): ?int { return $this->id; } public function getEmail(): ?string { return $this->email; } public function setEmail(string $email): static { $this->email = $email; return $this; } /** * A visual identifier that represents this user. * * @see UserInterface */ public function getUserIdentifier(): string { return (string) $this->email; } /** * @see UserInterface */ public function getRoles(): array { $roles = $this->roles; // guarantee every user at least has ROLE_USER $roles[] = 'ROLE_USER'; return array_unique($roles); } public function setRoles(array $roles): static { $this->roles = $roles; return $this; } /** * @see PasswordAuthenticatedUserInterface */ public function getPassword(): string { return $this->password; } public function setPassword(string $password): static { $this->password = $password; return $this; } /** * @see UserInterface */ public function eraseCredentials(): void { // If you store any temporary, sensitive data on the user, clear it here // $this->plainPassword = null; } } Inoltre, è stato anche aggiornato il file /src/config/packages/security.yaml con le informazioni sulla classe Utente: providers: # used to reload user from session & other features (e.g. switch_user) app_user_provider: entity: class: App\Entity\Utente property: email Come registrare gli utenti sul sistema in Symfony Prima di effettuare l’accesso al sistema è necessario aggiungere un utente sul nostro database. Tipicamente, nell’ambito di una web-app, la registrazione avviene tramite un form contenente una serie di campi quali e-mail/username e password. Tale form può essere creato manualmente oppure attraverso la generazione automatica con MakerBundle. Inoltre, grazie al bundle VerifyEmailBundle è possibile anche istanziare un processo automatico di verifica e-mail tramite un URL signed. Installiamo questo bundle e, poi, procediamo alla creazione del form di registrazione con il comando make:   composer require symfonycasts/verify-email-bundle php bin/console make:registration-form Il comando make:registration-form ha:   generato il controller per la gestione delle route previste per il processo di registrazione utente generato la classe form con i campi e-mail e password generato il template Twig per la visualizzazione del form generato la classe EmailVerifier con il processo di verifica e-mail generato il template Twig dell’e-mail di verifica aggiornato la classe utente con i metodi per la verifica dell’e-mail   Accediamo alla route register per visualizzare il form di registrazione utente.   Una volta compilati i campi presenti nel form e premuto “Register” viene inviata una e-mail all’indirizzo specificato nel campo Email con il seguente messaggio:   Notiamo, all’interno del corpo della e-mail, il link per la conferma dell’indirizzo. Premendo tale link l’applicazione verificherà l’utente.  Accediamo al database tramite phpMyAdmin ed esploriamo la tabella “utente”. Notiamo che è stato inserito un nuovo record, con l’hash della password compilata nel form di registrazione e il flag is_verified a true (valore 1). Come gestire l’accesso degli utenti al sistema in Symfony Ora che abbiamo aggiunto un nuovo utente al nostro sistema, possiamo procedere con lo step successivo, ovvero l’accesso tramite autenticazione con e-mail e password. Per la generazione del form di login utilizziamo il comando make:auth: php bin/console make:auth   Selezioniamo lo stile di autenticazione “Login form authenticator” e confermiamo le opzioni successive per generare la classe SecurityController che conterrà le route /login e /logout. Accediamo, quindi, tramite browser all’URL symfony-test.local/login:   Inseriamo e-mail e password dell’utente creato precedentemente e premiamo sul pulsante “Sign in”. Se i dati inseriti sono corretti, il sistema effettuerà il redirect verso la route impostata e attiverà la sessione utente. Accediamo al Profiler per verificare l’utente loggato a sistema.   Dalla schermata in alto, notiamo come sia presente un token che definisce la sessione utente del nostro account personale.   Tutti i template Twig generati in automatico con MakerBundle possono essere personalizzati per adattare i form allo stile del progetto web specifico.

Sei indeciso sul percorso? 💭

Parliamone! Scrivici su Whatsapp e risponderemo a tutte le tue domande per capire quale dei nostri corsi è il più adatto alle tue esigenze.

Oppure chiamaci al 800 128 626