Best Practices per l’Architettura MVC in PHP: Errori Comuni e Come Evitarli – Part 1: I Controller

Best Practices per l'Architettura MVC in PHP: Errori Comuni e Come Evitarli - Part 1: I Controller

By on Thu Jan 9 in mvc, PHP, Programming


0
(0)

Sommario

  1. Introduzione
  2. MVC in PHP
  3. Perché questo articolo?
  4. Errore Comune: Confondere le Responsabilità
  5. Comprendere i Ruoli di Modello, Vista e Controller
  6. Ruoli e Best Practices per l’Architettura MVC in PHP
  7. Rispetto delle Responsabilità: Vantaggi
  8. Dove scrivere la Business Logic
  9. Classe di Servizio: I vantaggi
  10. Mantenere i Controller Leggeri: S.O.L.I.D. e Single Action Controller
  11. Conclusione

Introduzione

In questo articolo esploreremo alcune best practices per l’architettura MVC in PHP.

L’architettura MVC (Model-View-Controller) è uno dei più vecchi e popolari modelli di architettura standard del settore, inventato da Trygve Reenskaug a fine degli anni settanta. Ad oggi é uno degli approcci più utilizzati nello sviluppo di applicazioni web, grazie alla sua capacità di separare le responsabilitá e migliorare la gestione del codice.

In un’applicazione MVC, il (M)odello si concentra sulla gestione dei dati, la (V)ista si occupa della presentazione dei dati, e il (C)ontroller gestisce le interazioni dell’utente e coordina la logica applicativa. Questa separazione consente di creare applicazioni più modulari, facilmente manutenibili e scalabili.

MVC in PHP

In PHP, i maggiori framework come Laravel e Symfony sfruttano appieno i principi dell’architettura MVC per organizzare il codice in modo chiaro e funzionale.

Tuttavia, nonostante la sua popolarità, implementare correttamente il pattern MVC non è sempre semplice.

Ho giá scritto un articolo riguardo l’MVC e sul come sia possibile costruire facilmente un semplice framework MVC in PHP.

Perché questo articolo?

Ci tengo a precisare che scrivo questo articolo basandomi sulla mia esperienza personale. Ho lavorato in diverse aziende dove PHP era il linguaggio principale. Alcune di queste hanno anche provato a passare a tecnologie più moderne, come Go, ma si sono ritrovate a gestire due team: uno che gestiva la nuova infrastruttura in Go e uno che continuava a occuparsi di PHP, sia per alcune nuove funzionalitá che per tutto il vecchio progetto. Ma il business chiama, e non c’è mai tempo per rifattorizzare quelle enormi classi da 2 o 3 mila righe.

Non c’è tempo perché il business ha sempre la priorità. E poi ci sono nuovi sviluppatori da integrare, gli standup, e… la pausa sigaretta e caffè. E infine ovviamente i meeting e i team-building.

Il pattern che ho notato é che con una base di codice confusa, poco organizzata e poco gestibile, con manager poco inclini al miglioramento, i nuovi programmatori perdono in fretta la voglia di aggiungere vero valore al software, e i piú anziani tendono a guardarsi intorno dopo un pó. E poca passione nel nostro campo significa guai seri in futuro.

In questo articolo quindi, esploreremo alcuni degli errori comuni che si verificano durante l’implementazione dell’architettura MVC in PHP e forniró delle best practices per l’architettura MVC in PHP. Vedremo come migliorare la qualità del codice, mantenendo l’architettura chiara e scalabile. L’obiettivo è aiutare gli sviluppatori ad evitare gli errori più frequenti e a rendere un’applicazione più solida, manutenibile e pronta a crescere nel tempo.

Comprendere i Ruoli di Modello, Vista e Controller

Una corretta implementazione dell’architettura MVC (Model-View-Controller) dipende dalla chiara comprensione dei ruoli di ciascun componente. Questo pattern è progettato per separare le responsabilità e migliorare l’organizzazione del codice, ma spesso gli sviluppatori, soprattutto i principianti, finiscono per sovrapporre le funzionalità di Modello, Vista e Controller, causando confusione e codice difficile da manutenere.

Errore Comune: Confondere le Responsabilità

Uno degli errori più comuni è mescolare le responsabilità tra i componenti.

Esempi tipici includono:

  • Inserire logica di business nel Controller, rendendolo troppo complesso e difficile da testare.
  • Aggiungere logica di business o accesso ai dati direttamente nella Vista, con codice PHP che estrae dati dal database o esegue calcoli complessi.
  • Utilizzare il Modello per attività che dovrebbero essere gestite dalla Vista o dal Controller, come la formattazione dell’output.

Questi errori possono portare a un codice poco flessibile e difficile da scalare.

Ruoli e Best Practices per l’Architettura MVC in PHP

Per evitare questi problemi, è fondamentale rispettare i ruoli specifici di ciascun componente dell’architettura MVC. Di seguito alcune best practices per l’architettura MVC in PHP.

Il Modello: Gestione dei Dati e della Logica di Business

  • Il Modello è responsabile dell’accesso ai dati, che può includere letture/scritture da un database, file o API.
  • Contiene la logica di business, ovvero le regole e i comportamenti specifici dell’applicazione. Ad esempio, calcoli per uno sconto o la verifica della validità di una transazione.
  • Deve essere autonomo e non dipendere dalla Vista o dal Controller, permettendo così di riutilizzare la logica in contesti diversi.

Ecco un esempio:

class ProductModel 
{
    public function getProductsByCategory(int $categoryId) 
    {
        return Database::query("SELECT * FROM products WHERE category_id = ?", [$categoryId]);
    }

    public function calculateDiscount(float $price, int $discountPercentage) 
    {
        return $price - ($price * $discountPercentage / 100);
    }
}

Il Controller: Gestione della Logica Applicativa e Coordinamento

  • Il Controller agisce come intermediario tra Modello e Vista.
  • Riceve input dall’utente (tramite URL, form o API) e decide quali Modelli chiamare e quali dati passare alla Vista.
  • Deve essere il più leggero possibile, delegando la logica al Modello.

Ecco un esempio:

class ProductController 
{
    private $model;

    public function __construct(ProductModel $model) 
    {
        $this->model = $model;
    }

    public function showProducts(int $categoryId) 
    {
        $products = $this->model->getProductsByCategory($categoryId);

        return view('views/products.php', $products);
    }
}

La Vista: Presentazione dei Dati

  • La Vista è responsabile esclusivamente della presentazione dei dati all’utente.
  • Non deve contenere logica di business o accedere direttamente ai dati, ma solo mostrare ciò che è stato preparato dal Controller.
  • Usare template engines (come Twig o Blade) può aiutare a mantenere la Vista pulita e leggibile.

Ecco un esempio:

<!-- views/products.php -->
<h1>Prodotti</h1>
<ul>
    <?php foreach ($products as $product): ?>
        <li><?= htmlspecialchars($product['name']) ?> - €<?= number_format($product['price'], 2) ?></li>
    <?php endforeach; ?>
</ul>

Rispetto delle Responsabilità: Vantaggi

  • Manutenibilità: Ogni componente è indipendente, rendendo più facile identificare e correggere eventuali problemi.
  • Riutilizzabilità: Il Modello può essere riutilizzato in diversi contesti senza dipendere dalla Vista o dal Controller.
  • Scalabilità: Separare le responsabilità rende più semplice aggiungere nuove funzionalità senza compromettere il codice esistente.

Rispettare i ruoli del Modello, Vista e Controller è il primo passo per costruire applicazioni solide, manutenibili e scalabili.

Dove scrivere la Business Logic

La decisione su dove collocare la logica di business (nel Modello o in una classe di servizio separata) dipende dall’approccio e dalle best practices che si vogliono seguire nel progetto.

Quando Usare il Modello per la Logica di Business

Tradizionalmente, nell’architettura MVC, il Modello si occupa sia dell’accesso ai dati che della logica di business. Questo approccio funziona bene per applicazioni semplici o medie, dove la logica è strettamente legata ai dati del dominio. Ad esempio:

Calcolare il totale di un ordine basandosi sui prodotti associati. Verificare che una prenotazione non superi la disponibilità. In questi casi, integrare la logica direttamente nel Modello può rendere il codice più compatto e facile da seguire.

Quando Usare una Classe di Servizio

Per applicazioni più complesse, separare la logica di business in una classe di servizio è spesso una scelta migliore. Ecco perché:

  1. Separazione delle Responsabilità
  • Il Modello si concentra esclusivamente sui dati e sulle loro relazioni.
  • Le classi di servizio gestiscono la logica di business, che può combinare più Modelli o includere logiche complesse non direttamente legate a un singolo Modello.
  1. Riutilizzabilità
  • Una classe di servizio è più facilmente riutilizzabile in contesti diversi, senza dover instanziare direttamente un Modello.
  • La logica può essere condivisa tra vari componenti dell’applicazione, come API, script CLI, o batch process.
  1. Testabilità
  • Separare la logica di business in una classe di servizio rende il codice più facile da testare in modo isolato.
  • I Modelli possono essere testati solo per la loro interazione con il database, mentre la logica di business può essere verificata indipendentemente.

Classe di Servizio: I vantaggi

Usare una classe di servizio per la logica di business è generalmente una scelta più robusta e scalabile, soprattutto per applicazioni grandi o complesse. Questo approccio:

  • Favorisce una chiara separazione delle responsabilità.
  • Migliora la riusabilità del codice.
  • Aumenta la testabilità delle singole componenti.

Mantenere i Controller Leggeri: S.O.L.I.D. e Single Action Controller

Nel design di un’applicazione MVC, uno degli aspetti più importanti è mantenere i controller leggeri e focalizzati. Questo si ottiene adottando il principio dei Single Action Controller e rispettando le best practices di programmazione come i principi S.O.L.I.D. I controller dovrebbero essere ridotti al minimo, con ciascuno dedicato a una singola responsabilità, ossia una singola azione. Questo approccio migliora la manutenibilità, la testabilità e la chiarezza del codice. Il rovescio della medaglia é quella di avere molte piú classi, ma piú contenute, nel progetto. Per fare esempio, invece di avere un solo controller che gestisce l’index, l’edit e il delete di un’entitá, seguendo la regola del Single Action Controller avremo tre differenti classi con un solo metodo invoke per ognuna di esse.

Errore Comune: Controller Troppo “Grassi”

Un errore comune è avere controller che contengono troppa logica, come ad esempio:

  • Gestione della validazione dei dati.
  • Elaborazione di più azioni in un singolo controller.
  • Logica complessa che non ha a che fare direttamente con la coordinazione tra vista e modello.

Questo rende il controller difficile da comprendere, testare e manutenere. Se un controller gestisce troppe azioni o troppe responsabilità, ogni cambiamento in una parte del codice potrebbe avere impatti imprevisti su altre funzionalità.

Best Practices: Single Action Controllers e S.O.L.I.D.

  1. Adotta i Single Action Controllers
    I Single Action Controllers seguono la filosofia che ogni controller dovrebbe occuparsi di una sola azione o di una singola responsabilità. Questo approccio rende il controller molto focalizzato e semplice da gestire. L’idea di base è che ogni controller debba essere il punto di coordinamento per una specifica azione, come visualizzare un elemento, salvare un dato o modificare una risorsa.

Ogni controller dovrebbe essere creato per gestire solo un’azione e passare i dati al modello o ad un servizio per compiere qualsiasi logica di business. La vista poi si occupa della presentazione.

Esempio di un Single Action Controller:

class CreateEntityController 
{
    public function __invoke(Request $request) 
    {
        $validation = $request->validated();

        $entity = EntityService::create($request->all());

        return redirect()->route('entity.show', ['id' => $entity->id]);
    }
}

In questo esempio, il controller è dedicato solo alla creazione di una entitá, senza preoccuparsi di altre operazioni come la modifica o la visualizzazione. Ogni controller può quindi essere più facile da testare e modificare senza introdurre bug in altre aree dell’applicazione.

  1. Utilizzare Servizi e Modelli per la Logica di Business
    Per evitare che i controller diventino troppo complessi, la logica di business (come la creazione di un utente, la gestione dei pagamenti, ecc.) dovrebbe essere spostata in classi di servizio o modelli. Questi servizi o modelli si occupano della logica pesante, mentre i controller dovrebbero restare snelli, limitandosi a gestire il flusso delle informazioni e a coordinare le azioni.

Esempio di utilizzo di una classe di servizio:

class EntityService 
{
    public function create(array $data) 
    {
        $entity = new EntityModel($data);
        $entity->save();

        return $entity;
    }
}

class CreateEntityController 
{
    private $entityService;

    public function __construct(EntityService $entityService) 
    {
        $this->entityService = $entityService;
    }

    public function store(Request $request) 
    {
        $validation = $request->validated();

        $entity = $this->entityService->create($request->all());

        return redirect()->route('entity.show', ['id' => $entity->id]);
    }
}

In questo caso, la logica di creazione dell’utente è delegata alla classe di servizio EntityService, mentre il controller è responsabile solo di gestire la richiesta e reindirizzare l’utente a una nuova pagina.

  1. Vantaggi dei Single Action Controllers
    Adottare i Single Action Controllers porta diversi vantaggi:
  • Testabilità: Ogni controller è responsabile di una sola azione, il che semplifica i test unitari. La logica complessa è esterna al controller, quindi il test si concentra esclusivamente sul comportamento dell’azione.
  • Manutenibilità: Un controller che gestisce una sola azione è facile da comprendere e modificare. Aggiungere nuove funzionalità non significa modificare controller esistenti, ma solo crearne di nuovi.
  • Scalabilità: Con i Single Action Controllers, è facile estendere l’applicazione aggiungendo nuovi controller senza compromettere la coesione o la chiarezza dell’applicazione.
  1. Applicare i Principi S.O.L.I.D.
    I principi S.O.L.I.D. aiutano a mantenere il codice chiaro, modulare e facilmente estendibile. Questi principi sono fondamentali per garantire che i controller rimangano snelli e ben strutturati:
  • S: Single Responsibility Principle (SRP): Ogni classe deve avere una sola responsabilità. Nel caso dei controller, ogni controller deve essere concentrato su un’unica azione e delegare tutto il resto ad altri componenti come servizi o modelli. Cosí come i controller, anche i servizi devono avere una singola responsabilitá.
  • O: Open/Closed Principle (OCP): Le classi devono essere progettate per essere estensibili senza modificare il loro codice originale. È possibile aggiungere nuove funzionalitá, ma non si deve mai modificare una classe esistente. Ad esempio, in molte situazioni si puó implementare lo Strategy Pattern.
  • L: Liskov Substitution Principle (LSP): Le classi derivate devono essere sostituibili con la loro classe base senza alterare il comportamento dell’applicazione.
  • I: Interface Segregation Principle (ISP): Le interfacce devono essere progettate per non obbligare le classi ad implementare metodi inutilizzati.
  • D: Dependency Inversion Principle (DIP): Le classi dovrebbero dipendere da interfacce astratte o classi di servizio, piuttosto che da classi concrete, per facilitare la testabilità e la manutenzione.

Conclusione

Mantenere i controller leggeri e rispettare i principi dell’architettura MVC non è solo una questione di stile: è una necessità per costruire applicazioni PHP robuste, scalabili e facili da mantenere. Adottando i Single Action Controllers e delegando la logica di business a classi di servizio dedicate, puoi evitare che i tuoi controller diventino un “imbuto” che raccoglie tutte le responsabilità, compromettendo la chiarezza e la testabilità del codice. Allo stesso modo, applicare i principi S.O.L.I.D. non solo migliora la qualità del codice, ma rende l’intero sistema più agile e preparato per affrontare l’evoluzione futura.

Ogni scelta che fai nell’architettura del tuo progetto influenzerà la sua manutenibilità nel lungo periodo. Se impari a mantenere i controller focalizzati, a separare le responsabilità e a scrivere codice testabile fin dall’inizio, stai costruendo una base solida su cui potrai facilmente aggiungere nuove funzionalità, risolvere bug e migliorare il sistema senza paura di compromettere l’intera applicazione. Non sottovalutare mai il potere di un’architettura ben progettata: è la chiave per il successo di ogni progetto software.

A presto con la seconda parte di “Best Practices per l’Architettura MVC in PHP: Errori Comuni e Come Evitarli”.

Se ti é piaciuto l’articolo, se hai imparato qualcosa o se pensi che qualcuno possa beneficiarne, sentiti libero di condividerlo sul tuo social preferito, cliccare 5 stelle o lasciare un commento per intavolare una discussione. Grazie!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Leave a Reply

Your email address will not be published. Required fields are marked *

No comments yet. Be the first one!