2010-05-22 11 views
11

He visto varios frameworks MVC, así como marcos ORM independientes para PHP, así como otras preguntas ORM aquí; sin embargo, la mayoría de las preguntas requieren marcos existentes para comenzar, que es no lo que estoy buscando. (También he leído this SO question, pero no estoy seguro de qué hacer ya que las respuestas son vagas.)Consejos/recursos/patrones para aprender a implementar un ORM básico

En su lugar, pensé que sería mejor aprender ensuciando las manos y escribir mi propio ORM, incluso uno simple. Excepto que realmente no sé cómo empezar, especialmente porque el código que veo en otros ORM es tan complicado.

Con mi 5.2.x PHP (esto es importante) framework MVC tengo una capa de abstracción de base de datos personalizada básica, que tiene:

  • métodos muy simples, como connect($host, $user, $pass, $base), query($sql, $binds), etc
  • subclases para cada DBMS que soporta
  • Una clase (y respectivas subclases) para representar resultado SQL establece

Pero no lo hace tienen:

  • funcionalidad Active Record, que supongo es una cosa ORM (corríjanme si me equivoco)

EDIT: para aclarar, lo único que tienen una capa de abstracción de base de datos. Todavía no tengo modelos, pero cuando los implemente quiero que sean modelos ORM nativos (por así decirlo), de ahí esta pregunta.

He leído un poco sobre ORM, y desde mi punto de vista proporcionan un medio para obtener más modelos de datos abstractos de la base de datos al representar los datos como nada más que clases/objetos basados ​​en PHP; de nuevo, corrígeme si estoy equivocado o me he perdido de alguna manera.

Aún así, me gustaría obtener algunos consejos simples de cualquier otra persona que haya incursionado más o menos con los marcos ORM. ¿Hay algo más que necesite tomar en cuenta, muestras académicas simples para que pueda consultar, o recursos que pueda leer?

Respuesta

11

Como esta pregunta es bastante antigua, supongo que ya has intentado escribir un ORM. No obstante, cuando escribí un ORM personalizado hace dos años, me gustaría compartir mi experiencia e ideas.

Como dije, implementé un ORM personalizado hace dos años e incluso lo utilicé con cierto éxito en proyectos pequeños a medianos. Lo integé en un CMS bastante popular que en ese momento (e incluso ahora) carece de dicha funcionalidad ORM. Además, en aquel entonces, marcos populares como Doctrine realmente no me convencían. Mucho ha cambiado desde entonces y Doctrine 2 evolucionó en un marco sólido, entonces, si ahora tuviera la opción entre implementar mi propio ORM o usar uno de los frameworks populares como Doctrine 2 para uso de producción, esto no sería ninguna duda: use el soluciones existentes y estables. PERO: la implementación de dicho marco (de manera simple) fue un ejercicio de aprendizaje muy valioso y me ayudó mucho al trabajar con ORM de código abierto más grandes, a medida que comprendes mejor las trampas y dificultades asociadas con el mapeo relacional de objetos.

No es demasiado difícil implementar la funcionalidad básica de ORM, pero tan pronto como el mapeo de las relaciones entre objetos entran en juego, se vuelve mucho, mucho más difícil/interesante.


¿Cómo comencé?

Lo que me enganchó fue el libro de Martin Fowlers Patterns of Enterprise Application Architecture. Si desea programar su propio ORM o incluso si solo está trabajando con algún marco ORM, compre este libro. Es uno de los recursos más valiosos que cubren muchas de las técnicas básicas y avanzadas relacionadas con el campo del mapeo relacional de objetos. Lea sobre esto, obtendrá muchas ideas geniales sobre los patrones detrás de un ORM.

Arquitectura Básica

Decidí que si me gustaría utilizar en lugar de un enfoque Active Record o algún tipo de Data Mapper. Esta decisión influye en cómo los datos de la base de datos se asignan a la entidad. Decidí implementar un Data Mapper simple, el mismo enfoque que Doctrine 2 o Hibernate en los usos de Java. Active Record es el enfoque de la funcionalidad ORM (si puede llamarlo así) en Zend Framework. Active Record es mucho más simple que Data Mapper, pero también mucho más limitado. Lea sobre estos patrones y verifique los marcos mencionados, obtiene la diferencia bastante rápido. Si decide ir con un Data Mapper, también debe leer en PHPs reflection API.

Consulta

que tenían el ambicioso objetivo de crear mi propio lenguaje de consulta, al igual que en Doctrina DQL o HQL en Hibernate. Pronto lo abandoné, ya que escribir un analizador/lexer de SQL personalizado parecía complicado (¡y realmente lo es!). Lo que hice fue implementar un Query Object, para encapsular la información que tabla está involucrada en la consulta (eso es importante ya que necesita mapear los datos de la base de datos a las clases relevantes para cada tabla).

Consulta para un objeto en mi ORM se veía así:

public function findCountryByUid($countryUid) { 
    $queryObject = new QueryObject(); 
    $queryObject->addSelectFields(new SelectFields('countries', '*')) 
      ->addTable(new Table('countries')) 
      ->addWhere('countries.uid = "' . intval($countryUid) . '"'); 

    $res = $this->findByQuery($queryObject); 
    return $res->getSingleResult(); 
} 

configuración

Normalmente, también es necesario tener algún tipo de formato de configuración, Hibernate utiliza XML (entre otros), Doctrine 2 usa anotaciones PHP, EZComponents usa matrices PHP en su Persistent Object component como formato de configuración. Eso es lo que usé, también, parecía una elección natural y el CMS con el que trabajé también usó el formato de configuración de PHP.

Con esa configuración, se definen

  • qué tabla se asigna a qué clase
  • qué campos debe quedar asignada a la instancia de clase
  • qué tipo los campos de la tabla tienen (int, string , etc.)
  • las relaciones entre las entidades (por ejemplo, una clase de usuario tiene una referencia a una clase UserGroup)
  • etc.

Y esa es la información que utiliza en su Data Mapper para asignar el resultado de DB a los objetos.

Implementación

yo decidimos ir con un enfoque basado en pruebas fuertes, debido a la naturaleza compleja de escribir un ORM personalizado. TDD o no, escribir muchas, muchas pruebas de unidades es una muy buena idea en un proyecto así. Aparte de eso: ensúciate las manos y mantén el libro de Fowlers cerca. ;-)


Como ya he dicho que era realmente vale la pena el esfuerzo, pero no le gustaría hacerlo de nuevo, tanto a causa de los marcos maduras que existen hoy en día.

Ya no uso mi ORM, funcionó, pero carecía de muchas características, entre otras: carga diferida, asignación de componentes, soporte de transacciones, almacenamiento en caché, tipos personalizados, declaraciones/parámetros preparados, etc. Y su rendimiento no era Es lo suficientemente bueno para usarlo en proyectos a gran escala.

No obstante, espero poder darte algunos puntos de partida en el campo de ORM, si aún no los conocías. ;-)

+0

Gran visión general. Usted tiene un punto válido acerca de la existencia de soluciones probadas, pero de todos modos introdujo algunas ideas importantes que debo tener en cuenta cuando me dedico al diseño de mi propio ORM. Me alegra que fue una gran experiencia de aprendizaje para ti también. Me aseguraré de buscar el libro de Fowler una vez que esté afuera. Si no aparecen nuevas respuestas durante unos días, puede obtener otro +15. ¡Gracias por tu tiempo y explicación! :) – BoltClock

2

Un ORM sencilla se puede construir utilizando __get() y __set() y un par de métodos personalizados (posiblemente utilizando __call()), aquí es un simple pseudo-código:

class ORM 
{ 
    private $table = null; 
    private $fields = array(); 

    function __construct($table) 
    { 
    $this->table = $table; 
    } 

    function __get($key) 
    { 
    return isset($this->fields[$key]) ? $this->fields[$key] : false; 
    } 

    function __set($key, $value) 
    { 
    $this->fields[$key] = $value; 
    } 

    function load($id, $field = 'id') 
    { 
    // populate $this->fields with SELECT * FROM $this->table WHERE $field = $id; 
    } 

    function save() 
    { 
    if (isset($this->fields['id'])) 
    { 
     // UPDATE $this->table SET $this->fields; 
    } 

    else 
    { 
     // INSERT INTO $this->table $this->fields; 
    } 
    } 
} 

$user = new ORM('user'); 

$user->name = 'name'; 
$user->pass = '1337'; 

$user->save(); 

Esto es sólo un ejemplo básico para conseguir que empezado. Puede agregar lógica adicional utilizando el método mágico __call() para obtener resultados de otros campos distintos de id, por ejemplo.

Tenga en cuenta que el ejemplo que proporcioné no maneja las relaciones, ahí es donde varias implementaciones ORM realmente difieren, sin embargo, normalmente no confío en ningún ORM para manejar las relaciones, ya que tienden a ser más lentas y no producen consultas eficientes

+0

¡Gracias! En cuanto a tu último punto, ¿cómo manejas las relaciones? – BoltClock

+1

@BoltClock: yo no. No soy un gran admirador de los ORM, por lo que normalmente solo escribo las consultas SQL completas directamente. Lea la siguiente pregunta para obtener inspiración: http://stackoverflow.com/questions/1336661/simpler-orm-for-php. –

Cuestiones relacionadas