Come funzionano le applicazioni?

Stai leggendo il documento fondamentale della documentazione di Nette. Imparerai l'intero principio di funzionamento delle applicazioni web. Dalla A alla Z, dal momento della nascita fino all'ultimo respiro dello script PHP. Dopo aver letto, saprai:

  • come funziona il tutto
  • cos'è Bootstrap, Presenter e il container DI
  • come appare la struttura delle directory

Struttura delle directory

Apri l'esempio dello scheletro dell'applicazione web chiamato WebProject e mentre leggi, puoi guardare i file di cui si parla.

La struttura delle directory assomiglia a qualcosa del genere:

web-project/
├── app/                      ← directory con l'applicazione
│   ├── Core/                 ← classi di base necessarie per il funzionamento
│   │   └── RouterFactory.php ← configurazione degli indirizzi URL
│   ├── Presentation/         ← presenter, template & co.
│   │   ├── @layout.latte     ← template del layout
│   │   └── Home/             ← directory del presenter Home
│   │       ├── HomePresenter.php ← classe del presenter Home
│   │       └── default.latte ← template dell'azione default
│   └── Bootstrap.php         ← classe di avvio Bootstrap
├── bin/                      ← script eseguiti dalla riga di comando
├── config/                   ← file di configurazione
│   ├── common.neon
│   └── services.neon
├── log/                      ← errori registrati
├── temp/                     ← file temporanei, cache, …
├── vendor/                   ← librerie installate da Composer
│   ├── ...
│   └── autoload.php          ← autoloading di tutti i pacchetti installati
├── www/                      ← directory pubblica o document-root del progetto
│   ├── .htaccess             ← regole mod_rewrite
│   └── index.php             ← file iniziale con cui si avvia l'applicazione
└── .htaccess                 ← vieta l'accesso a tutte le directory tranne www

Puoi modificare la struttura delle directory come preferisci, rinominare o spostare le cartelle, è completamente flessibile. Nette dispone inoltre di un intelligente autodetect e riconosce automaticamente la posizione dell'applicazione, inclusa la sua base URL.

Per applicazioni un po' più grandi, possiamo dividere le cartelle con presenter e template in sottodirectory e le classi in namespace, che chiamiamo moduli.

La directory www/ rappresenta la cosiddetta directory pubblica o document-root del progetto. Puoi rinominarla senza dover configurare nient'altro lato applicazione. È solo necessario configurare l'hosting in modo che la document-root punti a questa directory.

Puoi anche scaricare direttamente WebProject incluso Nette usando Composer:

composer create-project nette/web-project

Su Linux o macOS, imposta i permessi di scrittura per le directory log/ e temp/.

L'applicazione WebProject è pronta per essere eseguita, non è necessario configurare assolutamente nulla e puoi visualizzarla direttamente nel browser accedendo alla cartella www/.

Richiesta HTTP

Tutto inizia nel momento in cui l'utente apre una pagina nel browser. Cioè quando il browser bussa al server con una richiesta HTTP. La richiesta punta a un unico file PHP, che si trova nella directory pubblica www/, e questo è index.php. Supponiamo che si tratti di una richiesta all'indirizzo https://example.com/product/123. Grazie a un'adeguata configurazione del server anche questo URL viene mappato sul file index.php e questo viene eseguito.

Il suo compito è:

  1. inizializzare l'ambiente
  2. ottenere la factory
  3. avviare l'applicazione Nette, che gestirà la richiesta

Quale factory? Non produciamo trattori, ma pagine web! Aspetta, si chiarirà subito.

Con “inizializzazione dell'ambiente” intendiamo, ad esempio, che viene attivato Tracy, che è uno strumento fantastico per il logging o la visualizzazione degli errori. Sul server di produzione registra gli errori, su quello di sviluppo li visualizza direttamente. Quindi l'inizializzazione include anche la decisione se il sito web è in esecuzione in modalità produzione o sviluppo. Per questo Nette utilizza un intelligente autodetect: se avvii il sito web su localhost, viene eseguito in modalità sviluppo. Non devi quindi configurare nulla e l'applicazione è subito pronta sia per lo sviluppo che per la distribuzione in produzione. Questi passaggi vengono eseguiti e sono descritti in dettaglio nel capitolo sulla classe Bootstrap.

Il terzo punto (sì, abbiamo saltato il secondo, ma ci torneremo) è l'avvio dell'applicazione. La gestione delle richieste HTTP in Nette è affidata alla classe Nette\Application\Application (di seguito Application), quindi quando diciamo avviare l'applicazione, intendiamo specificamente chiamare il metodo con il nome appropriato run() sull'oggetto di questa classe.

Nette è un mentore che ti guida a scrivere applicazioni pulite secondo metodologie comprovate. E una di quelle assolutamente più comprovate si chiama dependency injection, abbreviata in DI. In questo momento non vogliamo appesantirti con la spiegazione della DI, per questo c'è un capitolo separato, l'importante è la conseguenza che gli oggetti chiave ci verranno solitamente creati da una factory di oggetti, chiamata container DI (abbreviato in DIC). Sì, è quella factory di cui si parlava poco fa. E ci produrrà anche l'oggetto Application, perciò abbiamo prima bisogno del container. Lo otteniamo tramite la classe Configurator e gli facciamo produrre l'oggetto Application, chiamiamo su di esso il metodo run() e così si avvia l'applicazione Nette. Esattamente questo accade nel file index.php.

Nette Application

La classe Application ha un unico compito: rispondere alla richiesta HTTP.

Le applicazioni scritte in Nette si dividono in molti cosiddetti presenter (in altri framework potresti incontrare il termine controller, è la stessa cosa), che sono classi, ognuna delle quali rappresenta una specifica pagina del sito web: ad esempio homepage; prodotto in un e-shop; modulo di login; feed sitemap ecc. Un'applicazione può avere da uno a migliaia di presenter.

Application inizia chiedendo al cosiddetto router di decidere a quale dei presenter passare la richiesta corrente per la gestione. Il router decide di chi è la responsabilità. Guarda l'URL di input https://example.com/product/123 e in base a come è impostato, decide che questo è il lavoro, ad esempio, per il presenter Product, dal quale vorrà come azione la visualizzazione (show) del prodotto con id: 123. È buona abitudine scrivere la coppia presenter + azione separata da due punti come Product:show.

Quindi il router ha trasformato l'URL nella coppia Presenter:action + parametri, nel nostro caso Product:show + id: 123. Come appare un tale router puoi vederlo nel file app/Core/RouterFactory.php e lo descriviamo in dettaglio nel capitolo Routing.

Andiamo avanti. Application conosce già il nome del presenter e può continuare. Creando l'oggetto della classe ProductPresenter, che è il codice del presenter Product. Più precisamente, chiede al container DI di creare il presenter, perché la creazione è compito suo.

Il presenter potrebbe assomigliare a questo:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $repository,
	) {
	}

	public function renderShow(int $id): void
	{
		// otteniamo i dati dal model e li passiamo al template
		$this->template->product = $this->repository->getProduct($id);
	}
}

La gestione della richiesta viene assunta dal presenter. E il compito è chiaro: esegui l'azione show con id: 123. Che nel linguaggio dei presenter significa che viene chiamato il metodo renderShow() e nel parametro $id riceve 123.

Un presenter può gestire più azioni, cioè avere più metodi render<Action>(). Ma consigliamo di progettare presenter con una o il minor numero possibile di azioni.

Quindi, è stato chiamato il metodo renderShow(123), il cui codice è un esempio fittizio, ma puoi vedere come vengono passati i dati al template, cioè scrivendo in $this->template.

Successivamente, il presenter restituisce una risposta. Questa può essere una pagina HTML, un'immagine, un documento XML, l'invio di un file dal disco, JSON o magari un redirect a un'altra pagina. È importante notare che se non diciamo esplicitamente come deve rispondere (che è il caso di ProductPresenter), la risposta sarà il rendering del template con la pagina HTML. Perché? Perché nel 99% dei casi vogliamo renderizzare un template, quindi il presenter considera questo comportamento come predefinito e vuole semplificarci il lavoro. Questo è lo scopo di Nette.

Non dobbiamo nemmeno specificare quale template renderizzare, ne deduce il percorso da solo. Nel caso dell'azione show, prova semplicemente a caricare il template show.latte nella directory con la classe ProductPresenter. Tenterà anche di trovare il layout nel file @layout.latte (maggiori dettagli sulla ricerca dei template).

E successivamente renderizza i template. Con questo, il compito del presenter e dell'intera applicazione è completato e l'opera è finita. Se il template non esistesse, verrebbe restituita una pagina con errore 404. Maggiori informazioni sui presenter si trovano nella pagina Presenter.

Per sicurezza, proviamo a riepilogare l'intero processo con un URL leggermente diverso:

  1. L'URL sarà https://example.com
  2. avviamo l'applicazione, viene creato il container e viene eseguito Application::run()
  3. il router decodifica l'URL come coppia Home:default
  4. viene creato l'oggetto della classe HomePresenter
  5. viene chiamato il metodo renderDefault() (se esiste)
  6. viene renderizzato il template ad es. default.latte con il layout ad es. @layout.latte

Potresti aver incontrato molti nuovi concetti ora, ma crediamo che abbiano senso. Creare applicazioni in Nette è un'enorme comodità.

Template

Dato che abbiamo menzionato i template, in Nette si utilizza il sistema di templating Latte. Questo perché è il sistema di templating più sicuro per PHP, e allo stesso tempo il sistema più intuitivo. Non devi imparare molto di nuovo, ti basta la conoscenza di PHP e alcuni tag. Tutto si trova nella documentazione.

Nel template si creano link ad altri presenter & azioni in questo modo:

<a n:href="Product:show $productId">dettaglio prodotto</a>

Semplicemente, invece dell'URL reale, scrivi la coppia nota Presenter:action e specifichi eventuali parametri. Il trucco sta in n:href, che dice che questo attributo sarà elaborato da Nette. E genererà:

<a href="/product/456">dettaglio prodotto</a>

La generazione dell'URL è gestita dal router menzionato in precedenza. Infatti, i router in Nette sono eccezionali perché possono eseguire non solo trasformazioni da URL a coppia presenter:action, ma anche viceversa, cioè generare un URL dal nome del presenter + azione + parametri. Grazie a ciò, in Nette puoi cambiare completamente le forme degli URL in tutta l'applicazione finita, senza cambiare un singolo carattere nel template o nel presenter. Semplicemente modificando il router. Grazie a ciò funziona anche la cosiddetta canonizzazione, che è un'altra caratteristica unica di Nette, che contribuisce a un migliore SEO (ottimizzazione della reperibilità su Internet) impedendo automaticamente l'esistenza di contenuti duplicati su URL diversi. Molti programmatori lo trovano sorprendente.

Componenti Interattivi

Sui presenter dobbiamo rivelarti ancora una cosa: hanno un sistema di componenti integrato. Qualcosa di simile potrebbe essere familiare ai veterani di Delphi o ASP.NET Web Forms, qualcosa di lontanamente simile è alla base di React o Vue.js. Nel mondo dei framework PHP, è una caratteristica assolutamente unica.

I componenti sono unità riutilizzabili separate che inseriamo nelle pagine (cioè nei presenter). Possono essere form, datagrid, menu, sondaggi, praticamente qualsiasi cosa che abbia senso usare ripetutamente. Possiamo creare i nostri componenti o usare alcuni dell'enorme offerta di componenti open source.

I componenti influenzano fondamentalmente l'approccio alla creazione di applicazioni. Ti apriranno nuove possibilità di comporre pagine da unità pre-preparate. E inoltre hanno qualcosa in comune con Hollywood.

Container DI e Configurazione

Il container DI o factory di oggetti è il cuore dell'intera applicazione.

Non preoccuparti, non è una scatola nera magica, come potrebbe sembrare dalle righe precedenti. In realtà, è una classe PHP piuttosto noiosa, che viene generata da Nette e salvata nella directory della cache. Ha molti metodi chiamati come createServiceAbcd() e ognuno di essi sa come creare e restituire un oggetto. Sì, c'è anche il metodo createServiceApplication(), che crea Nette\Application\Application, di cui avevamo bisogno nel file index.php per avviare l'applicazione. E ci sono metodi che creano i singoli presenter. E così via.

Agli oggetti che il container DI crea, per qualche motivo, si dice servizi.

Ciò che è veramente speciale di questa classe è che non la programmi tu, ma il framework. Genera effettivamente il codice PHP e lo salva su disco. Tu dai solo istruzioni su quali oggetti il container deve saper creare e come esattamente. E queste istruzioni sono scritte nei file di configurazione, per i quali si usa il formato NEON e quindi hanno anche l'estensione .neon.

I file di configurazione servono puramente a istruire il container DI. Quindi, se ad esempio specifico nella sezione session l'opzione expiration: 14 days, il container DI, durante la creazione dell'oggetto Nette\Http\Session che rappresenta la sessione, chiamerà il suo metodo setExpiration('14 days') e così la configurazione diventerà realtà.

C'è un intero capitolo preparato per te che descrive cosa può essere configurato e come definire i propri servizi.

Non appena ti addentrerai un po' nella creazione di servizi, incontrerai la parola autowiring. Questa è una chicca che ti semplificherà la vita in modo incredibile. Sa passare automaticamente gli oggetti dove ne hai bisogno (ad esempio nei costruttori delle tue classi), senza che tu debba fare nulla. Scoprirai che il container DI in Nette è un piccolo miracolo.

Dove andare dopo?

Abbiamo esaminato i principi di base delle applicazioni in Nette. Finora molto superficialmente, ma presto approfondirai e col tempo creerai fantastiche applicazioni web. Dove continuare? Hai già provato il tutorial Scriviamo la prima applicazione?

Oltre a quanto descritto sopra, Nette dispone di un intero arsenale di classi utili, un layer di database, ecc. Prova a sfogliare la documentazione solo per curiosità. O il blog. Scoprirai molte cose interessanti.

Che il framework ti porti molta gioia 💙

versione: 4.0