Guide galattiche per aspiranti programmatori

developer in giacca e cravatta di spalle al display di un pc
Lezione 29 / 33

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:

repository doctrine

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));
    }

dump entity con find

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).

flusso doctrine orm

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'])

dump symfony

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:

dump symfony

Utilizzando come parametro il valore 10, invece, ne riceviamo solo uno:

dump symfony

 

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

repository 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

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.