UD3: PORTAFOLIS - ESTRUCTURES DE CONTROL¶

Duració de la unitat: 11 sessions
INTRODUCCIÓ¶
En aquesta unitat prendrem com a base la revisió de programació vista en la unitat anterior, i aprofundirem més en determinades estructures de control dels llenguatges PHP, al mateix temps que començarem a desenvolupar el primer projecte del curs: el portafolis.
En una primera fase prepararem l'entorn de desenvolupament mitjançant la tecnologia Docker. A continuació es proporcionarà part de la interfície gràfica de l'aplicació i es realitzaran exercicis bàsics amb estructures de control i tipus de dades compostes. Finalment es proposarà la realització d'activitats de desenvolupament que completen el prototip.
Aquest projecte abastarà les unitats 3 a la 5, però en la present unitat no s'utilitzarà accés a base de dades, sinó que s'utilitzaran dades incrustades en el codi i fitxers.
En la unitat 2 del curs has d'haver-te familiaritzat amb el bàsic del llenguatge PHP. En qualsevol cas, revisa i tingues a mà la informació dels següents enllaços:
AVALUACIÓ¶
El present document, junt amb el seu corresponent butlletí d'activitats (publicat addicionalment), cobreix els següents criteris d'avaluació:
| RESULTATS D'APRENENTATGE | CRITERIS D'AVALUACIÓ |
|---|---|
| RA3. Escriu blocs de sentències incrustats en llenguatges de marques, seleccionant i utilitzant les estructures de programació. | a) S'han utilitzat mecanismes de decisió en la creació de blocs de sentències. b) S'han utilitzat bucles i s'ha verificat el seu funcionament. c) S'han utilitzat matrius (arrays) per a emmagatzemar i recuperar conjunts de dades. d) S'han creat i utilitzat funcions. g) S'han afegit comentaris al codi. |
ENTORN DE DESENVOLUPAMENT¶
El primer pas per a configurar l'entorn de desenvolupament és la instal·lació de Docker. En realitat podríem preparar l'entorn de diferents formes (directament instal·lant en la màquina local, mitjançant màquina virtual...), però la utilització de Docker es veu justificada per la seua fàcil utilització, la profundització en aquesta tecnologia en altres mòduls del curs (Desplegament d'aplicacions web), i la seua extensa utilització en la indústria (en tot tipus d'entorns).
Per a la Instal·lació de Docker és recomanable seguir la documentació oficial:
Si encara no tens instal·lat Docker en la teua màquina, és el moment de fer-ho!
Una vegada tingues instal·lat Docker, crea la següent estructura de directoris en la ruta en la qual vages a crear el projecte:

Per ara la carpeta src està buida. Anem a crear el fitxer docker-compose.yml (està a la mateixa altura que la carpeta src!) amb el següent contingut:

Per a crear la indentació (espais a l'esquerra) prem el tabulador tantes vegades com siga necessari.
Ja ho tindríem tot per a poder executar el nostre codi, però... com és possible? Aquest fitxer docker-compose.yml va a llançar un contenidor preconfigurat (com si fora una petita màquina virtual) amb PHP 7.4 i el servidor web Apache. A més, va a exposar el port 80 i el va a fer correspondre amb el port 80 del host (la nostra màquina local); per últim, tot el que hi haja en el directori src va a estar muntat en la ruta /var/www/html (on recull Apache el codi a executar) del sistema de fitxers del contenidor, amb la qual cosa l'únic que haurem de fer és incloure tot el nostre codi en la carpeta src, i el contenidor s'encarregarà de la resta.
Anem a fer una prova. Anem a la ruta on estiga el fitxer docker-compose.yml i executem la següent comanda:
docker-compose up

En primer lloc veiem que s'han realitzat una sèrie de descàrregues per a conformar el contenidor (aquestes descàrregues només es van a realitzar la primera vegada, a no ser que fem una neteja de Docker). Una vegada fet això es llança el contenidor (web_1) i es comencen a mostrar les línies de log d'Apache. Si ara consultem en el navegador l'adreça 127.0.0.1:80 (equivalent a introduir localhost, que és un àlies configurat en el fitxer /etc/hosts), veiem el resultat:

Si consultem el log d'Apache en la consola:

Veiem que sí s'ha registrat activitat, però: per què no es mostra res en el navegador?
La raó és que encara no hem introduït cap fitxer en src que Apache puga prendre per a interpretar. En el propi log ho diu:
Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.php,index.html) found, and server-generated directory index forbidden by Options directive
Per tant anem a introduir un fitxer index.php amb el típic "Hola món":

Refresquem el navegador i veiem l'eixida:

Borrem el fitxer index.php, i src torna a estar buida.
Podem dir que estem preparats per a començar el nostre projecte.
PROJECTE - PREPARACIÓ DE LA INTERFÍCIE¶
Juntament amb el present document es proporciona també el punt de partida del projecte, que va a consistir en un document HTML, anomenat index.html (fes clic per a descarregar-lo). S'han utilitzat les següents llibreries per a conformar la interfície d'usuari:
- Bootstrap 5.2, per al disseny d'interfícies responsives.
- Tema Flatly basat en bootstrap, que proporciona la fulla d'estils de la interfície.
- Font awesome 6.2, per a la utilització d'icones.
Si ens fixem en el contingut del fitxer index.html proporcionat no es diferencia en res (excepte en la utilització de les llibreries anteriors) d'una de les pàgines estàtiques programades durant el primer curs del cicle:

Anem a començar a incrustar PHP en el codi HTML.
Utilització de plantilles¶
El primer pas que realitzarem és fragmentar el codi HTML proporcionat. Per què? perquè hi ha determinades parts que sempre es repeteixen en cadascuna de les pàgines: la capçalera i el peu de pàgina.
Així, anem a crear una carpeta "templates" (plantilles, en anglès) dins de "src", per a emmagatzemar tots aquells fragments de codi HTML que podem reutilitzar d'una pàgina a una altra.
Dins de templates anem a crear dos arxius: header.php i footer.php. També anem a crear un arxiu "index.php", però aquesta vegada dins de "src" (NO dins de "templates"!). L'estructura, per ara, queda de la següent manera:

ACTIVITAT: A classe, analitzarem index.html i debatrem com segmentar el codi. A continuació reorganitzem el codi d'index.html entre footer.php i header.php.
Una vegada configurades les plantilles, queda molt poc codi HTML en index.html. Ara anem a posar el restant del codi HTML en index.php i buidar completament index.html (després eliminem aquest fitxer). index.php queda de la següent manera:

Com podem ara tornar a estructurar la pàgina d'inici amb les plantilles? Això es veurà en el següent apartat.
Conformació de les pàgines de l'aplicació¶
Per a poder estructurar les pàgines de l'aplicació basant-nos en les plantilles, podem utilitzar la instrucció include. Modificant el fitxer index.php, quedaria de la següent manera:

Ara, amb el contenidor Docker iniciat, si refresquem la pàgina, tornarem a obtindre el mateix resultat que amb la pàgina web estàtica:

Sembla que no haja canviat res, però PHP ja està treballant per a nosaltres. Hem passat d'una pàgina web estàtica, recollida en un sol document, a una pàgina modular generada amb PHP. En un lloc web amb multitud de pàgines això pot ser un gran avantatge.
ATENCIÓ: l'ordre en què han sigut carregats els fitxers és important. Si escrivíssim el següent fitxer index.php:

Obtindríem el següent resultat:

L'HTML generat ni tan sols seria correcte, però els navegadors web moderns són capaços de prendre un document HTML incorrecte sintàcticament i representar la millor aproximació.
Tornem a la versió anterior d'index.php després d'aquesta petita prova.
Ja tindríem el bàsic del projecte fet. Hem vist la funció include, però som programadors amb molta curiositat, i ens preguntem si "include" ens serveix per a tots els casos o existeixen altres possibilitats per a incloure fitxers en PHP. En el pròxim apartat aprofundirem en això.
Inclusió de fitxers en PHP¶
La idea d'utilitzar diferents fitxers és la reutilització del codi, la qual cosa comporta una major modularitat i un millor manteniment del mateix. Un fitxer no té per què ser una plantilla, com hem vist fins ara, sinó que també podria ser codi PHP que poguéssim arribar a utilitzar en diferents parts de l'aplicació. També es poden donar diferents circumstàncies, i és per això que disposem de diverses opcions:
- include(ruta/arxiu); include_once(ruta/arxiu);
- require(ruta/arxiu); require_once(ruta/arxiu);
NOTA: Si l'arxiu es troba a la mateixa altura (en el sistema de fitxers) que el fitxer en el qual s'inclou, llavors només cal especificar el nom de l'arxiu a incloure; si els dos arxius no es troben a la mateixa altura, llavors és possible especificar la ruta (absoluta o relativa) de l'arxiu a incloure.
Les particularitats de cada instrucció són:
- require: llança un error fatal si no troba l'arxiu.
- include: si no troba l'arxiu, emet una advertència (warning)
- Les funcions _once només es carreguen una vegada, si ja ha sigut inclosa prèviament, no ho torna a fer, evitant bucles.
Per exemple, col·loquem les següents funcions en l'arxiu biblioteca.php:
<?php
function suma(int $a, int $b) : int {
return $a + $b;
}
function resta(int $a, int $b) : int {
return $a - $b;
}
?>
I posteriorment en un altre arxiu incloem l'anterior:
<?php
include_once("biblioteca.php");
echo suma(10,20);
echo resta(40,20);
?>
Completem la resta de pàgines¶
En aquest apartat completarem les pàgines i les plantilles de l'aplicació per a poder afegir-los posteriorment codi PHP.
index.php
Anem a introduir un projecte de prova perquè es puga visualitzar en la pàgina d'inici, quedaria de la següent manera:

Anem a utilitzar una imatge per a cada projecte. Per a poder fer això hem d'emmagatzemar les imatges en l'estructura de directoris, per això creem una carpeta "static" dins de src, i dins de static crearem una altra anomenada "images":

NOTA: si has de descarregar imatges d'Internet, assegura't que la llicència d'aquestes imatges t'ho permet. Utilitza, si és necessari, un lloc com pixabay.com per a descarregar imatges amb llicència lliure.
Descarrega't una imatge en la carpeta "images", renombra-la a "projecte1.jpg" i completa el fitxer "index.php" amb el següent codi:
<?php include("templates/header.php"); ?>
<div class="container">
<a href="#">
<div class="card" style="width: 18rem;">
<img class="card-img-top" src="static/images/proyecto1.jpg" alt="Proyecto 1">
<div class="card-body">
<h5 class="card-title">Proyecto 1</h5>
<p class="card-text">Descripción del proyecto 1.</p>
</div>
</div>
</a>
</div>
<?php include("templates/footer.php"); ?>
Ara hauria d'aparéixer un projecte en la pàgina principal.
projecte.php
Creem aquest nou fitxer a la mateixa altura que index.php i afegim el següent contingut:
<?php include("templates/header.php"); ?>
<div class="container">
<h2>Título de muestra</h2>
<h4><a href="#">Año</a></h4>
<span>Categorías: </span>
<a href="#"><button class="btn btn-sm btn-default">Categoría 1</button></a>
<br> <br>
<div class="row">
<div class="col-sm">
<img src="static/images/proyecto1.jpg" alt="Proyecto 1" class="img-responsive"><br>
</div>
<div class="col-sm">Descripción</div>
</div>
</div>
<?php include("templates/footer.php"); ?>
Per a consultar el resultat, introduïm en la barra de navegació la URL "localhost/projecte.php":

contacte.php
Descarreguem aquesta imatge en el directori corresponent (amb el nom businessman.png), creem contacte.php a la mateixa altura que index.php, i inserim el següent codi:
<?php include("templates/header.php"); ?>
<div class="container">
<h2 class="mb-5">Contacto</h2>
<div class="row">
<div class="col-md">
<img src="static/images/businessman.png" class="img-fluid rounded">
</div>
<div class="col-md">
<h3>Nombre y apellidos</h3>
<p>Ciclo Superior DAW.</p>
<p>Apasionado del mundo de la programación en general, y de las tecnologías web en particular.</p>
<p>Si tienes cualquier tipo de pregunta, contacta conmigo por favor.</p>
<p>Teléfono: 87654321</p>
</div>
</div>
</div>
<?php include("templates/footer.php"); ?>
El resultat, al consultar la URL localhost/contacte.php, hauria de ser el següent:

En aquests moments tenim ja moltes coses preparades per a poder començar a afegir dinamisme a la nostra aplicació. En els següents apartats veurem les diferents estructures que ens ho permetran.
MECANISMES DE DECISIÓ¶
Teoria prèvia¶
Els mecanismes de decisió en PHP són molt semblants als de qualsevol altre llenguatge de programació. Tenim tres possibilitats:
Consultem cadascun dels enllaços i els revisem.
Projecte - Activar opcions de menú¶
Anem a veure un exemple d'utilització d'una estructura de decisió per a poder activar les opcions del menú de navegació en funció de la pàgina que es trobe activa en cada moment. Per defecte, l'opció INICI és la que es troba activa:

Si anem a la URL "localhost/contacte.php", llavors l'opció de menú "CONTACTE" és la que hauria de ressaltar-se (lletres blanques amb fons fosc). Com ho fem?
Aquestes opcions de menú es configuren en header.php, en el següent bloc:

Ací veiem que el primer element <a> té l'atribut "active" en la class, però els altres no. El que cal aconseguir és que aquest atribut "active" siga dependent de la URL en la qual estem.
Podem arribar a saber en quina URL estem des del codi PHP? La resposta és "sí", mitjançant el que s'anomena variables superglobals. Abans de revisar-les, anem a veure com utilitzar-les perquè les opcions de menú "INICI" i "CONTACTE" s'activen/desactiven en funció de que es visite la corresponent pàgina (deixarem l'opció "CATEGORIES" per a més endavant). Anem a introduir sentències if-else perquè els elements <a> tinguen un valor diferent en l'atribut class, en funció de la pàgina actual. El codi de la barra de navegació quedaria de la següent manera:

Hem utilitzat la variable superglobal $_SERVER['SCRIPT_NAME'] per a esbrinar el script que s'ha executat en el servidor, i en funció del seu valor definim un valor diferent per a l'atribut class de l'element <a>. Prova a anar a "localhost/contacte.php" i observa els resultats.
Simplificant el codi, ens queda de la següent manera:

En aquest enllaç trobaràs una referència de les variables superglobals que ens proporciona PHP. Aquestes variables s'utilitzaran més endavant en les activitats de la unitat, i posteriorment també quan creem formularis. Llig l'enllaç amb especial deteniment i pregunta tot allò que no entengues.
ARRAYS¶
Ha arribat el moment en què necessitem dades per a poder mostrar en la nostra aplicació. Com encara no anem a abordar les bases de dades, necessitem emmagatzemar d'alguna manera totes aquelles dades que puga contindre la nostra aplicació (projectes, categories, etc.).
Per això anem a treballar primer amb les estructures de dades denominades arrays. En qualsevol cas, encara que accedírem a bases de dades posteriorment, continuaríem utilitzant també arrays per a determinades coses.
Teoria¶
Comencem fent una revisió del que són els arrays en PHP. Per a això anem a revisar l'apartat Arrays d'aquest enllaç.
Projecte - Definició de dades¶
En aquest pas del projecte ens proposem emmagatzemar diferents dades:
- Projectes
- Categories de projectes (seran etiquetes amb les tecnologies en les quals es basa un projecte)
Comencem pels projectes. Fent una ullada a index.php, podem extraure les següents dades que identificaran a un projecte:
- Títol
- Descripció
- Imatge
La millor opció per a emmagatzemar aquestes dades, en forma d'array, serà un array associatiu els elements del qual siguen, al seu torn, arrays associatius, de la forma:
$proyectos = [
[
"clave" => "proyecto1",
"titulo" => "Título proyecto 1",
"descripcion" => "Descripción proyecto 1",
"imagen" => "static/images/proyecto1.jpg"
],
[
"clave" => "proyecto2",
"titulo" => "Título proyecto 2",
"descripcion" => "Descripción proyecto 2",
"imagen" => "static/images/proyecto1.jpg"
],
[
"clave" => "proyecto3",
"titulo" => "Título proyecto 3",
"descripcion" => "Descripción proyecto 3",
"imagen" => "static/images/proyecto1.jpg"
],
];
Ara la pregunta és: on és més convenient definir aquesta variable? en index.php? en header.php? en un fitxer dedicat que actue com a repositori de les dades per a poder accedir des de qualsevol altre fitxer? Anem a optar per l'última opció.
ACTIVITAT: Creem un fitxer dades.php a la mateixa altura que index.php i definim la variable $projectes. A més creem una altra variable $categories, que serà un altre array associatiu amb clau i nom de categoria. Quina penses que és la millor estructura per a l'array de categories? quin valor establiries per a la clau de les categories?
Després de completar el fitxer, l'estructura de directoris queda de la següent manera:

BUCLES¶
Teoria¶
Ha arribat el moment de donar una veritable sensació de dinamisme a la nostra aplicació web mitjançant la representació de les dades que hem vingut emmagatzemant mitjançant arrays, en la secció anterior. Això ho aconseguirem utilitzant bucles, que són sentències iteratives que ens permetran recórrer els arrays creats i poder controlar què fem amb cadascun dels seus elements.
Per a començar, revisem el bàsic dels bucles en aquest enllaç.
Projecte - Llistat de projectes¶
El que més ens urgeix en aquest moment és mostrar els nostres projectes en la pàgina de benvinguda del lloc web. Per a això, el més pràctic és utilitzar un bucle foreach. Per a això, primer hem d'incloure el fitxer dades.php en index.php, i modificar el codi de manera que quede així (s'ha modificat també l'HTML perquè quede millor distribuït):
Si refresquem la pantalla d'inici:

FUNCIONS¶
Teoria¶
Abans de començar a desenvolupar lògica més complexa per a la nostra aplicació, és convenient que aprenguem a utilitzar els mecanismes mitjançant els quals podrem modularitzar molt millor el nostre codi, al mateix temps que propiciarem un millor manteniment del mateix.
Això ho aconseguirem mitjançant la utilització de funcions en PHP. En aquest enllaç trobaràs el bàsic.
A més, existeixen les funcions predefinides de PHP, que ens proporcionaran funcionalitats ja programades, sense necessitat de tornar-les a implementar. En aquest enllaç tens una referència.
Projecte - Ordenació¶
En aquest pas del projecte anem a crear una funció que prenga un paràmetre de la URL i ordene els projectes segons el seu títol per ordre alfabètic descendent.
Per a això, anem a fragmentar i distribuir el següent codi, que s'encarrega de l'ordenació d'arrays per un atribut d'un subarray:
usort($proyectos, 'ordenaTituloProyectoDesc');
function ordenaTituloProyectoDesc($a, $b) {
return strcmp($b['titulo'], $a['titulo']);
}
Creem un nou fitxer utils.php per a emmagatzemar les nostres funcions i codi reutilitzable, a la mateixa altura que index.php, i introduïm només la definició de la funció "ordenaTitolProjecteDesc". COMPTE, inclou només la definició de la funció:
function ordenaTituloProyectoDesc(\$a, \$b) {
return strcmp(\$b\[\'titulo\'\], \$a\[\'titulo\'\]);
}
Després d'això, incloem utils.php en index.php i ja tindrem la lògica d'ordenació en la pàgina que volem.
Ja tenim la lògica, però... com fem perquè aquesta lògica s'execute? Vam veure anteriorment que existeixen les variables superglobals, i que una d'elles ens dona accés als paràmetres de la URL. Es tracta de la variable $_GET.
Si volguérem senyalitzar a la pàgina index.php que volem ordenar els projectes en ordre descendent, podríem introduir una URL que apunte a index.php, però amb un paràmetre (anem a anomenar-lo "sort") amb valor "-1", de la forma:
http://localhost/index.php?sort=-1
D'aquesta manera, mitjançant el superglobal $_GET podríem recuperar el valor de "sort" simplement consultant $_GET['sort'].
Inserint el següent codi en index.php, aconseguim l'ordenació desitjada, al passar el paràmetre sort en la URL:

Al revisitar la pàgina amb el paràmetre sort, veiem que els projectes s'han ordenat per ordre descendent, segons el seu títol:

ACTIVITAT: modifica el codi anterior per a utilitzar ISSET.
COMENTARIS¶
Els comentaris són el mecanisme que tenen la majoria dels llenguatges de programació per a afegir observacions al codi, amb la sintaxi més bàsica d'aprendre, però la utilització de la qual és la més difícil de recordar. Que no et passe com en el següent meme:

Els comentaris són importantíssims, una eina molt útil tant per a altres membres d'un equip de desenvolupament, com per a la pròpia persona que està desenvolupant el codi en aquell moment. Pensa en el teu jo del futur quan insertes comentaris, ell/ella t'ho agrairà.
El bàsic sobre comentaris en PHP ho trobaràs ací.