34063724 Doctrine Nacho Martin Jornadas Symfony en Castellon Julio 2010
-
Upload
eduardo-pliego-mere -
Category
Documents
-
view
21 -
download
6
Transcript of 34063724 Doctrine Nacho Martin Jornadas Symfony en Castellon Julio 2010
organizan patrocinan
colaboran
Jornadas Symfony 5 y 6 de julio 2010Universitat Jaume I, Castellón
http://decharlas.uji.es/symfony
DoctrineNacho Martín
Jornadas Symfony 5 y 6 de julio 2010Universitat Jaume I, Castellón
http://decharlas.uji.es/symfony
Jornadas Symfony 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
Activación//config/ProjectConfiguration.class.phppublic function setup(){ $this->enablePlugins(array('sfDoctrinePlugin')); $this->disablePlugins(array('sfPropelPlugin'));}
#config/databases.ymlall: 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 YAMLTodo: 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) }
4
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
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
$q = Doctrine_Query::create() ->select('l.*, i.name, t.name') ->from('Todo l') ->innerJoin('l.Items i') ->leftJoin('l.Tags t');echo $q->getSqlQuery();
Jornadas Symfony http://decharlas.uji.es/symfony
Objetos
$list = new Todo();
//Manipulación$list->name = "Cosas que hacer en Denver";$list['name'] = "Cosas que hacer en Denver";$list->set('name', "Cosas que hacer en Denver");
//Accesoecho $list->name;echo $list['name']; //Recomendado (hidratación)echo $list->get('name');
Métodos de acceso y manipulación
123
123
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( [0] => Array ( [id] => 1 [name] => Cosas que hacer en Denver [description] => Cuando hayamos muerto [created_at] => 2010-06-22 23:55:02 [updated_at] => 2010-06-22 23:55:02 [Items] => Array ( [0] => Array ( [id] => 1 [name] => Comer gambas [text] => [todo_id] => 1 [created_at] => 2010-06-22 23:55:02 [updated_at] => 2010-06-22 23:55:02 )
[1] => Array ( … ) ) ))
$q = Doctrine_Query::create() ->from('Todo l') ->innerJoin('l.Items i');
$lists = $q->execute(); //Record echo $lists[0]['name']; $lists = $q->fetchArray(); //Array echo $lists[0]['name']; //O bien $q->execute(array(), Doctrine::HYDRATE_ARRAY); print_r($lists);
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/gettersclass 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() $item = new Item(); $item['name'] = "La broma infinita"; $item->link('Todo',array($list['id'])); $item->save(); //Borrar $list->Items[0]->delete(); //Siempre nos quedará DQL $q = Doctrine_Query::create() ->delete('Item') ->addWhere('todo_id = ?', $list['id']) ->whereIn('name', array($item['name'], 'otro nombre')); $q->execute();
●Las relaciones son intuitivas
●Siempre podemos recurrir a DQL
●Pero en DQL no se ejecutarán preDelete(), postDelete()... (!)
Jornadas Symfony http://decharlas.uji.es/symfony
Many to many$q = Doctrine_Query::create() ->from('Todo l') ->leftJoin('l.TodoTag tt') ->leftJoin('tt.Tag t');print_r($q->fetchArray());
//Equivalente$q = Doctrine_Query::create() ->from('Todo l') ->leftJoin('l.Tags t');print_r($q->fetchArray());
Array( [0] => Array ( [id] => 27 [name] => Cosas que hacer en Denver [description] => Cuando hayamos muerto [created_at] => 2010-06-23 20:35:41 [updated_at] => 2010-06-23 20:35:41 [TodoTag] => Array ( [0] => Array ( [tag_id] => 2 [todo_id] => 27 [Tag] => Array ( [id] => 2 [name] => Turismo ) ) ) ))
● Podemos olvidarnos “ ”de la tabla intermedia
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● …
Mucha tela que cortar
¿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 \Models\Usuario();$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 \Entities\Usuario;$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 \Entities\Usuario; $user->setNombre('Nacho'); $em->persist($user);}$inicio = microtime(true);$em->flush();$final = microtime(true);echo $final-$inicio."\n";
//////////////////////////$inicio = microtime(true);for ($i=0; $i<1000; $i++){ mysql_query("INSERT INTO usuarios (nombre) VALUES ('Nacho')", $link);}$final = microtime(true);echo $final-$inicio."\n";
0.377s
41.4s
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
http://github.com/guilhermeblanco/Doctrine2-Sluggable-Functional-Behavior
http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior http://www.doctrine-project.org/blog/doctrine2-versionable
Ejemplos:
Jornadas Symfony http://decharlas.uji.es/symfony
Migraciones (I)Hacen el esquema versionable
BD (antes) BD (después)
Esquema Fichero demigración
Jornadas Symfony http://decharlas.uji.es/symfony
Migraciones (I)Hacen el esquema versionable
BD (antes) BD (después)
Esquema
Comparar
Fichero demigración
Jornadas Symfony http://decharlas.uji.es/symfony
Migraciones (I)Hacen el esquema versionable
BD (antes) BD (después)
Esquema
Comparar
Fichero demigración
Generar
Jornadas Symfony http://decharlas.uji.es/symfony
Migraciones (I)Hacen el esquema versionable
BD (antes) BD (después)
Esquema
Comparar
Fichero demigración
Generar
Jornadas Symfony http://decharlas.uji.es/symfony
Migraciones (I)Hacen el esquema versionable
BD (antes) BD (después)
Esquema
Comparar
Fichero demigración
Generar
Migrar
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 ;) :[email protected]
twitter:@nacmartinhttp://nacho-martin.com