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

Leave a Reply

Required fields are marked *.