Presentación

Presione o espacio para avanzar.

Presione para retroceder.

Avance y retroceda con la rueda del mouse.

Ctrl + + o - para zoom.

Gestos en pantalla en dispositivos móviles.

Mapeador Objeto/Relacional
Persistencia no invasiva y por alcance
en PHP



por Leonardo Tadei
<leonardo@tadei.com.ar>
http://leonardo.tadei.com.ar

Pegasus Tech Supply
<leonardot@pegasusnet.com.ar>
Software Freedom Day 2011
Congreso Internacional de Software Libre
15 de Octubre
Biblioteca Nacional, CABA, Argentina


Creative Commons Attribution 2.5 Argentina License
http://creativecommons.org/licenses/by/2.5/ar/

Mapeador Objeto/Relacional

Persistencia no invasiva y por alcance

en PHP

Qué es Mapeo Objeto/Relacional



La técnica por la cual, en un contexto de
Programación Orientada a Objetos (POO),
se almacenan los Objetos del Modelo
en un Gestor de Bases de Datos Relacional (RDBMS).


"Los Objetos Persisten"

Model - View - Controller

La intención de esta arquitectura es
"cuidar" al Modelo.

MVC

Problemas

  • Persistencia
  • Desajuste por impedancia
  • Active Record
  • Data Access Object



"El objetivo es tener un Modelo para un dominio y preservarlo"

Solución ideal

ODBMS

usar una Base de Datos Orientada a Objetos (ODBMS, object database management system).


Requieren binding con el lenguaje.
No hay disponibles para PHP

Mapeo Objeto-Relacional

Es la solución viable en PHP



Estrategias de Mapeo

El problema del desajuste por impedancia
nos lleva a tener que elegir estrategias de mapeo.


  • Clase concreta a tabla
  • Jerarquía de clases a tabla
  • Modelo a tabla universal


Los ORM ayudan a implementar estrategias de mapeo.

Romper el paradigma o viciar el Modelo

La idea subyacente a la arquitectura MVC
es preservar el Modelo.

Existen implementaciones de ORM que pierden de vista la preservación del modelo proponiendo mecanismos aparentemente inocentes, pero que implican o violar el paradigma de la POO o introducir cambios en el Modelo que no tienen que ver con las reglas del negocio.

Mala práctica 1: Heredar del ORM

<?php

class Cliente extends MyORM {...}

$c = new Cliente();
// código que modifica el valor de los atributos
$c->save();

?>
				
  • Heredo de un desconocido
  • Testear el Modelo es testear el ORM
  • El Objeto Cliente del Modelo tiene ahora comportamiento que no es propiamente del Modelo

Mala práctica 2: Violar el encapsulamiento

<?php

class Cliente {
      public $nombre = '';
      public $limiteCredito = 0.00;
      ...
}

?>
				
Se violan principios fundamentales de la POO:
  • Encapsulamiento
  • Ocultación de la información

Mala práctica 3: Usar Anotaciones en PHP

<?php

class Cliente {
      /**
      * @ORM\Id
      * @ORM\Column(type="integer")
      * @ORM\GeneratedValue(strategy="AUTO")
      */
      protected $id;
      ...
}

?>
				
  • Los comentarios no tienen sintaxis validable
  • Optimizadorse y ofuscadores los eliminan

Mala práctica 4: Acceder a varias instancias a través de una instancia

<?php

$c1 = Cliente::findById(34);
...

$c2 = Cliente::findByName('Perez');

?>
				
  • Pédida de la identidad del Objeto
  • Abuso de métodos estáticos

Propuesta dentro de la POO

Dada la clase:

<?php

class Auto {
      private $patente; // la patente del automóvil
      private $carga; // peso en Kg
      function __construct($p = '', $c = 0) {
            $this->setPatente($p);
            $this->setCarga($c);
      }
      function setPatente($v) { $this->patente= $v; }
      function getPatente() { return $this->patente; }
      function setCarga($v) { $this->carga = $v; }
      function getCarga() { return $this->carga; }
      function calcularConsumo() {
            return 8 * (1 + $this->getCarga() / 1000;
      }
}
?>
  • Atributos privados
  • Ninguna mención a la persistencia o al ORM

Propuesta dentro de la POO

Persistencia no Invasiva del Objeto

<?php

$p = new Persistent(); // instanciamos al ORM
$a1 = new Auto('GNU-007', 280); // creamos una instancia de Auto
print('El consumo es: '. $a1->calcularConsumo()); // mostramos el consumo
$p->save($a1); // el mapeador persiste al objeto

?>

Propuesta dentro de la POO

Hidratación del Objeto

<?php

$p = new Persistent(); // instanciamos al ORM
$a1 = $p->retrieve('Auto','GNU-007'); // hidratamos la instancia
print('El consumo es: '. $a1->calcularConsumo()); // mostramos el consumo
$a->setCarga(500);
print('El consumo cambió: '. $a1->calcularConsumo()); // mostramos el consumo
$p->save($a1); // el mapeador persiste al objeto con su estado actual

?>
El ORM funciona como un repositorio virtual de Objetos. Intenta dar la sensación de que, una vez creado un objeto, este existe, y por tanto no debe ser vuelto a crear para ser usado nuevamente.

Propuesta dentro de la POO

Eliminación permanente del Objeto

<?php

$p = new Persistent(); // instanciamos al ORM
$a1 = $p->retrieve('GNU-007'); // hidratamos la instancia
print('El consumo es: '. $a1->calcularConsumo()); // mostramos el consumo
$p->delete($a1); // el Objeto es removido del repositorio
var_dump($a1); // devuelve NULL: el Objeto ya no existe

?>
Para persistir el Objeto, como para actualizarlo o eliminarlo, siempre se trabaja con una instancia del mismo.

Colecciones de Objetos

A partir de PHP 5, se propone trabajar con Colecciones
implementando la interfaz iteratorAggregate.
<?php

class CollectionAuto implements IteratorAggregate {
      private $items = array(); // la implementación queda oculta.
      // Métodos
      public function getIterator() {
            return new ArrayIterator($this->items);
      }
      // el método add() solo acepta instancias de Auto
      public function add(Auto $obj) {
            $this->items[] = $obj;
      }
}
?>
Nuestra propuesta de mapeo para trabajar con Colecciones requiere la implementación de esta interfaz.

Colecciones de Objetos

Hidratación de una Colección
<?php

$p = new Persistent(); // instanciamos al ORM
$c = new CollectionAuto(); // instanciamos la colección
$total = $p->retrieveCollection('Auto', $c); // hidrata los Autos y los agrega a $c
print("Existen $total instancias de Auto en el repositorio.");
foreach ($c as $auto) { // recorre los autos hidratados
      var_dump($auto);
}
?>
Las colecciones se devuelven paginadas, y pueden usarse filtros y ordenamientos, siempre haciendo referencia a los atributos del Objeto.

Como la colección es un Objeto, también puede tener atributos, y estos atributos pueden estar mapeados, con lo que se aprovecha la potencia de los RDBMS para devolver totalizadores.

Persistencia por alcance

Dadas las clases:
<?php

class Provincia {
      private $nom;
      public function __construct ($nom='') {
            $this->setNom($nom);
      }
      public function setNom ($v) { $this->nom = $v; }
      public function getNom() { return $this->nom; }
}
class Localidad {
      private $nom;
      private $provincia; // Instancia de Provincia
      public function __construct ($nom='', Provincia $provincia = NULL) {
            $this->setNom($nom);
            $this->setProvincia($provincia);
      }
      public function setNom ($v) { $this->nom = $v; }
      public function getNom() { return $this->nom; }
      public function setProvincia ($v) { $this->provincia = $v; }
      public function getProvincia() { return $this->provincia; }
}
?>

Mapeo de Agregaciones y Composiciones

Un ejemplo de mapeo usado por nuestro ORM:
<Persistent>
      <Provincia>
            <Table><Name>Provincias</Name></Table>
            <ObjectId>nom</ObjectId>
      </Provincia>
      <Localidad>
            <Table><Name>Localidades</Name></Table>
            <ObjectId>nom</ObjectId>
            <Attributes>
                  <provincia>
                        <field>id_pro</field>
                        <relation type="agregation">1-1</relation>
                        <reference>Provincia</reference>
                  </provincia>
            </Attributes>
      </Localidad>
</Persistent>
Nuestro mapeador asume que todo atributo no mapeado tiene un campo en la proyección con el mismo nombre.

Algunas Conclusiones

  • La persistencia no invasiva y por alcance en PHP es posible
  • Permite ajustarse a la POO cuando se diseña el Modelo
  • El Testeo de Unidad solo ejercita al Modelo
  • Se respeta la identidad de un Objeto y de una colección
  • Se conserva la potencia de los RDBMS para SUM(), COUNT(), MIN(), MAX() y AVG()
  • Funciona bajo carga considerable en sistemas en produccién
  • Se pierde algo de performace, pero se gana a cambio robustez, metodología y buenas prácticas

Trabajo futuro

  • Implementar niveles de depurado
  • Distribución de responsabilidades
  • Soportar atributos no persistentes
  • Gererar internamente querys parametrizadas para PDO
  • Ampliar mapeo para INSERT, UPDATE y DELETE
  • Implementarlo con el patrón de diseño Proxie
  • Ponerle un nombre bonito y un logotipo ;-)

Preguntas, dudas, sugerencias



Descarga del paper (PDF)

Descarga del paper http://pegasusnet.com.ar/difusion/2012-10-ORM (HTML)

Descarga directa del ORM

Descarga del ORM (internet)
http://pegasusnet.com.ar/difusion/2012-10-ORM/phporm.tar.gz

Mapeador Objeto/Relacional
Persistencia no invasiva y por alcance
en PHP



Gracias


por Leonardo Tadei
<leonardo@tadei.com.ar>
http://leonardo.tadei.com.ar

Pegasus Tech Supply
<leonardot@pegasusnet.com.ar>

Creative Commons Attribution 2.5 Argentina License
http://creativecommons.org/licenses/by/2.5/ar/
Soy un dios