Acces a Bases de dades
Accés a BBDD des de PHP
- Per a accedir a BBDD mysql o mariabd tenim dues opcions:
- El driver natiu mysqli o El driver genèric PDO
- En aquest curs utilitzarem PDO, encara que l’ús de mysqli és molt similar al de PDO
- Accions a realitzar:
- Establir connexions
- Executar sentències SQL
- Obtenir els registres afectats o retornats per una sentència SQL.
- Emprar transaccions
- Gestionar els errors que es produïsquen durant la connexió o en l’establiment de la mateixa
Establiment de la connexió
- Haurem de instanciar un objecte de la classe PDO passant-li els següents paràmetres (només el primer és obligatori):
- Origen de dades (DSN). Cadena de text que indica què controlador es va a utilitzar i, a continuació, separades pel caràcter dos punts, els paràmetres específics necessaris pel controlador, com per exemple, el nom o adreça IP del servidor i el nom de la base de dades
- Nom d’usuari amb permisos per a establir la connexió
- Contrasenya de l’usuari
- Opcions de connexió, emmagatzemades en forma de array
Per exemple, si utilitzem el controlador per a Mysql, la cadena DSN rebrà els paràmetres següents (a continuació del prefix mysql:
- host. Nom o adreça IP del servidor
- port. Nombre de port TCP en el qual escolta el servidor
- dbname. Nom de la base de dades
$conn = new PDO ('mysql:host=mysql;port=3306;dbname=juegoRol', 'root', '1234');
Class PDOException
Totes les excepcions que genera PDO són del tipus PDOException que hereta de la classe Exception. Si hagueren errors de connexió, es llançarà una excepció PDOException. Si no capturem l’excepció, es finalitzarà el script i mostrarà informació de seguiment, que podria revelar detalls de la connexió a la base de dades, incloent el nom d’usuari i la contrasenya
Presentació i recuperació de dades
- Si la consulta genera un conjunt de dades (SELECT), s’utilitza el mètode query per executa una sentència SQL. La sentencia retorna false si hi ha errors o un conjunt de resultats com un objecte PDOStatement
<?php
$parametres_connexio = 'mysql:host=localhost;port=3306;dbname=test';
$usuari = 'root';
$passwd = '...';
try {
$bd = new PDO($parametres_connexio,$usuari,$passwd);
echo "Connexio realitzada correctament!!<br/>";
$sql = "SELECT producte, unitats FROM stock";
$productes = $bd->query($sql);
echo $productes->rowCount()." elements<br/>";
echo '<ul>';
//dues maneres
foreach ($productes->fetchAll as $producte){
echo '<li>'.$producte['producte'].' '.$producte['unitats'].'</li>';
}
foreach ($productes->fetchAll(PDO::FETCH_OBJ) as $producte){
echo '<li>'.$producte->producte.' '.$producte->unitats.'</li>';
}
echo '</ul>';
} catch (PDOException $e) {
echo 'Error con la base de datos: ' . $e->getMessage();
}
-
Podem recuperar les dades utilitzan la instrucció fetchAll() utilitzant l’estil que vulgam
-
Treballar amb consultes d’esta manera és vulnerable a injeccions SQL. En el seu lloc es recomana utilitzar consultes preparades.
Inserció, esborrat i modificat
S’utilitza el mètode PDO::exec, que executa una sentència SQL i retorna el nombre de registres afectats
$registres = $pdo->exec('DELETE FROM stock WHERE unitats=0');
echo "\<p>S'han esborrat $registres registres.\</p>";
Aquesta sentència és vulnerable a injeccions SQL. En el seu lloc es recomana utilitzar consultes preparades
<?php
// datos conexión
$cadena_conexion = 'mysql:host=localhost;port=3306;dbname=test';
$usuario = 'root';
$clave = '';
try {
// conectar
$bd = new PDO($cadena_conexion, $usuario, $clave);
echo "Conexión realizada con éxito<br>";
// insertar nuevo usario
$ins = "insert into usuarios(nombre, clave, rol) values('Alberto', '33333', '1');";
$resul = $bd->exec($ins);
//comprobar errores
if($resul) {
echo "insert correcto <br>";
echo "Filas insertadas: " . $resul . "<br>";
}else print_r( $bd->errorinfo());
// para los autoincrementos
echo "Código de la fila insertada" . $bd->lastInsertId() . "<br>";
// actualizar
$upd = "update usuarios set rol = 0 where rol = 1";
$resul = $bd->exec($upd);
//comprobar errores
if($resul){
echo "update correcto <br>";
echo "Filas actualizadas: " . $resul->rowCount() . "<br>";
}else print_r( $bd->errorinfo());
// borrar
$del = "delete from usuarios where nombre = 'Luisa'";
$resul = $bd->exec($del);
//comprobar errores
if($resul){
echo "delete correcto <br>";
echo "Filas borradas: " . $resul->rowCount() . "<br>";
}else print_r( $bd->errorinfo());
} catch (PDOException $e) {
echo 'Error con la base de datos: ' . $e->getMessage();
}
Consultes preparades
Ens aporten dos avantatges importants:
- Per a sentències que seran executades en múltiples ocasions amb diferents paràmetres optimitza el rendiment de l’aplicació.
- Ajuda a prevenir injeccions SQL eliminant la necessitat de entrecomillar manualment els paràmetres.
Utilitzem el mètode prepare de la classe PDO que retorna un objecte de la classe PDOStatement. A més, els paràmetres es poden marcar utilitzant signes d’interrogació o amb nom, utilitzant :.
$pdoSt = $pdo->prepare('INSERT INTO família (cod,nom) VALUES (?, ?)');
$pdoSt = $pdo->prepare('INSERT INTO família (cod,nom) VALUES (:cod,:nom)');
Abans d’executar la consulta, cal assignar un valor als paràmetres.Utilitzem el mètode bindParam de la classe PDOStatement. Si hem utilitzat signe d’interrogació posarem l’índex del paràmetre començant per 1. Si usem paràmetres amb nom, indiquem el nom en la cridada a bindParam.
$cod_producte = " TABLET";
$nom_producte = " Tablet PC";
$pdoSt->bindParam(1,$cod_producte);
$pdoSt->bindParam(2,$nom_producte);
$pdoSt->bindParam(":cod",$cod_producte);
$pdoSt->bindParam(":nom",$nom_producte);
Una vegada preparada la consulta i enllaçats els paràmetres amb els seus valors, s’executa la consulta utilitzant el mètode execute de la classe PDOStatement. També, és possible assignar els valors dels paràmetres en el moment d’executar la consulta, utilitzant un array (associatiu o amb claus numèriques depenent de la forma en què hages indicat els paràmetres) en la cridada a execute
<?php
$cadena_conexion = 'mysql:host=localhost;port=3306;dbname=test';
$usuario = 'root';
$clave = '';
try {
$bd = new PDO($cadena_conexion, $usuario, $clave);
echo "Conexión realizada con éxito<br>";
$sql = 'SELECT nombre, clave, rol FROM usuarios';
$usuarios = $bd->query($sql);
echo "Número de usuarios: " . $usuarios->rowCount() . "<br>";
foreach ($usuarios->fetchAll(PDO::FETCH_OBJ) as $usu) {
print "Nombre : " . $usu->nombre;
print "Clave : " . $usu->clave . "<br>";
}
/* consulta preparada, parametros por orden */
$preparada = $bd->prepare("select nombre from usuarios where rol = ?");
$preparada->execute( array(0));
echo "Usuarios con rol 0: " . $preparada->rowCount() . "<br>";
foreach ($preparada->fetchAll(PDO::FETCH_OBJ) as $usu) {
print "Nombre : " . $usu->nombre . "<br>";
}
/* consulta preparada, parametros por nombre */
$preparada_nombre = $bd->prepare("select nombre from usuarios where rol = :rol");
$preparada_nombre->execute( array(':rol' => 0));
echo "Usuarios con rol 0: " . $preparada->rowCount() . "<br>";
foreach ($preparada_nombre->fetchAll(PDO::FETCH_OBJ) as $usu) {
print "Nombre : " . $usu->nombre . "<br>";
}
} catch (PDOException $e) {
echo 'Error con la base de datos: ' . $e->getMessage();
}
QueryBuilder
Un Query Builder conté una sèrie de funcions llestes per a realitzar les operacions més comunes amb una base de dades, però sense usar el llenguatge SQL directament. Podem instal·lar alguno com Eloquent o el de Doctrine. En esta sèrie de videos es mostra com anar implementant un:
Mètode find :
Video
Utilitzant el mètode find dins d’una classe per a implementar un modelo de dades:
Video
Mètode insert:
Video
Transaccions
Una transacció consistix en un conjunt d’operacions que han de realitzar-se de forma atòmica. Es a dir, o es realitzen totes o cap.
Per defecte PDO treballa en manera autocommit, així esconfirma de forma automàtica cada sentència que executa el servidor.
Per a treballar amb transaccions, PDO incorpora tres mètodes:
- beginTransaction. Deshabilita la manera autocommit i comença una nova transacció, que finalitzarà quan executes un dels dos mètodes següents.
- commit. Confirma la transacció actual
- rollback. Reverteix els canvis duts a terme en la transacció actual
Una vegada executat un commit o un rollback, es tornarà a la manera de confirmació automàtica
<?php
$cadena_conexion = 'mysql:host=localhost;port=3306;dbname=test';
$usuario = 'homestead';
$clave = 'secret';
try {
$bd = new PDO($cadena_conexion, $usuario, $clave);
echo "Conexión realizada con éxito<br>";
// comenzar la transacción
$bd->beginTransaction();
$ins = "insert into usuarios(nombre, clave, rol) values('Fernando', '33333', '1')";
$resul = $bd->exec($ins);
// se repite la consulta
// falla porque el nombre es unique
$resul = $bd->exec($ins);
if(!$resul){
echo "Error: " . print_r($bd->errorinfo());
// deshace el primer cambio
$bd->rollback();
echo "<br>Transacción anulada<br>";
}else{
// si hubiera ido bien...
$bd->commit();
}
} catch (PDOException $e) {
echo 'Error al conectar: ' . $e->getMessage();
}