pattern patron imf español data active design-patterns oop datamapper

design-patterns - patron - datamapper php



Mapeador de datos y relaciones: ¿Estrategias de implementación? (3)

Antes de comenzar, asumo que has leído el libro PoEAA de Fowler de principio a fin. =) Además, consideraré que ya pensó en los primeros problemas iniciales que enfrenta al tratar con ORMs. Puedo resaltar uno fácil, como llamar a un DataMapper varias veces usando el mismo identificador y devolver siempre el mismo objeto (leído como IdentityMap).

Importante: ¿Se permite que un asignador de datos use otro asignador de datos?

Solo es posible que un DataMapper acceda a otro si el segundo es una referencia débil en el segundo.

Digamos que en la mayoría de los casos necesita tanto el objeto de la empresa como el objeto de la dirección, porque siempre lo muestra en una lista todos juntos. En este caso, CompanyDataMapper no solo obtiene objetos de la compañía, sino que realiza una consulta SQL con JOIN para obtener también todos los campos del objeto de dirección. Finalmente, itera sobre el conjunto de registros y alimenta los nuevos objetos con sus valores correspondientes, asignando el objeto de dirección al objeto de la compañía.

El problema que intentas discutir aquí parece simple en la práctica, pero es un poco complejo detrás de escena.

En primer lugar, no debe tener una getAddress (Compañía), sino beneficiarse de tener objetos Proxy. Un proxy es una representación no inicializada de una instancia dada. En este caso, un Proxy contiene una referencia a la entrada que está buscando. Debe extenderse desde su objeto original y debe proporcionar un método de inicialización, junto con un DataMapper relacionado para cargarlo.

La segunda parte acerca de UNIRSE y cargar varios objetos a la vez se llama Hydrator. Los hidratadores reciben una estructura plana de líneas y columnas y se convierten en un gráfico de objetos. Pero realmente entra en un tema aparte: si estás tratando puramente con objetos, ¿por qué vas a buscar tablas? Tratar de adoptar un enfoque de búsqueda de objetos lo llevaría a implementar un tipo de OQL (Object Query Language).

Importante: ¿Es esto cierto? ¿Debo enviar 100 consultas para obtener 100 objetos de dirección, si tengo 10 compañías con cada 10 direcciones?

Tratar con una colección de objetos es una pesadilla en PHP. Sí, el lenguaje apesta mucho por la falta de una implementación de colección potente. Básicamente, debe tratar con diferentes situaciones aquí: - nueva instancia y todos los elementos en esta lista de elementos son nuevos - nueva instancia y todos los elementos en esta lista de elementos son preexistentes - nueva instancia y elementos en esta lista de elementos se mezclan entre lo nuevo y lo preexistente - instancia preexistente y sin tocar nada en la lista de elementos - instancia preexistente y manipulación de elementos en la lista

Estoy siendo muy simplista aquí, pero el punto principal que quiero destacar es la necesidad de un objeto Colección. Hay dos de ellos: uno que trata con nuevas listas y otro que trata con listas existentes. El que se ocupa de las listas existentes debe poder cargar la colección una vez que intente acceder a algo dentro de ella. Esa es la única manera de no tener n + 1 problemas.

Aquí también destaca el próximo gran problema que tendrías que enfrentar. Las asociaciones pueden ser unidireccionales o bidireccionales. Esto significa que la Compañía sabe acerca de la Dirección pero la Dirección no tiene idea de que la Compañía es unidireccional, mientras que un Usuario es parte de muchos Grupos y los Grupos contienen muchos Usuarios es una asociación bidireccional. Las cosas fácilmente se convierten en una pesadilla aquí y es por eso que necesita patrones de Mapeo para entender adecuadamente lo que está pasando.

Tratar con muchos a muchos es lo mismo que tratar con las colecciones en general.

Hay una parte importante que aún no has considerado. Si construyo todo mi gráfico de objetos (Compañía y Dirección) y decido persistirlos ... ¿es necesario que persistan ambos o tengo que decir manualmente lo que quiero persistir? Ambas formas tienen diferentes conjuntos de problemas. Supongamos que desea el primer enfoque. Acaba de ingresar en lo que considero uno de los patrones de diseño más complejos para implementar: UnitOfWork. Entonces tendría que lidiar con la clasificación del orden de las entidades que se aplicarán para no generar problemas de restricción (lea Clasificación topológica sobre cómo resolver esto). Si adopta el segundo enfoque, puede ingresar fácilmente en una situación en la que sienta que su herramienta está rota, principalmente porque es muy fácil tener su gráfico de objetos en un estado inconsistente.

Finalmente ... ¿planeas hacer CUALQUIER apoyo para la herencia? Si es positivo, toda su planificación acaba de ingresar a un nivel completamente nuevo. = (Tratar de explicarme me llevaría un libro. Pero puedo señalar algunos patrones de diseño que puede observar: Herencia de tabla concreta (1 clase, 1 tabla), Herencia de tabla única (N clases, 1 tabla) y Herencia de tabla de clase (N Clases, tablas M).

Puedo profundizar en muchos puntos diferentes aquí, pero los ORM normalmente conducen a explosiones en la cabeza. Me detendré por ahora.

PD: soy uno de los principales desarrolladores de Doctrine ORM. A menos que esté haciendo esto con fines de estudio, no se moleste en intentar crear otro. Es extremadamente complejo, requiere mucho tiempo y exige mucha planificación sobre cómo funcionarán las cosas antes de que incluso codifiques la primera línea. De hecho, planeamos Doctrine ORM por 2 años y tardamos 1 año en implementar de manera confiable la funcionalidad principal. No te estoy desanimando, pero como dijo Fowler en su artículo de odio de ORM, es una solución compleja para un problema incluso complejo.

Casi he terminado mi asignador de datos, pero ahora estoy en el punto de las relaciones.

Intentaré ilustrar mis ideas aquí. No pude encontrar buenos artículos / información sobre este tema, así que quizás estoy reinventando la rueda (seguro que sí, podría usar un gran marco, pero quiero aprender haciéndolo).

Relaciones 1: 1

Primero, veamos las relaciones 1: 1. En general, cuando tenemos una clase de dominio llamada "Compañía" y una llamada "Dirección", nuestra clase de Compañía tendrá algo así como address_id. Digamos que en la mayoría de los casos simplemente mostramos una lista de Compañías, y la dirección solo es necesaria cuando alguien mira los detalles. En ese caso, mi Data Mapper (CompanyDataMapper) simplemente carga perezosamente, lo que significa que solo recuperará esa address_id de la base de datos, pero no hará una unión para obtener los datos de la dirección también.

En general, tengo un método getter para cada relación. Entonces, en este caso, hay un método getAddress (Company companyObject). Toma un objeto de empresa, busca su propiedad de dirección y, si es NULL, busca el objeto de dirección correspondiente de la base de datos, utilizando la clase de asignador para ese objeto de dirección (AddressDataMapper), y asigna ese objeto de dirección a la propiedad de dirección del lugar especificado. objeto de la empresa.

Importante: ¿Se permite que un asignador de datos use otro asignador de datos?

Digamos que en la mayoría de los casos necesita tanto el objeto de la empresa como el objeto de la dirección, porque siempre lo muestra en una lista todos juntos. En este caso, CompanyDataMapper no solo obtiene objetos de la compañía, sino que realiza una consulta SQL con JOIN para obtener también todos los campos del objeto de dirección. Finalmente, itera sobre el conjunto de registros y alimenta los nuevos objetos con sus valores correspondientes, asignando el objeto de dirección al objeto de la compañía.

Suena simple, hasta ahora.

1: n Relaciones

¿Que tal esto? La única diferencia a 1: 1 es que una empresa puede tener varios objetos de dirección. Vamos a echar un vistazo: cuando la mayoría de las veces solo estamos interesados ​​en la Compañía, el Asignador de datos solo establecerá la propiedad de direcciones del objeto de la compañía en NULL. La propiedad de las direcciones es una matriz que puede hacer referencia a ninguna, una o varias direcciones. Pero no lo sabemos todavía, ya que cargamos perezosamente, por lo que es NULL. Pero, ¿y si también necesitaríamos todas las direcciones en la mayoría de los casos? ¿Si mostraríamos una lista grande con todas las compañías junto con todas sus direcciones? En este caso, las cosas empiezan a ponerse realmente feas. Primero, no podemos unirnos a la tabla de direcciones cincuenta veces por cada objeto de dirección (creo firmemente que eso es imposible, y si lo es, el rendimiento sería inferior a cero). Entonces, cuando pensamos esto más adelante, es imposible NO cargar perezosamente en este caso.

Importante: ¿Es esto cierto? ¿Debo enviar 100 consultas para obtener 100 objetos de dirección, si tengo 10 compañías con cada 10 direcciones?

m: n relaciones

Digamos que un objeto de dirección solo contiene el país, estado, ciudad, carretera y número de casa. Pero una casa podría ser una gran torre de negocios con muchas compañías. Como uno de esos modernos edificios de oficinas donde cualquiera puede alquilar una pequeña ROM para mostrar esa torre en su sitio web. Entonces: muchas empresas pueden compartir la misma dirección.

Todavía no tengo planes para lidiar con ese tipo de problema.

Importante: Probablemente no sea un problema mayor que el de las relaciones 1: n.

Si alguien conoce un buen recurso que incluya detalles sobre cómo resolverlo / implementarlo, ¡me encantaría un enlace!


Espero con interés cualquier respuesta que obtenga sobre este tema, pero mientras tanto, ¿por qué no simplemente saltar a Amazon (o al distribuidor de libros local) y finalmente comprar?

Estos libros contienen los patrones originales a los que se ha señalado en varias de sus preguntas y se consideran trabajos de referencia en Patrones de diseño y Arquitectura de software.


Yo también estoy trabajando en este problema. Para empezar, he adaptado el patrón del asignador de datos de los Objetos, Patrones y Práctica de PHP de Matt Zandstra (2d Ed). Ahora veo que ha salido una nueva edición.

Quizás la parte más ingeniosa de la configuración son los objetos "Colecciones". No estoy seguro de qué idioma está usando, así que le ahorraré los detalles. Basta con decir que PHP tiene una interfaz de iterador que hace posible cargar una matriz (mapa, en otros idiomas) al principio y transformar los datos sin procesar en objetos (¿hidratar?) Sobre la marcha, mientras se realiza el bucle.

Al igual que usted, estoy luchando con cómo cargar las relaciones. Lo que he encontrado hasta ahora es que puedo escribir mi consulta JOIN masiva en la clase Mapper y crear una colección deshidratada para el objeto de destino y colar los datos de los objetos relacionados al mismo tiempo.

Realmente no me gusta "Lazy Load" porque conduce a muchas consultas de base de datos. Ofende a mi sensibilidad perfeccionista saber que estoy utilizando decenas o cientos de consultas para lograr que se podrían hacer en una.

Yo también estoy esperando más respuestas.