Jornadas Symfony                               5 y 6 de julio 2010
                                               Universitat Jaume I, Castellón
http://decharlas.uji.es/symfony

organizan                         patrocinan




colaboran
Doctrine
          Nacho Martín




                                  5 y 6 de julio 2010
Jornadas Symfony                  Universitat Jaume I, Castellón
http://decharlas.uji.es/symfony
¿Qué es Doctrine?

            ●
             Object Relational Mapper hecho para
            PHP >=5.2.3 (Doctrine 2.0 PHP >5.3)

            ●
                   Basado en Hibernate (Java)

            ●
                   ¿Y Propel?




Jornadas Symfony                                http://decharlas.uji.es/symfony
Componentes




Jornadas Symfony         http://decharlas.uji.es/symfony
Activación
           //config/ProjectConfiguration.class.php
           public function setup()
           {
             $this->enablePlugins(array('sfDoctrinePlugin'));
             $this->disablePlugins(array('sfPropelPlugin'));
           }

           #config/databases.yml
           all:
            doctrine:
               class: sfDoctrineDatabase
               param:
                 dsn: 'mysql:host=localhost;dbname=midb'
                 username: usuario
                 password: secreto

Jornadas Symfony                                           http://decharlas.uji.es/symfony
Una aplicación de ejemplo

                      ●
                          Lista ToDo

                      ●
                          Con items (one-many)

                      ●
                          Y tags (many-many)




Jornadas Symfony                       http://decharlas.uji.es/symfony
El esquema YAML
  Todo:
   actAs:
    Timestampable: ~
   columns:
    name: { type: string(255), notnull: true}
    description: { type: string(1024) }
   relations:
    Tags: { class: Tag, refClass: TodoTag, local: todo_id, foreign: tag_id, foreignAlias: Todos}

  Item:
    actAs:
     Timestampable: ~
    columns:
     name: { type: string(255) }
     text: { type: string(4000) }
     todo_id: { type: integer, notnull: true }
    relations:
     Todo: { class: Todo, onDelete: CASCADE, local: todo_id, foreign: id, foreignAlias: items }

  TodoTag:
   columns:
    tag_id: { type: integer, primary: true }
    todo_id: { type: integer, primary: true }
   relations:
    Tag: { onDelete: CASCADE, local: tag_id, foreign: id }
    Todo: { onDelete: CASCADE, local: todo_id, foreign: id }

  Tag:
   columns:
    name: { type: string(255) }

Jornadas Symfony                                                                                   http://decharlas.uji.es/symfony
El modelo (columnas)
    abstract class BaseTodo extends sfDoctrineRecord
    {
      public function setTableDefinition()
      {
         $this->setTableName('todo');
         $this->hasColumn('name', 'string', 255, array(
            'type' => 'string',
            'notnull' => true,
            'length' => '255',
            ));
         $this->hasColumn('description', 'string', 1024, array(
            'type' => 'string',
            'length' => '1024',
            ));
      }


Jornadas Symfony                                     http://decharlas.uji.es/symfony
El modelo (relaciones)
            public function setUp()
           {
             parent::setUp();
             $this->hasMany('Tag as Tags', array(
                 'refClass' => 'TodoTag',
                 'local' => 'todo_id',
                 'foreign' => 'tag_id'));

                   $this->hasMany('Item as items', array(
                      'local' => 'id',
                      'foreign' => 'todo_id'));

                   $this->hasMany('TodoTag', array(
                      'local' => 'id',
                      'foreign' => 'tag_id'));

               //Behaviour
               $timestampable0 = new Doctrine_Template_Timestampable();
               $this->actAs($timestampable0);
           }
      }



Jornadas Symfony                                                  http://decharlas.uji.es/symfony
Fixtures (Doctrine 1)
            Todo:
             denver:
              name: Cosas que hacer en Denver
              description: Cuando hayamos muerto

            Item:
              gambas:
               name: Comer gambas
               Todo: denver
              gangsters:
               name: Cosas de gangsters
               Todo: denver

            Tag:
             turismo:
              name: Turismo gangsteril
              Todos: [denver]

Jornadas Symfony                                   http://decharlas.uji.es/symfony
DQL

           ●
               Simplifica SQL y es portable

           ●
               Incorpora POO a SQL




Jornadas Symfony                              http://decharlas.uji.es/symfony
DQL
          $q = Doctrine_Query::create()
             ->select('l.*, i.name, t.name')
             ->from('Todo l')
             ->innerJoin('l.Items i')
             ->leftJoin('l.Tags t');
         echo $q->getSqlQuery();


       SELECT t.id AS t__id, t.name AS t__name, t.description
       AS t__description, t.created_at AS t__created_at,
       t.updated_at AS t__updated_at, i.id AS i__id, i.name AS
       i__name, t2.id AS t2__id, t2.name AS t2__name FROM
       todo t INNER JOIN item i ON t.id = i.todo_id LEFT JOIN
       todo_tag t3 ON (t.id = t3.todo_id) LEFT JOIN tag t2 ON
       t2.id = t3.tag_id


Jornadas Symfony                                     http://decharlas.uji.es/symfony
Objetos
           Métodos de acceso y manipulación

           $list = new Todo();

       //Manipulación
     1 $list->name = "Cosas que hacer en Denver";
     2 $list['name'] = "Cosas que hacer en Denver";
     3 $list->set('name', "Cosas que hacer en Denver");

       //Acceso
     1 echo $list->name;
     2 echo $list['name']; //Recomendado (hidratación)
     3 echo $list->get('name');



Jornadas Symfony                                    http://decharlas.uji.es/symfony
Hidratación (I)
           ●
             En objetos
           ●
             En arrays (más rápido)
           ●
             Scalar
           ●
             Single Scalar
           ●
             Bajo demanda
           ●
             …


           ¿No es suficiente?
           ¡Escribe el tuyo!




Jornadas Symfony                      http://decharlas.uji.es/symfony
Hidratación (II)
  $q = Doctrine_Query::create()
   ->from('Todo l')
   ->innerJoin('l.Items i');

  $lists = $q->execute(); //Record
  echo $lists[0]['name'];




Jornadas Symfony                     http://decharlas.uji.es/symfony
Hidratación (II)
                                       Array
  $q = Doctrine_Query::create()        (
   ->from('Todo l')                       [0] => Array
                                             (
   ->innerJoin('l.Items i');
                                               [id] => 1
                                               [name] => Cosas que hacer en Denver
  $lists = $q->execute(); //Record             [description] => Cuando hayamos muerto
  echo $lists[0]['name'];                      [created_at] => 2010-06-22 23:55:02
                                               [updated_at] => 2010-06-22 23:55:02
                                               [Items] => Array
  $lists = $q->fetchArray(); //Array              (
  echo $lists[0]['name'];                            [0] => Array
  //O bien $q->execute(array(),                         (
  Doctrine::HYDRATE_ARRAY);                               [id] => 1
                                                          [name] => Comer gambas
  print_r($lists);                                        [text] =>
                                                          [todo_id] => 1
                                                          [created_at] => 2010-06-22 23:55:02
                                                          [updated_at] => 2010-06-22 23:55:02
                                                        )

                                                     [1] => Array
                                                        (…)
                                                 )
                                            )
                                       )




Jornadas Symfony                                                            http://decharlas.uji.es/symfony
Hidratación (III)

         ●
           El acceso por arrays funciona en los dos
         métodos de hidratación
         ●
           La hidratación por arrays es más eficiente
         si solo queremos consultar datos directos
         de la BD
         ●
           fetchArray() es un alias de execute() con
         la hidratación por array
         ●
           Uso de foreach, count(), isset(), unset()


Jornadas Symfony                              http://decharlas.uji.es/symfony
Definiendo setters/getters
               class Todo extends BaseTodo
               {
                 //Nuevo getter
                 public function getDescriptionHtml()
                 {
                   return Markdown::parse(htmlspecialchars(
                                          $this->description));
                 }

                 //Sobrecarga del setter
                 public function setDescription($description)
                 {
                   return $this->_set('description',
               Markdown::parse(htmlspecialchars($description)));
                 }
               }

Jornadas Symfony                                          http://decharlas.uji.es/symfony
Relaciones
                             $list = new Todo();
                             $list['name'] = "Libros para este verano";
                             $list->Items[]->name = "Hablemos de Langostas";
                             $list->save();

                             //Usando link()
   ●
    Las relaciones son       $item = new Item();
   intuitivas                $item['name'] = "La broma infinita";
                             $item->link('Todo',array($list['id']));
   ●
    Siempre podemos recurrir $item->save();
   a DQL                     //Borrar
                             $list->Items[0]->delete();
   ●
    Pero en DQL no se
                             //Siempre nos quedará DQL
   ejecutarán preDelete(),   $q = Doctrine_Query::create()
   postDelete()... (!)         ->delete('Item')
                               ->addWhere('todo_id = ?', $list['id'])
                               ->whereIn('name', array($item['name'], 'otro
                                           nombre'));
                             $q->execute();



Jornadas Symfony                                                  http://decharlas.uji.es/symfony
Many to many              Array
      $q = Doctrine_Query::create() (
                                       [0] => Array
        ->from('Todo l')
                                          (
        ->leftJoin('l.TodoTag tt')          [id] => 27
        ->leftJoin('tt.Tag t');             [name] => Cosas que hacer en Denver
      print_r($q->fetchArray());            [description] => Cuando hayamos muerto
                                            [created_at] => 2010-06-23 20:35:41
      //Equivalente                         [updated_at] => 2010-06-23 20:35:41
      $q = Doctrine_Query::create()         [TodoTag] => Array
        ->from('Todo l')                       (
        ->leftJoin('l.Tags t');                   [0] => Array
      print_r($q->fetchArray());                     (
                                                        [tag_id] => 2
                                                        [todo_id] => 27
      ●
        Podemos “olvidarnos”                            [Tag] => Array
                                                           (
      de la tabla intermedia                                 [id] => 2
                                                             [name] => Turismo
                                                           )
                                                     )
                                               )
                                          )
                                    )



Jornadas Symfony                                                      http://decharlas.uji.es/symfony
Mucha tela que cortar
           ●
             Behaviours
           ●
             Validadores
           ●
             Migraciones
           ●
             Herencia
           ●
             Caché
           ●
             Event listeners
           ●
             …




Jornadas Symfony                   http://decharlas.uji.es/symfony
Mucha tela que cortar
           ●
             Behaviours
           ●
             Validadores
           ●
             Migraciones
           ●
             Herencia
           ●
             Caché
           ●
             Event listeners
           ●
             …




         ¿Pero y Doctrine2?
                               Veamos Doctrine2
Jornadas Symfony                                  http://decharlas.uji.es/symfony
Doctrine2

           ●
             Reescritura completa del código para
           PHP 5.3
           ●
             Mejoras importantes de rendimiento
           ●
             Menos magia
           ●
             Caché mejorada
           ●
             Entidades




Jornadas Symfony                              http://decharlas.uji.es/symfony
Entidades (I)
    <?php

    namespace Entities;

    /** @Entity @Table(name="usuarios") */
    class Usuario
    {
        /**
         * @Id @Column(type="integer")
         * @GeneratedValue(strategy="AUTO")
         */
        private $id;
        /** @Column(type="string", length=50) */
        private $nombre;
        /**
         * @OneToOne(targetEntity="Direccion")
         * @JoinColumn(name="direccion_id", referencedColumnName="id")
         */
        private $direccion;


      DocBlock Annotations
Jornadas Symfony                                             http://decharlas.uji.es/symfony
Entidades (II)
              public function getId()
              {
                  return $this->id;
              }

              public function getNombre()
              {
                  return $this->nombre;
              }

              public function setNombre($nombre)
              {
                  $this->nombre = $nombre;
              }

              public function getDireccion()
              {
                  return $this->direccion;
              }

              public function setDireccion(Direccion $direccion)
              {
                  if ($this->direccion !== $direccion) {
                      $this->direccion = $direccion;
                      $direccion->setUsuario($this);
                  }
              }
       }

Jornadas Symfony                                                   http://decharlas.uji.es/symfony
Entidades (III)
           ●
             No descienden de ninguna clase, están
           “separadas” del ORM, aunque mapeadas
           por él
           ●
             Menos magia. Es más fácil entender qué
           está pasando
           ●
             Más rápidas
           ●
             Herencia
           ●
             Gestionadas por el Entity Manager
           ●
             Sí, se pueden escribir en YAML y XML ;)


Jornadas Symfony                             http://decharlas.uji.es/symfony
Fixtures
             $em = $this->getEntityManager();

             $user1 = new ModelsUsuario();
             $user1->nombre = 'Nacho';



         ●
           Adiós al YAML. Se escriben en PHP
         ●
           ¿Por qué?
                   ●
                     Es más rápido cargarlas
                   ●
                     El código para tratar fixtures en YAML introdujo muchos
                   bugs en el pasado




Jornadas Symfony                                                http://decharlas.uji.es/symfony
persist() y flush()
                   $user = new EntitiesUsuario;
                   $user->setNombre('Nacho');
                   $entitymanager->persist($user);
                   $entitymanager->flush();



           ●
             Atención al uso de espacios de nombre
           ●
             Persist “marca” el objeto para guardar
           ●
             Flush ejecuta la unidad de trabajo



Jornadas Symfony                                     http://decharlas.uji.es/symfony
Rendimiento (I)
          for ($i=0; $i<1000; $i++){
            $user = new EntitiesUsuario;
            $user->setNombre('Nacho');
            $em->persist($user);
          }
          $inicio = microtime(true);
          $em->flush();
          $final = microtime(true);
          echo $final-$inicio."n";    0.377s
          //////////////////////////
          $inicio = microtime(true);
          for ($i=0; $i<1000; $i++){
            mysql_query("INSERT INTO usuarios (nombre) VALUES
          ('Nacho')", $link);
          }

                                       41.4s
          $final = microtime(true);
          echo $final-$inicio."n";



Jornadas Symfony                                          http://decharlas.uji.es/symfony
Rendimiento (II)
           ●
             Doctrine2 gestiona las transacciones por
           nosotros
           ●
             Así que es más rápido que código
           PHP+SQL mal optimizado
           ●
             (Por supuesto usar transacciones en
           PHP+SQL es más rápido que Doctrine2)
           ●
             También podemos controlar las
           transacciones nosotros



Jornadas Symfony                               http://decharlas.uji.es/symfony
Eventos Lifecycle
           ●
             pre/postRemove
           ●
             pre/postPersist
           ●
             pre/postUpdate
           ●
             postLoad : carga desde BD
           ●
             loadClassMetadata : carga desde metadatos
           (annotations, yaml, xml)
           ●
             onFlush
               /** @Entity @HasLifecycleCallbacks */
               class Usuario
               {
                 //(...)
                 /** @PostPersist */
                   public function doAlgoOnPostPersist()
                   {
                      $this->nombre = 'Me han cambiado en el postpersist';
                   }

Jornadas Symfony                                                             http://decharlas.uji.es/symfony
Behaviours (I)
            En Doctrine2 son código normal de PHP que
           extiende la funcionalidad base de las entidades
          /** @HasLifecycleCallbacks */
          class BlogPost
          {
             //(...)
             public function __construct()
             {
                $this->created = $this->updated = new DateTime("now");
             }

               /**
                * @PreUpdate
                */
               public function updated()
               {
                  $this->updated = new DateTime("now");
               }
          }


Jornadas Symfony                                                   http://decharlas.uji.es/symfony
Behaviours (II)
            ¿Pero cómo hacer el código reutilizable entre
           entidades?
             Usando interfaces, eventos y código PHP
            orientado a objetos
             Ejemplos:

               http://github.com/guilhermeblanco/Doctrine2-Sluggable-Functional-Behavior

               http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior

               http://www.doctrine-project.org/blog/doctrine2-versionable




Jornadas Symfony                                                                    http://decharlas.uji.es/symfony
Migraciones (I)
             Hacen el esquema versionable




             BD (antes)                     BD (después)




              Esquema                        Fichero de
                                             migración
Jornadas Symfony                                 http://decharlas.uji.es/symfony
Migraciones (I)
             Hacen el esquema versionable




              BD (antes)                BD (después)
                           Comparar




              Esquema                       Fichero de
                                            migración
Jornadas Symfony                                http://decharlas.uji.es/symfony
Migraciones (I)
             Hacen el esquema versionable




              BD (antes)                BD (después)
                           Comparar
                                       Generar


              Esquema                        Fichero de
                                             migración
Jornadas Symfony                                 http://decharlas.uji.es/symfony
Migraciones (I)
             Hacen el esquema versionable




              BD (antes)                BD (después)
                           Comparar
                                       Generar


              Esquema                        Fichero de
                                             migración
Jornadas Symfony                                 http://decharlas.uji.es/symfony
Migraciones (I)
             Hacen el esquema versionable

                                      Migrar


              BD (antes)                  BD (después)
                           Comparar
                                         Generar


              Esquema                          Fichero de
                                               migración
Jornadas Symfony                                   http://decharlas.uji.es/symfony
Migraciones (II)
             Aspecto de un fichero de migración
        class Version20100416130401 extends AbstractMigration
        {
           public function up(Schema $schema)
           {
             $table = $schema->createTable('users');
             $table->addColumn('username', 'string');
             $table->addColumn('password', 'string');
           }

              public function down(Schema $schema)
              {
                $schema->dropTable('users');
              }
        }

Jornadas Symfony                                     http://decharlas.uji.es/symfony
Migraciones (III)
             Gestionadas desde la consola:
           ●
               Diff: tras cambiar una entidad, genera la
           migración necesaria para cambiar la BD
           ●
             Dry-run: muestra el SQL para cerciorarnos de
           que es lo que esperamos
           ●
             Status: muestra en qué estado (versión,
           migraciones posibles, fecha...) estamos
           ●
             Migrate: ejecuta la migración (hacia adelante o
           hacia atrás → revertir)
           ●
             Write-sql: en lugar de migrar, escribe el SQL a
           un fichero

Jornadas Symfony                                           http://decharlas.uji.es/symfony
MongoDB (ODM)

           ●
            El ODM tiene el mismo aspecto que el ORM
           (métodos parecidos, Entidad → Documento,
           EntityManager → DocumentManager,...)
           ●
            Mañana hay una charla sobre MongoDB y
           Symfony ;)




Jornadas Symfony                               http://decharlas.uji.es/symfony
¿Preguntas?

                     Si surgen más tarde ;) :
                    nitram.ohcan@gmail.com
                        twitter:@nacmartin
                     http://nacho-martin.com



Jornadas Symfony                                http://decharlas.uji.es/symfony

ORM Doctrine

  • 1.
    Jornadas Symfony 5 y 6 de julio 2010 Universitat Jaume I, Castellón http://decharlas.uji.es/symfony organizan patrocinan colaboran
  • 2.
    Doctrine Nacho Martín 5 y 6 de julio 2010 Jornadas Symfony Universitat Jaume I, Castellón http://decharlas.uji.es/symfony
  • 3.
    ¿Qué es Doctrine? ● Object Relational Mapper hecho para PHP >=5.2.3 (Doctrine 2.0 PHP >5.3) ● Basado en Hibernate (Java) ● ¿Y Propel? Jornadas Symfony http://decharlas.uji.es/symfony
  • 4.
    Componentes Jornadas Symfony http://decharlas.uji.es/symfony
  • 5.
    Activación //config/ProjectConfiguration.class.php public function setup() { $this->enablePlugins(array('sfDoctrinePlugin')); $this->disablePlugins(array('sfPropelPlugin')); } #config/databases.yml all: doctrine: class: sfDoctrineDatabase param: dsn: 'mysql:host=localhost;dbname=midb' username: usuario password: secreto Jornadas Symfony http://decharlas.uji.es/symfony
  • 6.
    Una aplicación deejemplo ● Lista ToDo ● Con items (one-many) ● Y tags (many-many) Jornadas Symfony http://decharlas.uji.es/symfony
  • 7.
    El esquema YAML Todo: actAs: Timestampable: ~ columns: name: { type: string(255), notnull: true} description: { type: string(1024) } relations: Tags: { class: Tag, refClass: TodoTag, local: todo_id, foreign: tag_id, foreignAlias: Todos} Item: actAs: Timestampable: ~ columns: name: { type: string(255) } text: { type: string(4000) } todo_id: { type: integer, notnull: true } relations: Todo: { class: Todo, onDelete: CASCADE, local: todo_id, foreign: id, foreignAlias: items } TodoTag: columns: tag_id: { type: integer, primary: true } todo_id: { type: integer, primary: true } relations: Tag: { onDelete: CASCADE, local: tag_id, foreign: id } Todo: { onDelete: CASCADE, local: todo_id, foreign: id } Tag: columns: name: { type: string(255) } Jornadas Symfony http://decharlas.uji.es/symfony
  • 8.
    El modelo (columnas) abstract class BaseTodo extends sfDoctrineRecord { public function setTableDefinition() { $this->setTableName('todo'); $this->hasColumn('name', 'string', 255, array( 'type' => 'string', 'notnull' => true, 'length' => '255', )); $this->hasColumn('description', 'string', 1024, array( 'type' => 'string', 'length' => '1024', )); } Jornadas Symfony http://decharlas.uji.es/symfony
  • 9.
    El modelo (relaciones) public function setUp() { parent::setUp(); $this->hasMany('Tag as Tags', array( 'refClass' => 'TodoTag', 'local' => 'todo_id', 'foreign' => 'tag_id')); $this->hasMany('Item as items', array( 'local' => 'id', 'foreign' => 'todo_id')); $this->hasMany('TodoTag', array( 'local' => 'id', 'foreign' => 'tag_id')); //Behaviour $timestampable0 = new Doctrine_Template_Timestampable(); $this->actAs($timestampable0); } } Jornadas Symfony http://decharlas.uji.es/symfony
  • 10.
    Fixtures (Doctrine 1) Todo: denver: name: Cosas que hacer en Denver description: Cuando hayamos muerto Item: gambas: name: Comer gambas Todo: denver gangsters: name: Cosas de gangsters Todo: denver Tag: turismo: name: Turismo gangsteril Todos: [denver] Jornadas Symfony http://decharlas.uji.es/symfony
  • 11.
    DQL ● Simplifica SQL y es portable ● Incorpora POO a SQL Jornadas Symfony http://decharlas.uji.es/symfony
  • 12.
    DQL $q = Doctrine_Query::create() ->select('l.*, i.name, t.name') ->from('Todo l') ->innerJoin('l.Items i') ->leftJoin('l.Tags t'); echo $q->getSqlQuery(); SELECT t.id AS t__id, t.name AS t__name, t.description AS t__description, t.created_at AS t__created_at, t.updated_at AS t__updated_at, i.id AS i__id, i.name AS i__name, t2.id AS t2__id, t2.name AS t2__name FROM todo t INNER JOIN item i ON t.id = i.todo_id LEFT JOIN todo_tag t3 ON (t.id = t3.todo_id) LEFT JOIN tag t2 ON t2.id = t3.tag_id Jornadas Symfony http://decharlas.uji.es/symfony
  • 13.
    Objetos Métodos de acceso y manipulación $list = new Todo(); //Manipulación 1 $list->name = "Cosas que hacer en Denver"; 2 $list['name'] = "Cosas que hacer en Denver"; 3 $list->set('name', "Cosas que hacer en Denver"); //Acceso 1 echo $list->name; 2 echo $list['name']; //Recomendado (hidratación) 3 echo $list->get('name'); Jornadas Symfony http://decharlas.uji.es/symfony
  • 14.
    Hidratación (I) ● En objetos ● En arrays (más rápido) ● Scalar ● Single Scalar ● Bajo demanda ● … ¿No es suficiente? ¡Escribe el tuyo! Jornadas Symfony http://decharlas.uji.es/symfony
  • 15.
    Hidratación (II) $q = Doctrine_Query::create() ->from('Todo l') ->innerJoin('l.Items i'); $lists = $q->execute(); //Record echo $lists[0]['name']; Jornadas Symfony http://decharlas.uji.es/symfony
  • 16.
    Hidratación (II) Array $q = Doctrine_Query::create() ( ->from('Todo l') [0] => Array ( ->innerJoin('l.Items i'); [id] => 1 [name] => Cosas que hacer en Denver $lists = $q->execute(); //Record [description] => Cuando hayamos muerto echo $lists[0]['name']; [created_at] => 2010-06-22 23:55:02 [updated_at] => 2010-06-22 23:55:02 [Items] => Array $lists = $q->fetchArray(); //Array ( echo $lists[0]['name']; [0] => Array //O bien $q->execute(array(), ( Doctrine::HYDRATE_ARRAY); [id] => 1 [name] => Comer gambas print_r($lists); [text] => [todo_id] => 1 [created_at] => 2010-06-22 23:55:02 [updated_at] => 2010-06-22 23:55:02 ) [1] => Array (…) ) ) ) Jornadas Symfony http://decharlas.uji.es/symfony
  • 17.
    Hidratación (III) ● El acceso por arrays funciona en los dos métodos de hidratación ● La hidratación por arrays es más eficiente si solo queremos consultar datos directos de la BD ● fetchArray() es un alias de execute() con la hidratación por array ● Uso de foreach, count(), isset(), unset() Jornadas Symfony http://decharlas.uji.es/symfony
  • 18.
    Definiendo setters/getters class Todo extends BaseTodo { //Nuevo getter public function getDescriptionHtml() { return Markdown::parse(htmlspecialchars( $this->description)); } //Sobrecarga del setter public function setDescription($description) { return $this->_set('description', Markdown::parse(htmlspecialchars($description))); } } Jornadas Symfony http://decharlas.uji.es/symfony
  • 19.
    Relaciones $list = new Todo(); $list['name'] = "Libros para este verano"; $list->Items[]->name = "Hablemos de Langostas"; $list->save(); //Usando link() ● Las relaciones son $item = new Item(); intuitivas $item['name'] = "La broma infinita"; $item->link('Todo',array($list['id'])); ● Siempre podemos recurrir $item->save(); a DQL //Borrar $list->Items[0]->delete(); ● Pero en DQL no se //Siempre nos quedará DQL ejecutarán preDelete(), $q = Doctrine_Query::create() postDelete()... (!) ->delete('Item') ->addWhere('todo_id = ?', $list['id']) ->whereIn('name', array($item['name'], 'otro nombre')); $q->execute(); Jornadas Symfony http://decharlas.uji.es/symfony
  • 20.
    Many to many Array $q = Doctrine_Query::create() ( [0] => Array ->from('Todo l') ( ->leftJoin('l.TodoTag tt') [id] => 27 ->leftJoin('tt.Tag t'); [name] => Cosas que hacer en Denver print_r($q->fetchArray()); [description] => Cuando hayamos muerto [created_at] => 2010-06-23 20:35:41 //Equivalente [updated_at] => 2010-06-23 20:35:41 $q = Doctrine_Query::create() [TodoTag] => Array ->from('Todo l') ( ->leftJoin('l.Tags t'); [0] => Array print_r($q->fetchArray()); ( [tag_id] => 2 [todo_id] => 27 ● Podemos “olvidarnos” [Tag] => Array ( de la tabla intermedia [id] => 2 [name] => Turismo ) ) ) ) ) Jornadas Symfony http://decharlas.uji.es/symfony
  • 21.
    Mucha tela quecortar ● Behaviours ● Validadores ● Migraciones ● Herencia ● Caché ● Event listeners ● … Jornadas Symfony http://decharlas.uji.es/symfony
  • 22.
    Mucha tela quecortar ● Behaviours ● Validadores ● Migraciones ● Herencia ● Caché ● Event listeners ● … ¿Pero y Doctrine2? Veamos Doctrine2 Jornadas Symfony http://decharlas.uji.es/symfony
  • 23.
    Doctrine2 ● Reescritura completa del código para PHP 5.3 ● Mejoras importantes de rendimiento ● Menos magia ● Caché mejorada ● Entidades Jornadas Symfony http://decharlas.uji.es/symfony
  • 24.
    Entidades (I) <?php namespace Entities; /** @Entity @Table(name="usuarios") */ class Usuario { /** * @Id @Column(type="integer") * @GeneratedValue(strategy="AUTO") */ private $id; /** @Column(type="string", length=50) */ private $nombre; /** * @OneToOne(targetEntity="Direccion") * @JoinColumn(name="direccion_id", referencedColumnName="id") */ private $direccion; DocBlock Annotations Jornadas Symfony http://decharlas.uji.es/symfony
  • 25.
    Entidades (II) public function getId() { return $this->id; } public function getNombre() { return $this->nombre; } public function setNombre($nombre) { $this->nombre = $nombre; } public function getDireccion() { return $this->direccion; } public function setDireccion(Direccion $direccion) { if ($this->direccion !== $direccion) { $this->direccion = $direccion; $direccion->setUsuario($this); } } } Jornadas Symfony http://decharlas.uji.es/symfony
  • 26.
    Entidades (III) ● No descienden de ninguna clase, están “separadas” del ORM, aunque mapeadas por él ● Menos magia. Es más fácil entender qué está pasando ● Más rápidas ● Herencia ● Gestionadas por el Entity Manager ● Sí, se pueden escribir en YAML y XML ;) Jornadas Symfony http://decharlas.uji.es/symfony
  • 27.
    Fixtures $em = $this->getEntityManager(); $user1 = new ModelsUsuario(); $user1->nombre = 'Nacho'; ● Adiós al YAML. Se escriben en PHP ● ¿Por qué? ● Es más rápido cargarlas ● El código para tratar fixtures en YAML introdujo muchos bugs en el pasado Jornadas Symfony http://decharlas.uji.es/symfony
  • 28.
    persist() y flush() $user = new EntitiesUsuario; $user->setNombre('Nacho'); $entitymanager->persist($user); $entitymanager->flush(); ● Atención al uso de espacios de nombre ● Persist “marca” el objeto para guardar ● Flush ejecuta la unidad de trabajo Jornadas Symfony http://decharlas.uji.es/symfony
  • 29.
    Rendimiento (I) for ($i=0; $i<1000; $i++){ $user = new EntitiesUsuario; $user->setNombre('Nacho'); $em->persist($user); } $inicio = microtime(true); $em->flush(); $final = microtime(true); echo $final-$inicio."n"; 0.377s ////////////////////////// $inicio = microtime(true); for ($i=0; $i<1000; $i++){ mysql_query("INSERT INTO usuarios (nombre) VALUES ('Nacho')", $link); } 41.4s $final = microtime(true); echo $final-$inicio."n"; Jornadas Symfony http://decharlas.uji.es/symfony
  • 30.
    Rendimiento (II) ● Doctrine2 gestiona las transacciones por nosotros ● Así que es más rápido que código PHP+SQL mal optimizado ● (Por supuesto usar transacciones en PHP+SQL es más rápido que Doctrine2) ● También podemos controlar las transacciones nosotros Jornadas Symfony http://decharlas.uji.es/symfony
  • 31.
    Eventos Lifecycle ● pre/postRemove ● pre/postPersist ● pre/postUpdate ● postLoad : carga desde BD ● loadClassMetadata : carga desde metadatos (annotations, yaml, xml) ● onFlush /** @Entity @HasLifecycleCallbacks */ class Usuario { //(...) /** @PostPersist */ public function doAlgoOnPostPersist() { $this->nombre = 'Me han cambiado en el postpersist'; } Jornadas Symfony http://decharlas.uji.es/symfony
  • 32.
    Behaviours (I) En Doctrine2 son código normal de PHP que extiende la funcionalidad base de las entidades /** @HasLifecycleCallbacks */ class BlogPost { //(...) public function __construct() { $this->created = $this->updated = new DateTime("now"); } /** * @PreUpdate */ public function updated() { $this->updated = new DateTime("now"); } } Jornadas Symfony http://decharlas.uji.es/symfony
  • 33.
    Behaviours (II) ¿Pero cómo hacer el código reutilizable entre entidades? Usando interfaces, eventos y código PHP orientado a objetos Ejemplos: http://github.com/guilhermeblanco/Doctrine2-Sluggable-Functional-Behavior http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior http://www.doctrine-project.org/blog/doctrine2-versionable Jornadas Symfony http://decharlas.uji.es/symfony
  • 34.
    Migraciones (I) Hacen el esquema versionable BD (antes) BD (después) Esquema Fichero de migración Jornadas Symfony http://decharlas.uji.es/symfony
  • 35.
    Migraciones (I) Hacen el esquema versionable BD (antes) BD (después) Comparar Esquema Fichero de migración Jornadas Symfony http://decharlas.uji.es/symfony
  • 36.
    Migraciones (I) Hacen el esquema versionable BD (antes) BD (después) Comparar Generar Esquema Fichero de migración Jornadas Symfony http://decharlas.uji.es/symfony
  • 37.
    Migraciones (I) Hacen el esquema versionable BD (antes) BD (después) Comparar Generar Esquema Fichero de migración Jornadas Symfony http://decharlas.uji.es/symfony
  • 38.
    Migraciones (I) Hacen el esquema versionable Migrar BD (antes) BD (después) Comparar Generar Esquema Fichero de migración Jornadas Symfony http://decharlas.uji.es/symfony
  • 39.
    Migraciones (II) Aspecto de un fichero de migración class Version20100416130401 extends AbstractMigration { public function up(Schema $schema) { $table = $schema->createTable('users'); $table->addColumn('username', 'string'); $table->addColumn('password', 'string'); } public function down(Schema $schema) { $schema->dropTable('users'); } } Jornadas Symfony http://decharlas.uji.es/symfony
  • 40.
    Migraciones (III) Gestionadas desde la consola: ● Diff: tras cambiar una entidad, genera la migración necesaria para cambiar la BD ● Dry-run: muestra el SQL para cerciorarnos de que es lo que esperamos ● Status: muestra en qué estado (versión, migraciones posibles, fecha...) estamos ● Migrate: ejecuta la migración (hacia adelante o hacia atrás → revertir) ● Write-sql: en lugar de migrar, escribe el SQL a un fichero Jornadas Symfony http://decharlas.uji.es/symfony
  • 41.
    MongoDB (ODM) ● El ODM tiene el mismo aspecto que el ORM (métodos parecidos, Entidad → Documento, EntityManager → DocumentManager,...) ● Mañana hay una charla sobre MongoDB y Symfony ;) Jornadas Symfony http://decharlas.uji.es/symfony
  • 42.
    ¿Preguntas? Si surgen más tarde ;) : nitram.ohcan@gmail.com twitter:@nacmartin http://nacho-martin.com Jornadas Symfony http://decharlas.uji.es/symfony