Desenvolupament web dinàmic backend amb PHP
Conceptes¶
Sessió 9: Bases de dades¶
Objectiu Sessió 9
L'objectiu d'aquesta sessió serà la d'implementar la persistència de la informació usant bases de dades. Una BdD ens permet desar informació que el backend recuperarà per a generar contingut dinàmic durant la navegació.
Introducció¶
Més informació php.net o bé w3schools
Normalment usarem una BdD MySQL, tot i que usant PDO ens podem conectar a qualsevol BdD que hi tinguem drivers de conexió.
El llenguatge PHP disposa de dues opcions:
- mysqli: que pot ser procedimental o objectes i es connecta a una BdD MySQL
- PDO (PHP Data Objects): que està basada en objectes i ens permet connectar a qualsevol BdD
El SGBDR ens permet treballar com a interfície per a la nostra aplicació PHP i ens permet accedir a la informació guardada a la BdD
Tenim dos conceptes:
- QUÈ: serà la manera que direm com accedim a la informació. Usant SQL.
- COM: és el SGDDR que realitzarà internament l'accés a la informació.
El SQL es divideix en tres blocs:
- DML: Llenguatge de Manipulació de Dades. Per exemple: SELECT, INSERT, UPDATE, DELETE, ...
- DDL: Llenguatge de Definició de Dades. Per exemple: CREATE, ALTER, DROP, ...
- DCL: Llenguatge de Control de Dades. Per exemple: GRANT, REVOKE, ...
Lògica d'accés a la informació d'una BdD¶
De manera bàsica farem:
- Conexió a una BdD, indicant servidor, usuari, contrasenya i BdD
- Preparar sentència SQL
- Executar sentència SQL
- Si hem fet SQL SELECT
- Recollim informació
- Acció amb la informació
- Tancar conexió
Si usem PDO, tindrem 3 classes definides:
- PDO: Conexió entre PHP i la BdD
- PDOException: Control de les excepcions ocorregudes
- PDOStatement: Consulta preparada, executem i resultat obtingut.
Conexió a una BdD¶
Usant MySQLi
<?php
$servername = "localhost";
$username = "username";
$password = "password";
// Create connection
$conn = mysqli_connect($servername, $username, $password);
// Check connection
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully";
?>
Usant PDO
<?php
$servername = "localhost";
$username = "username";
$password = "password";
try {
$conn = new PDO("mysql:host=$servername;dbname=myDB", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connected successfully";
} catch(PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
?>
Executar una sentència simple¶
Podem executar i obtenir els resultats d'una sentència SQL simple de manera ràpida i fàcil amb PDO->query()
$SQL = "SELECT * FROM CLients ORDER BY Nom";
$rst = $conn->query($SQL);
Ens retorna un array de resultats.
* NOTA
L'execució d'una sentència SQL amb query no controla la injectació d'atacs SQL
Si que ho podrem controlar millor amb `prepare()` i `execute()`.
També podem usar PDO->exec()
, executa una sentència i ens retorna el número de resgistres.
$SQL = "DELETE FROM CLients WHERE id=3";
$count = $conn->exec($SQL);
echo "$count registres eliminats";
Ens anirà molt bé per executar consultes SQL que no retornin específicament resultats d'una SELECT
, per exemple sentències DDL, com CREATE TABLE
.
Preparar una sentència SQL¶
La classe PDOStatement és la que usarem per tractar les sentències SQL.
Una instància de la classe PDOStatement es crea quan es crida a PDO->prepare()
.
Associem els paràmetres amb bindparam()
i tot seguit execute()
.
Amb això obtenim millora del rendiment i seguretat en la consulta. Per exemple evitar injectació de SQL per part de robots en accés a formularis web.
Preparar -> Associar valors -> Executar
un exemple:
$SQL = "SELECT * FROM Alumnes WHERE DNI=:DNI";
$rst = $conn->prepare($SQL);
$DNI = "40000000-A";
$rst->bindparam(":DNI",$DNI);
$rst->execute();
bindparam()
tant ens permet utilitzar ?
com :variable
$SQL = "INSERT INTO Alumnes (DNI,Nom) VALUES (?,?)";
$rst = $conn->prepare($SQL);
$DNI = "40000000-A";
$Nom = "Laia Bosch";
$rst->bindparam(1,$DNI);
$rst->bindparam(2,$Nom);
$rst->execute();
També ens permet passar un array de valors:
$SQL = "INSERT INTO Alumnes (DNI,Nom) VALUES (:DNI,:Nom)";
$rst = $conn->prepare($SQL);
$DNI = "40000001-B";
$Nom = "Maria Soler";
$rst->execute(array(':DNI'=>$DNI,':Nom'=>$Nom));
Podem usar tant:
- bindParam(): la variable s'enllaça com una referència i només s'avalua quane s crida execute().
- bindValue(): la variable s'enllaça al valor fins que s'eceuta la sentència amb execute().
Consultar dades¶
Una vegada s'ha executat la consulta s'obté el resultat de la mateixa.
Per fer-ho usem fetch
. Això ens retorna un registre amb les dades obtingudes de la consulta SQL.
Cal indicar com obtenim l'array de dades:
- PDO::FETCH_ASSOC: torna un array indexat amb les claus dels noms de les columnes de la taula de la bdd.
- PDO::FETCH_NUM: torna un array indexat on la clau són números de posició.
Tenim més formes d'obtenir els resultats
llavors només cal recorrer l'array obtingut
$SQL = "SELECT * FROM CLients ORDER BY Nom";
$rst = $conn->query($SQL);
while ($row = $rst->fetch())
echo $row["DNI"] . " - " . $row["Nom"];
Un exemple d'usar retorn d'un objecte
$SQL = "SELECT * FROM CLients ORDER BY Nom";
$rst = $conn->query($SQL);
while ($row = $rst->fetch(PDO::FETCH_OBJ))
echo $row->DNI . " - " . $row->Nom;
Si volem obtenir tots els registres usarem fetchAll()
. Ara obtindrem tots els registres i haurem de recorrer l'array obtingut
$SQL = "SELECT * FROM CLients ORDER BY Nom";
$rst = $conn->query($SQL);
$clients = $rst->fetchAll()
foreach($clients as $client)
echo $client["DNI"] . " - " . $client["Nom"];
Transaccions¶
Les transaccions ens permeten controlar l'execució de diverses sentències agrupades de SQL i que ens cal que cap d'elles ens retorni errada. Ens permetrà desfer-les totes en cas d'errada o confirma-les si totes han anat bé.
Una transacció s'inicia amb PDO::beginTransaction()
Validem totes les sentències SQL executades amb PDO::commit()
Desfem totes les sentències SQL executades amb PDO::rollback()
Per tant podem controlar la integritat de les dades segons s'haginpogut executar totes les sentències SQL correctment o no.
Usarem el try - catch
per controlar la transacció.
try
{
$conn->beginTransaction();
$conn->query($SQL1);
$conn->query($SQL2);
$conn->query($SQL3);
...
$conn->commit();
echo "Sentències SQL correctes";
}
catch (Exception $e)
{
$conn->rollback();
echo "Hi ha hagut alguna errada";
}
Com ho farem amb PHP i POO¶
L'objectiu d'aquest apartat és aplicar els conceptes apresos de Programació Orientada a Objectes (POO) per estructurar el nostre codi PHP de forma modular i mantenible, integrant-hi l'accés a bases de dades.
Organitzarem el nostre codi en classes, segons el principi de responsabilitat única:
- Classe de connexió a la base de dades (Database)
- Classes de model (entitats) que representen taules de la base de dades (per exemple,
Usuari
,Producte
, etc.) - Repositoris o serveis per encapsular la lògica d’accés a dades
Classe de connexió (Database)
Una classe que encapsula la connexió PDO:
class Database {
private $host = "localhost";
private $dbname = "botiga";
private $user = "usuari";
private $pass = "contrasenya";
private $pdo;
public function connect() {
if (!$this->pdo) {
$dsn = "mysql:host=$this->host;dbname=$this->dbname;charset=utf8";
$this->pdo = new PDO($dsn, $this->user, $this->pass);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $this->pdo;
}
}
Classe Model (exemple: Usuari)
Una classe que representa les dades i estructura de la taula usuaris
.
class Usuari {
private $id;
private $nom;
private $email;
public function __construct($id, $nom, $email) {
$this->id = $id;
$this->nom = $nom;
$this->email = $email;
}
public function getNom() {
return $this->nom;
}
public function getEmail() {
return $this->email;
}
}
Classe Repositori (exemple: UsuariRepository)
Aquesta classe conté les operacions de base de dades relacionades amb els usuaris.
class UsuariRepository {
private $pdo;
public function __construct($db) {
$this->pdo = $db;
}
public function obtenirTots() {
$stmt = $this->pdo->query("SELECT * FROM usuaris");
$usuaris = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$usuaris[] = new Usuari($row['id'], $row['nom'], $row['email']);
}
return $usuaris;
}
}