Doctrine 2 ORM en Zend Framewok: parte II

Por zsamer en Noviembre 23, 2009

Continuando con nuestro articulo anterior Doctrine 2 ORM en Zend Framewok: parte I, en esta oportunidad quiero compartir unos tips de como integrar ORM Doctrine 2 con Zend Framework, para ello va ser necesario haber leido también el articulo sobre Bootstrapping con Zend Application, que será nuestra base para la integración.

Lo primero que tenemos que hacer es descargar Doctrine 2 (actualmente 2.0.0-ALPHA3), descomprimir en la carpeta "library" del proyecto Zend y luego agregar en application.ini las siguientes lineas:

CODE:
  1. autoloaderNamespaces.doctrine = "Doctrine"
  2. autoloaderNamespaces.proxies = "Proxies"

Con esto agregamos las librerías de Doctrine 2 en el Include Path de PHP.

Lo segundo que tenemos que hacer es crear un método _init en la clase Boostrap, a éste método le llamaremos _initDoctrine, obviamente ;-)

PHP:
  1. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
  2. {
  3.     protected function _initDoctrine() {
  4.         $config = new Configuration();
  5.         $cache = new ApcCache();
  6.         $config->setMetadataCacheImpl($cache);
  7.         $config->setQueryCacheImpl($cache);
  8.  
  9.         $config->setProxyDir(APPLICATION_PATH . "/../library" . DIRECTORY_SEPARATOR.'Proxies');
  10.         $config->setProxyNamespace('Proxies');
  11.        
  12.         $connPDO = array();
  13.  
  14.         $this->bootstrap('db');
  15.         $connPDO['pdo'] = $this->getResource('db')->getConnection();
  16.  
  17.         $conn = DriverManager::getConnection($connPDO, $config);
  18.         $em = EntityManager::create($conn, $config);
  19.  
  20.         Zend_Registry::set('em', $em);
  21.         return $em;
  22.     }
  23. }

Podemos observar que hace uso de namespace incluido en la versión 5.3 de PHP, también observamos que se hace uso de la conexión a la base de datos de Zend Db ($this->bootstrap('db')), configurado en "application.ini", obtenemos el objeto PDO de conexión y se lo entregamos al DriverManager de Doctrine 2 ;-) todo muy elegante ¿no?, finalmente lo guardamos en el Zend_Registry.

Luego creamos las clase Entity, que representan un objeto de dominio del sistema:

Address:

PHP:
  1. <?php
  2. namespace doctrineorm\models\entity;
  3.  
  4. /**
  5. * @Entity
  6. * @Table(name="addresses")
  7. */
  8. class Address {
  9.  
  10.     /**
  11.      * @Id
  12.      * @Column(type="integer")
  13.      * @GeneratedValue(strategy="AUTO")
  14.      */
  15.     private $id;
  16.  
  17.     /** @Column(type="string", length=255) */
  18.     private $street;
  19.  
  20.     /** @OneToOne(targetEntity="User", mappedBy="address") */
  21.     private $user;
  22.  
  23.     public function getId() {
  24.         return $this->id;
  25.     }
  26.  
  27.     public function getStreet() {
  28.         return $this->street;
  29.     }
  30.  
  31.     public function setStreet($street) {
  32.         $this->street = $street;
  33.     }
  34.  
  35.     public function getUser() {
  36.         return $this->user;
  37.     }
  38.  
  39.     public function setUser(User $user) {
  40.         if ($this->user !== $user) {
  41.             $this->user = $user;
  42.             $user->setAddress($this);
  43.         }
  44.     }
  45. }

y
User:

PHP:
  1. <?php
  2.  
  3. namespace doctrineorm\models\entity;
  4.  
  5. /**
  6. * @Entity
  7. * @Table(name="users")
  8. */
  9. class User {
  10.    
  11.     /**
  12.      * @Id
  13.      * @Column(type="integer")
  14.      * @GeneratedValue(strategy="AUTO")
  15.      */
  16.     private $id;
  17.  
  18.     /** @Column(type="string", length=50) */
  19.     private $name;
  20.  
  21.     /**
  22.      * @OneToOne(targetEntity="Address")
  23.      * @JoinColumn(name="address_id", referencedColumnName="id")
  24.      */
  25.     private $address;
  26.  
  27.     public function getId() {
  28.         return $this->id;
  29.     }
  30.  
  31.     public function getName() {
  32.         return $this->name;
  33.     }
  34.  
  35.     public function setName($name) {
  36.         $this->name = $name;
  37.     }
  38.  
  39.     public function getAddress() {
  40.         return $this->address;
  41.     }
  42.  
  43.     public function setAddress(Address $address) {
  44.         if ($this->address !== $address) {
  45.             $this->address = $address;
  46.             $address->setUser($this);
  47.         }
  48.     }
  49. }

Sin entrar en mucho detalle, ya que no es objetivo del post abundar, vemos que las relaciones entre clases se configuran mediante anotaciones (docblock annotations), más información aquí....

Nuestra clase de Modelo DAO UserDao:

PHP:
  1. <?php
  2. /**
  3. * Description of UserDao
  4. *
  5. * @author Andrés Guzmán
  6. */
  7.  
  8. require_once "doctrineorm/models/entity/User.php";
  9. require_once "doctrineorm/models/entity/Address.php";
  10.  
  11. use doctrineorm\models\entity\User;
  12. use doctrineorm\models\entity\Address;
  13.  
  14. class Doctrineorm_Model_UserDao
  15. {
  16.     public function getEntityManager() {
  17.         if(Zend_Registry::isRegistered('em')) {
  18.             return Zend_Registry::get('em');
  19.         }
  20.         return null;
  21.     }
  22.    
  23.     public function findAll() {
  24.         return $this->getEntityManager()->createQuery('select u from doctrineorm\models\entity\User u')->getResult();
  25.     }
  26.  
  27.     public function save(User $user) {
  28.         $this->getEntityManager()->persist($user);
  29.         $this->getEntityManager()->flush();
  30.     }
  31.  
  32.     /* ... etc, otros métodos del DAO (Data Access Object) ...*/
  33. }

La clase controladora IndexController.php dentro del módulo doctrineorm:

PHP:
  1. class Doctrineorm_IndexController extends Zend_Controller_Action
  2. {
  3.     private $_model;
  4.  
  5.     public function init()
  6.     {
  7.         parent::init();
  8.         $this->_model = new Doctrineorm_Model_UserDao();
  9.     }
  10.  
  11.     public function indexAction()
  12.     {
  13.         $this->view->title = "Hello World! Doctrine 2 and Zend Framework!";
  14.         $this->view->users = $this->_model->findAll();
  15.     }
  16. }

Finalmente la vista index.phtml:

PHP:
  1. <h3><?php echo $this->title ?></h3>
  2.  
  3. <?php foreach ($this->users as $user): ?>
  4. <strong>Name:</strong> <?php echo $user->getName()?> &nbsp;-&nbsp;
  5. <strong>Address:</strong> <?php echo $user->getAddress()->getStreet()?>
  6. <br />
  7. <?php endforeach; ?>

Como ven, es bastante simple de integrar con Doctrine 2, bienvenidos al mundo de ORM con Doctrine 2 y Zend Framework!

Hasta la próxima! ;-)

Comentarios

7 Responses to “Doctrine 2 ORM en Zend Framewok: parte II”

  1. Doctrine 2 ORM en Zend Framewok: parte I | Zend Framework: Estado del Arte on Noviembre 24th, 2009 12:52 pm

    [...] Continuamos en: Doctrine 2 ORM en Zend Framewok: parte II [...]

  2. Colleen Dick on Noviembre 24th, 2009 9:32 pm

    Gracias por el articulo, me dio pausa.

    Vine aqui esperando algo bien bien sencillo como lo hace symfony la integracion entre Doctrine y ZF,

    Esperaba que fuera mas sencillo que, por ejemplo, integrar Doctrine 1.2 con las versiones actuales de ZF que funcionan sobre pre PHP5.3

    En primer lugar este ejemplo tiene mas require y use! y para mi lo ideal sera dejar todo aquello al autoloader y no tener que agragarlo para cada archivo.

    En segundo, la clases que se llaman Entity parecen ser algo que Doctrine mismo puede crear automaticamente basada en o la especificacion YAML o el esquema nativa SQL de la base de datos.

    En tercero, todos los objectos en el bootstrapper paracen agregar mas (no menos) posibilidades de enredarsese, ahora tenemos EntityManager, y ahora me toca aprender la estructura y los metodos de aun mas objetos. luego tambien una DAO para cada clase.

    En cuarto, ?cuanto se necesitara de optimizacion para borrar el efecto del overhead de esta complejidad adicional?

    Solamente preguntas para hacer pensar! :)

  3. zsamer on Noviembre 24th, 2009 9:41 pm

    Estimada @ColleenDick, ningún problema, sigue utilizando 1.2 ;-)

    Personalmente para mi no agrega ninguna complejidad adicional, me es muy simple y cómodo usarlo.

    Cuando se agreguen los namespace en ZF (versión dos) los require desaparecen y por ahora si no quieres require/include, pues bien usas la nomenclatura de nombre por directorios: Doctrineorm_Model_User ¿conoces ese método?

    Suerte con la 1.2 :-)

  4. zsamer on Noviembre 25th, 2009 1:53 am

    Hola @ColleenDick, continuando con lo anterior (ahora tengo un poco más de tiempo) …

    Creo que deberías actualizarte un poco más al respecto y evitar comentarios sin tener mucha idea del tema:

    En primer lugar, Doctrine 2 es un 30% más rápido que la versión 1.2, te invito a leer mi último artículo sobre Doctrine 2.

    En segundo lugar, por donde lo mires es mucho mejor Doctrine 2, a nivel de arquitectura, performance, escabilidad, buenas prácticas, componentes más desacoplados, limpios y reutilizables etc…

    Tercero, las clases que se llaman Entity las puedes crear manualmente o generar automáticamente desde alguna fuente como YAML u otra (lo cual no era mi objetivo abordar).

    Como cuarto lugar, un DAO (Data Access Object) es una implementacion de patrón de diseño que es independiente del Doctrine o de cualquier otro ORM o Layer de BD. Como cualquier patrón es opcional implementarlo, para un buen y robusto diseño, escalable y para reutilizar objetos del modelo se debería de implementar ;-)

    Quinto lugar, todos los objetos en el Bootstrap, personalmente no me enreda, todo lo contrario. Creo que tus conocimientos de OOP y arquitectura de software deben ser pocos para complicarte por cosas tan sencillas.

    Sexto lugar, nunca he necesitado optimización para borrar el efecto del overhead, de hecho no he tenido esos problemas, Doctrine 2 es más rápido y maneja internamente un sistema de cache muy eficiente ;-), incluso sin cache es mas rápido que la 1.2

    Por ultimo, no por nada Benjamin Eberlei (Contribuidor de Zend Framework y Doctrine 2) ya está trabajando en la integración de Doctrine 2 con Zend Framework.

    Te recomiendo que te actualices un poco antes de comentar ;-)

    Saludos!

  5. nelson on Diciembre 27th, 2009 10:26 pm

    Bueno yo me vengo arriesgando con una solucion en flex desde que Zend no tenia ni Zend_Amf(Me tocaba con AMFPHP) y Doctrine ni siquiera tenia una versión totalmente estable y tuve muchos contradictores(Y sin embargo lleve proyectos de producción asi), me gustaría que ellos vieran el estado en el que se muestra aquí.

    Por el momento vengo ya utilizando Flash Builder y Flex SDK 3.4.1, Zend 1.9.8 y Doctrine 1.2.1 y totalmente estable mi aplicación.

    Espero para el 2010 comenzar a migrar a Zend 2.0, Doctrine 2.0 y Flex 4! Wow potencia total.

    Por ahora no es recomendable ya que en ninguno de los casos son versiones estables para producción.

    Pero me siento orgulloso de haber tenido desde hace tiempo una muy buena intuición, en hora buena ya estamos llegando a un estado ideal!

  6. antonio on Enero 6th, 2010 11:07 pm

    No entiendo donde van los archivos.

  7. aleix on Septiembre 2nd, 2010 4:37 pm

    Ansioso por empezar un nuevo proyecto con Doctrine 2 y ZF. Gracias por el artículo. Será de gran ayuda

Deja tu comentario




XHTML: puedes usar estas etiquetas: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>