SlideShare una empresa de Scribd logo
1 de 74
Descargar para leer sin conexión
Introducción a
Doctrine 2 ORM
Por J.R.Laguardia
Introducción a Doctrine 2 ORM
 Fundamentos de Doctrine
 Que es Doctrine.
 Componentes de Doctrine.
 Instalando Doctrine.
 Un proyecto de ejemplo.
 Entidades y mapeado
 Las entidades.
 Ciclo de vida de una entidad.
 Estados de una entidad.
 Tipos de datos y mapeado.
 Creando las estructuras de nuestro
ejemplo.
 Asociaciones entre entidades
 Tipos de asociaciones y cardinalidad.
 Parte propietaria y parte inversa.
 Hidratación de datos.
 Una extensión de Doctrine: Data
Fixtures
 Insertando datos en nuestro ejemplo.
 Consultas en Doctrine
 DQL, Querybuilder y SQL Nativo.
 Repositorios por defecto y
personalizados.
 Optimizando nuestro ejemplo.
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Que es Doctrine
 Doctrine es un conjunto de librerías que proveen un
sistema para la persistencia de datos en PHP.
 Funciona como una capa de abstracción, situada entre
nuestra aplicación y el SGDB.
 Esta diseñada para ser compatible con los SGBD mas
comunes, como MySQL, MSSQL, PostgreSQL, SQLite y
Oracle.
 Puede dar soporte, a través de ODM (Object Document
Model) a SGBD NoSQL, como MongoDB, CouchDB,
OrientDB…
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Que es Doctrine
 Ampliamente utilizado de forma integrada con otros
frameworks PHP, como Symfony, Zend Framework,
Codeigniter, etc, etc… aunque puede utilizarse sin ellos.
 Doctrine maneja los datos como objetos PHP
(Entidades), de forma similar a lo que hace Hibernate
en JAVA con los POJO (Plain Old Java Object).
 La abstracción de las Entidades permite reusar nuestro
código a pesar de que cambiemos el SGBD de trabajo.
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Que es Doctrine
Doctrine emplea internamente dos patrones de diseño importantes:
Introducción a Doctrine 2 ORM
Data Mapper
• En Doctrine, el objeto que
implementa este patrón se
denomina Entity Manager.
• Realiza las inserciones,
actualizaciones y borrado en
la BD de los datos de las
entidades gestionadas.
• Informa (hidrata) los objetos
en memoria con datos
obtenidos de la BD.
Unit of Work
• Este patrón es el empleado
por el Entity Manager para
acceder a la BD de forma
transaccional.
• Mantiene el estado de las
entidades gestionadas por el
Entity Manager.
Fundamentos de Doctrine
Componentes de Doctrine
ORM
DBAL
Common
 ORM.
Mapeador Relacional de Objetos. Permite
el acceso a las tablas de las bases de
datos a través de un API orientado al
objeto. Construido sobre DBAL.
 DBAL.
Capa de Abstracción de Base de Datos.
Provee de un interfaz común de acceso a
los diferentes SGBD. Es similar a PDO,
construida sobre ella, y por lo tanto,
debilmente ligada a esta.
 COMMON.
Utilidades que no están en la SPL, tales
como un autoloader de clases, un parser
de anotaciones, estructuras avanzadas
(p.ej: collections) y un sistema de caché.
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Instalando Doctrine
Doctrine se puede instalar usando PEAR (para todo el sistema), o como
dependencia del proyecto, mediante Composer.
El método recomendado es el de instalación mediante Composer,
quedando PEAR para las versiones mas antiguas. Si no tenemos instalado
Composer, podemos hacerlo tecleando en consola:
curl -sS http://getcomposer.org/installer | php
El archivo descargado (composer.phar) puede ser renombrado y
movido a una carpeta que esté en el PATH de ejecución de usuario, para
que pueda ser invocado desde cualquier punto. p.ej:
mv composer.phar /home/<user>/bin/composer
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Instalando Doctrine
Una vez instalado Composer, para instalar Doctrine como dependencia
de un proyecto basta con situarnos en su carpeta raíz, y creamos el
fichero composer.json donde introducimos la dependencia:
{
"require": {
"doctrine/orm": "*“
}
}
La descarga e instalación de Doctrine se realiza tecleando en consola:
composer install
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Instalando Doctrine
Al finalizar la descarga, la instalación habrá creado una nueva carpeta
vendor que contendrá a Doctrine y sus dependencias, y un fichero
composer.lock con el estado de las dependencias del proyecto.
Antes de poder usar Doctrine en nuestro proyecto debemos configurarlo
de forma que pueda ser capaz de establecer conexión con el SGBD, y
que el autoloader pueda cargar las clases de Doctrine, las entidades y el
resto de clases de nuestro proyecto.
Veamos nuestro proyecto de ejemplo…
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
Creamos una carpeta test que será la raíz de nuestro proyecto
(p.ej: /var/www/test ), y dentro de ella, creamos la siguiente
estructura de carpetas:
bin.....Scripts de utilidades de nuestra aplicación.
config..Ficheros de configuración.
src.....Fuentes de Entidades, Clases, etc,etc.
web.....Carpeta pública de la aplicación.
Dentro de la carpeta raíz, creamos el fichero composer.json
donde estableceremos las dependencias del proyecto y otros
datos.
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
El fichero composer.json en la carpeta raíz:
{
"name": "Test/Cine",
"type": "project",
"description": "Ejemplo para una introduccion a Doctrine",
"require": {
"doctrine/orm": "2.4.*",
},
"autoload": {
"psr-0": { "": "src/" }
}
}
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
Lo siguiente será configurar la aplicación para hacer uso de
Doctrine y sus herramientas. En la carpeta config crearemos el
fichero config.php:
<?php
// Configuracion de la aplicación
// Acceso a la base de datos
$dbParams = [
'driver' =>'pdo_mysql',
'host' =>'127.0.0.1',
'dbname' =>'test',
'user' =>'test',
'password' =>'test‘
];
// Estamos en modo desarrollo?
$dev = true;
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
En la carpeta src crearemos el bootstrap.php:
<?php
use DoctrineORMToolsSetup;
use DoctrineORMEntityManager;
require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/../config/config.php';
$entitiesPath = array(__DIR__.'/Cine/Entity');
$config = Setup::createAnnotationMetadataConfiguration($entitiesPath,
$dev);
$entityManager = EntityManager::create($dbParams, $config);
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
En la carpeta config, creamos un fichero cli-config.php, para la
configuración de las utilidades de línea de comandos (CLI) de
Doctrine:
<?php
// Configuracion del CLI de Doctrine.
// Dependencia del objeto ConsoleRunner
use DoctrineORMToolsConsoleConsoleRunner;
// Incluimos el bootstrap para obtener el 'Entity Manager'
require_once __DIR__.'/../src/bootstrap.php';
// Devolvemos el objeto HelperSet de consola
return ConsoleRunner::createHelperSet($entityManager);
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
Si hemos relizado correctamente los pasos anteriores,
deberíamos poder invocar Doctrine desde la consola,
situándonos previamente en la carpeta raíz del proyecto y
ejecutando:
php vendor/bin/doctrine.php
El proyecto ya tiene Doctrine instalado y configurado para
usarse, tanto como capa de persistencia, como sus herramientas
de consola.
Antes de empezar a crear entidades y manejarlas con Doctrine
deberemos tener creada nuestra BD, con sus credenciales de
acceso, tal y como se especificaron en el fichero config.php.
Introducción a Doctrine 2 ORM
Fundamentos de Doctrine
Un proyecto de ejemplo
Nuestro proyecto de ejemplo contará con varias entidades
relacionadas entre sí (Película, Comentario y Etiqueta), de
forma que una película pueda tener comentarios y/o etiquetas, y
cada una de estas últimas puede aparecer en una o varias
películas. Inicialmente, tendremos:
Introducción a Doctrine 2 ORM
Película
• Id
• Titulo
• TituloOriginal
• Director
• Año
Comentario
• Id
• Texto
• Fecha
Etiqueta
• Nombre
Entidades y el mapeado
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Las Entidades
Las entidades en Doctrine están definidas como clases PHP, que
deben cumplir una serie de características:
 No pueden ser final o contener métodos final.
 Las propiedades/atributos persistentes deben ser private o protected.
 No pueden implementar __clone() o __wakeup() , o si lo hacen, debe ser de
forma segura.
 No pueden usar func_get_args() para implementar un método con un número
variable de parámetros.
 Las propiedades/atributos persistentes solo deben accederse directamente
desde dentro de la entidad y por la instancia de la misma en si. El resto de
accesos debe realizarse mediante los correspondientes getters y setters.
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Las Entidades
 Al crear una entidad, Doctrine no invoca al constructor de la clase,
este solo es invocado si creamos una instancia con new. Esto permite
el uso del constructor para la inicialización de estructuras, establecer
valores por defecto o requerir parámetros.
 En Doctrine, una entidad es un objeto con identidad. Se utiliza el
patrón Identity Map para hacer un seguimiento de las entidades y sus
ids, de tal forma que la instancia de la entidad con un determinado id
sea única, sin importar las variables que apunten a ella, o el tipo de
consulta usado.
 Este patrón de seguimiento facilita el mantenimiento de los estados de
la entidad a lo largo de su ciclo de vida.
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Estados de una entidad
Introducción a Doctrine 2 ORM
•Estado de la instancia de la entidad, que no tiene una
identidad persistente y no está asociada a un Entity
Manager (p.ej: creada con el operador new).
NEW
MANAGED
DETACHED
REMOVED
•Estado de la instancia de la entidad, con identidad
persitente, que está asociada a un Entity Manager, y
por tanto, gestionada por el.
•Estado de la instancia de la entidad, con identidad
persitente, pero que NO está asociada a un Entity
Manager, y por tanto, NO gestionada por el.
•Estado de la instancia de la entidad, con identidad
persitente, que está asociada a un Entity Manager,
que será eliminada de la BD en la siguiente
transacción.
Entidades y el mapeado
Ciclo de vida de una entidad
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Tipos de datos y mapeado
 El estado persistente de una entidad está representado por sus
variables de instancia. Es decir, como objeto PHP, su estado
persistente estará definido por el valor de sus campos/propiedades.
 Estos campos o propiedades pueden ser de diferentes tipos. Aunque
Doctrine soporta un amplio conjunto de tipos de datos, esto no
significa que sea el mismo que el de los tipos nativos de PHP, o los del
SGBD utilizado.
 Mediante el mapeado de datos, informaremos a Doctrine de que
campos definirán el estado de la entidad y el tipo de datos de cada
uno.
 El mapeado de datos, junto al de asociaciones, generan unos
metadatos que sirven al ORM para gestionar las entidades y sus
relaciones.
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Tipos de datos y mapeado
Doctrine provee de varias formas de generar el mapeado para
metadatos:
Introducción a Doctrine 2 ORM
Anotaciones
•Los metadatos son
incrustados dentro
de bloques de
comentarios,
análogos a los
utilizados por
herramientas como
PHPDocumentor.
•Es posible definir
nuevos bloques de
anotaciones.
XML
•Utiliza ficheros XML
con un esquema
propio para el
mapeado de datos
con Doctrine.
•Cada entidad debe
tener su propio
fichero descriptor,
cuyo nombre será el
Full Qualified Name
de la entidad.
YML
•Utiliza el formato
YML de documentos.
•Cada entidad debe
tener su propio
fichero descriptor,
cuyo nombre será el
Full Qualified Name
de la entidad.
PHP
•Utiliza código nativo
PHP mediante el API
de la clase
ClassMetadata.
•El código puede
escribirse en
ficheros o dentro de
una función estática
llamada
loadMetadata($cla
ss) en la propia
entidad.
Entidades y el mapeado
Tipos de datos y mapeado
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Creando las entidades de nuestro ejemplo
Podremos en práctica lo anterior, creando la clase inicial Pelicula,
aunque de momento sin relacionar con las demás. Después iremos
creando el resto de entidades.
Dentro de la carpeta src, crearemos la ruta Cine/Entity/ , que habíamos
especificado previamente en nuestro bootstrap.php, y que servirá de
almacen de nuestras clases para las entidades.
Usaremos el sistema de anotaciones para el mapeado de datos y
asociaciones entre entidades. Nuestro fichero Pelicula.php contendrá lo
siguiente:
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Creando las entidades de nuestro ejemplo
src/Cine/Entity/Pelicula.php
<?php
namespace CineEntity;
use DoctrineORMMappingEntity;
use DoctrineORMMappingTable;
use DoctrineORMMappingIndex;
use DoctrineORMMappingId;
use DoctrineORMMappingGeneratedValue;
use DoctrineORMMappingColumn;
/**
* Pelicula
*
* @Entity
* @Table( name="movie", indexes={ @Index(name="year_idx", columns="year") } )
*
*/
class Pelicula {
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Creando las entidades de nuestro ejemplo
/**
* @var int
*
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
private $id;
/**
* @var string
*
* @Column(type="string", length=100, name="spanish_title")
*/
private $titulo;
/**
* @var string
*
* @Column(type="string", length=100, name="original_title", nullable=false)
*/
private $tituloOriginal;
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Creando las entidades de nuestro ejemplo
/**
* @var string
*
* @Column(type="string", length=100)
*/
private $director;
/**
* @var int
*
* @Column(type="integer", name="year", nullable=false, unique=false,
options={"unsigned":true, "default":0})
*/
private $anyo;
}
Hacemos lo mismo con las otras entidades, creando los ficheros
Comentario.php y Etiqueta.php en la misma carpeta.
Introducción a Doctrine 2 ORM
Entidades y el mapeado
Creando las entidades de nuestro ejemplo
Una vez creadas las clases de las entidades, Doctrine permite crear de
forma automática los getters y los setters para cada una de las clases
mediante línea de comandos. Situandonos en el directorio raíz de nuestro
proyecto teclearemos:
php vendor/bin/doctrine.php orm:generate:entities src/
De la misma forma, Doctrine es capaz de crear de forma automática, el
esquema de DB que corresponde a esas entidades:
php vendor/bin/doctrine.php orm:schema-tool:create
Esto debería haber creado la estructura de tablas en la BD, con sus
campos definidos tal y como especificamos en la información definida en
las anotaciones.
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Tipos de asociaciones y cardinalidad
Introducción a Doctrine 2 ORM
UnidireccionalUnidireccional
• Las entidades relacionadas pueden ser obtenidas desde
las entidades principales. Solo tienen lado propietario
(owner).
BidireccionalBidireccional
• Las entidades relacionadas pueden ser obtenidas desde
las principales, y a su vez, las principales pueden ser
obtenidas desde las relacionadas. Tienen un lado
propietario (owner) y un lado inverso (inverse).
TIPOSDEASOCIACIONES
Asociaciones entre entidades
Tipos de asociaciones y cardinalidad
Introducción a Doctrine 2 ORM
1 : 1 (Uno a Uno)1 : 1 (Uno a Uno)
• Cada entidad principal solo puede tener una entidad asociada.
• Se indica mediante la etiqueta @OneToOne
1 : N (Uno a muchos)1 : N (Uno a muchos)
• Cada entidad principal puede tener varias entidades asociadas.
• Se indica mediante la etiqueta @OneToMany
N : 1 (Muchos a Uno)N : 1 (Muchos a Uno)
• Varias entidades tienen una misma entidad asociada. Solo disponible en
asociaciones bidireccionales, como parte inversa de una 1:N.
• Se indica mediante la etiqueta @ManyToOne
N : N (Muchos a Muchos)N : N (Muchos a Muchos)
• Varias entidades tienen asociadas otro conjunto de varias entidades.
• Se indica mediante la etiqueta @ManyToMany
CARDINALIDAD
Asociaciones entre entidades
Hidratación de datos
En Doctrine, la hidratación (hydration) es el nombre que recibe el
proceso de obtener un resultado final de una consulta a la BD y
mapearlo a un ResultSet.
Los tipos de resultados que devuelve el proceso pueden ser:
● Entidades
● Arrays estrcuturados
● Arrays escalares
● Variables simples
Por lo general, la hidratación es un proceso que consume muchos
recursos, por lo que recuperar solo los datos que vayamos a necesitar
supondrá una mejora del rendimiento y/o consumo de dichos recursos.
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Hidratación de datos
Por defecto, Doctrine, en el resultado de una consulta, recupera la
información de las entidades asociadas a la principal. Esto lo realiza
empleando diferentes estrategias de recuperación, que pueden
especificarse como valor del atributo fetch en las asociaciones:
Introducción a Doctrine 2 ORM
LAZY
• Es el valor por
defecto (se puede
obviar).
• Una vez cargados
los datos de la
entidad principal, los
de las relacionadas
se obtienen con una
segunda consulta
SQL.
EAGER
• Los datos de la
entidad principal se
recuperan junto a
los de las entidades
relacionadas
haciendo uso de
JOINs en una misma
consulta.
EXTRA_LAZY
• Se cargan los datos
de la entidad
principal, pero los de
en las entidades
relacionadas solo
tiene disponibles los
métodos de la
Collection que no
impliquen la carga
total.
Asociaciones entre entidades
Parte propietaria y parte inversa
 Doctrine solo gestiona la parte propietaria (owner) de una asociación.
Esto significa que siempre deberemos identificar ese lado de la misma.
 En una asociación bidireccional, la parte propietaria siempre tendrá
un atributo inversedBy, y la parte inversa tendrá el atributo
mappedBy.
 Por defecto, las asociaciones @OneToOne y @ManyToOne son
persistidas en SQL utilizando una columna con el id y una clave
foránea. Las asociaciones @ManyToMany utilizan una tabla
intermedia.
 Los nombres de estas tablas y columnas son generados de forma
automática por Doctrine, pero se pueden cambiar utilizando las
anotaciones @JoinColumn y @JoinTable.
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Asociacion @ManyToOne entre las entidades Comentario y Peliculas
(Lado propietario).
Editamos nuestra clase entidad Comentario.php y añadimos…
/**
* @var Pelicula
*
* @ManyToOne( targetEntity="Pelicula", inversedBy="comentarios")
*/
private $pelicula;
Esto generará un nuevo campo en la tabla con el identificador de la
pelicula a la que corresponde ese comentario, por defecto ‘pelicula_id’
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Asociación @OneToMany entre las entidades Comentario y Peliculas
(Lado inverso).
Editamos nuestra clase entidad Pelicula.php y añadimos…
use DoctrineORMMappingOneToMany;
use DoctrineCommonCollectionsArrayCollection;
…
/**
* @var Comentario[]
*
* @OneToMany( targetEntity="Comentario", mappedBy="pelicula")
*/
private $comentarios;
La propiedad añadida almacena una colección de ids de entidades
Comentario.
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Cambios en Peliculas.php… (cont)
Nuevo constructor:
/**
* Inicializamos colecciones
*/
public function __construct()
{
$this->comentarios = new ArrayCollection();
}
Abrimos una consola en la raíz de nuestro proyecto y (re)generamos los
getters y setters de nuestras entidades modificadas:
php vendor/bin/doctrine.php orm:generate:entities src/
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Cambios en Peliculas.php… (cont)
El tipo de datos ArrayCollection de la propiedad, nos generará unos
métodos AddComentario() y RemoveComentario(). Solo queda
añadir una línea en el primero de ellos, que nos asegurará la persistencia
de la parte propietaria de la asociación.
/**
* Add comentarios
*
* @param CineEntityComentario $comentarios
* @return Pelicula
*/
public function addComentario(CineEntityComentario $comentarios)
{
$this->comentarios[] = $comentarios;
$comentarios->setPelicula($this);
return $this;
}
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Asociación @ManyToMany entre las entidades Etiquetas y Peliculas
(Lado inverso).
Editamos el fichero Etiqueta.php y añadimos…
use DoctrineORMMappingManyToMany;
use DoctrineCommonCollectionsArrayCollection;
…
/**
* @var Pelicula[]
*
* @ManyToMany( targetEntity="Pelicula", mappedBy="etiquetas“ )
*/
private $peliculas;
Creamos un constructor para inicializar la colección…
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Cambios en Etiqueta.php …(cont)
// Inicializa coleccion
public function __construct()
{
$this->peliculas = new ArrayCollection();
}
Implementamos el método __toString para poder hacer un ‘cast’ de la
entidad a una cadena…
// Cast del objeto como cadena
public function __toString()
{
return $this->getNombre();
}
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Cambios en Etiqueta.php …(cont)
Generamos getters y setters de nuevo, lo que nos creará los métodos
para la nueva propiedad: AddPelicula() y RemovePelicula().
Modificaremos el primero, añadiendo la línea que determina la
persistencia del lado propietario:
/**
* Add películas
*
* @param CineEntityPelicula $películas
* @return Etiqueta
*/
public function addPelicula(CineEntityPelicula $peliculas)
{
$this->peliculas[] = $peliculas;
$peliculas->addEtiqueta($this);
return $this;
}
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Asociación @ManyToMany entre las entidades Etiquetas y Peliculas
(Lado propietario).
Editamos el fichero Pelicula.php y añadimos…
use DoctrineORMMappingManyToMany;
use DoctrineORMMappingJoinTable;
use DoctrineORMMappingJoinColumn;
...
/**
* @var Etiqueta[]
*
* @ManyToMany( targetEntity="Etiqueta", inversedBy="peliculas",
* fetch="EAGER", cascade={"persist"}, orphanRemoval=true
* )
* @JoinTable(
* name="movie_tag",
* inverseJoinColumns={ @JoinColumn(name="tag_name", referencedColumnName="name") }
* )
*/
private $etiquetas;
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Cambios en el fichero Pelicula.php …(cont)
Finalmente, añadimos la inicialización de la nueva colección al
cosntructor…
// Inicializamos colecciones
public function __construct()
{
. . .
$this->etiquetas = new ArrayCollection();
}
Volvemos a generar los getters y setters, creándose los métodos
AddEtiqueta() y RemoveEtiqueta(), aunque esta vez no hay que
añadir la persistencia de la parte propietaria a AddEtiqueta(), ya que se
hace de forma automática al haberlo establecido como un atributo de la
asociación ( cascade={“Persist”} ).
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Parte propietaria y parte inversa
Tras los cambios realizados deberíamos poder obtener el esquema de la BD:
php vendor/bin/doctrine.php orm:schema-tool:update --force
Introducción a Doctrine 2 ORM
Una vez generadas nuestras entidades y el esquema de la base de datos
correspondiente, es necesario introducir datos para poder empezar a
crear el código de nuestra aplicación.
Doctrine posee una extensión (DataFixtures) destinada para este fin,
que se instala a través de Composer, como una dependencia mas del
proyecto.
Para ello, podemos editar el fichero composer.json de la carpeta ráiz del
proyecto y añadir la dependencia y actualizar, o simplemente, decirle a
composer que lo haga por nosotros. Desde la raíz del proyecto
ejecutamos:
composer require doctrine/data-fixtures:1.*
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Una extensión de Doctrine: DataFixtures
 Una vez instalada extensión, iremos a la carpeta de nuestro proyecto
y dentro de la ruta src/Cine, crearemos dentro una carpeta
DataFixtures, al mismo nivel que Entity. Esta carpeta contendrá
las clases (Fixtures) que harán la carga de datos para nuestras
entidades.
 Las clases de dicha carpeta han de implementar el
FixtureInterface para poder ser utilizadas.
 Su uso se realizará mediante la invocación de un script PHP (load-
fixtures.php) que situaremos en la carpeta bin de nuestro proyecto
y que invocaremos desde consola.
Ahora crearemos nuestras Fixtures y nuestro script de carga para el
ejemplo…
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Una extensión de Doctrine: DataFixtures
Contenido del fichero src/Cine/DataFixtures/LoadPeliculasData.php
<?php
namespace CineDataFixtures;
use CineEntityPelicula;
use DoctrineCommonDataFixturesFixtureInterface;
use DoctrineCommonPersistenceObjectManager;
class LoadPeliculasData implements FixtureInterface {
// Array de datos de ejemplo
private $datos = [
[ ‘titulo’=>’’,’titulo_original’=>’’,’anyo’=>’’,’director’=>’’],
. . .
[ ‘titulo’=>’’,’titulo_original’=>’’,’anyo’=>’’,’director’=>’’],
] //array de arrays asociativos
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
// Metodo del interfaz 'FixtureInterface' a implementar
public function load(ObjectManager $manager) {
foreach ($this->datos as $p)
{
// Creamos un nuevo objeto de la entidad (estado = NEW)
$pelicula = new Pelicula();
// Informamos los atributos de nuestra entidad
$película
->setTitulo( $p['titulo'] )
->setTituloOriginal( $p['titulo_original'] )
->setDirector( $p['director'] )
->setAnyo( $p['anyo'] );
// Hacemos que la nueva entidad pase a ser gestionada (estado = MANAGED)
$manager->persist($pelicula);
}
// Persistimos las entidades gestionadas
$manager->flush();
}
}
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
La entidad Comentarios es dependiente de la entidad Pelicula. Nuestra
clase de Fixtures para la carga de comentarios será una clase que deberá
implementar también el DependentFixtureInterface.
Fichero src/Cine/DataFixtures/LoadComentariosData.php:
<?php
namespace CineDataFixtures;
use CineEntityComentario;
use DoctrineCommonDataFixturesDependentFixtureInterface;
use DoctrineCommonDataFixturesFixtureInterface;
use DoctrineCommonPersistenceObjectManager;
class LoadComentariosData implements FixtureInterface, DependentFixtureInterface {
// Array de datos de ejemplo
private $datos = ['','',''...''];
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
// Metodo del interfaz 'FixtureInterface' a implementar
public function load(ObjectManager $manager) {
$numComentarios = 5;
// Obtenemos la lista de películas
$peliculas = $manager->getRepository('CineEntityPelicula')->findAll();
foreach ($peliculas as $p) {
// # aleatorio de comentarios por pelicula (pero al menos uno).
$total = mt_rand(1, $numComentarios);
for ($i = 1; $i <= $total; $i++) {
$comentario = new Comentario();
$comentario
->setTexto($this->datos[$i])
->setFecha(new DateTime(sprintf('-%d weeks', $total - $i)))
->setPelicula($p);
// Gestionamos entidad
$manager->persist($comentario);
}
}
// Persistimos las entidades
$manager->flush();
}
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
// Metodo del interfaz 'DependentFixtureInterface' a implementar
public function getDependencies() {
return ['CineDataFixturesLoadPeliculasData'];
}
}
Y finalmente, src/Cine/DataFixtures/LoadEtiquetasData.php:
<?php
namespace CineDataFixtures;
use CineEntityEtiqueta;
use DoctrineCommonDataFixturesDependentFixtureInterface;
use DoctrineCommonDataFixturesFixtureInterface;
use DoctrineCommonPersistenceObjectManager;
class LoadEtiquetasData implements FixtureInterface, DependentFixtureInterface {
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
// Metodo del interfaz 'FixtureInterface' a implementar
public function load(ObjectManager $manager) {
$num_etiquetas = 5;
// Preparamos un array de etiquetas
$etiquetas = [];
for ($i = 1; $i <= $num_etiquetas; $i++) {
$etiqueta = new Etiqueta();
$etiqueta->setNombre(sprintf("Etiqueta%d", $i));
$etiquetas[] = $etiqueta;
}
// Obtenemos la lista de películas
$peliculas = $manager->getRepository('CineEntityPelicula')->findAll();
// Agregamos un nuermo aleatorio de etiquetas a cada película
$agregar = rand(1, $num_etiquetas);
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
foreach ($peliculas as $p) {
for ($i = 0; $i < $agregar; $i++) {
$p->addEtiqueta( $etiquetas[$i]);
}
$agregar = rand(1, $num_etiquetas);
}
// Persistimos entidades gestionadas
$manager->flush();
}
// Metodo del interfaz 'DependentFixtureInterface' a implementar
public function getDependencies() {
return ['CineDataFixturesLoadPeliculasData'];
}
}
Una vez implementadas nuestras Fixtures, debemos crear el script que
las cargará y ejecutará. Este script (load-fixtures.php) lo situaremos en
la carpeta bin de nuestro proyecto. Su contenido es el siguiente:
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
load-fixtures.php
<?php
// load-fixtures.php – Script de carga de datos para entidades
//
// Incluimos nuestro bootstrap para tener acceso al 'EntityManager‘
require_once __DIR__.'/../src/bootstrap.php';
// Resolvemos dependencias de clases
use DoctrineCommonDataFixturesLoader;
use DoctrineCommonDataFixturesPurgerORMPurger;
use DoctrineCommonDataFixturesExecutorORMExecutor;
// Obtenemos un cargador y le indicamos la ruta de las Fixtures
$loader = new Loader();
$loader->loadFromDirectory(__DIR__.'/../src/Cine/DataFixtures');
// Objeto para purgado (vaciado) de entidades
$purger = new ORMPurger();
// Objeto que ejecutara la carga de datos
$executor = new ORMExecutor($entityManager, $purger);
$executor->execute($loader->getFixtures());
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
Podemos comprobar el funcionamiento de la carag de datos, abriendo
una consola enla carpeta raíz de nuestro proyecto y tecleando para:
…eliminar el esquema de la BD anterior:
php vendor/bin/doctrine.php orm:schema-tool:drop --force
…regenerar el esquema a partir de la ultimas definiciones de las
entidades:
php vendor/bin/doctrine.php orm:schema-tool:create
…cargar nuestros datos a partir de las Fixtures:
php bin/load-fixtures.php
Introducción a Doctrine 2 ORM
Asociaciones entre entidades
Insertando datos en nuestro ejemplo
Consultas en Doctrine
Introducción a Doctrine 2 ORM
Introducción a Doctrine 2 ORM
Consultas en Doctrine
• DQL - Lenguaje de consulta
específico del dominio Doctrine.
Doctrine
Query
Language
• Helper Class para construcción de
consultas mediante un API.
QueryBuilder
• Uso de SQL propio del SGBD
mediante NativeQuery o Query.
SQL Nativo
Consultas en Doctrine
Doctrine Query Language
Introducción a Doctrine 2 ORM
PROS
 Es similar a SQL, pero posee características propias (inspirado en
HQL).
 Gestiona objetos y propiedades en lugar de tablas y campos.
 Permite simplificar algunas construcciones de las consultas
gracias al uso de metadatos (p.ej: Claúsulas ON en los JOINS).
 Es Independiente del SGBD utilizado.
CONS
 Posee algunas diferencias de sintaxis con respecto al SQL
estándar.
 No posee toda la funcionalidad y optimizaciones del SQL
específico de cada SGBD.
 Tiene limitaciones a la hora de implementar ciertas consultas
(p.ej: subconsultas).
Consultas en Doctrine
QueryBuilder
Introducción a Doctrine 2 ORM
Es una clase creada para ayudar a construir consultas DQL mediante el
uso de un interfaz fluido, a través de un API.
El QueryBuilder se crea mediante el método createQueryBuilder()
heredado del repositorio base de la entidad o desde el EntityManager.
En la creación de una instancia de QueryBuilder desde el repositorio de
la entidad debemos especificar un parámetro (string), que es el alias de
la entidad principal.
$qb=$entityRepo->createQueryBuilder(‘u’);
Equivale a la creación desde el EntityManager:
$qb = $entityManager->createQueryBuilder();
$qb->select(‘u’);
Consultas en Doctrine
QueryBuilder
Introducción a Doctrine 2 ORM
Podemos obtener la consulta DQL generada por el QueryBuilder
mediante el uso de su método getDQL().
$qb = $entityManager->createQueryBuilder();
$qb->select('p')->from('CineEntityPelicula','p');
$queryDQL = $qb->getDQL();
De forma similar, previa obtención del objeto Query asociado al
QueryBuilder, podemos acceder al SQL resultante mediante el método
getSQL().
$query = $qb->getQuery();
$querySQL = $query->getSQL();
Consultas en Doctrine
QueryBuilder
Introducción a Doctrine 2 ORM
 Las consultas DQL hacen uso interno de los Prepared Statements
de SQL, por motivos de seguridad (p.ej: SQL injections) y rendimiento
(unidad transaccional).
 Por defecto, y a menos que se especifique otra cosa, una consulta DQL
obtiene todas las entidades relacionadas con la principal. Esto puede
provocar problemas de recursos o de rendimiento si no se gestiona
bien.
 La naturaleza de las relaciones entre entidades es conocida por
Doctrine gracias a los metadatos de sus asociaciones, por ello no es
necesario especificar cláusulas ON o USING en los JOIN.
 Las clases QueryBuilder y Query gestionan un caché de las
consultas. El comportamiento y naturaleza de este caché es diferente
según estemos en modo desarrollo o producción.
Consultas en Doctrine
SQL Nativo
Introducción a Doctrine 2 ORM
• Los resultados se mapean a entidades Doctrine
mediante ResultSetMapBuilder.
• Se utiliza Doctrine ORM.
• Solo se soportan consultas SELECT.
NativeQuery
• Los resultados no se mapean a entidades Doctrine.
• Se utiliza Doctrine DBAL.
Query
Consultas en Doctrine
SQL Nativo
Introducción a Doctrine 2 ORM
Usando NativeQuery (mapeando a entidades)
$rsmb = new ResultSetMappingBuilder($entityManager);
$rsmb->addRootEntityFromClassMetadata('CineEntityPelicula', 'p');
$rsmb->addJoinedEntityFromClassMetadata(
'CineEntityComentario',
'c',
'p',
'comentarios',
[
'id' => 'comment_id'
]
);
$sql = <<<SQL
SELECT movie.id, movie.original_title, comment.id as comment_id, comment.texto,
comment.fecha
FROM movie INNER JOIN comment ON movie.id = comment.pelicula_id
WHERE movie.year >= 1988
ORDER BY movie.id, comment.fecha
SQL;
$query = $entityManager->createNativeQuery($sql, $rsmb);
$result = $query->getResult();
Consultas en Doctrine
SQL Nativo
Introducción a Doctrine 2 ORM
Usando Query (sin mapeado a entidades)
$sql = <<<SQL
SELECT spanish_title AS titulo, COUNT(comment.id) AS comentarios
FROM movie JOIN comment ON comment.pelicula_id = movie.id
GROUP BY movie.id
ORDER BY comentarios DESC, spanish_title ASC
LIMIT 5;
SQL;
$query = $entityManager->getConnection()->query($sql);
$result = $query->fetchAll();
Consultas en Doctrine
Repositorios de Entidades Personalizados
Introducción a Doctrine 2 ORM
Cuando generamos una entidad, Doctrine nos proporciona un repositorio
base por defecto para dicha entidad.
El repositorio base consta de una serie de métodos comunes para operar
con la entidad:
 find(id)
Retorna la entidad con el identificador id, o null si no existe.
 findAll()
Retorna un array con todas las entidades del repositorio.
 findBy( array(criterios) [, array(ordenacion)] )
Retorna un array con las entidades que cumplan los criterios especificados
en el primer parámetro, y ordenados por los del segundo parámetro
(opcional).
 findOneBy( array(criterios) )
Análogo al findBy() pero devolviendo solo un elemento, o nulo si no existe.
Consultas en Doctrine
Repositorios de Entidades Personalizados
Introducción a Doctrine 2 ORM
Algunos de los métodos del repositorio base pueden utilizarse de forma
abreviada, permitiendo no especificar como parámetro el nombre de la
propiedad. P.ej:
findByTitulo(‘valor’), findOneByTitulo(‘valor’)
Equivalen a:
findBy(‘Titulo’=>’valor’), findOneBy(‘Titulo’=>’valor’)
Esto se debe a la utilización del método ‘mágico’ __call() de PHP que
hace Doctrine a la hora de localizar el nombre del método de la clase.
Consultas en Doctrine
Repositorios de Entidades Personalizados
Introducción a Doctrine 2 ORM
 El repositorio base de una entidad puede ser extendido a fin de proporcionar
métodos específicos para consultas de usuario.
 Este repositorio personalizado permite optimizar la obtención de resultados en
las consultas, obteniendo mas control en la hidratación de los datos (p.ej:
seleccionar parcialmente entidades y/o especificar operaciones en la consulta
que no se harían de forma automática).
 Doctrine permite especificar la clase que utilizaremos como repositorio de la
entidad en la etiqueta @Entity, dentro de las anotaciones de dicha entidad.
@Entity(repositoryClass=“PeliculaRepository”)
 La clase (vacía) será generada mediante las herramientas de consola (CLI) de
Doctrine, ejecutando:
php vendor/bin/doctrine.php orm:generate-repositories /src
Consultas en Doctrine
Optimizando nuestro ejemplo
Una optimización básica de nuestro ejemplo sería la construcción de un
repertorio personalizado para la entidad Pelicula.
Este constará de métodos que nos permitan recuperar solamente los
datos que necesitamos mostrar en cada momento. Para ello crearemos
consultas DQL de forma que…
 Se seleccionen solo los campos específicos de las entidades
asociadas en lugar de recuperar todos ellos (comportamiento por
defecto).
 Se haga en una sola operación lo que de otra forma requeriría mas
de una (p.ej: Recuperar datos de una entidad asociada con
fetch=‘lazy’).
Introducción a Doctrine 2 ORM
public function findConNumComentarios(){
return $this
->createQueryBuilder('p')
->leftJoin('p.comentarios', 'c')
->addSelect('COUNT(c.id)')
->groupBy('p.id')
->getQuery()
->getResult();
}
Introducción a Doctrine 2 ORM
Consultas en Doctrine
Optimizando nuestro ejemplo
public function findTeniendoEtiquetas(array $etiquetas) {
return $queryBuilder = $this
->createQueryBuilder('p')
->addSelect('e.nombre
->addSelect('COUNT(c.id)')
->join('p.etiquetas', 'e')
->leftJoin('p.comentarios', 'c')
->where('e.nombre IN (:etiquetas)')
->groupBy('p.id')
->having('COUNT(e.nombre) >= :numEtiquetas')
->setParameter('etiquetas', $etiquetas)
->setParameter('numEtiquetas', count($etiquetas))
->getQuery()
->getResult();
}
Introducción a Doctrine 2 ORM
Consultas en Doctrine
Optimizando nuestro ejemplo
public function findConComentarios($id) {
return $this
->createQueryBuilder('p')
->addSelect('c')
->leftJoin('p.comentarios', 'c')
->where('p.id = :id')
->orderBy('c.fecha', 'ASC')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
Introducción a Doctrine 2 ORM
Consultas en Doctrine
Optimizando nuestro ejemplo
Gracias por su atención
jrlaguardia@gmail.com
Proyecto de ejemplo
https://github.com/gonfert/cine
Recursos
Doctrine Project Site
http://www.doctrine-project.org
Notes on Doctrine 2
http://www.krueckeberg.org/notes/d2.html
Mastering Symfony2 performance
http://labs.octivi.com/mastering-symfony2-
performance-doctrine/
Bibliografía
Persistence in PHP with Doctrine ORM
(Packt Publishing)
Proyectos similares
Propel
http://propelorm.org
Red Bean PHP4
http://redbeanphp.com
Spot ORM
http://phpdatamapper.com
Introducción a Doctrine 2 ORM

Más contenido relacionado

La actualidad más candente

ORDS - Oracle REST Data Services
ORDS - Oracle REST Data ServicesORDS - Oracle REST Data Services
ORDS - Oracle REST Data ServicesJustin Michael Raj
 
Git - An Introduction
Git - An IntroductionGit - An Introduction
Git - An IntroductionBehzad Altaf
 
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...NTT DATA Technology & Innovation
 
Exadata I/O Resource Manager (Exadata IORM)
Exadata I/O Resource Manager (Exadata IORM)Exadata I/O Resource Manager (Exadata IORM)
Exadata I/O Resource Manager (Exadata IORM)Monowar Mukul
 
Zabbix - Gerenciando relatórios personalizados com Jasper Reports
Zabbix - Gerenciando relatórios personalizados com Jasper ReportsZabbix - Gerenciando relatórios personalizados com Jasper Reports
Zabbix - Gerenciando relatórios personalizados com Jasper ReportsZabbix BR
 
Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)
Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)
Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)NTT DATA Technology & Innovation
 
SVN Tool Information : Best Practices
SVN Tool Information  : Best PracticesSVN Tool Information  : Best Practices
SVN Tool Information : Best PracticesMaidul Islam
 
Introduction to git and github
Introduction to git and githubIntroduction to git and github
Introduction to git and githubAderemi Dadepo
 
Git Introduction Tutorial
Git Introduction TutorialGit Introduction Tutorial
Git Introduction TutorialThomas Rausch
 
Learning git
Learning gitLearning git
Learning gitSid Anand
 
Banco de Dados (parte 01)
Banco de Dados (parte 01)Banco de Dados (parte 01)
Banco de Dados (parte 01)Alex Camargo
 
Gstreamer plugin devpt_1
Gstreamer plugin devpt_1Gstreamer plugin devpt_1
Gstreamer plugin devpt_1shiv_nj
 
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう  by SRA OSS, Inc. 日本支社 高塚遥[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう  by SRA OSS, Inc. 日本支社 高塚遥
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥Insight Technology, Inc.
 
Kali linux guia español
Kali linux guia españolKali linux guia español
Kali linux guia españolIt-servicios
 

La actualidad más candente (20)

ORDS - Oracle REST Data Services
ORDS - Oracle REST Data ServicesORDS - Oracle REST Data Services
ORDS - Oracle REST Data Services
 
Git - An Introduction
Git - An IntroductionGit - An Introduction
Git - An Introduction
 
Introduction to git
Introduction to gitIntroduction to git
Introduction to git
 
Git tutorial
Git tutorialGit tutorial
Git tutorial
 
Intro to Git and GitHub
Intro to Git and GitHubIntro to Git and GitHub
Intro to Git and GitHub
 
Source control
Source controlSource control
Source control
 
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
 
Git
GitGit
Git
 
Exadata I/O Resource Manager (Exadata IORM)
Exadata I/O Resource Manager (Exadata IORM)Exadata I/O Resource Manager (Exadata IORM)
Exadata I/O Resource Manager (Exadata IORM)
 
Git - Level 2
Git - Level 2Git - Level 2
Git - Level 2
 
Zabbix - Gerenciando relatórios personalizados com Jasper Reports
Zabbix - Gerenciando relatórios personalizados com Jasper ReportsZabbix - Gerenciando relatórios personalizados com Jasper Reports
Zabbix - Gerenciando relatórios personalizados com Jasper Reports
 
Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)
Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)
Apache Bigtop3.2 (仮)(Open Source Conference 2022 Online/Hiroshima 発表資料)
 
SVN Tool Information : Best Practices
SVN Tool Information  : Best PracticesSVN Tool Information  : Best Practices
SVN Tool Information : Best Practices
 
Introduction to git and github
Introduction to git and githubIntroduction to git and github
Introduction to git and github
 
Git Introduction Tutorial
Git Introduction TutorialGit Introduction Tutorial
Git Introduction Tutorial
 
Learning git
Learning gitLearning git
Learning git
 
Banco de Dados (parte 01)
Banco de Dados (parte 01)Banco de Dados (parte 01)
Banco de Dados (parte 01)
 
Gstreamer plugin devpt_1
Gstreamer plugin devpt_1Gstreamer plugin devpt_1
Gstreamer plugin devpt_1
 
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう  by SRA OSS, Inc. 日本支社 高塚遥[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう  by SRA OSS, Inc. 日本支社 高塚遥
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
 
Kali linux guia español
Kali linux guia españolKali linux guia español
Kali linux guia español
 

Destacado

Magento 2 Code Generation Tools
Magento 2 Code Generation ToolsMagento 2 Code Generation Tools
Magento 2 Code Generation ToolsÓscar Recio Soria
 
Подобряване на ефективността на регионалната структура на националния статист...
Подобряване на ефективността на регионалната структура на националния статист...Подобряване на ефективността на регионалната структура на националния статист...
Подобряване на ефективността на регионалната структура на националния статист...Светла Иванова
 
Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...
Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...
Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...Maurizio De Filippis
 
Presentation STEM careers on 9th Math International Conference
Presentation STEM careers on 9th Math International ConferencePresentation STEM careers on 9th Math International Conference
Presentation STEM careers on 9th Math International ConferencePanagiota Argiri
 
Bang portfolio
Bang portfolioBang portfolio
Bang portfolioAine Doris
 
Drupal and security - Advice for Site Builders and Coders
Drupal and security - Advice for Site Builders and CodersDrupal and security - Advice for Site Builders and Coders
Drupal and security - Advice for Site Builders and CodersArunkumar Kupppuswamy
 

Destacado (12)

Jpa
JpaJpa
Jpa
 
Magento 2 Code Generation Tools
Magento 2 Code Generation ToolsMagento 2 Code Generation Tools
Magento 2 Code Generation Tools
 
Q and A - Rav Dhillon Argyle Coins
Q and A - Rav Dhillon Argyle CoinsQ and A - Rav Dhillon Argyle Coins
Q and A - Rav Dhillon Argyle Coins
 
Подобряване на ефективността на регионалната структура на националния статист...
Подобряване на ефективността на регионалната структура на националния статист...Подобряване на ефективността на регионалната структура на националния статист...
Подобряване на ефективността на регионалната структура на националния статист...
 
Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...
Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...
Allora Blu & Gli amici di Davide il drago ONLUS: "Nati per vivere" di M. Jank...
 
Presentation STEM careers on 9th Math International Conference
Presentation STEM careers on 9th Math International ConferencePresentation STEM careers on 9th Math International Conference
Presentation STEM careers on 9th Math International Conference
 
New marketer breed
New marketer breed New marketer breed
New marketer breed
 
Bang portfolio
Bang portfolioBang portfolio
Bang portfolio
 
College mediacoaches bibliotheken 200317
College mediacoaches bibliotheken   200317College mediacoaches bibliotheken   200317
College mediacoaches bibliotheken 200317
 
Casos clinicos epaderm cream
Casos clinicos epaderm creamCasos clinicos epaderm cream
Casos clinicos epaderm cream
 
4ª aula (segmentação de mercados)
4ª aula (segmentação de mercados)4ª aula (segmentação de mercados)
4ª aula (segmentação de mercados)
 
Drupal and security - Advice for Site Builders and Coders
Drupal and security - Advice for Site Builders and CodersDrupal and security - Advice for Site Builders and Coders
Drupal and security - Advice for Site Builders and Coders
 

Similar a Introduccion a Doctrine 2 ORM

Programación_del_lado_del_servidor......
Programación_del_lado_del_servidor......Programación_del_lado_del_servidor......
Programación_del_lado_del_servidor......SaulSalinasNeri
 
Persistencia de objetos con Hibernate
Persistencia de objetos con HibernatePersistencia de objetos con Hibernate
Persistencia de objetos con HibernateMauro Gomez Mejia
 
PresentacióN1
PresentacióN1PresentacióN1
PresentacióN1Rokr02
 
Arquitectura N-Capas y ADo.NET
Arquitectura N-Capas y ADo.NETArquitectura N-Capas y ADo.NET
Arquitectura N-Capas y ADo.NETRoberto Taborda
 
Persistencia De Objetos(Hibernate)
Persistencia De Objetos(Hibernate)Persistencia De Objetos(Hibernate)
Persistencia De Objetos(Hibernate)Ronald Cuello
 
Daniel espinosa garzon
Daniel espinosa garzonDaniel espinosa garzon
Daniel espinosa garzonorus004
 
Conceptualizacion lenguajes de programacion
Conceptualizacion lenguajes de programacionConceptualizacion lenguajes de programacion
Conceptualizacion lenguajes de programacionorus004
 
Framework .NET 3.5 07 Programación orientada a objetos
Framework .NET 3.5 07 Programación orientada a objetosFramework .NET 3.5 07 Programación orientada a objetos
Framework .NET 3.5 07 Programación orientada a objetosAntonio Palomares Sender
 
MADs about Drupal: Programación de entities para D7
MADs about Drupal: Programación de entities para D7MADs about Drupal: Programación de entities para D7
MADs about Drupal: Programación de entities para D7Pablo López Escobés
 
UDA-Anexo gestión de properties
UDA-Anexo gestión de propertiesUDA-Anexo gestión de properties
UDA-Anexo gestión de propertiesAnder Martinez
 
Patrones de diseño II
Patrones de diseño IIPatrones de diseño II
Patrones de diseño IIkaolong
 
Bases De Datos Orientadas A Objetos2
Bases De Datos Orientadas A Objetos2Bases De Datos Orientadas A Objetos2
Bases De Datos Orientadas A Objetos2Cristina Huerta
 

Similar a Introduccion a Doctrine 2 ORM (20)

Programación_del_lado_del_servidor......
Programación_del_lado_del_servidor......Programación_del_lado_del_servidor......
Programación_del_lado_del_servidor......
 
Persistencia de objetos con Hibernate
Persistencia de objetos con HibernatePersistencia de objetos con Hibernate
Persistencia de objetos con Hibernate
 
PresentacióN1
PresentacióN1PresentacióN1
PresentacióN1
 
Arquitectura N-Capas y ADo.NET
Arquitectura N-Capas y ADo.NETArquitectura N-Capas y ADo.NET
Arquitectura N-Capas y ADo.NET
 
Persistencia De Objetos(Hibernate)
Persistencia De Objetos(Hibernate)Persistencia De Objetos(Hibernate)
Persistencia De Objetos(Hibernate)
 
POO
POOPOO
POO
 
Benita ppp unidad 1
Benita ppp unidad 1Benita ppp unidad 1
Benita ppp unidad 1
 
Daniel espinosa garzon
Daniel espinosa garzonDaniel espinosa garzon
Daniel espinosa garzon
 
Conceptualizacion lenguajes de programacion
Conceptualizacion lenguajes de programacionConceptualizacion lenguajes de programacion
Conceptualizacion lenguajes de programacion
 
Framework .NET 3.5 07 Programación orientada a objetos
Framework .NET 3.5 07 Programación orientada a objetosFramework .NET 3.5 07 Programación orientada a objetos
Framework .NET 3.5 07 Programación orientada a objetos
 
Data storage
Data storageData storage
Data storage
 
T3 - JPA
T3 - JPAT3 - JPA
T3 - JPA
 
MADs about Drupal: Programación de entities para D7
MADs about Drupal: Programación de entities para D7MADs about Drupal: Programación de entities para D7
MADs about Drupal: Programación de entities para D7
 
UDA-Anexo gestión de properties
UDA-Anexo gestión de propertiesUDA-Anexo gestión de properties
UDA-Anexo gestión de properties
 
manual de C#
manual de C#manual de C#
manual de C#
 
Manual c# 2
Manual c# 2Manual c# 2
Manual c# 2
 
Manual de c#
Manual de c#Manual de c#
Manual de c#
 
Patrones de diseño II
Patrones de diseño IIPatrones de diseño II
Patrones de diseño II
 
Curso introductorio a Raptor.js con Node.js
Curso introductorio a Raptor.js con Node.jsCurso introductorio a Raptor.js con Node.js
Curso introductorio a Raptor.js con Node.js
 
Bases De Datos Orientadas A Objetos2
Bases De Datos Orientadas A Objetos2Bases De Datos Orientadas A Objetos2
Bases De Datos Orientadas A Objetos2
 

Introduccion a Doctrine 2 ORM

  • 1. Introducción a Doctrine 2 ORM Por J.R.Laguardia
  • 2. Introducción a Doctrine 2 ORM  Fundamentos de Doctrine  Que es Doctrine.  Componentes de Doctrine.  Instalando Doctrine.  Un proyecto de ejemplo.  Entidades y mapeado  Las entidades.  Ciclo de vida de una entidad.  Estados de una entidad.  Tipos de datos y mapeado.  Creando las estructuras de nuestro ejemplo.  Asociaciones entre entidades  Tipos de asociaciones y cardinalidad.  Parte propietaria y parte inversa.  Hidratación de datos.  Una extensión de Doctrine: Data Fixtures  Insertando datos en nuestro ejemplo.  Consultas en Doctrine  DQL, Querybuilder y SQL Nativo.  Repositorios por defecto y personalizados.  Optimizando nuestro ejemplo. Introducción a Doctrine 2 ORM
  • 4. Fundamentos de Doctrine Que es Doctrine  Doctrine es un conjunto de librerías que proveen un sistema para la persistencia de datos en PHP.  Funciona como una capa de abstracción, situada entre nuestra aplicación y el SGDB.  Esta diseñada para ser compatible con los SGBD mas comunes, como MySQL, MSSQL, PostgreSQL, SQLite y Oracle.  Puede dar soporte, a través de ODM (Object Document Model) a SGBD NoSQL, como MongoDB, CouchDB, OrientDB… Introducción a Doctrine 2 ORM
  • 5. Fundamentos de Doctrine Que es Doctrine  Ampliamente utilizado de forma integrada con otros frameworks PHP, como Symfony, Zend Framework, Codeigniter, etc, etc… aunque puede utilizarse sin ellos.  Doctrine maneja los datos como objetos PHP (Entidades), de forma similar a lo que hace Hibernate en JAVA con los POJO (Plain Old Java Object).  La abstracción de las Entidades permite reusar nuestro código a pesar de que cambiemos el SGBD de trabajo. Introducción a Doctrine 2 ORM
  • 6. Fundamentos de Doctrine Que es Doctrine Doctrine emplea internamente dos patrones de diseño importantes: Introducción a Doctrine 2 ORM Data Mapper • En Doctrine, el objeto que implementa este patrón se denomina Entity Manager. • Realiza las inserciones, actualizaciones y borrado en la BD de los datos de las entidades gestionadas. • Informa (hidrata) los objetos en memoria con datos obtenidos de la BD. Unit of Work • Este patrón es el empleado por el Entity Manager para acceder a la BD de forma transaccional. • Mantiene el estado de las entidades gestionadas por el Entity Manager.
  • 7. Fundamentos de Doctrine Componentes de Doctrine ORM DBAL Common  ORM. Mapeador Relacional de Objetos. Permite el acceso a las tablas de las bases de datos a través de un API orientado al objeto. Construido sobre DBAL.  DBAL. Capa de Abstracción de Base de Datos. Provee de un interfaz común de acceso a los diferentes SGBD. Es similar a PDO, construida sobre ella, y por lo tanto, debilmente ligada a esta.  COMMON. Utilidades que no están en la SPL, tales como un autoloader de clases, un parser de anotaciones, estructuras avanzadas (p.ej: collections) y un sistema de caché. Introducción a Doctrine 2 ORM
  • 8. Fundamentos de Doctrine Instalando Doctrine Doctrine se puede instalar usando PEAR (para todo el sistema), o como dependencia del proyecto, mediante Composer. El método recomendado es el de instalación mediante Composer, quedando PEAR para las versiones mas antiguas. Si no tenemos instalado Composer, podemos hacerlo tecleando en consola: curl -sS http://getcomposer.org/installer | php El archivo descargado (composer.phar) puede ser renombrado y movido a una carpeta que esté en el PATH de ejecución de usuario, para que pueda ser invocado desde cualquier punto. p.ej: mv composer.phar /home/<user>/bin/composer Introducción a Doctrine 2 ORM
  • 9. Fundamentos de Doctrine Instalando Doctrine Una vez instalado Composer, para instalar Doctrine como dependencia de un proyecto basta con situarnos en su carpeta raíz, y creamos el fichero composer.json donde introducimos la dependencia: { "require": { "doctrine/orm": "*“ } } La descarga e instalación de Doctrine se realiza tecleando en consola: composer install Introducción a Doctrine 2 ORM
  • 10. Fundamentos de Doctrine Instalando Doctrine Al finalizar la descarga, la instalación habrá creado una nueva carpeta vendor que contendrá a Doctrine y sus dependencias, y un fichero composer.lock con el estado de las dependencias del proyecto. Antes de poder usar Doctrine en nuestro proyecto debemos configurarlo de forma que pueda ser capaz de establecer conexión con el SGBD, y que el autoloader pueda cargar las clases de Doctrine, las entidades y el resto de clases de nuestro proyecto. Veamos nuestro proyecto de ejemplo… Introducción a Doctrine 2 ORM
  • 11. Fundamentos de Doctrine Un proyecto de ejemplo Creamos una carpeta test que será la raíz de nuestro proyecto (p.ej: /var/www/test ), y dentro de ella, creamos la siguiente estructura de carpetas: bin.....Scripts de utilidades de nuestra aplicación. config..Ficheros de configuración. src.....Fuentes de Entidades, Clases, etc,etc. web.....Carpeta pública de la aplicación. Dentro de la carpeta raíz, creamos el fichero composer.json donde estableceremos las dependencias del proyecto y otros datos. Introducción a Doctrine 2 ORM
  • 12. Fundamentos de Doctrine Un proyecto de ejemplo El fichero composer.json en la carpeta raíz: { "name": "Test/Cine", "type": "project", "description": "Ejemplo para una introduccion a Doctrine", "require": { "doctrine/orm": "2.4.*", }, "autoload": { "psr-0": { "": "src/" } } } Introducción a Doctrine 2 ORM
  • 13. Fundamentos de Doctrine Un proyecto de ejemplo Lo siguiente será configurar la aplicación para hacer uso de Doctrine y sus herramientas. En la carpeta config crearemos el fichero config.php: <?php // Configuracion de la aplicación // Acceso a la base de datos $dbParams = [ 'driver' =>'pdo_mysql', 'host' =>'127.0.0.1', 'dbname' =>'test', 'user' =>'test', 'password' =>'test‘ ]; // Estamos en modo desarrollo? $dev = true; Introducción a Doctrine 2 ORM
  • 14. Fundamentos de Doctrine Un proyecto de ejemplo En la carpeta src crearemos el bootstrap.php: <?php use DoctrineORMToolsSetup; use DoctrineORMEntityManager; require_once __DIR__.'/../vendor/autoload.php'; require_once __DIR__.'/../config/config.php'; $entitiesPath = array(__DIR__.'/Cine/Entity'); $config = Setup::createAnnotationMetadataConfiguration($entitiesPath, $dev); $entityManager = EntityManager::create($dbParams, $config); Introducción a Doctrine 2 ORM
  • 15. Fundamentos de Doctrine Un proyecto de ejemplo En la carpeta config, creamos un fichero cli-config.php, para la configuración de las utilidades de línea de comandos (CLI) de Doctrine: <?php // Configuracion del CLI de Doctrine. // Dependencia del objeto ConsoleRunner use DoctrineORMToolsConsoleConsoleRunner; // Incluimos el bootstrap para obtener el 'Entity Manager' require_once __DIR__.'/../src/bootstrap.php'; // Devolvemos el objeto HelperSet de consola return ConsoleRunner::createHelperSet($entityManager); Introducción a Doctrine 2 ORM
  • 16. Fundamentos de Doctrine Un proyecto de ejemplo Si hemos relizado correctamente los pasos anteriores, deberíamos poder invocar Doctrine desde la consola, situándonos previamente en la carpeta raíz del proyecto y ejecutando: php vendor/bin/doctrine.php El proyecto ya tiene Doctrine instalado y configurado para usarse, tanto como capa de persistencia, como sus herramientas de consola. Antes de empezar a crear entidades y manejarlas con Doctrine deberemos tener creada nuestra BD, con sus credenciales de acceso, tal y como se especificaron en el fichero config.php. Introducción a Doctrine 2 ORM
  • 17. Fundamentos de Doctrine Un proyecto de ejemplo Nuestro proyecto de ejemplo contará con varias entidades relacionadas entre sí (Película, Comentario y Etiqueta), de forma que una película pueda tener comentarios y/o etiquetas, y cada una de estas últimas puede aparecer en una o varias películas. Inicialmente, tendremos: Introducción a Doctrine 2 ORM Película • Id • Titulo • TituloOriginal • Director • Año Comentario • Id • Texto • Fecha Etiqueta • Nombre
  • 18. Entidades y el mapeado Introducción a Doctrine 2 ORM
  • 19. Entidades y el mapeado Las Entidades Las entidades en Doctrine están definidas como clases PHP, que deben cumplir una serie de características:  No pueden ser final o contener métodos final.  Las propiedades/atributos persistentes deben ser private o protected.  No pueden implementar __clone() o __wakeup() , o si lo hacen, debe ser de forma segura.  No pueden usar func_get_args() para implementar un método con un número variable de parámetros.  Las propiedades/atributos persistentes solo deben accederse directamente desde dentro de la entidad y por la instancia de la misma en si. El resto de accesos debe realizarse mediante los correspondientes getters y setters. Introducción a Doctrine 2 ORM
  • 20. Entidades y el mapeado Las Entidades  Al crear una entidad, Doctrine no invoca al constructor de la clase, este solo es invocado si creamos una instancia con new. Esto permite el uso del constructor para la inicialización de estructuras, establecer valores por defecto o requerir parámetros.  En Doctrine, una entidad es un objeto con identidad. Se utiliza el patrón Identity Map para hacer un seguimiento de las entidades y sus ids, de tal forma que la instancia de la entidad con un determinado id sea única, sin importar las variables que apunten a ella, o el tipo de consulta usado.  Este patrón de seguimiento facilita el mantenimiento de los estados de la entidad a lo largo de su ciclo de vida. Introducción a Doctrine 2 ORM
  • 21. Entidades y el mapeado Estados de una entidad Introducción a Doctrine 2 ORM •Estado de la instancia de la entidad, que no tiene una identidad persistente y no está asociada a un Entity Manager (p.ej: creada con el operador new). NEW MANAGED DETACHED REMOVED •Estado de la instancia de la entidad, con identidad persitente, que está asociada a un Entity Manager, y por tanto, gestionada por el. •Estado de la instancia de la entidad, con identidad persitente, pero que NO está asociada a un Entity Manager, y por tanto, NO gestionada por el. •Estado de la instancia de la entidad, con identidad persitente, que está asociada a un Entity Manager, que será eliminada de la BD en la siguiente transacción.
  • 22. Entidades y el mapeado Ciclo de vida de una entidad Introducción a Doctrine 2 ORM
  • 23. Entidades y el mapeado Tipos de datos y mapeado  El estado persistente de una entidad está representado por sus variables de instancia. Es decir, como objeto PHP, su estado persistente estará definido por el valor de sus campos/propiedades.  Estos campos o propiedades pueden ser de diferentes tipos. Aunque Doctrine soporta un amplio conjunto de tipos de datos, esto no significa que sea el mismo que el de los tipos nativos de PHP, o los del SGBD utilizado.  Mediante el mapeado de datos, informaremos a Doctrine de que campos definirán el estado de la entidad y el tipo de datos de cada uno.  El mapeado de datos, junto al de asociaciones, generan unos metadatos que sirven al ORM para gestionar las entidades y sus relaciones. Introducción a Doctrine 2 ORM
  • 24. Entidades y el mapeado Tipos de datos y mapeado Doctrine provee de varias formas de generar el mapeado para metadatos: Introducción a Doctrine 2 ORM Anotaciones •Los metadatos son incrustados dentro de bloques de comentarios, análogos a los utilizados por herramientas como PHPDocumentor. •Es posible definir nuevos bloques de anotaciones. XML •Utiliza ficheros XML con un esquema propio para el mapeado de datos con Doctrine. •Cada entidad debe tener su propio fichero descriptor, cuyo nombre será el Full Qualified Name de la entidad. YML •Utiliza el formato YML de documentos. •Cada entidad debe tener su propio fichero descriptor, cuyo nombre será el Full Qualified Name de la entidad. PHP •Utiliza código nativo PHP mediante el API de la clase ClassMetadata. •El código puede escribirse en ficheros o dentro de una función estática llamada loadMetadata($cla ss) en la propia entidad.
  • 25. Entidades y el mapeado Tipos de datos y mapeado Introducción a Doctrine 2 ORM
  • 26. Entidades y el mapeado Creando las entidades de nuestro ejemplo Podremos en práctica lo anterior, creando la clase inicial Pelicula, aunque de momento sin relacionar con las demás. Después iremos creando el resto de entidades. Dentro de la carpeta src, crearemos la ruta Cine/Entity/ , que habíamos especificado previamente en nuestro bootstrap.php, y que servirá de almacen de nuestras clases para las entidades. Usaremos el sistema de anotaciones para el mapeado de datos y asociaciones entre entidades. Nuestro fichero Pelicula.php contendrá lo siguiente: Introducción a Doctrine 2 ORM
  • 27. Entidades y el mapeado Creando las entidades de nuestro ejemplo src/Cine/Entity/Pelicula.php <?php namespace CineEntity; use DoctrineORMMappingEntity; use DoctrineORMMappingTable; use DoctrineORMMappingIndex; use DoctrineORMMappingId; use DoctrineORMMappingGeneratedValue; use DoctrineORMMappingColumn; /** * Pelicula * * @Entity * @Table( name="movie", indexes={ @Index(name="year_idx", columns="year") } ) * */ class Pelicula { Introducción a Doctrine 2 ORM
  • 28. Entidades y el mapeado Creando las entidades de nuestro ejemplo /** * @var int * * @Id * @GeneratedValue * @Column(type="integer") */ private $id; /** * @var string * * @Column(type="string", length=100, name="spanish_title") */ private $titulo; /** * @var string * * @Column(type="string", length=100, name="original_title", nullable=false) */ private $tituloOriginal; Introducción a Doctrine 2 ORM
  • 29. Entidades y el mapeado Creando las entidades de nuestro ejemplo /** * @var string * * @Column(type="string", length=100) */ private $director; /** * @var int * * @Column(type="integer", name="year", nullable=false, unique=false, options={"unsigned":true, "default":0}) */ private $anyo; } Hacemos lo mismo con las otras entidades, creando los ficheros Comentario.php y Etiqueta.php en la misma carpeta. Introducción a Doctrine 2 ORM
  • 30. Entidades y el mapeado Creando las entidades de nuestro ejemplo Una vez creadas las clases de las entidades, Doctrine permite crear de forma automática los getters y los setters para cada una de las clases mediante línea de comandos. Situandonos en el directorio raíz de nuestro proyecto teclearemos: php vendor/bin/doctrine.php orm:generate:entities src/ De la misma forma, Doctrine es capaz de crear de forma automática, el esquema de DB que corresponde a esas entidades: php vendor/bin/doctrine.php orm:schema-tool:create Esto debería haber creado la estructura de tablas en la BD, con sus campos definidos tal y como especificamos en la información definida en las anotaciones. Introducción a Doctrine 2 ORM
  • 32. Asociaciones entre entidades Tipos de asociaciones y cardinalidad Introducción a Doctrine 2 ORM UnidireccionalUnidireccional • Las entidades relacionadas pueden ser obtenidas desde las entidades principales. Solo tienen lado propietario (owner). BidireccionalBidireccional • Las entidades relacionadas pueden ser obtenidas desde las principales, y a su vez, las principales pueden ser obtenidas desde las relacionadas. Tienen un lado propietario (owner) y un lado inverso (inverse). TIPOSDEASOCIACIONES
  • 33. Asociaciones entre entidades Tipos de asociaciones y cardinalidad Introducción a Doctrine 2 ORM 1 : 1 (Uno a Uno)1 : 1 (Uno a Uno) • Cada entidad principal solo puede tener una entidad asociada. • Se indica mediante la etiqueta @OneToOne 1 : N (Uno a muchos)1 : N (Uno a muchos) • Cada entidad principal puede tener varias entidades asociadas. • Se indica mediante la etiqueta @OneToMany N : 1 (Muchos a Uno)N : 1 (Muchos a Uno) • Varias entidades tienen una misma entidad asociada. Solo disponible en asociaciones bidireccionales, como parte inversa de una 1:N. • Se indica mediante la etiqueta @ManyToOne N : N (Muchos a Muchos)N : N (Muchos a Muchos) • Varias entidades tienen asociadas otro conjunto de varias entidades. • Se indica mediante la etiqueta @ManyToMany CARDINALIDAD
  • 34. Asociaciones entre entidades Hidratación de datos En Doctrine, la hidratación (hydration) es el nombre que recibe el proceso de obtener un resultado final de una consulta a la BD y mapearlo a un ResultSet. Los tipos de resultados que devuelve el proceso pueden ser: ● Entidades ● Arrays estrcuturados ● Arrays escalares ● Variables simples Por lo general, la hidratación es un proceso que consume muchos recursos, por lo que recuperar solo los datos que vayamos a necesitar supondrá una mejora del rendimiento y/o consumo de dichos recursos. Introducción a Doctrine 2 ORM
  • 35. Asociaciones entre entidades Hidratación de datos Por defecto, Doctrine, en el resultado de una consulta, recupera la información de las entidades asociadas a la principal. Esto lo realiza empleando diferentes estrategias de recuperación, que pueden especificarse como valor del atributo fetch en las asociaciones: Introducción a Doctrine 2 ORM LAZY • Es el valor por defecto (se puede obviar). • Una vez cargados los datos de la entidad principal, los de las relacionadas se obtienen con una segunda consulta SQL. EAGER • Los datos de la entidad principal se recuperan junto a los de las entidades relacionadas haciendo uso de JOINs en una misma consulta. EXTRA_LAZY • Se cargan los datos de la entidad principal, pero los de en las entidades relacionadas solo tiene disponibles los métodos de la Collection que no impliquen la carga total.
  • 36. Asociaciones entre entidades Parte propietaria y parte inversa  Doctrine solo gestiona la parte propietaria (owner) de una asociación. Esto significa que siempre deberemos identificar ese lado de la misma.  En una asociación bidireccional, la parte propietaria siempre tendrá un atributo inversedBy, y la parte inversa tendrá el atributo mappedBy.  Por defecto, las asociaciones @OneToOne y @ManyToOne son persistidas en SQL utilizando una columna con el id y una clave foránea. Las asociaciones @ManyToMany utilizan una tabla intermedia.  Los nombres de estas tablas y columnas son generados de forma automática por Doctrine, pero se pueden cambiar utilizando las anotaciones @JoinColumn y @JoinTable. Introducción a Doctrine 2 ORM
  • 37. Asociaciones entre entidades Parte propietaria y parte inversa Asociacion @ManyToOne entre las entidades Comentario y Peliculas (Lado propietario). Editamos nuestra clase entidad Comentario.php y añadimos… /** * @var Pelicula * * @ManyToOne( targetEntity="Pelicula", inversedBy="comentarios") */ private $pelicula; Esto generará un nuevo campo en la tabla con el identificador de la pelicula a la que corresponde ese comentario, por defecto ‘pelicula_id’ Introducción a Doctrine 2 ORM
  • 38. Asociaciones entre entidades Parte propietaria y parte inversa Asociación @OneToMany entre las entidades Comentario y Peliculas (Lado inverso). Editamos nuestra clase entidad Pelicula.php y añadimos… use DoctrineORMMappingOneToMany; use DoctrineCommonCollectionsArrayCollection; … /** * @var Comentario[] * * @OneToMany( targetEntity="Comentario", mappedBy="pelicula") */ private $comentarios; La propiedad añadida almacena una colección de ids de entidades Comentario. Introducción a Doctrine 2 ORM
  • 39. Asociaciones entre entidades Parte propietaria y parte inversa Cambios en Peliculas.php… (cont) Nuevo constructor: /** * Inicializamos colecciones */ public function __construct() { $this->comentarios = new ArrayCollection(); } Abrimos una consola en la raíz de nuestro proyecto y (re)generamos los getters y setters de nuestras entidades modificadas: php vendor/bin/doctrine.php orm:generate:entities src/ Introducción a Doctrine 2 ORM
  • 40. Asociaciones entre entidades Parte propietaria y parte inversa Cambios en Peliculas.php… (cont) El tipo de datos ArrayCollection de la propiedad, nos generará unos métodos AddComentario() y RemoveComentario(). Solo queda añadir una línea en el primero de ellos, que nos asegurará la persistencia de la parte propietaria de la asociación. /** * Add comentarios * * @param CineEntityComentario $comentarios * @return Pelicula */ public function addComentario(CineEntityComentario $comentarios) { $this->comentarios[] = $comentarios; $comentarios->setPelicula($this); return $this; } Introducción a Doctrine 2 ORM
  • 41. Asociaciones entre entidades Parte propietaria y parte inversa Asociación @ManyToMany entre las entidades Etiquetas y Peliculas (Lado inverso). Editamos el fichero Etiqueta.php y añadimos… use DoctrineORMMappingManyToMany; use DoctrineCommonCollectionsArrayCollection; … /** * @var Pelicula[] * * @ManyToMany( targetEntity="Pelicula", mappedBy="etiquetas“ ) */ private $peliculas; Creamos un constructor para inicializar la colección… Introducción a Doctrine 2 ORM
  • 42. Asociaciones entre entidades Parte propietaria y parte inversa Cambios en Etiqueta.php …(cont) // Inicializa coleccion public function __construct() { $this->peliculas = new ArrayCollection(); } Implementamos el método __toString para poder hacer un ‘cast’ de la entidad a una cadena… // Cast del objeto como cadena public function __toString() { return $this->getNombre(); } Introducción a Doctrine 2 ORM
  • 43. Asociaciones entre entidades Parte propietaria y parte inversa Cambios en Etiqueta.php …(cont) Generamos getters y setters de nuevo, lo que nos creará los métodos para la nueva propiedad: AddPelicula() y RemovePelicula(). Modificaremos el primero, añadiendo la línea que determina la persistencia del lado propietario: /** * Add películas * * @param CineEntityPelicula $películas * @return Etiqueta */ public function addPelicula(CineEntityPelicula $peliculas) { $this->peliculas[] = $peliculas; $peliculas->addEtiqueta($this); return $this; } Introducción a Doctrine 2 ORM
  • 44. Asociaciones entre entidades Parte propietaria y parte inversa Asociación @ManyToMany entre las entidades Etiquetas y Peliculas (Lado propietario). Editamos el fichero Pelicula.php y añadimos… use DoctrineORMMappingManyToMany; use DoctrineORMMappingJoinTable; use DoctrineORMMappingJoinColumn; ... /** * @var Etiqueta[] * * @ManyToMany( targetEntity="Etiqueta", inversedBy="peliculas", * fetch="EAGER", cascade={"persist"}, orphanRemoval=true * ) * @JoinTable( * name="movie_tag", * inverseJoinColumns={ @JoinColumn(name="tag_name", referencedColumnName="name") } * ) */ private $etiquetas; Introducción a Doctrine 2 ORM
  • 45. Asociaciones entre entidades Parte propietaria y parte inversa Cambios en el fichero Pelicula.php …(cont) Finalmente, añadimos la inicialización de la nueva colección al cosntructor… // Inicializamos colecciones public function __construct() { . . . $this->etiquetas = new ArrayCollection(); } Volvemos a generar los getters y setters, creándose los métodos AddEtiqueta() y RemoveEtiqueta(), aunque esta vez no hay que añadir la persistencia de la parte propietaria a AddEtiqueta(), ya que se hace de forma automática al haberlo establecido como un atributo de la asociación ( cascade={“Persist”} ). Introducción a Doctrine 2 ORM
  • 46. Asociaciones entre entidades Parte propietaria y parte inversa Tras los cambios realizados deberíamos poder obtener el esquema de la BD: php vendor/bin/doctrine.php orm:schema-tool:update --force Introducción a Doctrine 2 ORM
  • 47. Una vez generadas nuestras entidades y el esquema de la base de datos correspondiente, es necesario introducir datos para poder empezar a crear el código de nuestra aplicación. Doctrine posee una extensión (DataFixtures) destinada para este fin, que se instala a través de Composer, como una dependencia mas del proyecto. Para ello, podemos editar el fichero composer.json de la carpeta ráiz del proyecto y añadir la dependencia y actualizar, o simplemente, decirle a composer que lo haga por nosotros. Desde la raíz del proyecto ejecutamos: composer require doctrine/data-fixtures:1.* Introducción a Doctrine 2 ORM Asociaciones entre entidades Una extensión de Doctrine: DataFixtures
  • 48.  Una vez instalada extensión, iremos a la carpeta de nuestro proyecto y dentro de la ruta src/Cine, crearemos dentro una carpeta DataFixtures, al mismo nivel que Entity. Esta carpeta contendrá las clases (Fixtures) que harán la carga de datos para nuestras entidades.  Las clases de dicha carpeta han de implementar el FixtureInterface para poder ser utilizadas.  Su uso se realizará mediante la invocación de un script PHP (load- fixtures.php) que situaremos en la carpeta bin de nuestro proyecto y que invocaremos desde consola. Ahora crearemos nuestras Fixtures y nuestro script de carga para el ejemplo… Introducción a Doctrine 2 ORM Asociaciones entre entidades Una extensión de Doctrine: DataFixtures
  • 49. Contenido del fichero src/Cine/DataFixtures/LoadPeliculasData.php <?php namespace CineDataFixtures; use CineEntityPelicula; use DoctrineCommonDataFixturesFixtureInterface; use DoctrineCommonPersistenceObjectManager; class LoadPeliculasData implements FixtureInterface { // Array de datos de ejemplo private $datos = [ [ ‘titulo’=>’’,’titulo_original’=>’’,’anyo’=>’’,’director’=>’’], . . . [ ‘titulo’=>’’,’titulo_original’=>’’,’anyo’=>’’,’director’=>’’], ] //array de arrays asociativos Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 50. // Metodo del interfaz 'FixtureInterface' a implementar public function load(ObjectManager $manager) { foreach ($this->datos as $p) { // Creamos un nuevo objeto de la entidad (estado = NEW) $pelicula = new Pelicula(); // Informamos los atributos de nuestra entidad $película ->setTitulo( $p['titulo'] ) ->setTituloOriginal( $p['titulo_original'] ) ->setDirector( $p['director'] ) ->setAnyo( $p['anyo'] ); // Hacemos que la nueva entidad pase a ser gestionada (estado = MANAGED) $manager->persist($pelicula); } // Persistimos las entidades gestionadas $manager->flush(); } } Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 51. La entidad Comentarios es dependiente de la entidad Pelicula. Nuestra clase de Fixtures para la carga de comentarios será una clase que deberá implementar también el DependentFixtureInterface. Fichero src/Cine/DataFixtures/LoadComentariosData.php: <?php namespace CineDataFixtures; use CineEntityComentario; use DoctrineCommonDataFixturesDependentFixtureInterface; use DoctrineCommonDataFixturesFixtureInterface; use DoctrineCommonPersistenceObjectManager; class LoadComentariosData implements FixtureInterface, DependentFixtureInterface { // Array de datos de ejemplo private $datos = ['','',''...'']; Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 52. // Metodo del interfaz 'FixtureInterface' a implementar public function load(ObjectManager $manager) { $numComentarios = 5; // Obtenemos la lista de películas $peliculas = $manager->getRepository('CineEntityPelicula')->findAll(); foreach ($peliculas as $p) { // # aleatorio de comentarios por pelicula (pero al menos uno). $total = mt_rand(1, $numComentarios); for ($i = 1; $i <= $total; $i++) { $comentario = new Comentario(); $comentario ->setTexto($this->datos[$i]) ->setFecha(new DateTime(sprintf('-%d weeks', $total - $i))) ->setPelicula($p); // Gestionamos entidad $manager->persist($comentario); } } // Persistimos las entidades $manager->flush(); } Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 53. // Metodo del interfaz 'DependentFixtureInterface' a implementar public function getDependencies() { return ['CineDataFixturesLoadPeliculasData']; } } Y finalmente, src/Cine/DataFixtures/LoadEtiquetasData.php: <?php namespace CineDataFixtures; use CineEntityEtiqueta; use DoctrineCommonDataFixturesDependentFixtureInterface; use DoctrineCommonDataFixturesFixtureInterface; use DoctrineCommonPersistenceObjectManager; class LoadEtiquetasData implements FixtureInterface, DependentFixtureInterface { Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 54. // Metodo del interfaz 'FixtureInterface' a implementar public function load(ObjectManager $manager) { $num_etiquetas = 5; // Preparamos un array de etiquetas $etiquetas = []; for ($i = 1; $i <= $num_etiquetas; $i++) { $etiqueta = new Etiqueta(); $etiqueta->setNombre(sprintf("Etiqueta%d", $i)); $etiquetas[] = $etiqueta; } // Obtenemos la lista de películas $peliculas = $manager->getRepository('CineEntityPelicula')->findAll(); // Agregamos un nuermo aleatorio de etiquetas a cada película $agregar = rand(1, $num_etiquetas); Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 55. foreach ($peliculas as $p) { for ($i = 0; $i < $agregar; $i++) { $p->addEtiqueta( $etiquetas[$i]); } $agregar = rand(1, $num_etiquetas); } // Persistimos entidades gestionadas $manager->flush(); } // Metodo del interfaz 'DependentFixtureInterface' a implementar public function getDependencies() { return ['CineDataFixturesLoadPeliculasData']; } } Una vez implementadas nuestras Fixtures, debemos crear el script que las cargará y ejecutará. Este script (load-fixtures.php) lo situaremos en la carpeta bin de nuestro proyecto. Su contenido es el siguiente: Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 56. load-fixtures.php <?php // load-fixtures.php – Script de carga de datos para entidades // // Incluimos nuestro bootstrap para tener acceso al 'EntityManager‘ require_once __DIR__.'/../src/bootstrap.php'; // Resolvemos dependencias de clases use DoctrineCommonDataFixturesLoader; use DoctrineCommonDataFixturesPurgerORMPurger; use DoctrineCommonDataFixturesExecutorORMExecutor; // Obtenemos un cargador y le indicamos la ruta de las Fixtures $loader = new Loader(); $loader->loadFromDirectory(__DIR__.'/../src/Cine/DataFixtures'); // Objeto para purgado (vaciado) de entidades $purger = new ORMPurger(); // Objeto que ejecutara la carga de datos $executor = new ORMExecutor($entityManager, $purger); $executor->execute($loader->getFixtures()); Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 57. Podemos comprobar el funcionamiento de la carag de datos, abriendo una consola enla carpeta raíz de nuestro proyecto y tecleando para: …eliminar el esquema de la BD anterior: php vendor/bin/doctrine.php orm:schema-tool:drop --force …regenerar el esquema a partir de la ultimas definiciones de las entidades: php vendor/bin/doctrine.php orm:schema-tool:create …cargar nuestros datos a partir de las Fixtures: php bin/load-fixtures.php Introducción a Doctrine 2 ORM Asociaciones entre entidades Insertando datos en nuestro ejemplo
  • 59. Introducción a Doctrine 2 ORM Consultas en Doctrine • DQL - Lenguaje de consulta específico del dominio Doctrine. Doctrine Query Language • Helper Class para construcción de consultas mediante un API. QueryBuilder • Uso de SQL propio del SGBD mediante NativeQuery o Query. SQL Nativo
  • 60. Consultas en Doctrine Doctrine Query Language Introducción a Doctrine 2 ORM PROS  Es similar a SQL, pero posee características propias (inspirado en HQL).  Gestiona objetos y propiedades en lugar de tablas y campos.  Permite simplificar algunas construcciones de las consultas gracias al uso de metadatos (p.ej: Claúsulas ON en los JOINS).  Es Independiente del SGBD utilizado. CONS  Posee algunas diferencias de sintaxis con respecto al SQL estándar.  No posee toda la funcionalidad y optimizaciones del SQL específico de cada SGBD.  Tiene limitaciones a la hora de implementar ciertas consultas (p.ej: subconsultas).
  • 61. Consultas en Doctrine QueryBuilder Introducción a Doctrine 2 ORM Es una clase creada para ayudar a construir consultas DQL mediante el uso de un interfaz fluido, a través de un API. El QueryBuilder se crea mediante el método createQueryBuilder() heredado del repositorio base de la entidad o desde el EntityManager. En la creación de una instancia de QueryBuilder desde el repositorio de la entidad debemos especificar un parámetro (string), que es el alias de la entidad principal. $qb=$entityRepo->createQueryBuilder(‘u’); Equivale a la creación desde el EntityManager: $qb = $entityManager->createQueryBuilder(); $qb->select(‘u’);
  • 62. Consultas en Doctrine QueryBuilder Introducción a Doctrine 2 ORM Podemos obtener la consulta DQL generada por el QueryBuilder mediante el uso de su método getDQL(). $qb = $entityManager->createQueryBuilder(); $qb->select('p')->from('CineEntityPelicula','p'); $queryDQL = $qb->getDQL(); De forma similar, previa obtención del objeto Query asociado al QueryBuilder, podemos acceder al SQL resultante mediante el método getSQL(). $query = $qb->getQuery(); $querySQL = $query->getSQL();
  • 63. Consultas en Doctrine QueryBuilder Introducción a Doctrine 2 ORM  Las consultas DQL hacen uso interno de los Prepared Statements de SQL, por motivos de seguridad (p.ej: SQL injections) y rendimiento (unidad transaccional).  Por defecto, y a menos que se especifique otra cosa, una consulta DQL obtiene todas las entidades relacionadas con la principal. Esto puede provocar problemas de recursos o de rendimiento si no se gestiona bien.  La naturaleza de las relaciones entre entidades es conocida por Doctrine gracias a los metadatos de sus asociaciones, por ello no es necesario especificar cláusulas ON o USING en los JOIN.  Las clases QueryBuilder y Query gestionan un caché de las consultas. El comportamiento y naturaleza de este caché es diferente según estemos en modo desarrollo o producción.
  • 64. Consultas en Doctrine SQL Nativo Introducción a Doctrine 2 ORM • Los resultados se mapean a entidades Doctrine mediante ResultSetMapBuilder. • Se utiliza Doctrine ORM. • Solo se soportan consultas SELECT. NativeQuery • Los resultados no se mapean a entidades Doctrine. • Se utiliza Doctrine DBAL. Query
  • 65. Consultas en Doctrine SQL Nativo Introducción a Doctrine 2 ORM Usando NativeQuery (mapeando a entidades) $rsmb = new ResultSetMappingBuilder($entityManager); $rsmb->addRootEntityFromClassMetadata('CineEntityPelicula', 'p'); $rsmb->addJoinedEntityFromClassMetadata( 'CineEntityComentario', 'c', 'p', 'comentarios', [ 'id' => 'comment_id' ] ); $sql = <<<SQL SELECT movie.id, movie.original_title, comment.id as comment_id, comment.texto, comment.fecha FROM movie INNER JOIN comment ON movie.id = comment.pelicula_id WHERE movie.year >= 1988 ORDER BY movie.id, comment.fecha SQL; $query = $entityManager->createNativeQuery($sql, $rsmb); $result = $query->getResult();
  • 66. Consultas en Doctrine SQL Nativo Introducción a Doctrine 2 ORM Usando Query (sin mapeado a entidades) $sql = <<<SQL SELECT spanish_title AS titulo, COUNT(comment.id) AS comentarios FROM movie JOIN comment ON comment.pelicula_id = movie.id GROUP BY movie.id ORDER BY comentarios DESC, spanish_title ASC LIMIT 5; SQL; $query = $entityManager->getConnection()->query($sql); $result = $query->fetchAll();
  • 67. Consultas en Doctrine Repositorios de Entidades Personalizados Introducción a Doctrine 2 ORM Cuando generamos una entidad, Doctrine nos proporciona un repositorio base por defecto para dicha entidad. El repositorio base consta de una serie de métodos comunes para operar con la entidad:  find(id) Retorna la entidad con el identificador id, o null si no existe.  findAll() Retorna un array con todas las entidades del repositorio.  findBy( array(criterios) [, array(ordenacion)] ) Retorna un array con las entidades que cumplan los criterios especificados en el primer parámetro, y ordenados por los del segundo parámetro (opcional).  findOneBy( array(criterios) ) Análogo al findBy() pero devolviendo solo un elemento, o nulo si no existe.
  • 68. Consultas en Doctrine Repositorios de Entidades Personalizados Introducción a Doctrine 2 ORM Algunos de los métodos del repositorio base pueden utilizarse de forma abreviada, permitiendo no especificar como parámetro el nombre de la propiedad. P.ej: findByTitulo(‘valor’), findOneByTitulo(‘valor’) Equivalen a: findBy(‘Titulo’=>’valor’), findOneBy(‘Titulo’=>’valor’) Esto se debe a la utilización del método ‘mágico’ __call() de PHP que hace Doctrine a la hora de localizar el nombre del método de la clase.
  • 69. Consultas en Doctrine Repositorios de Entidades Personalizados Introducción a Doctrine 2 ORM  El repositorio base de una entidad puede ser extendido a fin de proporcionar métodos específicos para consultas de usuario.  Este repositorio personalizado permite optimizar la obtención de resultados en las consultas, obteniendo mas control en la hidratación de los datos (p.ej: seleccionar parcialmente entidades y/o especificar operaciones en la consulta que no se harían de forma automática).  Doctrine permite especificar la clase que utilizaremos como repositorio de la entidad en la etiqueta @Entity, dentro de las anotaciones de dicha entidad. @Entity(repositoryClass=“PeliculaRepository”)  La clase (vacía) será generada mediante las herramientas de consola (CLI) de Doctrine, ejecutando: php vendor/bin/doctrine.php orm:generate-repositories /src
  • 70. Consultas en Doctrine Optimizando nuestro ejemplo Una optimización básica de nuestro ejemplo sería la construcción de un repertorio personalizado para la entidad Pelicula. Este constará de métodos que nos permitan recuperar solamente los datos que necesitamos mostrar en cada momento. Para ello crearemos consultas DQL de forma que…  Se seleccionen solo los campos específicos de las entidades asociadas en lugar de recuperar todos ellos (comportamiento por defecto).  Se haga en una sola operación lo que de otra forma requeriría mas de una (p.ej: Recuperar datos de una entidad asociada con fetch=‘lazy’). Introducción a Doctrine 2 ORM
  • 71. public function findConNumComentarios(){ return $this ->createQueryBuilder('p') ->leftJoin('p.comentarios', 'c') ->addSelect('COUNT(c.id)') ->groupBy('p.id') ->getQuery() ->getResult(); } Introducción a Doctrine 2 ORM Consultas en Doctrine Optimizando nuestro ejemplo
  • 72. public function findTeniendoEtiquetas(array $etiquetas) { return $queryBuilder = $this ->createQueryBuilder('p') ->addSelect('e.nombre ->addSelect('COUNT(c.id)') ->join('p.etiquetas', 'e') ->leftJoin('p.comentarios', 'c') ->where('e.nombre IN (:etiquetas)') ->groupBy('p.id') ->having('COUNT(e.nombre) >= :numEtiquetas') ->setParameter('etiquetas', $etiquetas) ->setParameter('numEtiquetas', count($etiquetas)) ->getQuery() ->getResult(); } Introducción a Doctrine 2 ORM Consultas en Doctrine Optimizando nuestro ejemplo
  • 73. public function findConComentarios($id) { return $this ->createQueryBuilder('p') ->addSelect('c') ->leftJoin('p.comentarios', 'c') ->where('p.id = :id') ->orderBy('c.fecha', 'ASC') ->setParameter('id', $id) ->getQuery() ->getOneOrNullResult(); } Introducción a Doctrine 2 ORM Consultas en Doctrine Optimizando nuestro ejemplo
  • 74. Gracias por su atención jrlaguardia@gmail.com Proyecto de ejemplo https://github.com/gonfert/cine Recursos Doctrine Project Site http://www.doctrine-project.org Notes on Doctrine 2 http://www.krueckeberg.org/notes/d2.html Mastering Symfony2 performance http://labs.octivi.com/mastering-symfony2- performance-doctrine/ Bibliografía Persistence in PHP with Doctrine ORM (Packt Publishing) Proyectos similares Propel http://propelorm.org Red Bean PHP4 http://redbeanphp.com Spot ORM http://phpdatamapper.com Introducción a Doctrine 2 ORM

Notas del editor

  1. &amp;lt;número&amp;gt; Introducción a Doctrine 2 ORM X Symfony Zaragoza
  2. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  3. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  4. El desarrollo de Doctrine comenzó en 2006. Actualmente Doctrine 2.x necesita PHP 5.4+ X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  5. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  6. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  7. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  8. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  9. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  10. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  11. La carpeta vendor aparecerá al instalar Doctrine mediante composer. Configuramos nuestro servidor web para que el índice (index.php) del sito web esté dentro de la carpeta web. También podemos usar el servidor web incorporado a PHP (5.4+), tecleando en la carpeta raíz del proyecto: php -S localhost:8000 -t web/ X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  12. Para utilizar Doctrine en el proyecto solo se necesita especificar la dependencia en la cláusula ‘require’ del fichero composer.json. El resto de datos son informativos pero no obligatorios, aunque no está de más especificar algunos datos identificativos del proyecto como buenas prácticas. La cláusula ‘autoload’ indicará a composer la carga automática de las clases de nuestra aplicación, que se encontrarán en la carpeta ‘src’, sin un nombre de namespace asignado y que seguirán el esquema de espacios de nombres y rutas de clase definido por el estándar PSR-0. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  13. En el objeto de configuración de Doctrine… Si el modo de desarrollo está activado: El cacheo se realiza en memoria, utilizando ArrayCache. Las clases delegadas (ProxyClasses) se generan en cada petición. Si el modo de desarrollo está desactivado (modo producción): Se comprueba la existencia de sistemas de cacheo. En el siguiente orden: APC, Xcache, Memcache y Redis ,a menos que se especifique uno como cuarto parámetro (opcional). Las clases delegadas (ProxyClasses) deben crearse explícitamente mediante las herramientas CLI de Doctrine. El tercer parámetro (opcional) especifica el directorio donde se lamacenarán las clases delegadas (ProxyClasses). X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  14. Este fichero estará presente en todo código que haga uso de la BBDD, incluido el de la configuración del interfaz de linea de comandos de Doctrine (CLI). Contenido y funciones del bootstrap: * Dependecias de clases de Doctrine * Establece el autocargador para librerias usadas por la aplicacion - Librerias de Doctrine. - Librerias propias de la app (/src). - Librerias extra instaladas con composer. * Carga la configuracion de la aplicación. * Fija la ruta donde se almacenaran las entidades. * Obtiene la configuracion de los metadatos de las entidades a traves de las anotaciones. Para ello, pasamos la ruta donde se definen las entidades y el modo de trabajo ($dev=true/false). El modo de trabajo (desarrollo/produccion) influye en las optimizaciones que usa doctrine en caches y metadatos. * Obtiene una instancia del &amp;apos;Entity Manager&amp;apos; para la aplicacion de nuestro ejemplo. En aplicaciones reales deberia usarse el patrón DI (Inyección de dpendencia). X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  15. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  16. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  17. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  18. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  19. Como crea nuevas entidades Doctrine. Doctrine necesita convertir los datos recuperados de la DB en entidades (objetos), esto es lo que se conoce por hidratación. Doctrine ya no utiliza el modo tradicional de construccion de objetos (mediante el operador new). Lo que hace ahora es: Construye una cadena que representa una versión vacía y serializada de la clase a crear. Deserializa esa cadena para convertirla en objeto. Esta es la razón por la que habría implementar de forma segura __wakeup(). Almacena ese objeto como proptotipo de la clase. Este proceso se realiza una sola vez por cada clase entidad y se realiza en el método newInstance() de la clase ClassMetadataInfo. Cada vez que se necesita hidratar una entidad, se clona el prototipo. Esta es la razon por la que habría que implementar de forma segura __clone(). Este método permite no utilizar el constructor de la entidad cada vez que una nueva entidad es hidratada. Cuando Doctrine hace esto, la entidad todavía no tiene ni datos o identificadores definidos, esto se realiza posteriormente. Haciendolo así, de forma que la logica de la clase solo se ejecuta cuando la entidad tenga identificadores, nos aseguramos que dicha lógica solo se ejecutará cuando deserialicemos o se clonemos la entidad, no cuando Doctrine lo haga. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  20. Identity Map: $article = $entityManager-&amp;gt;find(&amp;apos;CMS\Article&amp;apos;, 1234); $article2 = $entityManager-&amp;gt;find(&amp;apos;CMS\Article&amp;apos;, 1234); $article y $article2 apuntan a la misma instancia. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  21. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  22. El método find() hidrata y devuelve una entidad del tipo especificado por el primer parámetro e identificado por el segundo. El estado de la entidad devuelta es managed. Cualquier cambio realizado sobre esta entidad será sincronizado con la BD al llamar al metodo flush(). Podemos cambiar el estado de la entidad, de forma que el Entity Manager deje de gestionarla, ejecutando detach(). Su estado pasará a detached, y sus cambios serán ignorados al llamar a flush().Una entidad no gestionada (estado detached) puede volver a ser gestionada de nuevo (estado managed) ejecutando merge(). El borrado de una entidad se efectua marcándola para su eliminación mediante la llamada al método remove(). Su estado pasará a ser removed. El borrado efectivo de la entidad en la BD no se producirá si no se sincroniza este cambio mediante una ejecución del método flush().El método persist() se emplea para cambiar el estado a managed de aquellas entidades que no han sido sincronizadas nunca con la DB. Este sería el caso de aquellos objetos de nueva creación ( creados con new clase() ), cuyo estado por defecto es new. El Entity Manager dispone de mas métodos, tales como clear(), copy(), lock(), contains(), refresh().. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  23. Doctrine soporta la creación de tipos de datos definidos por el usuario. Basta impementar una clase dreivada de Doctrine\DBAL\Types\Type y hacer override de sus métodos. &amp;lt;?php namespace My\Project\Types; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Platforms\AbstractPlatform; class MyType extends Type { const MYTYPE = &amp;apos;mytype&amp;apos;; // modify to match your type name public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { // return the SQL used to create your column type. // To create a portable column type, use the $platform. } public function convertToPHPValue($value, AbstractPlatform $platform) { // This is executed when the value is read from the database. //Make your conversions here, optionally using the $platform. } public function convertToDatabaseValue($value, AbstractPlatform $platform) { // This is executed when the value is written to the database. // Make your conversions here, optionally using the $platform. } public function getName() { return self::MYTYPE; // modify to match your constant name } } X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  24. El sistema de generación de los metadatos se escoge en el fichero donde especificamos la configuración de nuestro proyecto Doctrine (bootstrap.php): […]// Create a simple &amp;quot;default&amp;quot; Doctrine ORM configuration for Annotations $isDevMode = true; $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__.&amp;quot;/src&amp;quot;), $isDevMode); // or if you prefer YAML or XML //$config = Setup::createXMLMetadataConfiguration(array(__DIR__.&amp;quot;/config/xml&amp;quot;), $isDevMode); //$config = Setup::createYAMLMetadataConfiguration(array(__DIR__.&amp;quot;/config/yaml&amp;quot;), $isDevMode); […] X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  25. @Entity Marca una clase como entidad. El atributo mas importante es ‘repositoryClass’, es opcional, y permite especificar una clase que será el repositorio de la entidad personalizado. Esta clase hereda de Entityrepository (repositorio base) y permite extender las capacidades del mismo. @Table Es opcional, por defecto el nombre de la tabla en la BD que corresponde a la entidad es el mismo que el de la entidad.Sin embargo, si queremos especificar otro, deberemos consignar esta etiqueta con su atributo ‘name’. Permite definir otras características de la tabla como índices o restricciones (constraints) para un campo o campos. @Id Define que campo va a ser una clave primaria. En caso de una clave formada por varios campos, todos ellos deberían llevar esta etiqueta. Permite especificar la estrategia de generación del mismo y el tipo. @Column Define que es un campo o propiedad persistente en Doctrine. Si alguna variable de instancia de la clase no lo lleva, no se considerará persistente. Permite especificar, nombre, tipo y características del campo. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  26. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  27. La creación de getters y setters puede ser invocada tantas veces como se necesite. Podemos especificar la creación de getters y setters para una entidad en concreto especificándola al final del comando con el parámetro --filter. Si realizamos cambios en la estrcutura de la entidad, tales como cambiar nombre o tipo de campos, añadir nuevos campos o borrarlos, o modificar relaciones, tendremos que actualizar el esquema de la base de datos. Doctrine provee de comando de consola para actualizar/modificar y borrar el esquema, aunque esto son operaciones peligrosas que pueden conllevar la perdida de datos. Crear esquema php vendor/bin/doctrine.php orm:schema-tool:create Actualizar esquema php vendor/bin/doctrine.php orm:schema-tool:update Borrar php vendor/bin/doctrine.php orm:schema-tool:drop X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  28. Ejemplos de asociación unidireccional: (One to Many) Usuario con sus Direcciones class Usuario { /** @ManyToOne(targetEntity=“Direccion&amp;quot;) */ private $direccion; } class Direccion {} (One to One) Cliente con envio class Cliente { /** @OneToOne(targetEntity=“Envio&amp;quot;) */ private $envio; } class Envio {} X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  29. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  30. La opción &amp;apos;extra_lazy&amp;apos; solo está disponible desde Doctrine 2.1 en adelante. La estrategia de &amp;apos;fetch&amp;apos; se puede cambiar &amp;apos;al vuelo&amp;apos;.: https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql &amp;lt;?php $query = $em-&amp;gt;createQuery(&amp;quot;SELECT u FROM MyProject\User u&amp;quot;); $query-&amp;gt;setFetchMode(&amp;quot;MyProject\User&amp;quot;, &amp;quot;address&amp;quot;, \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER); $query-&amp;gt;execute(); http://labs.octivi.com/mastering-symfony2-performance-doctrine/ http://doctrine-orm.readthedocs.org/en/latest/tutorials/extra-lazy-associations.html
  31. Esta ArrayCollection es un tipo de datos que figura en Doctrine\Common que encapsula el array clásico de PHP pero que implementa varios interfaces: ArrayAccess Countable Traversable Predefinidos en la SPL X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  32. Por defecto las nuevas entidades no son manejadas automáticamente por el EntityManager, y deben ser marcadas con Persist() de forma manual para cada entidad asociada. Esto puede evitarse especificando cascade={“Persist”} como atributo de la relación. De esta forma las entidades asociadas son perisitidas automáticamente al serlo la principal. Otro valor del mismo atributo es “remove”, que de especificarse, indicaría que las entidades asociadas deben borrarse al borrarse la principal. Estas operaciones en cascada no son las mismas que las que se hacen en SQL. Doctrine gestiona en memoria estas operaciones a través del ORM y pueden llegar a consumir muchos recursos, por lo que deben usarse con especial cuidado. El orphanRemoval=true indica que las entidades realcionadas que ya no estén vinculadas a la principal sean eliminadas de memoria. Si una etiqueta de la colección de etiquetas de una película es eleminada y esa película era la única vinculada a esa etiqueta, la entidad etiqueta será permanentemente borrada.El valor fetch=EAGER indica que se recuperen los datos medinate un JOIN en una sola consulta, en lugar de usar dos consultas (fecth=LAZY) que sería el valor por defecto. El identificador de etiqueta no lleva @GeneratedValue, por lo que Doctrine no puede adivinarlo. @JoinTable y @JoinColumn figuran como override del comportamiento por defecto X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  33. La consulta se parsea, y se genera un SQL que es ejecutado a través de Doctrine DBAL. Desde ese SQL se obtienen los datos, que son hidratados en forma de estructuras PHP, y que luegos son recuperadas como resultados. Normalmente Doctrine devuelve los resultados en forma de arrays de objetos PHP, pero Doctrine permite escribir hidratadores personalizados (Custom Hydrators), de forma que los datos puedan ser recuperados en cualquier tipo de estrcutura soportada por PHP. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  34. A la hora de crear una instancia de QueryBuilder, al especificar la entidad principal, el SQL geenrado por defecto, equivale a una consulta del tipo ‘SELECT * FROM entidad’. QueryBuilder cachea las consultas y gestionasu estdo interno para mejorar el rendimiento general. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  35. Doctrine está pensado para ser utilizado con sistemas de cache, tales como APC, Xcache, Memcache o Redis. En modo desarrollo: Se cachea en memoria usando ArrayCache. Las clases delegadas (proxy) se autogeneran en cada petición. En modo producción: Se busca un sistema de cache instalado, en este orden (APC, Xcache,Memcache,Redis), a menos que se especifique uno en concreto. Las clases delegadas (proxy) han de crearse específicamente medinate línea de comandos. El problema de consumo de recursos debido a la hidratación de los resultados puede ser resuelto utilizando una o varias vías de las siguientes: Utilizando selecciones parciales de entidades “SELECT partial u{id,name} FROM …”. Modificando la estrategia de carga (fecth) de las entidades relacionadas con la principal. Rediseño de las entidades afectadas y/o sus repositorios personalizados. En caso de tratamiento masivo de datos: utilización del método Iterate() del objeto Query. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  36. Doctrine está pensado para ser utilizado con sistemas de cache, tales como APC, Xcache, Memcache o Redis. En modo desarrollo: Se cachea en memoria usando ArrayCache. Las clases delegadas (proxy) se autogeneran en cada petición. En modo producción: Se busca un sistema de cache instalado, en este orden (APC, Xcache,Memcache,Redis), a menos que se especifique uno en concreto. Las clases delegadas (proxy) han de crearse específicamente medinate línea de comandos. El problema de consumo de recursos debido a la hidratación de los resultados puede ser resuelto utilizando una o varias vías de las siguientes: Utilizando selecciones parciales de entidades “SELECT partial u{id,name} FROM …”. Modificando la estrategia de carga (fecth) de las entidades relacionadas con la principal. Rediseño de las entidades afectadas y/o sus repositorios personalizados. En caso de tratamiento masivo de datos: utilización del método Iterate() del objeto Query. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  37. El mapeado de los resultados de una consulta en SQL nativo debe tener en cuenta los diferentes tipos de resultados que pueden darse: Entity Joined Entity Field Result Scalar Result Meta Result La clase ResultSetMapping es usada internamente por Doctrine cuando creamos consultas SQL, de forma transaparente al usuario. En este caso, al usar SQL nativo y para mapear esos resultados a entidades Doctrine, debemos usarla explícitamente mediante el método ResulSetMapBuilder del EntityManager. Las entidades obtenidas como resultado de la consulta solo tendrán informados los campos incluidos en el SELECT, por lo tanto, las llamadas a métodos de la entidad para obtener el contenido del resto de campos darán un resultado nulo. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  38. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  39. El repositorio de una entidad es una implementación del patrón de diseño TableGatWay. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  40. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  41. Los métodos del repositorio no hacen JOIN (SQL) de las entidades relacionadas con la principal, a menos que se haya espcificado el valor ‘EAGER’ en la cláusula ‘fetch’ de la anotación correspondiente a la relación entre entidades (el valor por defecto es ‘LAZY’. El comportamiento por defecto (fecth=LAZY) obliga a Doctrine a hacer otra consulta cuando necesita obtener los datos de las entidades asociadas a la principal. Esto penaliza el rendimiento. Solucionarlo mediante fecth=EAGER puede no ser aconsejable en todos los casos, ya que, dependiendo de la cantidad de entidades relacionadas podemos obtener un excesivo consumo de recursos innecesarios (p.ej: un listado, con determinada información por registro). Una forma de solucionar el problema anterior, sin necesidad de modificar la estrategia de ‘fetch’ de la relación, es utilizar consultas que obtengan la información que vamos a necesitar con el mínimo costo en consultas y en recursos. Esto implica la creación de repertorios de entidades personalizados. X Symfony Zaragoza Introducción a Doctrine 2 ORM &amp;lt;número&amp;gt;
  42. &amp;lt;número&amp;gt; Introducción a Doctrine 2 ORM X Symfony Zaragoza