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.
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
Tot seguit fem actuar el Controlador
<?php
$router = new RouterController();
$router->process(array(treuWEBROOT($_SERVER['REQUEST_URI'])));
?>
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
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à
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:
Ho assignarem a la propietat data
o bé directament:
o si les hem obtingut del model de dades:
Ara ja tenim les dades del cotxe carregades al model i podem cridar a la vista
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
-
Al vostre servidor creeu una carpeta MVC i copieu tot el codi
-
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
-
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 /.
-
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
-
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
- me -> eines internes BdD.php, Controller.php, RouterController.php, layout.html
- .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:
Ho assignarem a la propietat data
o bé directament:
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
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
- 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
- I les funcions de GET
Al nostre controlador fariem:
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:
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
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
|
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
Al controlador fariem
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
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.
Podeu observar com obtenim el id generat amb lastInsertId()
Al controlador fariem
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.
Ens cal que al controlador haguem carregat el id, ho fem amb el $_POST.
En aquestes accions del controlador ens faltaria mostrar alguna interfície indicant el feedback de si s'ha inserit, eliminat, ...
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ó
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
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
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
-
...