Guide galattiche per aspiranti programmatori

developer in miniatura con una lente di ingrandimento, confuso con alle spalle una scheda da cui parte un razzo
Lezione 30 / 33

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:

 

  1. Creare il form con una classe dedicata
  2. Renderizzare il form in un template Twig in modo che l’utente possa compilarlo e inviarlo
  3. 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:

form symfony

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.

ispeziona browser

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!”.

form symfony compilato


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:

analisi richiesta post symfony

 

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:

 

    1. 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.
    2. 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.

errore http 422

 

Correggendo l’errore e aggiornando il campo prezzo con un valore numerico, riceveremo in output il contenuto dell’oggetto Prodotto gestito dal form:

dump oggetto prodotto

 

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:  

regole di validazione form

 

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.

Contattaci senza impegno per informazioni sul corso

Scopriamo insieme se i nostri corsi fanno per te. Compila il form e aspetta la chiamata di uno dei nostri consulenti.

Contattaci senza impegno per informazioni sul corso

Pagamento rateale

Valore della rata: A PARTIRE DA 112 €/mese.

Esempio di finanziamento  

Importo finanziato: € 2440 in 24 rate da € 112 – TAN fisso 9,37% TAEG 12,57% – importo totale del credito € 2788,68.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/07/2023 al 31/12/2023.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

* In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Pagamento rateale

Valore della rata: A PARTIRE DA 183 €/mese.

Esempio di finanziamento 

Importo finanziato: € 3990 in 24 rate da € 183 – TAN fisso 9,37% TAEG 12,57% – importo totale del credito € 4496,56.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/07/2023 al 31/12/2023.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

* In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.