Carlos Robles frikiblog

Integrar Doctrine 2 con Zend Framework 2, desde una base de datos existente.

| 2 Comments

Más o menos podemos decir que doctrine esta pensado para no pensar en el servidor de bases de datos, en tablas,  ni en nada que no sea puramente las estructuras que vamos a utilizar en la programacion.
Por tanto se suele empezar creando entidades, que después doctrine transforma en lo que corresponda en el servidor de base de datos. Estamos totalmente abstraidos de la persistencia de datos.

Para mi, que siento una especie de amor por el diseño de modelos de base de datos, esto es un poco lo contrario a lo que me gustaria.

Mi forma de empezar un proyecto suele ser primero pensar a fondo un modelo, crear un diagrama con MySql WorkBench, desde ahi obtener el script sql y ejecutarlo directamente el servidor (Database > Forward engineering) y despues sincronizar cualquier cambio entre el modelo y el servidor. Despues, obtengo las entidades y el mapeo a partir del servidor de base de datos.

Esto es algo que se hace una vez al inicio del proyecto, pero es muy probable que haya que repetirlo si hay cambios importantes en el modelo. Hay que tener en cuenta que si la base de datos es compleja (muchas relaciones entre tablas, etc) puede que el resultado no sea el esperado, y tengas que retocar el mapeo a mano. Según la documentación de doctrine, automaticamente solo encotraremos el 70-80% de la informacion necesaria. Además, y copio directamente de la documentación “the detection from an existing database cannot detect inverse associations, inheritance types, entities with foreign keys as primary keys and many of the semantical operations on associations such as cascade”

En cualquier caso, me sigue pareciendo la mejor forma de empezar para los que nos gusta tener control total sobre como se modela la base de datos.

Requisitos

Este manual está pensado para integrar con Zend Framework 2. Por tanto, tendriamos que tener instalado:

  • ZendFramework 2
  • Doctrine 2
  • Los modulos doctrine-module y doctrine-orm-module
  • Además algunos módulos de Symfony:  console y yaml

Cómo instalar doctrine y sus modulos, lo veremos facilmente en la documentación de doctrine. Los de symfony es facil que ya nos los haya instalado el composer como dependencia de algun otro, si no es asi, tendremos que investigar un poco (no quiero salirme demasiado del tema).

Este mismo tutorial serviría para hacer la ingeniería inversa a un sistema que no fuese zend. En ese caso solo necesitariamos doctrine, y tener symfony instalado.

Procedimiento

Hay distintas formas de hacerlo. Una es desde consola, y otra es desde código fuente. A mi me gusta desde consola, porque es la que nunca me ha fallado, por tanto explicaré esta.

Lo primero, creamos una carpeta y dentro creamos un archivo llamado cli-config.php, con este contenido:

<?php

//1.Configuración
 $config = new \Doctrine\ORM\Configuration();
setMetadataCacheImpl($cache);
 $config->setQueryCacheImpl($cache);

// 2.Caché
 $cache = new \Doctrine\Common\Cache\ArrayCache();
 $config->setMetadataCacheImpl($cache);
 $config->setQueryCacheImpl($cache);

// 3. Driver
$driverImpl = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(array(__DIR__.'/Entities'));
// $driverImpl = new Doctrine\ORM\Mapping\Driver\YamlDriver('./config/yml/');
//$driverImpl = new Doctrine\ORM\Mapping\Driver\XmlDriver("./config/xml/");

$config->setMetadataDriverImpl($driverImpl);

//4. Proxies
 $config->setProxyDir(__DIR__ . './Proxies');
 $config->setProxyNamespace('Proxies');

//5. Conexión
 $connectionOptions = array(

'driver'    => 'pdo_mysql',

    'host'     => 'TU_HOST',
    'port'     => '3306',
     'user'     => 'TU_USUARIO',
    'password' => 'TU_BASE_DE_DATPS',
    'dbname'   => 'TU_CONTRASEÑA',
 'mapping_types'   => array ( 'enum'   => 'string')  //ojo aqui
 );

//6. EntityManager
 $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);

/** @var $em \Doctrine\ORM\EntityManager */
$platform = $em->getConnection()->getDatabasePlatform();

$platform->registerDoctrineTypeMapping('enum', 'string');//ojo aqui

//7.HelperSet
 $helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
 ));

?>

Ahora tenemos que buscar los binarios de doctrine. En ZF los podremos encontrar en distintos sitios, segun com hayamos instalado doctrine. El sitio habitual sería aquí:
\vendor\doctrine\orm\bin

Lo que yo recomiendo es copiar directamente el archivo cli-config.php. Ademas, creamos una carpeta Entities.

Destacamos algunas cosas:

Doctrine puede utilizar distintos drivers para crear  las Classes, Classes con anotaciones o los archivos de configuración (xml, yml), en el punto 3 puedes elegir el driver. A mi me gusta hacer clases con anotaciones

Ahora abres un terminal (Inicio > ejecutar > cmd) y vas a la carpeta donde estan los binarios de doctrine y el cli-config.php, y ejecutas el comando que corresponda:
Ahora abres una terminal donde tienes el archivo y puedes ejecutar cualquiera de los siquientes comandos, segun corresponda:

Generar mapeo con xml

doctrine orm:convert-mapping --from-database xml config/xml/

Generar mapeo con yml

doctrine orm:convert-mapping --from-database yml config/yml/

Generar Classes

doctrine orm:generate-entities Entities/

Generar Classes con anotaciones

doctrine orm:generate-entities --generate-annotations=true --regenerate-entities=true Entities/

Nota:Para generar las clases es necesario que hayas generado primero los archivos de mapeo ya sea xml o yml.

Nota:Doctrine no trabaja con los tipos ENUM. Para ello lo que hacemos es un mapeo de tipos, para decirle que los tipos enum los trate como un string.  hemos visto estas lineas:

 $connectionOptions = array(

//...
'mapping_types' => array ( 'enum' => 'string')
);

$platform->registerDoctrineTypeMapping('enum', 'string');

Las dos hacen exactamente lo mismo, pero he tenido casos en distintos sistemas donde uno funciona y el otro no. Como poner los dos no supone ningun problema, suelo usar los dos aunque sea redundante.

Al terminar la ejecución, dentro de la carpeta Entities, tendras una clase para cada una de las tablas de la base de datos, y estarán preparadas para trabajar con doctrine.

Integración:

Una vez tenemos las clases, donde colocarlas en nuestro proyecto depende de cada uno. Hay gente que tiene pocas entidades, y las coloca directamente dentro del modulo que las utiliza. Esto no es una buena práctica.

A mi personalmente, me gusta agrupar la capa de acceso a datos en su propia carpeta ajena a todo lo demás. En ZF, para no complicarme demasiado, creo un módulo con todas las clases referentes al acceso a datos, como Entidades, repositorios, etc.

Para eso, creo una estructura como la que se ve:

24-07-2013 12-09-39

Es decir, dentro de module, creo un modulo de la forma habitual, le llamo DBAL (data base access layer) y dentro de src creo la carpeta DBAL, que contendra las que necesitemos, como por ejemplo Entity, Model, Repository, etc. Dentro de Entity colocaremos las clases que acabamos de crear.

Además, deberemos cambiar el namespace de cada una, para  que podamos trabajar con ellos. Siempre utilizo DBAL\Entity, para todas las entidades, para ello la primera linea de los archivos de todas las entidades debe ser:

namespace DBAL\Entity;

Ahora, le decimos ZendFramework como puede cargar todas. Primero en

config/aplication.config.php, dentro del array ‘modules’ añadimos “DBAL”

return array(
 'modules' => array(
 'DoctrineModule',
 'DoctrineORMModule',
'DBAL',
//...

y en la configuracion del modulo

/module/DBAL/config/module.config.php

le decimos a doctrine como cargar las clases del namespace DBAL\Entity, asi:

<?php
return array(

 'doctrine' => array(
 'driver' => array(
'mi_annotation_driver' => array(
 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
 'cache' => 'array',
 'paths' => array(__DIR__ . '/../src/DBAL/Entity'),
 ),
 'orm_default' => array(
 'drivers' => array(
'DBAL\Entity' => 'mi_annotation_driver',
 ),
 ),
 ),
 )
);

Referencias:

http://jaehoo.wordpress.com/2011/11/17/ingenieria-en-inversa-en-php-con-doctrine-reverse-engineering-db/
http://parasitovirtual.wordpress.com/2011/03/07/configuracion-de-doctrine-2/
http://www.doctrine-project.org/docs/orm/2.1/en/tutorials/getting-started-xml-edition.html#a-first-prototype
http://www.doctrine-project.org/docs/orm/2.0/en/reference/tools.html
http://www.doctrine-project.org/docs/orm/2.1/en/reference/configuration.html
http://wildlyinaccurate.com/useful-doctrine-2-console-commands/

Facebook Twitter Linkedin Plusone Digg Delicious Reddit Email

2 Comments

  1. una consulta amigo, cual es el pro y la contra de usar Doctrine?
    es recomendable usarla?
    Gracias

    • Hola! Pues eso sería un debate muy largo, donde distinta gente te diria distintas cosas, y que tambien depende mucho del tipo de sistema que estes montando (su uso, su tamaño). Para mi, lo que es totalmente necesario es que haya una capa de acceso a datos totalmente diferenciada. Esto puede ser el DAL de doctrine, o puede ser otro sistema mas sencillo, o incluso uno que pueda hacer uno mismo.

      En cualquier caso, la necesidad de esta capa es algo que ningun programador serio debería olvidar, porque aporta unas cuantas cosas necesarias, como para empezar una buena separacion de los datos y el control, que da estabilidad, escalabilidad, y unas cuantas ventajas de las ampliamente perseguidas. El planteamiento es tener una coleccion de clases que se encargan de la comunicacion con la base de datos, de una forma lo mas generica posible (por ejemplo, pensando que se deberian poder utilizar sin cambiar nada en distintos proyectos), y despues, en cada proyecto tener una coleccion de objetos de negocio que se comunican con estas clases, pero que no saben nada de la base de datos. Esto aporta otra ventaja clara, que ees que si un dia cambias de sistema de base de datos (por ejemplo, de mysql a oracle, o incluso a un nosql) el impacto en tu programacion es minimo, y con pocos o ningun cambio todo seguiria funcionando, porque toda tu aplicacion se comunica con la base de datos a traves de un interfaz que no cambia independientemente de lo que haya detrás.
      Ademas, en esta capa puedes centralizar de una manera efectiva las medidas de seguridad. Todo esto son cosas que puede crear uno mismo, o puedes confiar en otras que ya estan hechas, y utilizar cualquier ORM opensource, que te aporta mucho mas, y son mucho mas estables desde el primer dia.

      Los famosos son doctrine y Propel. Los dos me gustan mucho. Yo utilizo doctrine mas, porque va un poco mas de la mano de zend, pero propel es muy bueno tambien. Entre uno y otro no iría a muerte con ninguno.

      Yo en muchos proyectos no utilizo doctrine sino que utilizo mi propia coleccion de clases genericas que crean la abstraccion de la base de datos, y otras de negocio que crean el mapeo a objectos.
      Muchas veces, hacer esto puede resultar mas sencillo y mas liviano que utilizar un sistema como doctrine o propel, pero otras veces, justo al contrario, resulta mas trabajoso y menos estable. Por tanto siempre es una buena opcion pensar en sistemas que ya esten hechos aunque pueda parecer que aportan mucho mas de lo que se necesita.

      Si estas en ZF2, en cuanto a utilizar las clases propias de Zend, te diré que aunque son muy buenas, y utilizan patrones de diseño muy loables, en mi opinion son un poco demasiado complicados de entender, hay que desarrollar demasiado codigo, no acaba de estar todo tan centralizado, y te aportan menos cosas automaticamente. Hay que pensar que zend es un ssitema muy amplio, que crece en muchas direcciones, mientras que doctrine o propel estan centrados en una cosa, y llevan tiempo creciendo en una sola direccion.

      Caundo trabajo con Zend, mi forma de hacerlo es como cuento en este post, crear primero la base de datos, y despues con las herramientas propias de doctrine crear automaticamente todas las clases necesarias, por tanto en cosa de minutos tengo la integracion completa con la base de datos, lo cual es maravilloso.

      Tambien, en esta respuesta de stackoverflow, aunque es sobre otras cosas, se puede ver un poco de debate sobre pros y contras de los orm en general. Esta bien leer los comentarios a la respuesta tambien, para ver el contraste de opiniones.

Leave a Reply

Required fields are marked *.