Diseño de Aplicaciones Web - UPMlaurel.datsi.fi.upm.es/_media/docencia/asignaturas/daw/... ·...
Transcript of Diseño de Aplicaciones Web - UPMlaurel.datsi.fi.upm.es/_media/docencia/asignaturas/daw/... ·...
Diseño de Aplicaciones Web
PHP
Gestión de Datos
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Almacenamiento de Datos en PHP
• Alternativas de almacenamiento en PHP:– Almacenamiento contra el API de MySQL.
– API extendida de MySQL.
– Objetos de datos: PDO (PHP Data Objects))
– Serialización y almacenamiento de objetos.
– Proyección de datos relacionales en objetos: ORM (Object-Relational
Mapping)• Doctrine
• Propel
• Xyster
DAW 2
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
MySQL en PHP
API de MySQL y MySQLi
DAW 3
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Establecimiento de conexión:
– El resultado de la llamada es un recurso (manejador al enlace
establecido) de la conexión con la BD.
– Dicho valor se usa como parámetro en las posteriores llamadas.
DAW 4
resource mysql_connect (
[ string $server = ini_get("mysql.default_host")
[, string $username = ini_get("mysql.default_user")
[, string $password = ini_get("mysql.default_password")
[, bool $new_link = false
[, int $client_flags = 0 ]]]]] )
$handler= mysql_connect('localhost', ‘chema', ‘patata');
if (!$ handler) {
die(‘Error de conexión: ' . mysql_error());
}
echo ‘Conexión establecida';
La referencia al servidor se
realiza con el formato:
servidor:puerto
Por ejemplo:
laurel.datsi.fi.upm.es:4577
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Selección de la base de datos:
– Utilizando el manejador al enlace se selecciona la BD a utilizar.
– Esta operación es análoga al mandado de MySQL:
DAW 5
bool mysql_select_db (string $database_name [, resource $link_identifier = NULL ] )
$handler= mysql_connect('localhost', ‘chema', ‘patata');
if (!$ handler) {
die(‘Error de conexión: ' . mysql_error());
}
echo ‘Conexión establecida';
$bd_ok = mysql_select_db('foo', $handler);
if (!$bd_ok) {
die ('No se puede accede a foo : ' . mysql_error());
}
USE base_de_datos;
El utilizar o no una base de datos no es
excluyente, en MySQL es posible
acceder tablas de otras bases de datos
utilizando la sintaxis:
bd.tabla
Ejemplo:
SELECT * FROM stock.productos;
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Consultas SQL:
Operaciones CRUD (Create, Read, Update and Delete):– Operaciones que devuelve listas de registos (típicamente SELECT,
SHOW, DESCRIBE, EXPLAIN): El valor devuelto es otro recurso
(manejador del cursor de la consulta) al que se le puede consultar
por el número de registros devueltos:
– Operaciones que modifican registros (típicamente INSERT, UPDATE,
DELETE, DROP): No se devuelve un recurso, pero se puede obtener
el número de registros afectados:
DAW 6
mixed mysql_query ( string $query [, resource $link_identifier = NULL ] )
int mysql_num_rows ( resource $result )
int mysql_affected_rows ([ resource $link_identifier = NULL ] )
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Recuperación de registros de un SELECT (modo cursor):
– Esta llamada devuelve un array con el siguiente registro del
manejador de cursos proporcionado o falso si no hay más registros.
– El tipo de resultado indica si el array producido está indexado por
números o por nombres de columnas (o por ambos).
DAW 7
array mysql_fetch_array ( resource $result [, int $result_type = MYSQL_BOTH ] )
mysql_connect('localhost', 'chema', 'patata');
mysql_select_db('foo');
$resultado = mysql_query('SELECT id, nombre FROM tabla');
while ($fila = mysql_fetch_array($resultado, MYSQL_BOTH)) {
printf('ID: %s Nombre: %s', $fila['id'], $fila['nombre']);
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Recuperación de registros de un SELECT (modo cursor con
objetos):
– La operativa es idéntica al caso anterior pero el elemento devuelto no
es un array sino que es un objeto con los atributos de los campos del
registro.
DAW 8
object mysql_fetch_object ( resource $result [, string $class_name [, array $params ]] )
mysql_connect('localhost', 'chema', 'patata');
mysql_select_db('foo');
$resultado = mysql_query('SELECT id, nombre FROM tabla');
while ($fila = mysql_fetch_object($resultado)) {
printf('ID: %s Nombre: %s', $fila->id, $fila->nombre);
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Lectura de un resultado de un SELECT (modo indexado):
– Esta alternativa permite acceder a los resultados indexando número
de fila y el campo.
– El indicador del campo puede ser tanto el índice, como el nombre del
mismo (incluyendo el formato “tabla.campo”).
DAW 9
string mysql_result ( resource $result , int $row [, mixed $field = 0 ] )
mysql_connect('localhost', 'chema', 'patata');
mysql_select_db('foo');
$resultado = mysql_query('SELECT id, nombre FROM tabla');
for ($f = 0; $f < mysql_num_rows($resultado); $f++) {
printf('ID: %s Nombre: %s',
mysql_result($resultado, $f, 'id'),
mysql_result($resultado, $f, 'nombre'));
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Sentencias de modificación de registros:– Vale con verificar si el resultado de ejecutar la consulta ha sido
verdadero o no.
DAW 10
mysql_connect('localhost', 'chema', 'patata');
mysql_select_db('foo');
$sql = "INSERT INTO agenda (nombre, direccion, telefono, email) ".
"VALUES ('$nombre', '$direccion', '$telefono', '$email')";
$resultado = mysql_query($sql);
if (!$resultado) {
print('Error en INSERT: '. mysql_error());
die();
}
$sql = "UPDATE agenda SET nombre='$nombre', direccion='$direccion',".
"telefono='$telefono', email='$email'";
$resultado = mysql_query($sql);
if(!$resultado) {
print('Error en UPDATE: '. mysql_error());
die();
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Otras funciones de utilidad:– Modificación de cadenas con secuencias de escape:
– Gestión de errores:
– Obtención de identificador generado automáticamente:
– Obtener el número de campos de una consulta SELECT:
– Borrado de una base de datos
• Equivale al mandato SQL:DAW 11
string mysql_real_escape_string ( string $unescaped_string
[, resource $link_identifier = NULL ] )
int mysql_errno ([ resource $link_identifier = NULL ] )
string mysql_error ([ resource $link_identifier = NULL ] )
int mysql_insert_id ([ resource $link_identifier = NULL ] )
int mysql_num_fields ( resource $result )
bool mysql_drop_db ( string $database_name [, resource $link_identifier = NULL ] )
DROP DATABASE base_de_datos;
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Gestión de transacciones:– No existen llamadas al API específicas para ello.
– Se realizan consultas SQL estándar usando la función mysql_query().
DAW 12
mysql_query("START TRANSACTION");
mysql_query("BEGIN");
$resultado = mysql_query("DELETE FROM facturas WHERE cliente_id='$id'");
if (!$resultado) {
mysql_query(“ROLLBACK");
die('Error al borrar FACTURAS: '. mysql_error());
}
$resultado = mysql_query("DELETE FROM clientes WHERE id='$id'");
if (!$resultado) {
mysql_query(“ROLLBACK");
die('Error al borrar CLIENTES: '. mysql_error());
}
mysql_query("COMMIT");
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
API de MySQL en PHP
Liberación de recursos:– Cerrado de la conexión (opuesto a mysql_connect()):
– Liberar la memoria de un cursos de consulta (el resultado de un
mysql_query() de tipo SELECT):
Conexiones persistentes:
– No se cierran cuando el script termina y se intentan reutilizar en otras
llamadas del mismo tipo.
DAW 13
bool mysql_close ([ resource $link_identifier = NULL ] )
bool mysql_free_result ( resource $result )
resource mysql_pconnect (
[ string $server = ini_get("mysql.default_host")
[, string $username = ini_get("mysql.default_user")
[, string $password = ini_get("mysql.default_password")
[, int $client_flags = 0 ]]]]] )
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Limitaciones API Nativa de MySQL
• Resulta necesario realizar las consultas “picando” SQL.
• Además: – Es necesario establecer la protección ante valores de campos que
puedan ser peligrosos:• Usando mysql_real_escape_string().
– A la hora de insertar registros con campos de autoincremento se
necesita consultar y gestionar los identificadores:• Usando mysql_insert_id().
• Y, sobre todo, es procedimental no Orientado a Objetos.
DAW 14
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Evolución de las APIs de MySQL
DAW 15
PHP 4 PHP 5
PHP 5.5.0API (procedimental) de MySQL:
• Funciones mysql_…()
API (orientada a objetos) de MySQL:
• Objeto mysqli
mysqli {
…
__construct ([ string $host = ini_get("mysqli.default_host")
[, string $username = ini_get("mysqli.default_user")
[,string $passwd = ini_get("mysqli.default_pw")
[, string $dbname = ""
[, int $port = ini_get("mysqli.default_port")
[, string $socket = ini_get("mysqli.default_socket") ]]]]]] )
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
MySQLi
DAW 16
$db = new mysqli('localhost', 'chema', 'patata', 'foo');
if ($db->connect_errno > 0){
die('Error de conexión: ' . $db->connect_error );
}
if (!$resultado = $db->query('SELECT id, nombre FROM tabla')){
die('Error en la consulta: ' . $db->error);
}
while ($fila = $resultado->fetch_array(MYSQLI_NUM)) {
printf('ID: %s Nombre: %s', $fila['id'], $fila['nombre']);
}
printf('Se han obtenido %s filas', $resultado->num_rows);
if (!$db->query('DELETE FROM tabla WHERE edad<18')){
die('Error en la consulta: ' . $db->error);
}
printf('Se han borrado %s filas', $db->affected_rows);
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Sentencias Preparadas en MySQLi
Permite tener mandatos y consultas SQL preprocesadas:
– Estas sentencias pueden estar parametrizadas:
– Para recoger los resultados también se enlazan variables:
DAW 17
$db = new mysqli('localhost', 'chema', 'patata', 'foo');
$sentencia = $db->prepare('SELECT id, nombre FROM tabla');
$sentencia->execute();
$sentencia = $db->prepare('SELECT id, nombre FROM tabla WHERE nombre=? ');
$nombre='Efigenia';
$sentencia->bind_param('s', $nombre);
$sentencia->bind_result($res_id, $res_nombre);
while($sentencia->fetch()) {
printf('ID: %s Nombre: %s', $resid, $resnombre);
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Limitaciones API de MySQLi
• En esencia es un recubrimiento orientado a objetos del API nativa.
• Se sigue teniendo que escribir SQL.
• Al igual que el API nativa de MySQL es completamente dependiente de
la tecnología de BD (MySQL).
DAW 18
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Objetos de Datos PHP
Generalización de Acceso a Bases de Datos y
PDO de PHP
DAW 19
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Interfaz PDO
• Objetivo: Independencia de la tecnología de base de datos.
• PDO (PHP Data Objects) es una extensión al estilo de otras
capas intermedias de estándares de acceso a bases de datos
(e.g., ODBC o JDBC).
DAW 20
PDO {
…
__construct ( string $dsn
[, string $username
[, string $password
[, array $options ]]] )
$db = new PDO("pgsql:host=localhost;dbname=foo", “chema", “patata" );
• El soporte de tecnologías de
bases de datos concretas se
realizan por medio de drivers.
• El driver y los parámetros de
configuración se proporcionan en
la creación del objeto PDO.
PostgreSQL:
MySQL:
SQLite:
$db = new PDO("mysql:host=localhost;dbname=foo", “chema", “patata" );
$db = new PDO("sqlite:/ruta/a/la/base_de_datos.sdb");
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Interfaz PDO
• Ejecución de consultas que devuelven registros:
• Ejecución de consultas que modifican registros:
DAW 21
try {
$db = new PDO("mysql:host=localhost;dbname=foo", “chema", “patata" );
foreach ($db->query('SELECT id, nombre FROM tabla') as $fila) {
printf('ID: %s Nombre: %s', $fila['id'], $fila['nombre']);
}
$cuantas = $db->exec('DELETE FROM tabla WHERE edad<18');
printf('Se han borrado %s filas', $cuantas);
}
catch (PDOException $e) {
echo $e->getMessage();
}
public PDOStatement PDO::query ( string $statement )
public int PDO::exec ( string $statement )
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Interfaz PDO
• Recuperación de datos con fetch():
– Como un array: nombre o índice
– Como un objeto (anónimo):
DAW 22
public mixed PDOStatement::fetch (
[ int $fetch_style
[, int $cursor_orientation = PDO::FETCH_ORI_NEXT
[, int$cursor_offset = 0 ]]] )
$array = $sentencia->fetch(PDO::FETCH_ASSOC);
printf('ID: %s Nombre: %s', $array['id'], $array['nombre']);
$sentencia = $db->query('SELECT id, nombre FROM tabla');
$objeto = $sentencia->fetch(PDO::FETCH_OBJ);
printf('ID: %s Nombre: %s', $objeto->id, $objeto->nombre);
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Interfaz PDO
– Como un objeto
de una clase
definida:
– Con un cursor:
DAW 23
class Persona {
public $id;
public $nombre;
public function nombre_mayusculas() {
return ucwords($this->nombre);
}
}
try {
$db = new PDO("mysql:host=localhost;dbname=foo", “chema", "patata" );
$sql = "SELECT id, nombre FROM tabla";
$sentencia = $db->query($sql);
$pepe = $sentencia->fetch(PDO::FETCH_CLASS, 'Persona');
echo $pepe->nombre_mayusculas();
$cursor = $sentencia->fetchALL(PDO::FETCH_CLASS, 'Persona');
foreach($cursor as $persona)
echo $persona->nombre_mayusculas();
}
catch(PDOException $e) {
echo $e->getMessage();
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Tecnologías Soportadas por PDO
Las tecnologías de bases de datos soportadas por PDO:– CUBRID: Cubrid databases.
– DBLIB: FreeTDS / Microsoft SQL Server / Sybase
– Firebird (http://firebird.sourceforge.net/): Firebird/Interbase 6
– IBM (IBM DB2)
– INFORMIX - IBM Informix Dynamic Server
– MYSQL (http://www.mysql.com/): MySQL 3.x/4.x/5.x
– OCI (http://www.oracle.com): Oracle Call Interface
– ODBC: ODBC v3 (IBM DB2, unixODBC y win32 ODBC)
– PGSQL (http://www.postgresql.org/): PostgreSQL
– SQLITE (http://sqlite.org/): SQLite 2.x/3.x
– SQLSRV (http://msdn.microsoft.com/): Microsoft SQL Server / SQL
Azure
– 4D (http://www.4d.com/): 4th Dimension.DAW 24
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Limitaciones PDO
• No quiero escribir más SQL!!!!!
DAW 25
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Necesidades de Almacenamiento de
Objetos
Limitaciones de Soluciones Relacionales y
Serialización de Objetos en PHP
DAW 26
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Gestión de Datos en Aplicaciones Web
• Necesidades de almacenamientos de datos:– Información estructurada
– Documentos (ficheros)
– Documentos (objetos)
• Desde el punto de vista de funcionalidad lo que necesita el
nivel de aplicación es que– Todo objeto de datos tenga su respaldo y
– Que el acceso al respaldo (grabar y recuperar) sea eficiente.
DAW 27
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Limitaciones del Modelo Relacional
• Las BD almacenan la información en filas y columnas.
• El desarrollo de las aplicaciones e hace sobre estructuras de
datos y objetos.– Se puede simular las referencias entre objetos y las colecciones de
datos por medio de relaciones entre filas de las tablas.
– Las restricciones que aseguran unicidad de identificadores o campos
son difíciles de asegurar e implementar en los objetos.
– Conceptos de campos dinámicos, algunas funcionalidades de las
herencias y ciertas colecciones de objetos se transcriben mal en el
modelo relacional.
DAW 28
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Almacenamiento de Objetos
• La orientación a objetos en su aplicación al almacenamientos
de datos puede ser:– Que las estructuras de datos a nivel aplicación sean objetos y se
almacenen como tales.
– Que las estructuras de datos a nivel aplicación sean objetos y se
almacenen como tablas (relacionales) de datos.
DAW 29
Aplicación OBJ OBJ
Objeto
Registro de una tabla
Objeto
Sistema de
Ficheros NoSQL
Fichero Documento
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Almacenamiento de Objetos
• Que las estructuras de datos a nivel aplicación sean objetos y
se almacenen como tales:– Serialización de objetos.
– E.g., JSON o XML:
DAW 30
{"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
]}
<employees>
<employee>
<firstName>John</firstName> <lastName>Doe</lastName>
</employee>
<employee>
<firstName>Anna</firstName> <lastName>Smith</lastName>
</employee>
<employee>
<firstName>Peter</firstName> <lastName>Jones</lastName>
</employee>
</employees>
...
var txt = JSON.stringify(employeeList, replacer);
...
var obj = JSON.parse(txt, reviver);
...
XML
JSON
JavaScript
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Serialización en PHP
Se apoya en dos métodos:
El métodos serialize() convierte un objeto en una cadena de caracteres y unserialize()
convierte una cadena de caracteres en un objeto.
Adicionalmente hay dos funciones a implementar en una clase serializable:
Que devuelve un array de los strings de los campos a serializar. Dicha función se invoca
inmediatamente antes de serializar.
Que se ejecuta inmediatamente después de deserializar el objeto.
La serialización es con un formato propio de PHP.
DAW 31
string serialize ( mixed $value )
mixed unserialize ( string $str )
public array __sleep ( void )
void __wakeup ( void )
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Serialización en PHP
DAW 32
class Empleado {
public $nombre;
public $apellido;
public $edad;
private $sueldo;
function __construct ($n, $a, $e, $s) {
$this->nombre = $n;
$this->apellido = $a;
$this->edad = $e;
$this->sueldo = $s;
}
}
$pepe = new Empleado("Pepe", "Potamo", 32, 1900);
var_dump($pepe);
$str=serialize($pepe);
print $str;
$copia = unserialize($str);
var_dump($copia);
object(Empleado)[1]
public 'nombre' => string 'Pepe' (length=4)public 'apellido' => string 'Potamo' (length=6)public 'edad' => int 32
private 'sueldo' => int 1900
O:8:"Empleado":4:{s:6:"nombre";s:4:"Pepe";s:8:"
apellido";s:6:"Potamo";s:4:"edad";i:32;s:16:"Em
pleadosueldo";i:1900;}
object(Empleado)[2]
public 'nombre' => string 'Pepe' (length=4)public 'apellido' => string 'Potamo' (length=6)public 'edad' => int 32
private 'sueldo' => int 1900
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Serialización en PHP
DAW 33
class Empleado {
public $nombre;
public $apellido;
public $edad;
private $clave;
function __construct ($n, $a, $e, $c) {
$this->nombre = $n;
$this->apellido = $a;
$this->edad = $e;
$this->clave = $c;
}
function __sleep() {
$this->clave=str_rot13($this->clave);
return array('nombre', 'apellido', 'edad', 'clave');
}
function __wakeup() {
$this->clave=str_rot13($this->clave);
}
}
object(Empleado)[1]
public 'nombre' => string 'Pepe' (length=4)public 'apellido' => string 'Potamo' (length=6)public 'edad' => int 32
private ' clave' => string ‘patata' (length=6)
O:8:"Empleado":4:{s:6:"nombre";s:4:"Pepe";s:8:"
apellido";s:6:"Potamo";s:4:"edad";i:32;s:16:"Em
pleadoclave";s:6:"cngngn";}
…
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Proyección Objeto-Relacional
ORM General y Versiones de Doctrine
DAW 34
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Proyección Objeto-Relacional
• En inglés “Object-Relational Mapping” (ORM)
• Propósito:– Guardar un objeto como una fila en una tabla de una BD.
– Recuperar datos de las tablas y convertirlos en objetos.
– Guardar y recrear asociaciones entre objetos.
• Objetivos del diseño:– Separar los servicios de mapping entre objetos y relaciones del resto
del programa.
– Minimizar el impacto de cambiar la tecnología (y modelo) de la BD.
DAW 35
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Ejemplo
DAW 36
Curso
nombre: String
horario: Date
créditos: int
Aula
número: int
bloque: int
plazas: int
Profesor
nombre: String
despacho: String
Alumno
nombre: String
matrícula: int
aula
1
curso
1
profesores
*
* cursos
* alumnosAdicionalmente, se puede
asumir que todas las clases
disponen de un atributo id()
que es único para todos los
objetos de esa clase (una
clave).
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Object-Relational Mapping
• Clase:– Debe tener un atributo que actúe
como identificador (id).
• Data mapper:– Convierte:
• Clase Tabla
• Objeto Fila
– Adapta los tipos de datos.
– Instancia los objetos.
• Tabla:– El atributo identificador es
típicamente la clave primaria.
DAW 37
AULAS
id VARCHAR(12)
número INTEGER
bloque INTEGER
plazas INTEGER
PK
Aula
número: int
bloque: int
plazas: int
ORM
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Object-Relational Mapping
DAW 38
(Aula) aqui
número=5101
bloque=5
plazas=125
AULAS
número
3201
5101
5102
…
id
C01
E02
E03
…
bloque
3
5
5
plazas
145
125
117
AlmacenadoRecuperación
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Object-Relational Mapping
• Funcionalidades de almacenamiento:– Almacenamiento explícito:
• Se indica explícitamente cuándo y qué almacenar.
• Llamada de tipo save().
– Almacenamiento de modificaciones realizadas:• Todo un conjunto de modificaciones se mandan a almacenar.
• Se realizan mediante llamada flush().
– Persistencia transparente:• Un objeto se declara como persistente.
• Cada cambio de uno de sus atributos se sincroniza con el soporte de
almacenamiento.
• Comienza en el momento en el que el elemento se registra en el gestor
correspondiente (register()).
• Es necesario capturar los métodos que modifican atributos.
DAW 39
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Active Record vs. Data Mapper
• Dos patrones de implementación de un ORM:
DAW 40
ACTIVE RECORD• Los objetos derivan de una clase de la
biblioteca de ORM de donde hereda
métodos explícitos de
almacenado/recuperación.
• El diseño de los objetos establece qué
atributos son almacenables y aspectos
tales como el tipo de datos y nombre de
la columna de la tabla.
• Se pueden construir los objetos del
código a la BD o viceversa.
Ejemplos: Doctrine 1.2 (PHP), Ruby on
Rails o Laravel
DATA MAPPER• Los objetos no heredan de nada, son
objetos “simples” del lenguaje.
• Los objetos están desacoplado de cómo
almacenarlos (los métodos que
implementan eso los mantiene un Entity
Manager).
• La interacción entre este elemento y la
BD es más estricta.
• Se puede ver como un DAL (Data
Access Layer) sofisticado que se las
entiende con objetos
Ejemplos: Doctrine 2+ (PHP), Boockself.js
o ROM
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Active Record vs. Data Mapper
• Ventajas de Active Record:– El código puede invocar expresamente al almacenamiento,
actualización y recuperación de datos donde resulte más adecuado
(es más flexible).
– Más habituales y generalizadas y de utilidad en prototipado rápido.
• Ventajas de Data Mapper:– Los objetos son más ligeros (no mantienen la implementación de los
métodos de almacenamiento).
– Al estar desacoplados los objetos de negocio del almacenamiento es
más fácil ser estricto con MVC.
– Se puede ver como un DAL (Data Access Layer) sofisticado que se
las entiende con objetos.
– Facilita la persistencia transparente.
DAW 41
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Active Record vs. Data Mapper
DAW 42
$alumna = new Alumno("j070212", "Elena", "Nito del Bosque");
$alumna->creditos_aprobados = 207;
$alumna->save();
…
$alumna->save();
…
EntityManager::persist($alumna);
Active Record Data Mapper
Implementaciones de ORM en PHP
Doctrine 1.2 Doctrine 2
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Identificadores en un ORM
• La identificación (identidad) de un objeto se implementa por
medio de dos mecanismos:– Está alojado en la misma ubicación (o son la misma referencia),
operador A==B (igual).
– Existe un método de comparación equals() o compareWith().
• En una BD la comparación está asociada a tener la misma
clave primaria.
• La comparación y la identidad de elementos está en la base
de la gestión de caches, de modificaciones y de transacciones.
DAW 43
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Relaciones entre Objetos
DAW 44
CURSOS
id VARCHAR(8)
nombre VARCHAR(64)
horario DATETIME
créditos INTEGER
PK
ORM
Curso
nombre: String
horario: Date
créditos: int
Profesor
nombre: String
despacho: String
curso
1
profesores
*
PROFESORES
id VARCHAR(12)
nombre VARCHAR(64)
despacho VARCHAR(16)
id_curso VARCHAR(8)
PK
FK
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Relaciones entre Objetos
• En el momento que los objetos están relacionados el
almacenamiento tiene que ser consistente con ello:– Si se almacena un objeto y hay modificaciones en objetos
relacionados con él se deben de guardar todas las modificaciones del
grafo.
– Si se da las persistencia transparente esto debe de hacerse de forma
automática.
DAW 45
creado
gestionado
desacoplado
eliminado
new …
Registrar
Combinar
Buscar
Desregistrar
Registrar
Liberar
basura
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Flujos de Desarrollo con ORMs
DAW 46
Code First: Se
comienza por el
desarrollo de las clase
y luego se proyectan
en BD
Database First: Se
comienza por el modelo
de datos implementado
en la BD y de él se
extraen las clases.
Variante Model First: Cuando se comienzo con una herramienta
de modelado (tipo UML) de la estructura de clases.
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Problemas con Database First
Este flujo se apoya en una herramienta que analiza la estructura
de la base de datos y general el código de las clases que la
implementan en el modelo de objetos.
DAW 47
Problemas:
• ¿Qué ocurre si sobre el código generado yo lo
modifico y luego vuelvo a solicitar que se re-genere
desde la base de datos?
• Se puede solucionar etiquetando las partes de
código generadas manual o automáticamente.
• Derivar una subclase de la clase generada
automáticamente.
Persona
Personas
DatoPersona
mi_método()
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine
• Implementación de un ORM para PHP.– Versión Doctrine 1.2 basada en el patrón de implementación de
Active Record.
DAW 48
© Wikipedia
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 1.2
• Las clases de objetos almacenables en BD deben extender la
clase Doctrine_Record:– Si se implementa como flujo Code First, se debe implementar el
método setTableDefinition():
– Utilización:
DAW 49
class Alumno extends Doctrine_Record {
public function setTableDefinition() {
$this->setTableName('Alumnos');
$this->hasColumn('nombre', 'string', 255, array('type' => 'string', 'length' => '255'));
$this->hasColumn('matricula', 'integer', 8, array('unsigned' => true));
}
}
CREATE TABLE Alumnos ( nombre VARCHAR(255), matricula INT(8) UNSIGNED);
$elena = new Alumno();
$elena->nombre ="Elena Nito del Bosque";
$elena->matricula = 70212;
$elena->save();
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 1.2
• Las relaciones entre tablas se diseñan como relaciones entro
clases:
DAW 50
class Alumno extends Doctrine_Record {
public function setTableDefinition() {
<…>
}
public function setUp() {
$this->hasMany('Notas as Notas', array('refClass' => 'Nota', 'local' => 'id', 'foreign' => 'alumno_id'));
}
}
class Nota extends Doctrine_Record {
public function setTableDefinition() {
$this->setTableName('Notas');
$this->hasColumn('asignatura', 'string', 255, array('type' => 'string', 'length' => '255'));
$this->hasColumn('alumno_id', 'integer', 8, array('unsigned' => true));
$this->hasColumn(‘calificacion', 'integer', 2, array('unsigned' => true));
}
public function setUp() {
$this->hasOne('Alumno', array('refClass' => 'Alumno', 'local' => 'alumno_id', 'foreign' => 'matricula'));
}
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 1.2
DAW 51
$elena = new Alumno();
$elena->nombre ="Elena Nito del Bosque";
$elena->matricula = 70212;
$nota = new Nota();
$nota->asignatura = "Diseño de Aplicaciones Web";
$nota->calificacion = 9;
$elena->Notas[] = $nota;
$nota->Alumno = $elena;
$elana->save();
Alumnos
Notas
tienen
ALUMNOS
Matrícula Nombre
70212 Elena Nito del BosqueNOTAS
Matrícula Asignatura Calificación
70212 Diseño de Aplicaciones Web 9
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Alternativas de Flujos de Generación
• Alternativa:– Declaración de estructura vía YAML:
• Fichero: alumno.yml
DAW 52
Alumno:
columns:
nombre: string (255)
matricula: integer (8)
Ficheros
YAML
Base de
Datos CódigoDoctrine_Core::generateTablesFromModels
Doctrine_Core::generateModelsFromDb
Doctrine_Core::generateSqlFromModels
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Alternativas de Flujos de Generación
• Model first:
• Database first:
DAW 53
echo 'Generando código... ';
Doctrine_Core::generateModelsFromYaml(‘estructura.yml', '/Modelos');
echo “hecho!\n";
echo 'Reconstruyendo tablas de la base de datos... ';
Doctrine_Core::dropDatabases();
Doctrine_Core::createDatabases();
Doctrine_Core::createTablesFromModels('/Modelos');
echo “hecho!\n";
echo 'Generando descripción... ';
Doctrine_Core::generateYamlFromDb( ‘estructura.yml', $conexion, $opciones );
echo “hecho!\n";
echo 'Generando código... ';
Doctrine_Core::generateModelsFromDb( '/Modelos', $conexion, $opciones );
echo “hecho!\n";
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Limitaciones de Doctrine 1.2
• La definición de las tablas se hace desde la definición de las
clase de forma programática.
• Si se hace desde la base de datos, la semántica de las
relaciones hay que hacerla a mano.
• Hay mucho código oscuro de PHP por debajo.
• Todos los objetos de datos deben derivar de la clase
Doctrine_Record.
DAW 54
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Versión 2 basada en el patrón de implementación Data
Mapper. – Inspirado en el diseño de la Java Persistence API (JPA).
– El diseño separa el soporte de persistencia de la lógica de dominio:• Eso permite almacenar los objetos de datos no sólo en SQL sino
también en NoSQL, XML, …
– Mejora en rendimiento sobre Doctrine 1.2 (x3).
– Los objetos de datos no tiene que derivar de ninguna clase del
soporte de almacenamiento.
– La selección de objetos y campos a almacenar se hace por medio de
anotaciones de mapeado.
– Soportado desde PHP 5.3 e integrado en Symfony.
DAW 55
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Creación de la clase:
Métodos (set/get):
DAW 56
namespace Entity;
class Alumno {
/** @var int */
private $matricula;
/** @var string */
private $nombre;
}
/**
* @return int
*/
public function getMatricula() {
return $this->matricula;
}
/**
* @param int $matricula
*/
public function setMatricula($matricula) {
$this->matricula = (int) $matricula;
}
/**
* @return string
*/
public function getNombre() {
return $this->nombre;
}
/**
* @param string $nombre
*/
public function setNombre($nombre) {
$this->nombre = (string) $nombre;
}
/** Constructor
* @param int $matricula
*/
public function __construct($matricula) {
$this->setMatricula($matricula);
}
Constructor
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Anotaciones @ORM para
el mapeo de la clase:– Identificar lo que va a ser
una entidad (tabla).
– Identificar los campos y sus
tipos.
– Identificar la clave.
DAW 57
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Alumno {
/**
* @ORM\Id()
* @ORM\Column(type="integer")
* @var int
*/
private $matricula;
/**
* @ORM\Column(type="string", length=255)
* @var string
*/
private $nombre;
// El resto
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Entity Manager (fichero config.php):– Es el componente encargado de almacenar/recuperar los datos entre
el objeto de datos (la clase) y el soporte de persistencia.
– En PHP es necesario activar el soporte de autocargado que permite
que el intérprete de PHP, si encuentra una referencia a una clase y
no está declarada explícitamente el use correspondiente, la pueda
carga de forma dinámica:
DAW 58
use Doctrine\Common\ClassLoader;
//Autocargado
require_once __DIR__ . '/version/autoload.php';
$cargador1 = new ClassLoader('Entity', __DIR__ . '/Librerias');
$cargador1->register();
$cargador2 = new ClassLoader('EntityProxy', __DIR__ . '/Librerias');
$ cargador2 ->register();
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Entity Manager (fichero config.php, cont.):– Configuración y metadatos:
DAW 59
use Doctrine\ORM\Configuration,
Doctrine\Common\Cache\ArrayCache,
Doctrine\Common\Annotations\AnnotationRegistry,
Doctrine\Common\Annotations\AnnotationReader,
Doctrine\ORM\Mapping\Driver\AnnotationDriver;
//Configuración
$config = new Configuration();
$cache = new ArrayCache();
$config->setQueryCacheImpl($cache);
$config->setProxyDir(__DIR__ . '/Librerias/EntityProxy');
$config->setProxyNamespace('EntityProxy');
$config->setAutoGenerateProxyClasses(true);
//mapping (example uses annotations, could be any of XML/YAML or plain PHP)
AnnotationRegistry::registerFile(__DIR__ . '/DoctrineDriver/DoctrineAnnotations.php');
$driver = new AnnotationDriver(new AnnotationReader(), array(__DIR__ . '/Librerias/Entity') );
$config->setMetadataDriverImpl($driver);
$config->setMetadataCacheImpl($cache);
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Entity Manager (fichero config.php, cont.):– Creación del EntityManager:
• Aquí se le pasan los parámetros (al estilo PDO) de cuál es la BD que
actuará como repositorio.
DAW 60
use Doctrine\ORM\ EntityManager;
//Obtener el EntityManager
$em = EntityManager::create(
array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite‘
),
$config);
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Creación de la base de datos desde el modelo:– Para ello se crea un pequeño ejecutable (crear.php) que usará el
fichero anterior (config.php):
– Que se lanzará como aplicación de consola:
DAW 61
//crear.php
use Symfony\Component\Console\Helper\HelperSet,
Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper,
Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper,
Doctrine\ORM\Tools\Console\ConsoleRunner;
require_once __DIR__ . '/config.php';
$helperSet = new HelperSet(array(
'em' => new EntityManagerHelper($em),
'conn' => new ConnectionHelper($em->getConnection()) ));
ConsoleRunner::run($helperSet);
$ php crear.php orm:schema-tool:create
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Ejemplo de uso (alta):
DAW 62
use Entity\Alumno;
require_once __DIR__ . '/config.php';
//Creamos un nuevo alumno
$elena = new Alumno(70212);
$elena->setNombre("Elena Nito del Bosque");
//Registramos a $elena en el EntityManager
$em->persist($elena);
//Cambiamos de nombre a $elena
$elena->setNombre("Elena Nito del Prado");
//Sincronizamos todos los cambios con la base de datos
$em->flush();
echo 'OK!';
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Ejemplo de uso (búsqueda y modificación):
DAW 63
require_once __DIR__ . '/config.php';
//Encontrar al alumno con matrícula = 70212
$alumno= $em->find('Entity\Alumno', 70212);
if($alumno) { //El EntityManager ha encontrado algún objeto de Entity\Alumno
echo 'He encontrado a un alumno (instancia de la clase ' . get_class($alumno)
. ') con nombre ' . $alumno->getNombre();
$alumno->setNombre("Cantalicio Floreal");
$em->flush();
}
else {
echo 'No he encontrado a nadie!!!';
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Ejemplo de uso (búsqueda general):
DAW 64
require_once __DIR__ . '/config.php';
//Un repository es el concepto de tabla relacional
// que contiene entidades de un tipo (clase) determinado
$repo = $em->getRepository('Entity\Alumno');
//Buscar el alumno con nombre = "Elena Nito del Bosque"
$elenas = $repo->findBy(array('nombre' => 'Elena Nito del Bosque'));
//Mostrar resultados
echo 'Encontrados ' . count($elenas) . ' alumnos con ese nombre:' . PHP_EOL;
foreach($elenas as $el) {
echo ' - ' . $el->getMatricula() . PHP_EOL;
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Ejemplo de uso (búsqueda DQL):– Extensión genérica similar a SQL.
DAW 65
require_once __DIR__ . '/config.php';
//Creamos una consulta DQL para buscar todos los alumnos del año 07
$alumnos = $em
->createQuery('SELECT a FROM Entity\Alumno a '
. ' WHERE a.matricula >= 70000 AND a.matricula <= 79999')
->getResult();
//Mostrar resultados
echo 'Encontrados ' . count($alumnos) . ' alumnos de ese año:' . PHP_EOL;
foreach($alumnos as $a) {
echo ' - ' . $a->getMatricula() . ": " . $a->getNombre() . PHP_EOL;
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Ejemplo de uso (borrar):
DAW 66
require_once __DIR__ . '/config.php';
//Buscamos el último matriculado
$alumnos = $em
->createQuery('SELECT a FROM Entity\Alumno a ORDER BY a.matricula DESC')
->setMaxResults(1) //sólo vamos a querer un resultado
->getResult();
if(!empty($alumnos)) {
$al = reset($alumnos);
echo 'El último número de matrícula es "' . $al->getMatricula()
. '" y corresponde al alumno "' . $al->getNombre() . '"' . PHP_EOL;
$em->remove($al);
$em->flush();
echo 'Alumno borrado!' . PHP_EOL;
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Definición de relaciones:– En Doctrine las relaciones no se
establecen explicitando los identificadores
que se cruzan.
– En cambio se dice las cardinalidades de
las relaciones de entidad contra entidad:• Un Alumno tiene N Notas
• Una Nota pertenece a 1 Alumno.
– Doctrine utiliza una nomenclatura propia:
DAW 67
Alumnos
Notas
tienen
Alumno::notas es una relación OneToMany con Notas mapeada por Nota::alumno
Nota::alumno es una relación ManyToOne con Alumnos invertida por Alumno::notas
Alumno::notas Nota::alumnoOneToMany mapped by
ManyToOneinverted by
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Definición de relaciones:– Cardinalidades disponibles:
• OneToMany (mapped by ManyToOne)
• ManyToOne (inversed by OneToMany)
• OneToOne (inversed/mapped by OneToOne)
• ManyToMany (inversed/mapped by ManyToMany)
– Las relaciones OneToMany y ManyToMany se representan en
Doctrine como instancias del interfaz
Doctrine\Common\Collections\Collection.
DAW 68
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Definición de relaciones:– Direccionalidad:
• Las relaciones pueden ser uni- o bi-direccionales (inversibles).
– Propietarios de las relaciones: • Toda relación tiene uno de sus extremos que es propietario de la
misma.
• Las relaciones bidireccionales tiene tanto lado propietario como lado
inverso.
• Las relaciones unidireccionales sólo tienen lado propietario.
• El lado que es propietario es el que verifica Doctrine para propagar los
cambios.
• Referencia:http://www.doctrine-project.org/docs/orm/2.0/en/reference/association-mapping.html
DAW 69
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Modificamos la clase Alumno:
DAW 70
/**
* @ORM\OneToMany(targetEntity="Entity\Nota", mappedBy="alumno")
* @var Collection
*/
private $notas;
public function __construct($matricula) {
//Inicializamos la colección. Doctrine interpreta Collections, no arrays!
$this->notas = new ArrayCollection();
$this->setMatricula($matricula);
}
/** @return Collection */
public function getNotas() {
return $this->notas;
}
/** @param Nota $nota */
public function addNota(Nota $nota) {
$this->notas->add($nota);
$nota->setAlumno($this);
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Creamos la clase Nota:
continua …DAW 71
/** @ORM\Entity */
class Nota {
/**@ORM\Id()
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @var int */
private $id;
/**@ORM\Column(type="string", length=255)
* @var string */
private $asignatura;
/**@ORM\Column(type="integer")
* @var int */
private $calificacion;
public function __construct($asignatura) {
$this->setAsignatura($asignatura);
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Creamos la clase Nota:
DAW 72
/** @ORM\ManyToOne(targetEntity="Entity\Alumno", inversedBy="notas")
* @var Alumno|null */
private $alumno;
// Los getter y setter de $asignatura y de $calificacion
/** @return Alumno|null */
public function getAlumno() {
return $this->alumno;
}
/** @param Alumno $alumno */
public function setAlumno(Alumno $alumno) {
if($alumno === null || $alumno instanceof Alumno) {
$this->alumno = $alumno;
}
else {
throw new InvalidArgumentException('$alumno debe ser instancia de Entity\Alumno o null!');
}
}
}
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
• Ejemplo (creación):
DAW 73
use Entity\Alumno,
Entity\Nota;
require_once __DIR__ . '/config.php';
$chema = new Alumno(080332);
$chema->setNombe('Chema Pamundi');
$em->persist($chema);
$nota = new Nota('Danzas eslavas');
$nota->setCalificacion(10);
$em->persist($nota);
$alumno->addNota($nota);
$em->flush();
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
DAW 74
require_once __DIR__ . '/config.php';
$alumno = $em->find('Entity\Alumno', 080332);
if($alumno) {
echo 'Encontrado alumno: ' . PHP_EOL
. $alumno->getMatricula() . ' => ' . $alumno->getNombre() . '(' . get_class($alumno) . ')' . PHP_EOL
. 'y ' . $alumno->getNotas()->count() . ' Notas asociadas con él: ' . PHP_EOL;
foreach($alumno->getNotas() as $nota) {
echo ' ' . $nota->getAsignatura() . ': ' . $nota->getCalificacion()
. ' (' . get_class($nota) . ')' . PHP_EOL;
}
}
else {
echo 'No se ha encontrado alumno con matrícula=080332';
}
• Ejemplo (consulta):
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
DAW 75
require_once __DIR__ . '/config.php';
$alumno = $em->find('Entity\Alumno', 080332);
if($alumno) {
echo 'Encontrado alumno: ' . PHP_EOL
. $alumno->getMatricula() . ' => ' . $alumno->getNombre() . '(' . get_class($alumno) . ')' . PHP_EOL
. 'y ' . $alumno->getNotas()->count() . ' Notas asociadas con él: ' . PHP_EOL;
foreach($alumno->getNotas() as $nota) {
$em->remove($nota);
}
$em->flush();
}
else {
echo 'No se ha encontrado alumno con matrícula=080332';
}
• Ejemplo (borrado):
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Doctrine 2
DAW 76
require_once __DIR__ . '/config.php';
echo 'Obtener todas las matrículas de honor de el año 08: ' . PHP_EOL;
$alumnos = $em
->createQuery('SELECT a FROM Entity\Alumno a JOIN a.notas n WHERE‘
. ' n.calificacion >= :calificacion AND ‘
. ' a.matricula >= (:ano*10000) AND a.matricula < ((:ano+1)*1000)')
->setParameter('calificacion', 10)
->setParameter('ano', 8)
->getResult();
echo 'Se encontraron ' . count($alumnos) . ' casos:' . PHP_EOL;
foreach($alumnos as $al) {
echo ' ' . $alumno->getMatricula() . ' => ' . $alumno->getNombre() . PHP_EOL;
}
• Ejemplo (consultas con DQL):
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Bases de Datos NoSQL Documentales
• No todo es SQL:– Más próximas a lo que sería el concepto
de un ORM.
– Son objetos que directamente se pueden
guardar y recuperar.
– Pero el sustrato de almacenamiento ya no
es una BD relacional.
– Formatos de almacenamiento
documental: XML, YAML, JSON o BSON.
– Bases de datos documentales puras:
MongoDB o CouchDB
– Otras variantes: Cassandra o Redis
– Extensiones de RDBMS: PostgresSQL o
InformixDAW 77
Fernando PérezJosé M. Peña
Santiago GonzálezFrancisco Rosales
Santiago RodríguezAntonio LaTorreJuan Morales
Bibliografía
• Gilmore, W.J. (2010) Beginning PHP 5 and MySQL: From
Novice to Professional
• Romer, M. (2013). Persistence in PHP with the Doctrine 2
ORM. Concepts, Techniques & Practical Solutions.
• Tutoriales:
http://php.net/manual/es/set.mysqlinfo.php
http://php.net/manual/es/book.pdo.php
http://doctrine-orm.readthedocs.org/en/latest/tutorials/getting-started.html
https://github.com/Ocramius/Doctrine2ORMSlidesTutorial (M. Pivetta)
DAW 78