Esempio backend OOP PHP con frontend AJAX JavaScript

Dopo aver cominciato a vedere elementi di programmazione ad oggetti in PHP (leggi qui) e un esempio realistico di classe che espone i dati di un database in JSON (leggi qui la gestione JSON e qui un esempio CRUD), non ci resta che vedere un’applicazione più o meno realistica di frontend da collegare al backend creato.

Abbiamo fondamentalmente due modi per visualizzare i dati delle query: o lasciamo che la classe PHP abbia metodi su misura per la rappresentazione di oggetti html come tabelle e simili create via echo e codice html , oppure continuiamo sulla falsa riga delle return JSON dei metodi PHP da visualizzare però attraverso semplici pagine html e AJAX/JavaScript (ad esempio leggi qui).

Vediamo un esempio minimale: creiamo una classe news che funga da backend per la corrispondente tabella preesistente su MySQL. Nella nostra classe realizziamo solo un metodo di visualizzazione di prova che espone i dati del db in JSON, ma la dinamica per gli altri metodi, CRUD e non, è la stessa.

La classe di backend News

<?php

class News
{
    private $titolo;
    private $testo;
    private $data;
    private $categoria;
    private $autore;

    /**
     * Costruttore
     */
    public function __construct($titolo, $testo, 
                                $data, $categoria, $autore)
    {
        $this->titolo = $titolo;
        $this->testo = $testo;
        $this->data = $data;
        $this->categoria = $categoria;
        $this->autore = $autore;
    }    

    /**
     * Metodi di accesso
     */
    public function getTitolo() {
        return $this->titolo;
    }

    public function getTesto() {
        return $this->testo;
    }

    public function getData() {
        return $this->data;
    }

    public function getCategoria() {
        return $this->categoria;
    }

    public function getAutore() {
        return $this->autore;
    }

    public function setTitolo($titolo): void {
        $this->titolo = $titolo;
    }

    public function setTesto($testo): void {
        $this->testo = $testo;
    }

    public function setData($data): void {
        $this->data = $data;
    }

    public function setCategoria($categoria): void {
        $this->categoria = $categoria;
    }

    public function setAutore($autore): void {
        $this->autore = $autore;
    }

        
    /**
     * Metodi di utilità
     */


    /**
     * Metodi JSON  
     */ 
    public static function visualizza()
    {
        try
        {
            $connessione = new mysqli("localhost","root", "","test");
            $query = $connessione->prepare("SELECT * FROM news");
            $query->execute();
            $risultato = $query->get_result();
            $righe = $risultato->fetch_all(MYSQLI_ASSOC);

            header("Content-type: application/json");
            echo json_encode($righe);
        }
        catch(Exception $e)
        {
            header("Content-type: application/json");
            echo json_encode(array("KO", $e->getMessage()));
        }
    }
}

Il file di routing

La classe direttamente o un suo metodo è un po’ strano ed insolito chiamarla direttamente da una pagina PHP. Occorre una classe nella maggioranza dei casi, o uno script come nel nostro che si occupi di capire quali sono le richieste che vengono dal frontend, per inizializzare e richiamare poi la classe o metodo corretto. Nei modelli MVC questa funzionalità viene detta routing o dispatcher.

Nel nostro caso il dispatcher è molto semplice: un file azione.php che intercetta una richiesta url parametrica in cui viene passata una azione. Sarà poi compito del dispatcher chiamare il metodo giusto. E’ un po’ il concetto di API: metto a disposizione un metodo in linguaggio semplice, anche pseudo umano, per non esporre direttamente la richiesta effettiva. Il programmatore frontend in pratica non deve sapere nulla del backend se non l’azione parametrizzata da richiedere. tutti gli script html avranno un ajax che invoca lo script azione.php con parametro azione=QUALCOSA.

Solitamente il dispatcher è il ponte tra backend e frontend e fornisce la rotto/strada per reperire le informazioni richieste e, in alcuni casi, un template grafico con cui esporli. Qui semplifichiamo un po’ e releghiamo il tutto al solo reperimento del JSON dati.

Altra considerazione. I JS richiamano per semplicità didattica un unico script azione.php. Seppure concettualmente giusto, non è realistico nell’ottica di una tecnologia REST moderna. Con una impostazione URL Rewrite del Server web apache/nginx, è possibile forzare ogni richiesta script su un unico file di risorsa/routing in modo trasparente:

azione.php?id=1&cat=categoria1    =>      azione/1/categoria1

Vi rimandiamo a successivi articoli su REST URL Rewrite.

<?php
include "news.php";

$azione = $_REQUEST["azione"];
switch($azione)
{
    case "visualizza":
        return News::visualizza();
}

Il frontend

A questo punto lato frontend è tutto semplice: voglio visualizzare un JSON di ritorno che verrà richiesto via AJAX e Javascript al dispatcher/routing e preparo il codice js per creare una semplice tabella dove visualizzare il JSON atteso.

<!DOCTYPE html>
<html lang="it">
    <head>
        <title>News</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
    </head>

    <body>
        <div class="container">
        <h1>Carica News con PHP OOP vs AJAX</h1>
        <!-- FRAMMENTO DI FORM/CODICE CHE SCATENA AJAX AL CAMBIO -->
     
        <br><br>
        <!-- VISUALIZZO IL RISULTATO AJAX -->
        <table class="table table-striped">
            <thead>
                <th scope="col">Titolo</th>
                <th scope="col">Testo</th>
                <th scope="col">Data</th>
                <th scope="col">Categoria</th>
                <th scope="col">Autore</th>
            </thead>
            <tbody id="news">

            </tbody>
        </table>
        </div>        
    </body>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
    <script>
    let xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = 
        function()
        {
            if (this.readyState == 4 && this.status == 200)        
            {
                news= document.querySelector("#news");
                news.innerHTML = ""; // resetta il corpo della tabella, non l'intestazione
                dati = JSON.parse(this.response);  
                for (let chiave in dati)
                {
                    let tr = document.createElement("tr");   
                    let td1 = document.createElement("td"); 
                    let td2= document.createElement("td"); 
                    let td3 = document.createElement("td"); 
                    let td4 = document.createElement("td");
                    let td5 = document.createElement("td");                    
                    
                    let contenuto1 = document.createTextNode(dati[chiave].titolo);
                    let contenuto2 = document.createTextNode(dati[chiave].testo);
                    let contenuto3 = document.createTextNode(dati[chiave].data);
                    let contenuto4 = document.createTextNode(dati[chiave].categoria);
                    let contenuto5 = document.createTextNode(dati[chiave].autore);

                    td1.appendChild(contenuto1);
                    td2.appendChild(contenuto2);
                    td3.appendChild(contenuto3);
                    td4.appendChild(contenuto4);
                    td5.appendChild(contenuto5);

                    tr.appendChild(td1);
                    tr.appendChild(td2);
                    tr.appendChild(td3);
                    tr.appendChild(td4);
                    tr.appendChild(td5);                    

                    news.appendChild(tr);

                }
            }

            if (this.readyState == 4 && this.status != 200)
            {
                console.log("Script PHP non trovato");
            }

        };

        xmlhttp.open("GET", "azione.php?azione=visualizza", true);
        xmlhttp.send();

    </script>
</html>

Ultima modifica 9 Maggio 2023