El patrón de diseño del repositorio

El patrón de diseño de repositorio, definido por Eric Evens en su libro de diseño impulsado por dominio, es uno de los patrones de diseño más útiles y más ampliamente aplicados jamás inventado. Cualquier aplicación debe funcionar con persistencia y con algún tipo de lista de elementos. Estos pueden ser usuarios, productos, redes, discos o de lo que sea su aplicación. Si tienes un blog, por ejemplo, tienes que lidiar con listas de publicaciones de blog y listas de comentarios. El problema que todas estas lógicas de administración de listas tienen en común es cómo conectar la lógica de negocios, las fábricas y la persistencia..


El patrón de diseño de fábrica

Como mencionamos en el párrafo introductorio, un Repositorio conectará las Fábricas con las Puertas de enlace (persistencia). Estos también son patrones de diseño y, si no está familiarizado con ellos, este párrafo arrojará algo de luz sobre el tema..

Una fábrica es un patrón de diseño simple que define una forma conveniente de crear objetos. Es una clase o conjunto de clases responsables de crear los objetos que necesita nuestra lógica de negocios. Una fábrica tradicionalmente tiene un método llamado "hacer()" y sabrá cómo tomar toda la información necesaria para construir un objeto y hacer el mismo edificio y devolver un objeto listo para usar a la lógica de negocios.

Aquí hay un poco más sobre Factory Pattern en un tutorial anterior de Nettuts +: Guía para principiantes sobre patrones de diseño. Si prefiere una vista más profunda del patrón de fábrica, consulte el primer patrón de diseño en el curso de patrones de diseño ágil que tenemos en Tuts+.


El patrón de puerta de enlace

También conocido como "Table Data Gateway" es un patrón simple que ofrece la conexión entre la lógica de negocios y la base de datos en sí. Su principal responsabilidad es hacer las consultas en la base de datos y proporcionar los datos recuperados en una estructura de datos típica del lenguaje de programación (como una matriz en PHP). Estos datos generalmente se filtran y modifican en el código PHP para que podamos obtener la información y las variables necesarias para crear nuestros objetos. Esta información debe ser pasada a las Fábricas..

El patrón de diseño de la puerta de enlace se explica y ejemplifica con gran detalle en un tutorial de Nettuts + sobre Evolución hacia una capa de persistencia. Además, en el mismo curso de Patrones de diseño ágil, la segunda lección de patrón de diseño es sobre este tema..


Los problemas que necesitamos resolver

Duplicación por manejo de datos

Puede que no sea obvio a primera vista, pero conectar Gateways a fábricas puede llevar a muchas duplicaciones. Cualquier software de tamaño considerable necesita crear los mismos objetos desde diferentes lugares. En cada lugar, necesitará usar la puerta de enlace para recuperar un conjunto de datos sin procesar, filtrar y trabajar esos datos para estar listos para ser enviados a las fábricas. Desde todos estos lugares, llamará a las mismas fábricas con las mismas estructuras de datos pero obviamente con datos diferentes. Sus objetos serán creados y proporcionados a usted por las Fábricas. Esto, inevitablemente, conducirá a una gran cantidad de duplicación en el tiempo. Y la duplicación se extenderá a través de clases o módulos distantes y será difícil de notar y corregir.

Duplicación por Reimplementación de lógica de recuperación de datos

Otro problema que tenemos es cómo expresar las consultas que necesitamos hacer con la ayuda de los Gateways. Cada vez que necesitamos algo de información de la puerta de enlace debemos pensar ¿qué necesitamos exactamente? ¿Necesitamos todos los datos sobre un solo tema? ¿Necesitamos sólo alguna información específica? ¿Queremos recuperar un grupo específico de la base de datos y realizar la clasificación o el filtrado refinado en nuestro lenguaje de programación? Todas estas preguntas deben abordarse cada vez que recuperamos información de la capa de persistencia a través de nuestro Gateway. Cada vez que hagamos esto, tendremos que encontrar una solución. Con el tiempo, a medida que nuestra aplicación crezca, nos enfrentaremos a los mismos dilemas en diferentes lugares de nuestra aplicación. Inadvertidamente, encontraremos soluciones ligeramente diferentes para los mismos problemas. Esto no solo requiere tiempo y esfuerzo extra, sino que también conduce a una duplicación sutil, en su mayoría muy difícil de reconocer. Este es el tipo de duplicación más peligroso..

Duplicación por Reimplementación de lógica de persistencia de datos

En los dos párrafos anteriores hablamos solo sobre la recuperación de datos. Pero el Gateway es bidireccional. Nuestra lógica de negocio es bidireccional. Tenemos que persistir de alguna manera nuestros objetos. Esto nuevamente nos lleva a muchas repeticiones si queremos implementar esta lógica según sea necesario en los diferentes módulos y clases de nuestra aplicación..


Los conceptos principales

Repositorio para la recuperación de datos

Un repositorio puede funcionar de dos maneras: recuperación de datos y persistencia de datos.


Cuando se utiliza para recuperar objetos de la persistencia, se llamará a un Repositorio con una consulta personalizada. Esta consulta puede ser un método específico por nombre o un método más común con parámetros. El repositorio es responsable de proporcionar e implementar estos métodos de consulta. Cuando se llama a un método de este tipo, el Repositorio se pondrá en contacto con la Puerta de enlace para recuperar los datos sin procesar de la persistencia. La puerta de enlace proporcionará datos de objetos sin procesar (como una matriz con valores). Luego, el Repositorio tomará estos datos, hará las transformaciones necesarias y llamará a los métodos de fábrica apropiados. Las fábricas proporcionarán los objetos construidos con los datos proporcionados por el repositorio. El Repositorio recogerá estos objetos y los devolverá como un conjunto de objetos (como una matriz de objetos o un objeto de colección como se define en la lección de Patrón compuesto en el curso de Patrones de diseño ágil).

Repositorio para la persistencia de datos

La segunda forma en que un Repositorio puede funcionar es proporcionar la lógica necesaria para extraer la información de un objeto y conservarla. Esto puede ser tan simple como serializar el objeto y enviar los datos serializados a la puerta de enlace para persistir o tan sofisticados como crear matrices de información con todos los campos y el estado de un objeto..


Cuando se utiliza para conservar información, la clase cliente es la que se comunica directamente con Factory. Imagina un escenario cuando se publica un nuevo comentario en una publicación de blog. Nuestro objeto de comentario es creado por nuestra lógica de negocios (la clase de Cliente) y luego se envía al Repositorio para que sea persistente. El repositorio conservará los objetos utilizando la puerta de enlace y, opcionalmente, los almacenará en caché en una lista local en la memoria. Los datos deben transformarse porque solo hay casos raros en que los objetos reales se pueden guardar directamente en un sistema de persistencia.


Conectando los puntos

La imagen de abajo es una vista de nivel superior sobre cómo integrar el Repositorio entre las Fábricas, la Puerta de enlace y el Cliente..


En el centro del esquema está nuestro Repositorio. A la izquierda, hay una interfaz para la puerta de enlace, una implementación y la persistencia en sí misma. A la derecha, hay una interfaz para las fábricas y una implementación de fábrica. Finalmente, en la parte superior está la clase cliente..

Como se puede observar desde la dirección de las flechas, las dependencias se invierten. El repositorio solo depende de las interfaces abstractas para Fábricas y Puertas de enlace. Gateway depende de su interfaz y la persistencia que ofrece. La fábrica depende solo de su interfaz. El cliente depende del Repositorio, que es aceptable porque el Repositorio tiende a ser menos concreto que el Cliente.


En perspectiva, el párrafo anterior respeta nuestra arquitectura de alto nivel y la dirección de las dependencias que queremos lograr.


Gestionar comentarios a publicaciones de blog con un repositorio

Ahora que hemos visto la teoría, es hora de un ejemplo práctico. Imagina que tenemos un blog en el que tenemos objetos Publicar y Comentario. Los comentarios pertenecen a las publicaciones y tenemos que encontrar una manera de persistirlas y recuperarlas..

El comentario

Comenzaremos con una prueba que nos obligará a pensar qué debe contener nuestro objeto Comentario.

la clase RepositoryTest extiende PHPUnit_Framework_TestCase function testACommentHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe tiene una opinión sobre el patrón del repositorio"; $ commentBody = "Creo que es una buena idea usar el patrón de repositorio para persistir y recuperar objetos."; $ comment = new Comment ($ postId, $ commentAuthor, $ commentAuthorEmail, $ commentSubject, $ commentBody); 

A primera vista, un comentario solo será un objeto de datos. Puede que no tenga ninguna funcionalidad, pero eso depende del contexto de nuestra aplicación para decidir. Para este ejemplo, supongamos que es un objeto de datos simple. Construido con un conjunto de variables..

Comentario de clase 

Solo creando una clase vacía y exigiéndola en la prueba, se hace pasar..

require_once '… /Comment.php'; la clase RepositoryTest extiende PHPUnit_Framework_TestCase […]

Pero eso está lejos de ser perfecto. Nuestra prueba no prueba nada todavía. Vamos a forzarnos a escribir todos los captadores en la clase Comentario.

function testACommentsHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe tiene una opinión sobre el patrón del repositorio"; $ commentBody = "Creo que es una buena idea usar el patrón de repositorio para persistir y recuperar objetos."; $ comment = new Comment ($ postId, $ commentAuthor, $ commentAuthorEmail, $ commentSubject, $ commentBody); $ this-> assertEquals ($ postId, $ comment-> getPostId ()); $ this-> assertEquals ($ commentAuthor, $ comment-> getAuthor ()); $ this-> assertEquals ($ commentAuthorEmail, $ comment-> getAuthorEmail ()); $ this-> assertEquals ($ commentSubject, $ comment-> getSubject ()); $ this-> assertEquals ($ commentBody, $ comment-> getBody ()); 

Para controlar la duración del tutorial, escribí todas las afirmaciones a la vez y las implementaremos también a la vez. En la vida real, tómalos uno por uno..

 Comentario de la clase private $ postId; autor privado $ privado $ authorEmail; sujeto privado $ $ cuerpo privado; function __construct ($ postId, $ author, $ authorEmail, $ subject, $ body) $ this-> postId = $ postId; $ this-> author = $ author; $ this-> authorEmail = $ authorEmail; $ this-> subject = $ subject; $ this-> body = $ body;  función pública getPostId () return $ this-> postId;  public function getAuthor () return $ this-> author;  función pública getAuthorEmail () return $ this-> authorEmail;  función pública getSubject () return $ this-> subject;  función pública getBody () return $ this-> body; 

Excepto por la lista de variables privadas, el resto del código fue generado por mi IDE, NetBeans, por lo que probar el código generado automáticamente puede ser un poco de sobrecarga algunas veces. Si no está escribiendo estas líneas por sí mismo, siéntase libre de hacerlo directamente y no se moleste con las pruebas para los creadores y constructores. Sin embargo, la prueba nos ayudó a exponer mejor nuestras ideas y documentar mejor lo que contendrá nuestra clase de Comentarios.

También podemos considerar estos métodos de prueba y clases de prueba como nuestras clases "Cliente" de los esquemas.


Nuestra puerta de entrada a la persistencia

Para mantener este ejemplo lo más simple posible, solo implementaremos una InMemoryPersistence para no complicar nuestra existencia con sistemas de archivos o bases de datos..

require_once '… /InMemoryPersistence.php'; la clase InMemoryPersistenceTest extiende PHPUnit_Framework_TestCase function testItCanPerisistAndRetrieveASingleDataArray () $ data = array ('data'); $ persistence = new InMemoryPersistence (); $ persistencia-> persistir ($ datos); $ this-> assertEquals ($ data, $ persistence-> retrieve (0)); 

Como de costumbre, comenzamos con la prueba más simple que podría fallar y también nos obligan a escribir algo de código. Esta prueba crea una nueva InMemoryPersistence objeto e intenta persistir y recuperar una matriz llamada datos.

require_once __DIR__. '/Persistence.php'; la clase InMemoryPersistence implementa Persistence private $ data = array (); función persistir ($ data) $ this-> data = $ data;  recuperar función ($ id) return $ this-> data; 

El código más simple para hacerlo pasar es simplemente mantener el entrante $ datos en una variable privada y devolverla en el recuperar método. El código tal como está ahora no se preocupa por lo enviado $ id variable. Es la cosa más simple que podría hacer pasar la prueba. También nos tomamos la libertad de introducir e implementar una interfaz llamada Persistencia.

interfaz persistencia función persistir ($ datos); función recuperar ($ ids); 

Esta interfaz define los dos métodos que cualquier Gateway necesita implementar.. Persistir y recuperar. Como probablemente ya has adivinado, nuestro Gateway es nuestro InMemoryPersistence La clase y nuestra persistencia física es la variable privada que guarda nuestros datos en la memoria. Pero volvamos a la implementación de esto en la persistencia de la memoria..

function testItCanPerisistSeveralElementsAndRetrieveAnyOfThem () $ data1 = array ('data1'); $ data2 = array ('data2'); $ persistence = new InMemoryPersistence (); $ persistencia-> persistir ($ datos1); $ persistencia-> persistir ($ data2); $ this-> assertEquals ($ data1, $ persistence-> retrieve (0)); $ this-> assertEquals ($ data2, $ persistence-> retrieve (1)); 

Añadimos otra prueba. En esta persistimos dos matrices de datos diferentes. Esperamos poder recuperar cada uno de ellos individualmente..

require_once __DIR__. '/Persistence.php'; la clase InMemoryPersistence implementa Persistence private $ data = array (); función persistir ($ data) $ this-> data [] = $ data;  recuperar función ($ id) return $ this-> data [$ id]; 

La prueba nos obligó a alterar ligeramente nuestro código. Ahora necesitamos agregar datos a nuestra matriz, no solo reemplazarlos con el que se envió a persiste (). También debemos considerar la $ id parámetro y devolver el elemento en ese índice.

Esto es suficiente para nuestra InMemoryPersistence. Si es necesario, podemos modificarlo más tarde..


Nuestra fábrica

Tenemos un cliente (nuestras pruebas), una persistencia con una puerta de enlace, y los objetos de comentarios que persisten. Lo siguiente que falta es nuestra fábrica..

Comenzamos nuestra codificación con un Prueba de repositorio expediente. Esta prueba, sin embargo, en realidad creó una Comentario objeto. Ahora necesitamos crear pruebas para verificar si nuestra Fábrica podrá crear Comentario objetos. Parece que tuvimos un error de juicio y nuestra prueba es más probable que sea una prueba para nuestra próxima Fábrica que para nuestro Repositorio. Podemos moverlo a otro archivo de prueba, CommentFactoryTest.

require_once '… /Comment.php'; la clase CommentFactoryTest extiende PHPUnit_Framework_TestCase function testACommentsHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe tiene una opinión sobre el patrón del repositorio"; $ commentBody = "Creo que es una buena idea usar el patrón de repositorio para persistir y recuperar objetos."; $ comment = new Comment ($ postId, $ commentAuthor, $ commentAuthorEmail, $ commentSubject, $ commentBody); $ this-> assertEquals ($ postId, $ comment-> getPostId ()); $ this-> assertEquals ($ commentAuthor, $ comment-> getAuthor ()); $ this-> assertEquals ($ commentAuthorEmail, $ comment-> getAuthorEmail ()); $ this-> assertEquals ($ commentSubject, $ comment-> getSubject ()); $ this-> assertEquals ($ commentBody, $ comment-> getBody ()); 

Ahora, esta prueba obviamente pasa. Y si bien es una prueba correcta, deberíamos considerar modificarla. Queremos crear un Fábrica objeto, pasar en una matriz y pedirle que cree un Comentario para nosotros.

require_once '… /CommentFactory.php'; la clase CommentFactoryTest extiende PHPUnit_Framework_TestCase function testACommentsHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe tiene una opinión sobre el patrón del repositorio"; $ commentBody = "Creo que es una buena idea usar el patrón de repositorio para persistir y recuperar objetos."; $ commentData = array ($ postId, $ commentAuthor, $ commentAuthorEmail, $ commentSubject, $ commentBody); $ comment = (new CommentFactory ()) -> make ($ commentData); $ this-> assertEquals ($ postId, $ comment-> getPostId ()); $ this-> assertEquals ($ commentAuthor, $ comment-> getAuthor ()); $ this-> assertEquals ($ commentAuthorEmail, $ comment-> getAuthorEmail ()); $ this-> assertEquals ($ commentSubject, $ comment-> getSubject ()); $ this-> assertEquals ($ commentBody, $ comment-> getBody ()); 

Nunca debemos nombrar nuestras clases según el patrón de diseño que implementan, pero Fábrica y Repositorio Representa más que el patrón de diseño en sí. Personalmente no tengo nada en contra de incluir estas dos palabras en los nombres de nuestra clase. Sin embargo, sigo recomendando y respetando el concepto de no nombrar a nuestras clases después de los patrones de diseño que utilizamos para el resto de los patrones..

Esta prueba es ligeramente diferente de la anterior, pero falla. Intenta crear un CommentFactory objeto. Esa clase no existe todavía. También intentamos llamar a un hacer() Método en él con una matriz que contiene toda la información de un comentario como una matriz. Este método se define en el Fábrica interfaz.

interfaz Factory function make ($ data); 

Esta es una muy comun Fábrica interfaz. Definió el único método requerido para una fábrica, el método que realmente crea los objetos que queremos.

require_once __DIR__. '/Factory.php'; require_once __DIR__. '/Comment.php'; la clase CommentFactory implementa Factory function make ($ components) return new Comment ($ components [0], $ components [1], $ components [2], $ components [3], $ components [4]); 

Y CommentFactory implementa el Fábrica interfaz exitosa tomando la $ componentes parámetro en su hacer() Método, crea y devuelve un nuevo. Comentario objetar con la información desde allí.

Mantendremos nuestra lógica de persistencia y creación de objetos lo más simple posible. Podemos, para este tutorial, ignorar con seguridad cualquier manejo de errores, validación y lanzamiento de excepciones. Pararemos aquí con la implementación de persistencia y creación de objetos..


Uso de un repositorio para persistir comentarios

Como hemos visto anteriormente, podemos usar un Repositorio de dos maneras. Para recuperar información de la persistencia y también para persistir información en la capa de persistencia. El uso de TDD es, en la mayoría de los casos, más fácil comenzar con el almacenamiento (persistencia) de la lógica y luego usar esa implementación existente para probar la recuperación de datos.

require_once '… /… /… /vendor/autoload.php'; require_once '… /CommentRepository.php'; require_once '… /CommentFactory.php'; la clase RepositoryTest extiende PHPUnit_Framework_TestCase función protegida tearDown () \ Mockery :: close ();  function testItCallsThePersistenceWhenAddingAComment () $ persistanceGateway = \ Mockery :: mock ('Persistence'); $ commentRepository = new CommentRepository ($ persistanceGateway); $ commentData = array (1, 'x', 'x', 'x', 'x'); $ comment = (new CommentFactory ()) -> make ($ commentData); $ persistanceGateway-> shouldReceive ('persist') -> once () -> with ($ commentData); $ commentRepository-> add ($ comment); 

Usamos Mockery para simular nuestra persistencia e inyectar ese objeto simulado en el Repositorio. Entonces llamamos añadir() en el repositorio. Este método tiene un parámetro de tipo. Comentario. Esperamos que la persistencia sea llamada con una matriz de datos similar a $ commentData.

require_once __DIR__. '/InMemoryPersistence.php'; clase CommentRepository private $ persistence; función __construct (persistencia $ persistencia = nula) $ this-> persistencia = $ persistencia? : nuevo InMemoryPersistence ();  función agregar (Comentario $ comentario) $ this-> persistence-> persist (array ($ comment-> getPostId (), $ comment-> getAuthor (), $ comment-> getAuthorEmail (), $ comment-> getSubject ( ), $ comment-> getBody ())); 

Como puedes ver, la añadir() El método es bastante inteligente. Encapsula el conocimiento acerca de cómo transformar un objeto PHP en una matriz simple utilizable por la persistencia. Recuerde, nuestra puerta de enlace de persistencia generalmente es un objeto general para todos nuestros datos. Puede y va a persistir en todos los datos de nuestra aplicación, por lo que el envío a los objetos haría que hiciera demasiado: tanto la conversión como la persistencia efectiva.

Cuando tienes un InMemoryPersistence Clase como lo hacemos, es muy rápido. Podemos usarlo como una alternativa para burlarnos de la puerta de enlace..

function testAPersistedCommentCanBeRetrievedFromTheGateway () $ persistanceGateway = new InMemoryPersistence (); $ commentRepository = new CommentRepository ($ persistanceGateway); $ commentData = array (1, 'x', 'x', 'x', 'x'); $ comment = (new CommentFactory ()) -> make ($ commentData); $ commentRepository-> add ($ comment); $ this-> assertEquals ($ commentData, $ persistanceGateway-> retrieve (0)); 

Por supuesto, si no tiene una implementación en memoria de su persistencia, la burla es la única manera razonable de hacerlo. De lo contrario, su prueba será demasiado lenta para ser práctica..

function testItCanAddMultipleCommentsAtOnce () $ persistanceGateway = \ Mockery :: mock ('Persistence'); $ commentRepository = new CommentRepository ($ persistanceGateway); $ commentData1 = array (1, 'x', 'x', 'x', 'x'); $ comment1 = (new CommentFactory ()) -> make ($ commentData1); $ commentData2 = array (2, 'y', 'y', 'y', 'y'); $ comment2 = (new CommentFactory ()) -> make ($ commentData2); $ persistanceGateway-> shouldReceive ('persist') -> once () -> with ($ commentData1); $ persistanceGateway-> shouldReceive ('persist') -> once () -> con ($ commentData2); $ commentRepository-> add (array ($ comment1, $ comment2)); 

Nuestro siguiente paso lógico es implementar una forma de agregar varios comentarios a la vez. Su proyecto puede no requerir esta funcionalidad y no es algo requerido por el patrón. De hecho, el patrón de repositorio solo dice que proporcionará un lenguaje de persistencia y consulta personalizado para nuestra lógica empresarial. Entonces, si nuestra lógica de bushiness siente la necesidad de agregar varios comentarios a la vez, el Repositorio es el lugar donde debe residir la lógica..

función add ($ commentData) if (is_array ($ commentData)) foreach ($ commentData as $ comment) $ this-> persistence-> persist (array ($ comment-> getPostId (), $ comment-> getAuthor (), $ comment-> getAuthorEmail (), $ comment-> getSubject (), $ comment-> getBody ())); else $ this-> persistence-> persist (array ($ commentData-> getPostId (), $ commentData-> getAuthor (), $ commentData-> getAuthorEmail (), $ commentData-> getSubject (), $ commentData-> getBody ( ))); 

Y la forma más sencilla de hacer que la prueba pase es simplemente verificar si el parámetro que estamos obteniendo es una matriz o no. Si es una matriz, recorreremos cada elemento y llamaremos la persistencia con la matriz que generamos desde una sola Comentario objeto. Y aunque este código es sintácticamente correcto y hace que la prueba pase, introduce una ligera duplicación de la que podemos deshacernos con bastante facilidad..

función add ($ commentData) if (is_array ($ commentData)) foreach ($ commentData as $ comment) $ this-> addOne ($ comment); else $ this-> addOne ($ commentData);  función privada addOne (Comentario $ comentario) $ this-> persistence-> persist (array ($ comment-> getPostId (), $ comment-> getAuthor (), $ comment-> getAuthorEmail (), $ comment-> getSubject (), $ comment-> getBody ())); 

Cuando todas las pruebas están en verde, siempre es tiempo de refactorizar antes de continuar con la próxima prueba que falla. Y lo hicimos con la añadir() método. Extrajimos la adición de un solo comentario en un método privado y lo llamamos desde dos lugares diferentes en nuestro público añadir() método. Esto no solo redujo la duplicación sino que también abrió la posibilidad de hacer el Agrega uno() Método público y dejar que la lógica empresarial decida si desea agregar uno o varios comentarios a la vez. Esto llevaría a una implementación diferente de nuestro Repositorio, con un Agrega uno() y otro addMany () metodos Sería una implementación perfectamente legítima del patrón de repositorio..


Recuperando Comentarios Con Nuestro Repositorio

Un repositorio proporciona un lenguaje de consulta personalizado para la lógica empresarial. Por lo tanto, los nombres y las funcionalidades de los métodos de consulta de un Repositorio dependen enormemente de los requisitos de la lógica empresarial. Usted construye su repositorio a medida que construye su lógica de negocios, ya que necesita otro método de consulta personalizado. Sin embargo, existen al menos uno o dos métodos que encontrará en casi cualquier Repositorio.

function testItCanFindAllComments () $ repository = new CommentRepository (); $ commentData1 = array (1, 'x', 'x', 'x', 'x'); $ comment1 = (new CommentFactory ()) -> make ($ commentData1); $ commentData2 = array (2, 'y', 'y', 'y', 'y'); $ comment2 = (new CommentFactory ()) -> make ($ commentData2); $ repository-> add ($ comment1); $ repositorio-> agregar ($ comentario2); $ this-> assertEquals (array ($ comment1, $ comment2), $ repository-> findAll ()); 

El primer método de este tipo se llama encuentra todos(). Esto debería devolver todos los objetos de los que es responsable el repositorio, en nuestro caso Comentarios. La prueba es simple, agregamos un comentario, luego otro, y finalmente queremos llamar encuentra todos() y obtener una lista que contenga ambos comentarios. Sin embargo, esto no es posible con nuestra InMemoryPersistence como es en este punto. Se requiere una pequeña actualización..

función retrieveAll () return $ this-> data; 

Eso es. Añadimos un recuperarAll () método que acaba de devolver el conjunto $ datos matriz de la clase. Sencillo y eficaz. Es hora de implementar encuentra todos() sobre el ComentarioRepositivo ahora.

función findAll () $ allCommentsData = $ this-> persistence-> retrieveAll (); $ comments = array (); foreach ($ allCommentsData as $ commentData) $ comments [] = $ this-> commentFactory-> make ($ commentData); devuelve $ comentarios; 

encuentra todos() llamará al recuperarAll () Método sobre nuestra persistencia. Ese método proporciona una gran variedad de datos. encuentra todos() recorrerá cada elemento y utilizará los datos según sea necesario para pasarlos a la fábrica. La fábrica proporcionará una Comentario un momento. Una matriz con estos comentarios se construirá y se devolverá al final de encuentra todos(). Simple y eficaz.

Otro método común que encontrará en los repositorios es buscar un objeto específico o un grupo de objetos en función de su clave característica. Por ejemplo, todos nuestros comentarios están conectados a una publicación de blog de un $ postId variable interna Puedo imaginar que en la lógica de negocios de nuestro blog casi siempre querríamos encontrar todos los comentarios relacionados con una publicación de blog cuando se muestre esa publicación. Así que un método llamado findByPostId ($ id) suena razonable para mi.

function testItCanFindCommentsByBlogPostId () $ repository = new CommentRepository (); $ commentData1 = array (1, 'x', 'x', 'x', 'x'); $ comment1 = (new CommentFactory ()) -> make ($ commentData1); $ commentData2 = array (1, 'y', 'y', 'y', 'y'); $ comment2 = (new CommentFactory ()) -> make ($ commentData2); $ commentData3 = array (3, 'y', 'y', 'y', 'y'); $ comment3 = (new CommentFactory ()) -> make ($ commentData3); $ repository-> add (array ($ comment1, $ comment2)); $ repositorio-> añadir ($ comentario3); $ this-> assertEquals (array ($ comment1, $ comment2), $ repository-> findByPostId (1)); 

Simplemente creamos tres comentarios simples. Los dos primeros tienen la misma. $ postId = 1, el tercero tiene $ postID = 3. Los agregamos todos al repositorio y luego esperamos una matriz con los dos primeros cuando hacemos una findByPostId () Para el $ postId = 1.

La función findByPostId ($ postId) return array_filter ($ this-> findAll (), la función ($ comment) usa ($ postId) return $ comment-> getPostId () == $ postId;); 

La implementación no podría ser más sencilla. Encontramos todos los comentarios utilizando nuestro ya implementado. encuentra todos() Método y filtramos la matriz. No tenemos forma de pedir la persistencia de hacer el filtrado por nosotros, así que lo haremos aquí. El código consultará cada uno Comentario objetar y comparar su $ postId con el que enviamos como parámetro. Genial. La prueba pasa. Pero siento que nos perdimos algo.

function testItCanFindCommentsByBlogPostId () $ repository = new CommentRepository (); $ commentData1 = array (1, 'x', 'x', 'x', 'x'); $ comment1 = (new CommentFactory ()) -> make ($ commentData1); $ commentData2 = array (1, 'y', 'y', 'y', 'y'); $ comment2 = (new CommentFactory ()) -> make ($ commentData2); $ commentData3 = array (3, 'y', 'y', 'y', 'y'); $ comment3 = (new CommentFactory ()) -> make ($ commentData3); $ repository-> add (array ($ comment1, $ comment2)); $ repositorio-> añadir ($ comentario3); $ this-> assertEquals (array ($ comment1, $ comment2), $ repository-> findByPostId (1)); $ this-> assertEquals (array ($ comment3), $ repository-> findByPostId (3)); 

Añadiendo una segunda afirmación para obtener el tercer comentario con el findByPostID () El método revela nuestro error. Siempre que pueda probar rutas o casos adicionales, como en nuestro caso con una aserción adicional simple, debería hacerlo. Estas simples afirmaciones adicionales o métodos de prueba pueden revelar problemas ocultos. Como en nuestro caso., array_filter () no reindexa la matriz resultante. Y mientras tenemos una matriz con los elementos correctos, los índices están desordenados..