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.