Le modèle de conception du référentiel

Le modèle de conception de référentiel, défini par Eric Evens dans son livre Domain Driven Design, est l’un des modèles de conception les plus utiles et les plus largement applicables jamais inventé. Toute application doit fonctionner avec persistance et avec une sorte de liste d'éléments. Ceux-ci peuvent être des utilisateurs, des produits, des réseaux, des disques ou tout ce que votre application concerne. Si vous avez un blog par exemple, vous devez gérer des listes d'articles de blog et des listes de commentaires. Le problème commun à toutes ces logiques de gestion de liste est de savoir comment connecter la logique métier, les usines et la persistance..


Le modèle de conception d'usine

Comme nous l'avons mentionné dans le paragraphe d'introduction, un référentiel connectera les usines aux passerelles (persistance). Ce sont également des modèles de conception et si vous ne les connaissez pas bien, ce paragraphe jettera un peu de lumière sur le sujet..

Une usine est un modèle de conception simple qui définit un moyen pratique de créer des objets. Il s'agit d'une classe ou d'un ensemble de classes responsables de la création des objets dont notre logique métier a besoin. Une usine a traditionnellement une méthode appelée "faire()" et il saura comment prendre toutes les informations nécessaires à la création d'un objet, puis créer l'objet lui-même et renvoyer un objet prêt à l'emploi à la logique métier.

Voici un peu plus sur le modèle d'usine dans un ancien tutoriel Nettuts +: Guide du débutant pour les modèles de conception. Si vous préférez une vue plus détaillée du modèle d'usine, consultez le premier modèle de conception du cours sur les modèles de conception agiles que nous avons sur Tuts.+.


Le modèle de la passerelle

Également appelé «passerelle de données de table», il s'agit d'un modèle simple qui assure la connexion entre la logique métier et la base de données. Sa responsabilité principale consiste à effectuer les requêtes sur la base de données et à fournir les données récupérées dans une structure de données typique du langage de programmation (comme un tableau en PHP). Ces données sont ensuite généralement filtrées et modifiées dans le code PHP afin que nous puissions obtenir les informations et les variables nécessaires à la création de nos objets. Cette information doit ensuite être transmise aux usines.

Le modèle de conception de la passerelle est expliqué et illustré de manière assez détaillée dans un didacticiel Nettuts + sur Evolving to a persistence layer. De plus, dans le même cours Agile Design Patterns, la deuxième leçon sur les modèles de conception porte sur ce sujet..


Les problèmes que nous devons résoudre

Duplication par traitement de données

Cela n’apparaît peut-être pas à première vue, mais la connexion de passerelles à des usines peut entraîner de nombreuses duplications. Tout logiciel de taille considérable doit créer les mêmes objets à partir d’endroits différents. À chaque endroit, vous devrez utiliser la passerelle pour récupérer un ensemble de données brutes, les filtrer et les utiliser pour pouvoir les envoyer aux usines. De tous ces endroits, vous appellerez les mêmes usines avec les mêmes structures de données mais évidemment avec des données différentes. Vos objets seront créés et fournis par les usines. Cela entraînera inévitablement beaucoup de duplication dans le temps. Et la duplication sera répartie dans des classes ou des modules distants et sera difficile à remarquer et à corriger..

Duplication par réimplémentation de logique de récupération de données

Un autre problème que nous avons est comment exprimer les questions que nous devons faire avec l'aide des passerelles. Chaque fois que nous avons besoin d'informations de la passerelle, nous devons réfléchir à ce dont nous avons exactement besoin. Avons-nous besoin de toutes les données sur un seul sujet? Avons-nous besoin que de quelques informations spécifiques? Voulons-nous extraire un groupe spécifique de la base de données et faire le tri ou le filtrage affiné dans notre langage de programmation? Toutes ces questions doivent être abordées chaque fois que nous récupérons des informations de la couche de persistance via notre passerelle. Chaque fois que nous faisons cela, nous devrons trouver une solution. Avec le temps, au fur et à mesure que notre application grandira, nous serons confrontés aux mêmes dilemmes à différents endroits de notre application. Par inadvertance, nous proposerons des solutions légèrement différentes aux mêmes problèmes. Cela prend non seulement du temps et des efforts supplémentaires, mais conduit également à une duplication subtile, la plupart du temps très difficile à reconnaître. C'est le type de duplication le plus dangereux.

Duplication par réimplémentation de logique de persistance de données

Dans les deux paragraphes précédents, nous n’avons parlé que de la récupération de données. Mais la passerelle est bidirectionnelle. Notre logique métier est bidirectionnelle. Nous devons en quelque sorte persister nos objets. Cela conduit encore une fois à beaucoup de répétition si nous voulons implémenter cette logique au besoin dans différents modules et classes de notre application..


Les principaux concepts

Référentiel pour la récupération de données

Un référentiel peut fonctionner de deux manières: la récupération et la persistance des données.


Lorsqu'il est utilisé pour extraire des objets de la persistance, un référentiel sera appelé avec une requête personnalisée. Cette requête peut être une méthode spécifique par son nom ou une méthode plus courante avec des paramètres. Le référentiel est chargé de fournir et d'implémenter ces méthodes de requête. Lorsqu'une telle méthode est appelée, le référentiel contactera la passerelle pour récupérer les données brutes de la persistance. La passerelle fournira des données d'objet brutes (comme un tableau avec des valeurs). Ensuite, le référentiel prendra ces données, effectuera les transformations nécessaires et appellera les méthodes Factory appropriées. Les usines fourniront aux objets construits les données fournies par le référentiel. Le référentiel collectera ces objets et les retournera sous la forme d'un ensemble d'objets (comme un tableau d'objets ou un objet de collection défini dans la leçon sur les modèles composites du cours sur les modèles de conception agiles).

Référentiel pour la persistance des données

La deuxième façon dont un référentiel peut fonctionner est de fournir la logique nécessaire pour extraire les informations d'un objet et les conserver. Cela peut être aussi simple que la sérialisation de l'objet et l'envoi des données sérialisées à la passerelle pour le conserver ou sophistiqué comme la création de tableaux d'informations avec tous les champs et l'état d'un objet..


Lorsqu'elle est utilisée pour conserver des informations, la classe de client est celle qui communique directement avec la fabrique. Imaginez un scénario lorsqu'un nouveau commentaire est publié dans un article de blog. Un objet Comment est créé par notre logique métier (la classe Client), puis envoyé au référentiel pour être conservé. Le référentiel conservera les objets à l'aide de la passerelle et les mettra éventuellement en cache dans une liste locale en mémoire. Les données doivent être transformées car, dans de rares cas, des objets réels peuvent être directement enregistrés dans un système de persistance..


Joindre les points

L'image ci-dessous est une vue de plus haut niveau sur la manière d'intégrer le référentiel entre les usines, la passerelle et le client..


Notre référentiel est au centre du schéma. A gauche, une interface pour la passerelle, une implémentation et la persistance elle-même. Sur la droite, il y a une interface pour les usines et une implémentation d'usine. Enfin, en haut, il y a la classe client.

Comme on peut l'observer dans la direction des flèches, les dépendances sont inversées. Le référentiel dépend uniquement des interfaces abstraites pour les usines et les passerelles. Gateway dépend de son interface et de la persistance qu’il offre. L'usine ne dépend que de son interface. Le client dépend du référentiel, ce qui est acceptable car le référentiel a tendance à être moins concret que le client..


Mis en perspective, le paragraphe ci-dessus respecte notre architecture de haut niveau et le sens des dépendances que nous souhaitons obtenir..


Gestion des commentaires dans les articles de blog avec un référentiel

Maintenant que nous avons vu la théorie, il est temps de donner un exemple concret. Imaginez que nous ayons un blog où nous avons des objets Post et Comment. Les commentaires appartiennent aux publications et nous devons trouver un moyen de les conserver et de les récupérer.

Le commentaire

Nous commencerons par un test qui nous obligera à réfléchir à ce que doit contenir notre objet Comment..

La classe RepositoryTest étend PHPUnit_Framework_TestCase function testACommentHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe a une opinion sur le modèle de référentiel"; $ commentBody = "Je pense que c'est une bonne idée d'utiliser le modèle de référentiel pour persister et récupérer des objets."; $ comment = new Comment ($ postId, $ commentAuthor, $ commentAuthorEmail, $ commentSubject, $ commentBody); 

À première vue, un commentaire ne sera qu'un objet de données. Il n’a peut-être aucune fonctionnalité, mais c’est au contexte de notre application de décider. Pour cet exemple, supposons simplement qu’il s’agit d’un simple objet de données. Construit avec un ensemble de variables.

Commentaire de la classe 

Il suffit de créer une classe vide et de l'exiger dans le test pour le faire passer.

require_once '… /Comment.php'; La classe RepositoryTest étend PHPUnit_Framework_TestCase […]

Mais c'est loin d'être parfait. Notre test ne teste encore rien. Forceons-nous à écrire tous les accesseurs de la classe Comment.

fonction testACommentsHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe a une opinion sur le modèle de référentiel"; $ commentBody = "Je pense que c'est une bonne idée d'utiliser le modèle de référentiel pour persister et récupérer des objets."; $ 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 ()); 

Pour contrôler la longueur du tutoriel, j'ai écrit toutes les assertions en même temps et nous les implémenterons également. Dans la vraie vie, prenez-les un à un.

 class Comment private $ postId; auteur privé; private $ authorEmail; privé $ sujet; organisme privé; function __construct ($ postId, $ author, $ authorEmail, $ subject, $ body) $ this-> postId = $ postId; $ this-> author = $ author; $ this-> authorEmail = $ authorEmail; $ this-> subject = $ subject; $ this-> body = $ body;  fonction publique getPostId () return $ this-> postId;  fonction publique getAuthor () return $ this-> author;  fonction publique getAuthorEmail () return $ this-> authorEmail;  fonction publique getSubject () return $ this-> subject;  fonction publique getBody () return $ this-> body; 

À l'exception de la liste des variables privées, le reste du code a été généré par mon IDE, NetBeans, de sorte que le test du code généré automatiquement peut parfois être un peu fastidieux. Si vous n'écrivez pas ces lignes vous-même, n'hésitez pas à les faire directement et ne vous embêtez pas avec des tests pour les installateurs et les constructeurs. Néanmoins, le test nous a permis de mieux exposer nos idées et de mieux documenter le contenu de notre classe de commentaires..

Nous pouvons également considérer ces méthodes de test et classes de test comme nos classes "Client" à partir des schémas.


Notre porte d'entrée à la persistance

Pour garder cet exemple aussi simple que possible, nous allons implémenter uniquement une instance InMemoryPersistence afin de ne pas compliquer notre existence avec des systèmes de fichiers ou des bases de données..

require_once '… /InMemoryPersistence.php'; class InMemoryPersistenceTest étend PHPUnit_Framework_TestCase function testItCanPerisistAndRetrieveASingleDataArray () $ data = array ('data'); $ persistence = new InMemoryPersistence (); $ persistence-> persist ($ data); $ this-> assertEquals ($ data, $ persistence-> retrieve (0)); 

Comme d'habitude, nous commençons par le test le plus simple qui pourrait échouer et nous obligeons également à écrire du code. Ce test crée un nouveau InMemoryPersistence objet et tente de persister et de récupérer un tableau appelé Les données.

require_once __DIR__. '/Persistence.php'; La classe InMemoryPersistence implémente la persistance private $ data = array (); fonction persistante ($ data) $ this-> data = $ data;  function retrieve ($ id) return $ this-> data; 

Le code le plus simple pour le faire passer est juste de garder le courrier entrant $ data dans une variable privée et le retourner dans le récupérer méthode. Le code tel qu'il est en ce moment ne se soucie pas de l'envoi $ id variable. C'est la chose la plus simple qui puisse faire passer le test. Nous avons également pris la liberté de présenter et de mettre en œuvre une interface appelée Persistance.

interface Persistence fonction persist ($ data); fonction de récupération ($ ids); 

Cette interface définit les deux méthodes que toute passerelle doit implémenter.. Persister et récupérer. Comme vous l'avez probablement déjà deviné, notre passerelle est notre InMemoryPersistence et notre persistance physique est la variable privée contenant nos données en mémoire. Mais revenons à l'implémentation de ceci dans la persistance de la mémoire.

fonction testItCanPerisistSeveralElementsAndRetrieveAnyOfThem () $ data1 = array ('data1'); $ data2 = array ('data2'); $ persistence = new InMemoryPersistence (); $ persistence-> persist ($ data1); $ persistence-> persist ($ data2); $ this-> assertEquals ($ data1, $ persistence-> retrieve (0)); $ this-> assertEquals ($ data2, $ persistence-> retrieve (1)); 

Nous avons ajouté un autre test. Dans celui-ci, nous persistons deux tableaux de données différents. Nous nous attendons à pouvoir récupérer chacun d'eux individuellement.

require_once __DIR__. '/Persistence.php'; La classe InMemoryPersistence implémente la persistance private $ data = array (); fonction persistante ($ data) $ this-> data [] = $ data;  function retrieve ($ id) return $ this-> data [$ id]; 

Le test nous a obligés à modifier légèrement notre code. Nous devons maintenant ajouter des données à notre tableau, pas simplement les remplacer par celles envoyées à persiste (). Nous devons également considérer la $ id paramètre et retourne l'élément à cet index.

C'est assez pour notre InMemoryPersistence. Si nécessaire, nous pouvons le modifier plus tard.


Notre usine

Nous avons un client (nos tests), une persistance avec une passerelle et des objets de commentaire à persister. La prochaine chose qui manque est notre usine.

Nous avons commencé notre codage avec un RepositoryTest fichier. Ce test a toutefois créé une Commentaire objet. Maintenant, nous devons créer des tests pour vérifier si notre usine sera capable de créer Commentaire objets. Il semble que nous ayons eu une erreur de jugement et que notre test soit plus probablement un test pour notre future usine que pour notre référentiel. Nous pouvons le déplacer dans un autre fichier de test, CommentFactoryTest.

require_once '… /Comment.php'; La classe CommentFactoryTest étend PHPUnit_Framework_TestCase fonction testACommentsHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe a une opinion sur le modèle de référentiel"; $ commentBody = "Je pense que c'est une bonne idée d'utiliser le modèle de référentiel pour persister et récupérer des objets."; $ 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 ()); 

Maintenant, ce test passe évidemment. Et même s’il s’agit d’un test correct, nous devrions envisager de le modifier. Nous voulons créer un Usine objet, passez un tableau et demandez-lui de créer un Commentaire pour nous.

require_once '… /CommentFactory.php'; La classe CommentFactoryTest étend PHPUnit_Framework_TestCase fonction testACommentsHasAllItsComposingParts () $ postId = 1; $ commentAuthor = "Joe"; $ commentAuthorEmail = "[email protected]"; $ commentSubject = "Joe a une opinion sur le modèle de référentiel"; $ commentBody = "Je pense que c'est une bonne idée d'utiliser le modèle de référentiel pour persister et récupérer des objets."; $ 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 ()); 

Nous ne devrions jamais nommer nos classes en fonction du modèle de conception qu'elles implémentent, mais Usine et Dépôt représentent plus que le modèle de conception lui-même. Personnellement, je n'ai rien contre l'inclusion de ces deux mots dans les noms de notre classe. Cependant, je recommande toujours fortement et je respecte le concept de ne pas nommer nos classes après les modèles de conception que nous utilisons pour le reste des modèles..

Ce test est légèrement différent du précédent, mais il échoue. Il essaie de créer un CommentFactory objet. Cette classe n'existe pas encore. Nous essayons aussi d'appeler un faire() méthode avec un tableau contenant toutes les informations d’un commentaire sous forme de tableau. Cette méthode est définie dans le Usine interface.

interface Factory function make ($ data); 

Ceci est un très commun Usine interface. Il a défini la seule méthode requise pour une fabrique, la méthode qui crée réellement les objets que nous voulons.

require_once __DIR__. '/Factory.php'; require_once __DIR__. '/Comment.php'; la classe CommentFactory implémente Factory function make ($ components) retourne un nouveau commentaire ($ components [0], $ components [1], $ components [2], $ components [3], $ components [4]); 

Et CommentFactory met en œuvre le Usine interface avec succès en prenant la composants $ paramètre dans son faire() méthode, crée et retourne une nouvelle Commentaire objet avec les informations à partir de là.

Nous allons garder notre logique de persistance et de création d’objets aussi simple que possible. Nous pouvons, pour ce didacticiel, ignorer en toute sécurité la gestion des erreurs, la validation et la génération d'exceptions. Nous nous arrêterons ici avec l'implémentation de la persistance et de la création d'objet.


Utilisation d'un référentiel pour conserver des commentaires

Comme nous l'avons vu ci-dessus, nous pouvons utiliser un référentiel de deux manières. Récupérer des informations de persistance et également conserver des informations sur la couche de persistance. Avec TDD, il est la plupart du temps plus facile de commencer par la partie de la logique qui enregistre (persister), puis d'utiliser cette implémentation existante pour tester la récupération des données..

require_once '… /… /… /vendor/autoload.php'; require_once '… /CommentRepository.php'; require_once '… /CommentFactory.php'; la classe RepositoryTest étend PHPUnit_Framework_TestCase fonction protégée 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 () -> avec ($ commentData); $ commentRepository-> add ($ comment); 

Nous utilisons Mockery pour nous moquer de notre persistance et injecter cet objet simulé au référentiel. Puis on appelle ajouter() sur le référentiel. Cette méthode a un paramètre de type Commentaire. Nous nous attendons à ce que la persistance soit appelée avec un tableau de données similaire à $ commentData.

require_once __DIR__. '/InMemoryPersistence.php'; class CommentRepository private $ persistence; function __construct (Persistence $ persistence = null) $ this-> persistence = $ persistence? : new InMemoryPersistence ();  function add (Comment $ comment) $ this-> persistence-> persist (array ($ comment-> getPostId (), $ comment-> getAuthor (), $ comment-> getAuthorEmail (), $ comment-> getSubject ( ), $ comment-> getBody ())); 

Comme vous pouvez le voir, le ajouter() la méthode est assez intelligente. Il encapsule la connaissance sur la façon de transformer un objet PHP en un tableau simple utilisable par la persistance. N'oubliez pas que notre passerelle de persistance est généralement un objet général pour toutes nos données. Il peut et va conserver toutes les données de notre application, donc envoyer des objets le ferait trop: conversion et persistance effective.

Quand vous avez un InMemoryPersistence classe comme nous le faisons, il est très rapide. Nous pouvons l'utiliser comme une alternative à se moquer de la passerelle.

test de fonctionAPersistedCommentCanBeRetrievedFromTheGateway () $ 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)); 

Bien sûr, si vous n'avez pas d'implémentation en mémoire de votre persistance, le seul moyen raisonnable de se moquer est de le faire. Sinon, votre test sera trop lent pour être pratique.

fonction 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 () -> avec ($ commentData1); $ persistanceGateway-> shouldReceive ('persist') -> once () -> avec ($ commentData2); $ commentRepository-> add (array ($ comment1, $ comment2)); 

Notre prochaine étape logique consiste à mettre en place un moyen d’ajouter plusieurs commentaires à la fois. Votre projet peut ne pas nécessiter cette fonctionnalité et ce n'est pas quelque chose qui est requis par le modèle. En fait, le modèle de référentiel indique uniquement qu'il fournira un langage de requête et de persistance personnalisé pour notre logique métier. Donc, si notre logique de fourmillement ressent le besoin d'ajouter plusieurs commentaires à la fois, le référentiel est l'endroit où la logique devrait résider..

fonction add ($ commentData) if (is_array ($ commentData)) poureach ($ commentData en tant que $ comment) $ this-> persistence-> persist (tableau ($ comment-> getPostId (), $ comment-> getAuthor (), $ comment-> getAuthorEmail (), $ comment-> getSubject (), $ comment-> getBody ())); else $ this-> persistence-> persist (tableau ($ commentData-> getPostId (), $ commentData-> getAuthor (), $ commentData-> getAuthorEmail (), $ commentData-> getSubject (), $ commentData-> getBody ( ))); 

Et le moyen le plus simple de réussir le test consiste simplement à vérifier si le paramètre que nous obtenons est un tableau ou non. S'il s'agit d'un tableau, nous allons parcourir chaque élément et appeler la persistance avec le tableau que nous générons à partir d'un seul Commentaire objet. Et bien que ce code soit syntaxiquement correct et fasse passer le test, il introduit une légère duplication dont nous pouvons nous débarrasser assez facilement..

fonction add ($ commentData) if (is_array ($ commentData)) poureach ($ commentData en tant que $ comment) $ this-> addOne ($ comment); else $ this-> addOne ($ commentData);  fonction privée addOne (Comment $ comment) $ this-> persistence-> persist (tableau ($ comment-> getPostId (), $ comment-> getAuthor (), $ comment-> getAuthorEmail (), $ comment-> getSubject (), $ comment-> getBody ())); 

Lorsque tous les tests sont verts, il est toujours temps de procéder à la refactorisation avant de continuer avec le prochain test ayant échoué. Et c'est ce que nous avons fait avec le ajouter() méthode. Nous avons extrait l'ajout d'un seul commentaire dans une méthode privée et l'avons appelé depuis deux endroits différents de notre public. ajouter() méthode. Cela a non seulement réduit le double emploi mais également ouvert la possibilité de ajoute un() méthode public et en laissant la logique métier décider si elle souhaite ajouter un ou plusieurs commentaires à la fois. Cela conduirait à une mise en œuvre différente de notre référentiel, avec une ajoute un() et un autre addMany () méthodes. Ce serait une implémentation parfaitement légitime du modèle de référentiel.


Récupérer des commentaires avec notre référentiel

Un référentiel fournit un langage de requête personnalisé pour la logique applicative. Ainsi, les noms et les fonctionnalités des méthodes de requête d'un référentiel sont à la hauteur des exigences de la logique métier. Vous construisez votre référentiel en même temps que votre logique métier, car vous avez besoin d'une autre méthode de requête personnalisée. Cependant, il existe au moins une ou deux méthodes que vous trouverez sur presque tous les référentiels..

fonction 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); $ repository-> add ($ comment2); $ this-> assertEquals (tableau ($ comment1, $ comment2), $ repository-> findAll ()); 

La première méthode s'appelle Trouver tout(). Cela devrait retourner tous les objets dont le référentiel est responsable, dans notre cas commentaires. Le test est simple, nous ajoutons un commentaire, puis un autre, et enfin nous voulons appeler Trouver tout() et obtenez une liste contenant les deux commentaires. Ceci n’est cependant pas réalisable avec notre InMemoryPersistence comme c'est à ce stade. Une petite mise à jour est nécessaire.

fonction retrieveAll () return $ this-> data; 

C'est tout. Nous avons ajouté un récupérerAll () méthode qui ne fait que renvoyer le tout $ data tableau de la classe. Simple et efficace Il est temps de mettre en œuvre Trouver tout() sur le CommentRepository à présent.

fonction findAll () $ allCommentsData = $ this-> persistence-> retrieveAll (); $ comments = array (); foreach ($ allCommentsData as $ commentData) $ comments [] = $ this-> commentFactory-> make ($ commentData); retourne $ commentaires; 

Trouver tout() appellera le récupérerAll () méthode sur notre persistance. Cette méthode fournit un tableau brut de données. Trouver tout() parcourt chaque élément et utilise les données nécessaires pour être transmises à la fabrique. L'usine fournira un Commentaire un temps. Un tableau avec ces commentaires sera construit et retourné à la fin de Trouver tout(). Simple et efficace.

Une autre méthode courante que vous trouverez dans les référentiels consiste à rechercher un objet ou un groupe d'objets spécifique en fonction de leur clé caractéristique. Par exemple, tous nos commentaires sont connectés à un article de blog par un $ postID variable interne. J'imagine que, dans la logique commerciale de notre blog, nous voudrions presque toujours trouver tous les commentaires liés à un article de blog lorsque celui-ci est affiché. Donc une méthode appelée findByPostId ($ id) semble raisonnable pour moi.

fonction 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)); $ repository-> add ($ comment3); $ this-> assertEquals (tableau ($ comment1, $ comment2), $ repository-> findByPostId (1)); 

Nous venons de créer trois commentaires simples. Les deux premiers ont le même $ postId = 1, le troisième a $ postID = 3. Nous les ajoutons tous au référentiel, puis nous attendons un tableau avec les deux premiers lorsque nous faisons un findByPostId () pour le $ postId = 1.

function findByPostId ($ postId) return array_filter ($ this-> findAll (), function ($ comment) use ($ postId) return $ comment-> getPostId () == $ postId;); 

La mise en œuvre ne pourrait être plus simple. Nous trouvons tous les commentaires en utilisant notre déjà mis en œuvre Trouver tout() méthode et nous filtrons le tableau. Nous n'avons aucun moyen de demander à la persistance de faire le filtrage pour nous, alors nous le ferons ici. Le code va interroger chaque Commentaire objecter et comparer ses $ postID avec celui que nous avons envoyé en paramètre. Génial. Le test est réussi. Mais je sens que nous avons raté quelque chose.

fonction 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-> a