Salta el contingut

MVC: Conceptes

Model Vista Controlador

El MVC és un patró d'arquitectura de desenvolupament que està basat en la POO i en dividir per capes les diferents funcionalitats del codi de l'aplicació.

Per tant, tindrem tindrem elements que interaccionen entre si i permeten una ampliació del codi de manera ràpida i flexible. És a dir que si volem afegir al nostre desenvolupament una funcionalitat sobre clients, només ens caldrà afegir l'objecte associat al model de dades de client i tindrem accés a tot el que ens fa falta.

Algunes característiques que podem tenir sobre desenvolupar seguint MVC:

  • seguretat de les dades
  • funcionalitats controlades
  • integració entre elements
  • facilitat de manteniment
  • usabilitat d'elements
  • major escabilitat
  • facilitat de proves

La majoria de frameworks actuals basats en servidor estan basats en MVC

Bàsicament tenim 3 nivells d'abstracció:

  • Model: representa la lògica de les dades i té accés a la BdD. A la pràctica serà un objecte amb les propietats de l'element i els mètodes d'accés a la des dades, estiguin en BdD o no.
  • Vista: representa les interfícies cap a l'usuari, tant d'entrada com de sortida. A la pràctica seràn HTMLS + CSS + javascript
  • Controlador: representa la lògica de l'aplicació. A la pràctica serà la part de codi en PHP que realitzarà unes accions o altres depenent de la ruta d'entrada i les accions de l'usuari a la interfície.

Com funciona el MVC?

El funcionament bàsic és:

  • L'usuari realitza una petició a la interfície o inicial per URL
  • Aquesta és rebuda pel Controlador: index.php. S'aplica la lògica que cal segons la petició.
  • Es realitza una crida al model de dades. Si cal, accés a la BdD, Web Service...
  • El controlador rep les dades i les envia a la vista.
  • La vista processa la informació rebuda, genera la interfície i l'entrega al navegador.

MVC

Anem a veure en el codi com es realitza amb més detall:

1- Inici actuació

El sistema s’inicia amb la crida HTTP GET a una URL: http://localhost/MVC

En aquest cas el primer que actua és el .htaccess que hi tenim la regla

RewriteRule !\.(css|js|icon|zip|rar|png|jpg|gif|pdf)$ index.php [L]

Atenció!

Ens podem trobar que l’Apache no permeti l’execució del .htaccess a la configuració del php.ini AllowOverride None, per tant ens caldrà configurar-ho al Directori virtual del Apache.

Cal que tingui configurat:

Allowoverride All

Trobareu tota la informació a M8, Mòdul de desplegament.

2- index.php

Sempre executem aquest fitxer index.php i aquest farà actuar al sistema

Els dos primers includes ens carreguen les configuracions

<?php
    include './config/funcionsInicials.php';
    include './config/baseDeDades.php';
?>

Tot seguit fem actuar el Controlador

<?php
    $router = new RouterController();
    $router->process(array(treuWEBROOT($_SERVER['REQUEST_URI'])));
?>
3- RouterController

El RouterController hereda el Controller, on hi tenim les propietats:

<?php
    protected $data = array();
    protected $view = "";
    protected $head = array('title' => '', 'description' => '');
    protected $twig = false;
?>

Que serà les que hem de usar a dins dels controladors dels bundles

Tenim el mètode renderView($error = null), que carrega la vista que ens interessa i la genera.

Podem observar com aquí com generem la vista a dins del Layout. Que és la vista general amb capçalera i peu i contindrà els menús que hi podem donar estil.

Tenim la funció process($params) a dins de RouteController.php , que mira si hem passat un bundle o no

Primer de tot executa el parseURL() amb la URL que li arriba amb el HTTP GET

<?php
    $_SERVER['REQUEST_URI'];
?>

Amb explodes de / obté les dades per saber bundle, accions, paràmetres…

I segons hagin passat un bundle o no carrega la plantilla layout amb l’index general, que són els fitxers layout.html i index.html que hi ha a la carpeta bundle.

Si hem passat un bundle a la ruta el que fa es carregar un nou controlador que ha d’estar a la carpeta controllers del bundle. I ha de tenir el nom de xxxController.php.

Interessant la funció redirect($url),que ens permetrà a dins de la lògica d’un controlador d’un bundle redirigir cap a un altre acció o bundle.

4- Controlador d’un bundle

Quan arribem al controlador d’un bundle, s’executa la funció process($params), on hi ha la ruta de les accions i paràmetres que s’han obtingut anteriorment, sobre la ruta URL.

La classe que es crea a dins del fitxer xxxcontroller.php serà

<?php
    class clientsController extends Controller
?>

I caldrà dissenyar i programar la lògica del controlador del bundle.

En la crida a http://localhost/MVC/cotxes/veure/23, $params serà un array on:

  • $params[0] serà l'acció veure
  • $params[1] serà el valor 23

Per tant la nostra lògica del controlador actuarà segons aquesta informació de la ruta.

<?php
    if ($params[0] == "veure")
    {
        $id = $params[1];

        // obtenir del model de dades la informació del cotxe 23
        // cridar a la vista de veure amb les dades del cotxe 23
    }
?>

Les dades a obtenir del Model de dades o les que ens pugui interessar crear seran amb arrays associatius, per exemple:

<?php
    $dades = array("marca" => "Seat", "model" => "Ibiza TFSI");
?>

Ho assignarem a la propietat data

<?php
    $dades = array("marca" => "Seat", "model" => "Ibiza TFSI");
    $this->data = $dades;
?>

o bé directament:

<?php
    $this->data = array("marca" => "Seat", "model" => "Ibiza TFSI");
?>

o si les hem obtingut del model de dades:

<?php
    $id = $params[1]; 
    $dades = $CotxeMNG->ObtenirCotxe($id);
    $this->data = $dades;
?>

Ara ja tenim les dades del cotxe carregades al model i podem cridar a la vista

<?php
    $this->twig = "veure.html";
?>

Ens quedaria el codi així:

<?php
    if ($params[0] == "veure")
    {
        $id = $params[1]; 
        $dades = $CotxeMNG->ObtenirCotxe($id);
        $this->data = $dades;

        $this->twig = "veure.html";
    }
?>

Cal protegir el nostre codi assegurant els valors de l'array $params[], amb isset($params[1])

En la lògica del nostre controlador podrem tenir en compte també si ens arriben dades per $_POST provinents d'un formulari. Llavors podriem tenir associat a una crida

http://localhost/MVC/clients/alta

El següent codi del controlador del bundle clients

<?php
    if ($params[0] == "alta")
    {
        if (isset($_POST)
        {
            // obtenir valors del formulari i generem array de dades
            // les validem
            // cridem al model de dades per guardar-les a la BdD
            // Mostrem missatge de dades guardades correctament
            // Tornem a l'índex
        }
        else
        {
            // mostrem el formulari d'alta
            // seria un simple fitxer HTML + CSS a dins de la carpeta /templates
            // el valor de l'action hauria de ser la mateixa ruta
            // el mètode hauria de ser POST
        }
    }
?>

Requeriments del MVC

Per a poder implementar el MVC proposat ens caldrà:

  • Servidor Apache, tant se val XAMPP, LAMP, ...
    • Que s'executi .htaccess
    • Mòdul Rewrite habilitat
  • Suport al llenguatge PHP, a partir de la versió 7.0
  • Bdd MySQL instal·lat
    • Tenir instal·lat phpmyadmin

Ho podem tenir instal·lat tant en un servidor físic com en un de virtual o fins i tot en un docker amb els serveis indicats

Instal·lació i configuració del MVC

Al drive del curs trobareu l'arxiu MVC.rar. I un fitxer d'instruccions LLEGEIXE-ME.txt

Nota

  • Arxiu MVC-v2.rar Millora la generació de la interfície Layout permetent ara executar-hi twig. Per tant configurar en temps de renderització la capçalera de la vista. Per exemple, indicar si tinc sessió iniciada a una intranet.

  • Arxiu MVC-v3.zip Actualització del TWIG a v3.14 suport PHP 8.1 mínim

  • Arxiu MVC-v311.zip Inclou bundle, propietats i mètodes per controlar si un usuari s'ha logat

  1. Al vostre servidor creeu una carpeta MVC i copieu tot el codi

  2. Crear la BdD amb la informació inicial necessària que trobareu a la carpeta /tools.

    • Ens interessa sobretot la creació de la BdD de nom 'basededades'
    • Tenim les taules exemple sobre llibres
  3. Modificar o configurar el fitxer .htaccess

    • Cal que la linia "RewriteBase /MVC" apunti al directori específic dins el teu domini. Si és l'arrel deixa /.
  4. Modificar o configurar el fitxer config/baseDeDades.php

    • Cal assegurar-se que els paràmetres d'accés a la Base de Dades són correctes a la línia
    • BdD::connect("127.0.0.1", "usuari", "password", "basededades");
    • Podeu canviar si s'escau les dades de l'usuari a la BdD i al fitxer
  5. Modificar o configurar el fitxer config/funcionsinicials.php

    • define('WEBBASE', "http://localhost/MVC/"); -> Ruta pels links
    • define('DIRBASE', "C:/xampp/htdocs/MVC"); -> Ruta de l'aplicació al disc
    • define('WEBROOT', str_replace("index.php", "", $_SERVER["SCRIPT_NAME"])); -> Ruta de l'arrel del web
    • define('ESTILS', "styles/style.css"); -> Fitxer d'estils
    • define('PGDEFAULT', ""); -> Per defecte
    • define('ERRDEFAULT', "./error.html"); -> pàgina d'errada
    • define('TITOL', "Benvinguts !!!!"); -> Títol del web
    • define('DESCRIPCIO', "... a casa meva ..."); -> Descripció del web

La màgia del .htaccess

Aquest fitxer farà que totes les crides a l'apliació basada en MVC sigui al controlador del model. De fet al fitxer index.php

Això provocarà que sempre sigui el controlador qui prengui les decissions de què fer respecte a la ruta URL cridada.

http://localhost/MVC/

El fitxer .htacces conté:

Options -Indexes
Options FollowSymLinks
Options SymLinksIfOwnerMatch
RewriteEngine On
RewriteBase /MVC
AddType application/x-httpd-php .php .phtml

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule !\.(css|js|icon|zip|rar|png|jpg|gif|pdf)$ index.php [L]

La última línia és la que ens indica que tot el que no sigui arxius css, js, jpg, ... vagi a executar a l'arxiu 'index.php'

Nota

Per que s'executi l'arxiu .htaccess cal configurar

AllowOverride All

Ho farem a dins del VirtualHost que s'estigui executant Una opció a dins de l'acriu 'apache2.conf' de '/etc/apache2'

Per que s'executi Rewrite cal habilitar el mòdul

$ sudo a2enmod Rewrite

i reiniciar Apache

$ sudo service apache2 restart

Rutes URL

En el MVC la ruta ens serveix per identificar al controlador quina és la lògica que ha d'executar

Per exemple:

http://localhost/MVC/clients

Això ens farà executar al Controlador i anar a la gestió de clients.

Caldrà desenvolupar el controlador de clients per que executi les accions necessaries a aquesta ruta

Podem tenir també

http://localhost/MVC/clients/veure/33

Això significarà que volem veure les dades del client amb id 33

Per tant farem executar tota la usabilitat de la nostra aplicació web, seguint les rutes URL

Bundles

Un Bundle serà la unitat conceptual de la nostra aplicació i anirà associada a una part de la ruta

El controlador principal extraurà de la URL la primera part per esbrinar quin és el Bundle que cal executar

Per exemple:

http://localhost/MVC/clients

Correspon al Bundle Clients

A partir del Bundle tindrem les accions a executar i possibles paràmetres.

En l'exemple anterior, no n'executem cap i pot significar el concepte index o portada dels clients.

Si tenim:

http://localhost/MVC/clients/veure/33

El Bundle seria clients. I l'acció seria veure i podriem associar 33 a l'identificador.

Nota

En l'etapa de disseny de l'aplicació caldrà definir cada una de les parts a desenvolupar i la seva ruta, accions i paràmetres a usar.

Cada Bundle creat tindrà la següent estructura de carpetes:

  • Controllers -> lògica
  • models -> dades
  • templates -> vistes

Estructura de carpetes i arxius del MVC

  • bundle -> aquí dins crearem un directori per cada "Classe" i dins podran haver-hi les carpetes:
    • controllers (ClasseController)
    • models (Classe i ClasseManager)
    • templates (plantilles twig)
    • views (plantilles php)
    • També hi ha dues vistes per defecte:
      • error.html -> Per quan generem la vista amb ERROR
      • index.html -> Portada del MVC
  • cache -> l'utilitza twig per guardar les plantilles preformatades (es recomanda buidar-la de tant en tant. Ara tenim el twig configurat perquè no s'utilitzi)
  • config-> Fitxers de configuració baseDeDades.php i funcionsInicials.php (cal adaptar alguns paràmetres)
  • images-> html per guardar imatges, etc...
  • tools -> eines per utilitzar, ara mateix script sql per la creació de la base i les taules inicials...
  • vendors-> per moduls externs + eines internes
    • me -> eines internes BdD.php, Controller.php, RouterController.php, layout.html
      • (així la carpeta bundle és només de l'usuari, igual que les altres) (si cal només treuria layout.php)
    • Twig -> twig
  • .htaccess, index.php, LLEGEIXME.txt

Bundle / Controller

L'objectiu bàsic del controlador és aplicar la lògica del web associada a les rutes.

Per exemple bàsic, si volem mostrar una pàgina de qui som, només caldrà crear un bundle QuiSom i al controlador, indicar la vista quisom.html que volem mostrar, associada a la ruta /quisom.

Com? Expliquem-ho a poc a poc.

A dins de la carpeta Controllers, tindrem un arxiu amb el nom del 'bundle + controllers + .php'. Per exemple:

clientscontroller.php

A dins d'aquest fitxer tindrem una classe de nom 'bundle + Controller'

class clientsController extends Controller

La classe heredada Controller ens prepararà els paràmatres de la ruta URL, i ens farà executar el controlador correcte segons el Bundle indicat.

I executarà la funció process($params)

$params és l'array que es genera automàticament al Controller, del processament de la ruta URL del HTTP GET.

function process()

<?php
    /**
     * Gestiona les sol·licituds de Cotxes
     * Class CotxesController
     */
    class CotxesController extends Controller
    {
        // lògica del controlador Cotxes
        public function process($params)
        {
            $this->twig = "portada.html";
        }
    }
?>

A dins d'aquesta funció és a on desenvoluparem tota la nostra lògica.

En la crida a http://localhost/MVC/cotxes/eliminar/22 $params serà un array on:

  • $params[0] serà l'acció eliminar
  • $params[1] serà el valor 22

Per tant la nostra lògica del controlador actuarà segons aquesta informació de la ruta.

<?php
    if (isset($params[0]) && $params[0] == "veure")
    {
        if (isset($params[1])
        {
            $id = $params[1];

            // obtenir del model de dades la informació del cotxe 22
            // cridar a la vista de veure amb les dades del cotxe 22
        }
        else
            // ERRADA. Ens falta el id a la ruta URL
    }
?>

Les dades a obternir del Model de dades o les que ens pugui interessar crear seran amb arrays associatius, per exemple:

<?php
    $dades = array("marca" => "Seat", "model" => "Ibiza TFSI");
?>

Ho assignarem a la propietat data

<?php
    $dades = array("marca" => "Seat", "model" => "Ibiza TFSI");
    $this->data = $dades;
?>

o bé directament:

<?php
    $this->data = array("marca" => "Seat", "model" => "Ibiza TFSI");
?>

o si les hem obtingut del model de dades:

<?php
    $id = $params[1]; 
    $CotxeMNG = new CotxeMNG();
    $dades = $CotxeMNG->ObtenirCotxe($id);
    $this->data = $dades;
?>

Ara ja tenim les dades del cotxe carregades al model i podem cridar a la vista

<?php
    $this->twig = "veure.html";
?>

Ens quedaria el codi així:

<?php
    if ($params[0] == "veure")
    {
        $id = $params[1]; 
        $dades = $CotxeMNG->ObtenirCotxe($id);
        $this->data = $dades;

        $this->twig = "veure.html";
    }
?>

Cal protegir el nostre codi assegurant els valors de l'array $params[], amb isset($params[1])

En la lògica del nostre controlador podrem tenir en compte també si ens arriben dades per $_POST provinents d'un formulari.

Llavors podriem tenir associat a una crida

http://localhost/MVC/clients/alta

El següent codi del controlador del bundle clients

<?php
    if ($params[0] == "alta")
    {
        if (isset($_POST)
        {
            // obtenir valors del formulari i generem array de dades
            // les validem
            // cridem al model de dades per guardar-les a la BdD
            // Mostrem missatge de dades guardades correctament
            // Tornem a l'índex
        }
        else
        {
            // mostrem el formulari d'alta
            // seria un simple fitxer HTML + CSS a dins de la carpeta /templates
            // el valor de l'action hauria de ser la mateixa ruta
            // el mètode hauria de ser POST
        }
    }
?>

Bundle / Model de Dades

L'objectiu del model de dades, és definir la interfície lògica (objecte), amb les seves propietats i mètodes que l'arquitectura MVC usarà a dins de la lògica (controlador) i enviarà a maquetar-se a les vistes.

El model de dades s'implementarà amb objectes que usarem com a contenidor de les dades que gestionem. Tindrà com a propietats bàsiques totes les dades que vulguem guardar d’una entitat, per exemple, d’un alumne: DNI, nom, població.

  • Per cada model crearem un fitxer xxxx.php a dins de la carpeta /bundle/xxxx/models

  • A dins de xxxx.php, crear una classe amb les propietats que seran les dades del mode

    class xxxx
    {
        //propietats
        var propietat1:
        var propietat2;
        …
        //métodes
        public functions funcions()
        {
            ...
        }
    }
    
  • Necessitem dos tipus de funcions

    • Unes per entrar valors a les dades de l’objecte
    • Unes per obtenir valors,
    • Si hem de passar diversos valors podem usar un array associatiu amb clau => valor
<?php
    $alumne = array(“dni”=>”40111222X”, “nom”=>”Judit Soler Pou”)
?>
  • Podem usar el constructor o una funció específica, per exemple Assignar()
<?php
    function __construct($valors=null)
    {
        if ($valors == null) 
        {
            $this->dni = null;
            $this->nom = null;
        }
        else 
        {
            if (is_array($valors)) 
            {
                if (isset($valors["dni"])) $this->dni = $valors["dni"];
                if (isset($valors["nom"])) $this->dni = $valors["nom"];
            }
        }
    }
?>
  • Tenim les funcions de SET
<?php
    public function setNom($nom = null)
    {
        if($nom)
            $this->nom = $nom;
    }
?>
  • I les funcions de GET
<?php
    public function getnom() 
    {
        return ($this->nom);
    }
?>

Al nostre controlador fariem:

<?php
    $dades = new Persona(array(“dni”=>”40111222X”, “nom”=>”Judit Soler Pou”));
?>

A partir d'aquí es poden crear tots els mètodes que ens faci falta per la gestió de dades de l'aplicació a desenvolupar

Tenim la classe:

<?php
    class Punts
    {
        //propietats
        var punts

        //métodes
        public function __construct()
        {
            $this->punts = 0;
        }

        public function Set_Punts($punts)
        {
            $this->punts = $punts;
        }

        public function Get_Punts()
        {
            return $this->punts;
        }
    }
?>

Per exemple si volem incrementar punts podriem fer:

public function IncrementarPunts()
{
    $this->punts++;
}

Nota

Treballar amb aquest model de dades implica que cada vegada s'instacia l'objecte d'una classe i s'executa el constructor. Quan es finalitza l'execució del codi PHP del MVC aquest objecte desapareix i es perden les dades assignades. Això implica que no tenim persistència de les dades en diferents execucions La solució passa per usar els mètodes construct i destruct per usar variables de sessió per guardar els valors desats a dins de l'objecte.

public function __construct()
{
    if (isset($_SESSION["punts"]))
        $this->punts = $_SESSION["punts"];
}

public function __destruct()
{
    $_SESSION["punts"] = $this->punts;
}

La solució definitiva serà usar una BdD per donar persistència a les dades.

Bundle / Model de Dades / BdD

El MVC es basa en BdD per a donar persistència de la informació que tenim als objectes de dades.

Podeu trobar tota la informació de com gestiona el PHP l'accés a una BdD MySQL.

Bàsicament ens interessa:

  • Connexió a una BdD MySQL
  • Preparem la consulta SQL a realitar
  • Executem la consulta SQL
  • Recuperem els valors de la consulta SQL
  • Tornem o assignem els valors a l'objecte de model de dades

Per aconseguir-ho tindrem una nova classe que realitzarà les accions sobre la BdD i heredarà l'objecte el qual volem gestionar

class ClientManager extends Client

Abans de res, cal establir les dades de connexió a la BdD

Ho farem al fitxer /config/baseDeDades.php

<?php
    // Connexió amb la Base de Dades
    BdD::connect("127.0.0.1", "usuari", "password", "basededades");
?>

I la classe que ens permet connectar a la Base de Dades serà BdD

Per tant podem usar directament BdD::$connection com a objecte PDO

També podriem fer:

$conn = new BdD();

Tornem a la classe Manager.

class ClientManager extends Client

Significa que declararem al nostre controlador:

$client = new ClientManager();

Això ens permetrà tant:

  • accedir a l'objecte Client
  • accedir als mètodes sobre accions de la BdD

En fase de disseny sobre l'objecte client, definirem:

  • propietats de l'objecte: dni, nom, població, ...
  • mètodes sobre les dades: sets, gets, ...

Sobre el mànager, definiriem:

  • Obtenir client de la BdD: li passariem un 'dni' i colocariem les dades a l'objecte client.
  • Borrar un client de la BdD: li passariem un 'dni' i el borrariem de la BdD. També podriem tenir un client carregat a l'objecte i usar les dades aquestes per borrar de la BdD.
  • Obtenir tots els clients: Executar una consulta sobre la BdD i retornar un array d'objectes clients.
  • ...

Mirem un exemple d'Editorials

<?php
    //
    // fitxer de dades
    //

    //creem una classe on hi guardem les dades de l'editorial
    class Editorial
    {
        var $idEditorial;
        var $nomEditorial;
        var $provincia;
        var $municipi;
        var $web;
        var $email;
        var $telefon;

        function __construct($valors=null)
        {
            if ($valors == null) {
                $this->nomEditorial = "<nomEditorial>";
                $this->provincia = "<provincia>";
                $this->municipi = "<Municipi>";
                $this->web = "<web>";
                $this->email = "<email>";
                $this->telefon  = "<Telèfon>";
            }
            else {
                if (is_array($valors)) {
                    if (isset($valors["idEditorial"])) $this->idEditorial = $valors["idEditorial"];
                    if (isset($valors["nomEditorial"])) $this->nomEditorial = $valors["nomEditorial"];
                    if (isset($valors["provincia"])) $this->provincia = $valors["provincia"];
                    if (isset($valors["municipi"])) $this->municipi = $valors["municipi"];
                    if (isset($valors["web"])) $this->web = $valors["web"];
                    if (isset($valors["email"])) $this->email = $valors["email"];
                    if (isset($valors["telefon"])) $this->telefon = $valors["telefon"];
                }
            }
        }
        public function Assignar($nomEditorial,$provincia,$municipi,$telefon,$email)
        {
            $this->nomEditorial = $nomEditorial;
            $this->provincia = $provincia;
            $this->municipi = $municipi;
            $this->web = $web;
            $this->email = $email;
            $this->telefon  = $telefon;
        }
        public function getEditorial($parametre = null) {
            return array(
                "idEditorial" =>$this->idEditorial,
                "nomEditorial" =>$this->nomEditorial,
                "provincia" => $this->provincia,
                "municipi" => $this->municipi,
                "web" => $this->web,
                "email" => $this->email,
                "telefon" => $this->telefon
            );
        }
        public function getNom() {
            return ($this->nomEditorial);
        }

    }
?>

Definim les dades d'una editorial i mètodes sobre elles

Ara mirem el Manager que serà l'accés a la BdD i treballa conjuntament amb l'objecte Editorial que hereda.

<?php
    class EditorialManager extends Editorial {
    /**
     * Returns an editorial from the database by a URL
     * @param string $url The URL
     * @return array|false The article or false if not found
     */
    public function selectEditorialBD($url=null)
    {
        $resposta = null;
        if ($url != null) {
            try {
                $consulta = (BdD::$connection)->prepare('
                    SELECT `idEditorial`, `nomEditorial`, `provincia`, `municipi`, `web`, `email`, `telefon`
                    FROM `editorial`
                    WHERE `idEditorial` = :idEditorial');
                $consulta->bindParam('idEditorial',$url);
                $qFiles = $consulta->execute();
                if ($qFiles > 0) {
                    $consulta->setFetchMode(PDO::FETCH_ASSOC); 
                    $result = $consulta->fetchAll();
                    foreach($result as $fila) {
                        $resposta = $fila;
                    }
                    $this->idEditorial = $resposta["idEditorial"];
                    $this->provincia = $resposta["provincia"];
                    $this->nomEditorial = $resposta["nomEditorial"];
                    $this->municipi = $resposta["municipi"];
                    $this->web = $resposta["web"];
                    $this->email = $resposta["email"];
                    $this->telefon = $resposta["telefon"];
                    $resposta = $this;
                }
            } catch(PDOException $e) {
                echo "Error: " . $e->getMessage();
            }
        }
        return $resposta;
    }

    /**
     * Returns a list of all editorials in the database
     * @return array All the editorials in the database
     */
    public function selectEditorialsBD()
    {
        $resposta = null;
        try {
            $consulta = (BdD::$connection)->prepare('
                SELECT `idEditorial`, `nomEditorial`, `provincia`, `municipi`, `web`, `email`, `telefon`
                FROM `editorial`
                ORDER BY `nomEditorial` ASC
            ');
            $qFiles = $consulta->execute();
            if ($qFiles > 0) {
                $consulta->setFetchMode(PDO::FETCH_ASSOC); 
                $result = $consulta->fetchAll();
                foreach($result as $fila) {
                    $resposta[] = new Editorial($fila);
                }
            }
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }
        return $resposta;
    }
    public function updateBD()
    {
        $qFiles = 0;
        try {
            $consulta = (BdD::$connection)->prepare("
                UPDATE editorial 
                SET `nomEditorial` = :nomEditorial, `provincia` = :provincia, `municipi` = :municipi, `web` = :web, `email` = :email, `telefon` = :telefon
                WHERE  `idEditorial` = :idEditorial
            ");
            $consulta->bindParam('idEditorial',$this->idEditorial);
            $consulta->bindParam('nomEditorial',$this->nomEditorial);
            $consulta->bindParam('provincia',$this->provincia);
            $consulta->bindParam('municipi',$this->municipi);
            $consulta->bindParam('web',$this->web);
            $consulta->bindParam('email',$this->email);
            $consulta->bindParam('telefon',$this->telefon);

            $qFiles = $consulta->execute();
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }
        return $qFiles;
    }
    public function insertBD()
    {
        $qFiles = 0;
        try {
            $consulta = (BdD::$connection)->prepare("
                INSERT INTO editorial(`nomEditorial`,`provincia`,`municipi`,`web`,`email`,`telefon`)
                VALUES (:nomEditorial,:provincia,:municipi,:web,:email,:telefon)
            ");
            $consulta->bindParam('nomEditorial',$this->nomEditorial);
            $consulta->bindParam('provincia',$this->provincia);
            $consulta->bindParam('municipi',$this->municipi);
            $consulta->bindParam('web',$this->web);
            $consulta->bindParam('email',$this->email);
            $consulta->bindParam('telefon',$this->telefon);

            $qFiles = $consulta->execute();
            $this->idEditorial = (BdD::$connection)->lastInsertId();
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }

        return $qFiles;
    }
    public function deleteBD()
    {
        $qFiles = 0;
        try {
            $consulta = (BdD::$connection)->prepare("
                DELETE
                FROM editorial 
                WHERE  `idEditorial` = :idEditorial
            ");
            $consulta->bindParam('idEditorial',$this->idEditorial);

            $qFiles = $consulta->execute();
            $this->idEditorial = null;
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }

        return $qFiles;
    }
}
?>

Mirem selectEditorialBD($url=null)

Rebem el paràmetre de quina editorial volem recuperar les dades

Farem consulta a la BdD i retornarem un objecte Editorial

<?php
    public function selectEditorialBD($url=null) // $url és el id editorial
    {
        $resposta = null;
        if ($url != null) { // si hem passat ide editorial consultem bdd
            try {
                // preparem la consulta amb la variable que hem passat com a id
                $consulta = (BdD::$connection)->prepare('
                    SELECT `idEditorial`, `nomEditorial`, `provincia`, `municipi`, `web`, `email`, `telefon`
                    FROM `editorial`
                    WHERE `idEditorial` = :idEditorial');
                $consulta->bindParam('idEditorial',$url);
                // executem la consulta
                $qFiles = $consulta->execute();
                if ($qFiles > 0) {
                    // si obtenim dades de la consulta
                    // obtenim en un array associatiu els registres obtingut de la consulta
                    $consulta->setFetchMode(PDO::FETCH_ASSOC); 
                    $result = $consulta->fetchAll(); // si fem fetchAll obtenim tots els registres. Podriem fer només fetch()
                    foreach($result as $fila) {
                        $resposta = $fila; // només guardem una resposta.
                    }
                    //Ara carreguem a l'objecte heredat editorial els valors de l'array.
                    $this->idEditorial = $resposta["idEditorial"];
                    $this->provincia = $resposta["provincia"];
                    $this->nomEditorial = $resposta["nomEditorial"];
                    $this->municipi = $resposta["municipi"];
                    $this->web = $resposta["web"];
                    $this->email = $resposta["email"];
                    $this->telefon = $resposta["telefon"];
                    // guardem a resposta l'objecte
                    $resposta = $this;
                }
            } catch(PDOException $e) {
                echo "Error: " . $e->getMessage();
            }
        }
        // retornem l'objecte Editorial
        return $resposta;
    }
?>

Al controlador fariem

<?php
    $editorialMNG = new EditorialManager();

    $id = $params[0];
    $editorial = $editorialMNG->selectEditorialBD($id);

    // Si no trobem la editorial buscada per id, anem a pàgina errada
    if (!$editorial)
            $this->redirect(ERRDEFAULT);
        // Si l'hem trobat continuem

    // HTML head
    $this->head = array(
            'title' => "Dades de l'editorial " . $editorial->getNom(),
            'description' => "Dades de l'editorial " . $editorial->getNom(),
            );

    // Obtenim les dades per passar a la vista
    $this->data = $editorial->getEditorial();

    // Vista d'una editorial
    $this->twig = 'editorialUn.html';
?>

A partir d'aquí caldria dissenyar la lògica de cada una de les diferents accions a realitzar sobre la BdD i objecte.

Per exemple, si volem obtenir totes les editorials, ens caldrà fer un SELECT de totes. Recorrer tots els registres obtinguts i retornar un array d'objectes

<?php
    /**
     * Returns a list of all editorials in the database
     * @return array All the editorials in the database
     */
    public function selectEditorialsBD()
    {
        $resposta = null;
        try {
            $consulta = (BdD::$connection)->prepare('
                SELECT `idEditorial`, `nomEditorial`, `provincia`, `municipi`, `web`, `email`, `telefon`
                FROM `editorial`
                ORDER BY `nomEditorial` ASC
            ');
            $qFiles = $consulta->execute();
            if ($qFiles > 0) {
                $consulta->setFetchMode(PDO::FETCH_ASSOC); 
                $result = $consulta->fetchAll();
                foreach($result as $fila) {
                    $resposta[] = new Editorial($fila);
                }
            }
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }
        return $resposta;
    }
?>
Al controlador:

1
2
3
4
5
6
7
8
<?php
    $editorialMNG = new EditorialManager();
    $editorials = $editorialMNG->selectEditorialsBD();
    foreach($editorials as $editorial) {
        $this->data['editorials'][] = $editorial->getEditorial();
    }
    $this->twig = 'editorialsTots.html';
?>

Si per exemple volem inserir una editorial, prepararem la consulta a partir de les dades que tenim ja carregades a l'objecte que farem al controlador.

<?php
    public function insertBD()
    {
        $qFiles = 0;
        try {
            $consulta = (BdD::$connection)->prepare("
                INSERT INTO editorial(`nomEditorial`,`provincia`,`municipi`,`web`,`email`,`telefon`)
                VALUES (:nomEditorial,:provincia,:municipi,:web,:email,:telefon)
            ");
            $consulta->bindParam('nomEditorial',$this->nomEditorial);
            $consulta->bindParam('provincia',$this->provincia);
            $consulta->bindParam('municipi',$this->municipi);
            $consulta->bindParam('web',$this->web);
            $consulta->bindParam('email',$this->email);
            $consulta->bindParam('telefon',$this->telefon);
                $qFiles = $consulta->execute();
            $this->idEditorial = (BdD::$connection)->lastInsertId();
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }

        return $qFiles;
    }
?>

Podeu observar com obtenim el id generat amb lastInsertId()

Al controlador fariem

1
2
3
4
5
6
7
<?php
    if (isset($_POST["submit"]))
    {
        $editorial = new EditorialManager($_POST);
        $files = $editorial->insertBD();
    }
?>

Li passem les dades de $_POST que vindrien directament del formulari. Ens ho carrega a l'objecte al constructor.

De manera similar ho fariem per borrar una editorial de la BdD.

<?php
    public function deleteBD()
    {
        $qFiles = 0;
        try {
            $consulta = (BdD::$connection)->prepare("
                DELETE
                FROM editorial 
                WHERE  `idEditorial` = :idEditorial
            ");
            $consulta->bindParam('idEditorial',$this->idEditorial);
                $qFiles = $consulta->execute();
            $this->idEditorial = null;
        } catch(PDOException $e) {
            echo "Error: " . $e->getMessage();
        }

        return $qFiles;
    }
    ?>

Ens cal que al controlador haguem carregat el id, ho fem amb el $_POST.

1
2
3
4
5
6
7
<?php
    if (isset($_POST["elimina"]) and isset($_POST["idEditorial"])) 
    {
        $editorial = new EditorialManager($_POST);
        $files = $editorial->deleteBD();
    }
?>

En aquestes accions del controlador ens faltaria mostrar alguna interfície indicant el feedback de si s'ha inserit, eliminat, ...

1
2
3
4
5
6
7
8
<?php
    // HTML head
    $this->head = array(
        'title' => "Registre eliminat " . $editorial->getNom(),
        'description' => $files .  " eliminats " . $editorial->getNom(),
    );
    $this->mostraTots();
?>

Bundle / Vistes

L'objectiu bàsic de la vista és generar la UX i UI del lloc web i usarem TWIG per fer-ne la maquetació de les dades.

Les vistes en el MVC són arxius HTML que contenen les interfícies de l'aplicació.

Aquests arxius estan a dins de la carpeta Templates del Bundle corresponent

Son generades automàticament i només cal indicar quina vista és:

$this->twig = "veure.html";

Si ens cal passar-hi dades el que fem és usar la propietat data al controlador

$this->data = array("nom"=>"Judit");

Treballem amb el concepte de diccionari de dades, això significa que la clau del valor que passem com a dades, serà el que haurem d'usar a dins de la vista per generar-la

Usarem el Twig per fer aquesta automatització

CODI HTML
<HTML>
    <p>
        Nom: {{ nom }}
    </p>
</HTML>

Visualitzarem a la interficie del navegador

Nom: Judit

El llenguatge twig és propu potent per automatizar la generació dinàmica de la interfície a partir de dades que li passem

Podrem:

  • Inserir valors de dades
  • Generar noves 'variables' per crear lògica
  • Usar alternatives per mostrar una part interficie o no
  • Usar repetitives per recorrer array de dades que li passem
  • ...

TWIG

Trobareu més informació al document TWIG al Classroom o web twig

Tenim les etiquetes

  • {{ }} -> per mostrar valors
  • {% %} -> per alternatives if o repetitives for

    {% for usuari in usuaris %}
        {% if usuari.genere = "H" %}
            Sr. {{ usuari.nom }}
        {% else %}
            Sra. {{ usuari.nom }}
        {% endif %}
    {% else %}
        No hi ha usuaris
    {% endfor %}
    

En aquest cas usuaris seria un array de l'estil:

`$usuaris = array(array("nom"=>"Carme"),array("nom"=>"Pere"), ...);`

Podem posar-hi comentaris

{# .... #}

Per definir una variable usarem 'set'

{{ set nom = "Carme" }}

I més endavant la podrem usar perfectament

    <html>
        {{ set nom = "Jordi" }}
        Nom: {{ nom }}
    </html>

Tenim una serie de modificadors o filtres, que posarem darrera de la variable amb |

{{ variable | filtre }}

Exemples de filtres:

  • upper: Converteix a majúscules
  • lower: Converteix a minúscules
  • capitalize: Cambia la 1a lletra a majúscula i la resta en minúscules.
  • striptags: elimina qualsevol etiqueta HTML que pugui contenir el valor de la variable
  • raw: mostra els valors en format original, sense aplicar cap sequüencia d'escape
  • date: mostra la data en format del php
  • trim: elimina espais en blanc al principi o final
  • ...
    {{ alumne.Cognom | Capitalize }}
    

En les condicions d'un IF podem usar els operadors típics de AND, OR, NOT, ==, <=, >=, ...

i també alguns operadors especials

  • divisibleby(numero): Mira si la variable es divisible pel valor indicat
  • empty: Mira si la variable és buida
  • even: Avalua si el valor de la variable és parell
  • odd: Avalua si el valor de la variable és senar
  • null: Controva si el valor de la variable és null
  • ...

        {% for article in articles %}
            {% if article.preu is divisibleby(2) %}
                <b>{{ article.preu }}</b>
            {% else %}
                {{ article.preu }}
            {% endif %}
        {% else %}
            No existeixen artícles
        {% endfor %}