De la procédure à la PHP orientée objet

Ce tutoriel s’inspire d’un discours de Robert C. Martin que j’ai regardé il ya un an environ. Le sujet principal de son exposé concerne la possibilité de choisir The Last Programming Language. Il aborde des sujets tels que: pourquoi un tel langage devrait-il exister? Et à quoi ça devrait ressembler? Toutefois, si vous lisez entre les lignes, une autre idée intéressante a attiré mon attention: les limites que chaque paradigme de programmation impose à nous, les programmeurs. Donc, avant de voir comment nous pourrions convertir une application PHP basée sur les procédures en une application orientée objet, je souhaite aborder un peu la théorie à l'avance.


Limitations du paradigme

Ainsi, chaque paradigme de programmation limite notre capacité à faire ce que nous voulons faire. Chacun d'eux prend quelque chose et fournit une alternative pour atteindre le même résultat. La programmation modulaire enlève une taille de programme illimitée. Il oblige le programmeur à utiliser des modules de taille maximale et chaque module se termine par une instruction "aller à" vers un autre module. La première limite concerne donc la taille. Ensuite, la programmation structurée et la programmation procédurale enlèvent l’instruction "aller-à-face" et limitent le programmeur à la séquence, à la sélection et à l’itération. Les séquences sont des affectations de variables, les sélections sont des décisions if-else et les itérations sont des boucles répétitives. Ce sont les blocs de construction de la plupart des langages de programmation et des paradigmes actuels.

La programmation orientée objet enlève les pointeurs vers les fonctions et introduit le polymorphisme. PHP n'utilise pas les pointeurs comme C, mais une variante de ces pointeurs vers des fonctions peut être observée dans les fonctions variables. Cela permet à un programmeur d'utiliser la valeur d'une variable comme nom d'une fonction, de manière à obtenir ce résultat:

function foo () echo "C'est foo";  function bar ($ param) echo "C'est une barre qui dit: $ param";  $ function = 'foo'; $ fonction (); // va dans foo () $ function = 'bar'; $ fonction ('test'); // va dans bar ()

Cela peut ne pas sembler important à première vue. Mais réfléchissez à ce que nous pouvons réaliser avec un outil aussi puissant. Nous pouvons envoyer une variable en tant que paramètre à une fonction, puis laisser cette fonction appeler l’autre, référencée par la valeur du paramètre. Ceci est incroyable. Cela nous permet de modifier les fonctionnalités d'une fonction sans le savoir. Sans la fonction même remarquant aucune différence.

Nous pouvons également faire des appels polymorphes avec cette technique.

Maintenant, au lieu de penser aux points de repère des fonctions, pensez à leur fonctionnement. Ne sont-ils pas simplement des déclarations cachées? En fait, ils sont, ou du moins, ils sont très semblables aux "go-to" indirects. Ce qui n'est pas très bon. Ce que nous avons ici est en fait un moyen astucieux de faire "go-to" sans l’utiliser directement. Je dois admettre qu'en PHP, comme le montre l'exemple ci-dessus, il est assez facile à comprendre, mais cela peut prêter à confusion avec des projets plus volumineux et de nombreuses fonctions différentes transmises d'une fonction à une autre. En C, il est encore plus obscur et difficile à comprendre.

Cependant, il ne suffit pas de supprimer des indicateurs de fonctions. La programmation orientée objet doit fournir un remplacement, et ce, de manière élégante. Il offre un polymorphisme avec une syntaxe simple. Et avec le polymorphisme, viennent les plus grandes offres de programmation orientée objet: le flux de contrôle est opposé à la dépendance au code source.


Dans l'image ci-dessus, nous avons illustré un exemple simple de la façon dont les appels polymorphes se produisent dans les deux paradigmes différents. Dans la programmation procédurale ou structurelle, le flux de contrôle est similaire à la dépendance au code source. Ils indiquent tous deux la mise en œuvre plus concrète du comportement d'impression.

En programmation orientée objet, nous pouvons inverser la dépendance du code source et la faire pointer vers une implémentation plus abstraite, tout en gardant le flux de contrôle pointant vers une implémentation plus concrète. Ceci est essentiel car nous voulons que notre contrôle atteigne la partie la plus concrète et la plus volatile de notre code afin d’obtenir notre résultat exactement comme nous le voulons, mais dans notre code source, nous voulons exactement le contraire. Dans notre code source, nous voulons que les éléments concrets et volatiles restent en retrait, soient faciles à modifier et n’affectent que le moins possible le reste de notre code. Laissez les parties volatiles changer fréquemment mais gardez les parties plus abstraites non modifiées. Vous pouvez en savoir plus sur le principe d'inversion de dépendance dans le document de recherche original rédigé par Robert C. Martin..


La tâche à accomplir

Dans ce chapitre, nous allons créer une application simple pour répertorier les calendriers Google et les événements qu’ils contiennent. Nous allons d'abord adopter une approche procédurale, en n'utilisant que des fonctions simples et en évitant tout type de classe ou d'objet. L'application vous permettra de répertorier vos calendriers et événements Google. Ensuite, nous allons pousser le problème un peu plus loin en conservant notre code de procédure et en commençant à l'organiser par comportement. Enfin, nous allons le transformer en une version orientée objet..


Client PHP Google API

Google fournit un client API pour PHP. Nous allons l'utiliser pour nous connecter à notre compte Google afin de pouvoir y manipuler des calendriers. Si vous souhaitez exécuter le code, vous devez configurer votre compte Google pour accepter les requêtes d'agenda..

Même si cela est obligatoire pour le tutoriel, ce n’est pas son sujet principal. Ainsi, au lieu de répéter les étapes à suivre, je vous indiquerai la documentation appropriée. Ne vous inquiétez pas, son installation est très simple et ne prend que cinq minutes environ.

Le code client PHP Google API est inclus dans chaque projet à partir de l'exemple de code joint à ce didacticiel. Je vous recommande d'utiliser celui-là. Si vous êtes curieux de savoir comment l’installer vous-même, consultez la documentation officielle..

Ensuite, suivez les instructions et remplissez les informations de la apiAccess.php fichier. Ce fichier sera requis à la fois par les exemples procéduraux et orientés objet. Il n'est donc pas nécessaire de le répéter. J'ai laissé mes clés à l'intérieur pour que vous puissiez plus facilement identifier et remplir les vôtres..

Si vous utilisez NetBeans, j'ai laissé les fichiers du projet dans les dossiers contenant les différents exemples. De cette façon, vous pouvez simplement ouvrir les projets et les exécuter immédiatement sur un serveur PHP local (PHP 5.4 est requis) en sélectionnant simplement Run / Run Project.

La bibliothèque client permettant de se connecter à l'API Google est orientée objet. Dans l’intérêt de notre exemple fonctionnel, j’ai écrit un petit ensemble de fonctions qui les englobent, les fonctionnalités dont nous avons besoin. De cette façon, nous pouvons utiliser une couche procédurale écrite sur la bibliothèque cliente orientée objet afin que notre code ne soit pas obligé d'utiliser des objets..

Si vous souhaitez vérifier rapidement que votre code et votre connexion à l'API Google fonctionnent, utilisez simplement le code ci-dessous. index.php fichier. Il devrait répertorier tous les calendriers que vous avez sur votre compte. Il devrait y avoir au moins un calendrier avec le résumé domaine étant votre nom. Si vous avez un calendrier avec les anniversaires de votre contact, celui-ci peut ne pas fonctionner avec cette API Google, mais ne paniquez pas, mais choisissez-en un autre..

require_once './google-api-php-client/src/Google_Client.php'; require_once './google-api-php-client/src/contrib/Google_CalendarService.php'; require_once __DIR__. '/… /ApiAccess.php'; require_once './functins_google_api.php'; require_once './functions.php'; session_start (); $ client = createClient (); if (! authenticate ($ client)) return; listAllCalendars ($ client);

Ce index.php Le fichier sera le point d’entrée de notre application. Nous n'utiliserons pas de framework web ou de fantaisie. Nous allons simplement simplement sortir du code HTML.


Une approche procédurale directe

Maintenant que nous savons ce que nous construisons et ce que nous allons utiliser, téléchargez le code source ci-joint. Je fournirai des extraits de celui-ci, mais pour voir le tout, vous voudrez avoir accès à la source originale.

Pour cette approche, nous voulons simplement que les choses fonctionnent. Notre code sera organisé de manière très rudimentaire, avec seulement quelques fichiers, comme ceci:

  • index.php - le seul fichier auquel nous avons accès directement à partir du navigateur et que nous lui transmettons des paramètres GET.
  • functions_google_api.php - le wrapper sur l'API Google dont nous avons parlé ci-dessus.
  • functions.php - où tout se passe.

functions.php abritera tout ce que notre application fait. La logique de routage, les présentations, ainsi que les valeurs et comportements pouvant y être enfouis. Cette application est assez simple, la logique principale est la suivante.


Nous avons une seule fonction appelée doUserAction (), qui décide avec un long sinon déclaration, quelles autres méthodes appeler en fonction des paramètres de la OBTENIR variable. Les méthodes se connectent ensuite à l'agenda Google à l'aide de l'API et affichent à l'écran ce que nous voulons demander..

function printCalendarContents ($ client) putTitle ('Ce sont vos événements pour'. getCalendar ($ client, $ _GET ['showThisCalendar']) ['summary']. 'calendar:'); foreach (retrieveEvents ($ client, $ _GET ['showThisCalendar'])) en tant que $ event) print ('
'. date ('Y-m-d H: m', strtotime ($ event ['créé'])))); putLink ('? showThisEvent ='. htmlentities ($ event ['id'])). '& calendarId ='. htmlentities ($ _GET ['showThisCalendar']), $ event ['summary']); impression('
'); impression('
');

Cet exemple est probablement la fonction la plus compliquée de notre code. Il appelle une fonction d'assistance nommée putTitle (), qui imprime simplement du HTML formaté pour le titre. Le titre contiendra le nom de notre calendrier qui peut être obtenu en appelant getCalendar () de functions_google_api.php. Le calendrier retourné sera un tableau contenant un résumé champ. C'est comment nous sommes après.

le $ client variable est passée partout dans toutes nos fonctions. Il est nécessaire de se connecter à l'API Google. Nous traiterons cela plus tard.

Ensuite, nous parcourons tous les événements du calendrier actuel. Cette liste de matrices est obtenue en exécutant l'appel API encapsulé dans retrieveEvents (). Pour chaque événement, nous imprimons la date à laquelle il a été créé, puis son titre..


Le reste du code est similaire à ce que nous avons déjà discuté et encore plus facile à comprendre. N'hésitez pas à jouer avec avant de passer à la section suivante.


Organiser le code de procédure

Notre code actuel est correct, mais je pense que nous pouvons faire mieux et l’organiser de manière plus appropriée. Vous pouvez trouver le projet avec le code organisé terminé sous le nom "GoogleCalProceduralOrganized" dans le code source joint..

Utilisation d'une variable client globale

La première chose qui me dérange au sujet de notre code non organisé, est que nous passons cette $ client variable dans comme un argument partout, plusieurs niveaux au sein de fonctions imbriquées. La programmation procédurale a un moyen intelligent de résoudre ce problème, une variable globale. Puisque $ client est défini dans index.php et au niveau mondial, tout ce que nous avons à changer est la manière dont nos fonctions l’utilisent. Donc au lieu d’attendre un $ client paramètre, nous pouvons utiliser:

function printCalendars () global $ client; putTitle ('Ce sont vos calendriers:'); foreach (getCalendarList ($ client) ['items'] en tant que $ calendar) putLink ('? showThisCalendar ='. htmlentities ($ calendar ['id']), $ calendar ['récapitulatif']); impression('
');

Comparez le code actuel au code nouvellement organisé pour voir la différence. Au lieu de passer $ client en paramètre, nous avons utilisé client global dans toutes nos fonctions et l'a transmis en tant que paramètre uniquement aux fonctions API Google. Techniquement, même les fonctions API de Google auraient pu utiliser le $ client variable de la portée globale, mais je pense qu'il est préférable de garder l'API aussi indépendante que possible.

Séparer la présentation de la logique

Certaines fonctions sont clairement destinées uniquement à l’impression d’images à l’écran, d’autres permettent de décider quoi faire et certaines sont un peu des deux. Lorsque cela se produit, il est parfois préférable de déplacer ces fonctions spécifiques dans leur propre fichier. Nous allons commencer par les fonctions utilisées uniquement pour imprimer des choses à l’écran, celles-ci seront déplacées dans un fichier. functions_display.php fichier. Voir les ci-dessous.

function printHome () print ('Exemple de bienvenue de Google Agenda sur NetTuts');  function printMenu () putLink ('? home', 'Home'); putLink ('? showCalendars', 'Show Calendars'); putLink ('? déconnexion', 'Déconnexion'); impression('

'); function putLink ($ href, $ text) print (sprintf ('% s |', $ href, $ text))); fonction putTitle ($ text) print (sprintf ('

% s

', $ text)); fonction putBlock ($ text) print ('
'. $ text.'
');

Le reste de ce processus de séparation de notre présentation de la logique nous oblige à extraire la partie présentation de nos méthodes. Voici comment nous l'avons fait avec l'une des méthodes.

function printEventDetails () global $ client; foreach (retrieveEvents ($ _GET ['calendarId']) sous forme de $ event) if ($ event ['id'] == $ _GET ['showThisEvent']) putTitle ('Détails de l'événement:'. $ event ['récapitulatif ']); putBlock ('Cet événement a un statut'. $ event ['status']); putBlock ('Il a été créé à'. date ('Ymd H: m', strtotime ($ event ['created'])). 'et dernière mise à jour à'. date ('Ymd H: m', strtotime ($ event ['mis à jour'])) . '.'); putBlock ('Pour cet événement, vous devez '. $ event ['summary']. '.'); 

Clairement, nous pouvons voir que tout ce qui est à l'intérieur de la si déclaration est juste le code de présentation et le reste est la logique métier. Au lieu d'une fonction volumineuse gérant tout, nous allons la diviser en plusieurs fonctions:

function printEventDetails () global $ client; foreach (retrieveEvents ($ _GET ['calendarId']) en tant que $ event) if (isCurrentEvent ($ event)) putEvent ($ event);  function isCurrentEvent ($ event) return $ event ['id'] == $ _GET ['showThisEvent']; 

Après la séparation, la logique métier est désormais très simple. Nous avons même extrait une petite méthode pour déterminer si l'événement est l'actuel. Tout le code de présentation est maintenant la responsabilité d'une fonction nommée putEvent ($ event) qui réside dans le functions_display.php fichier:

function putEvent ($ event) putTitle ('Détails de l'événement:'. $ event ['summary']); putBlock ('Cet événement a un statut'. $ event ['status']); putBlock ('Il a été créé à'. date ('Ymd H: m', strtotime ($ event ['created'])). 'et dernière mise à jour à'. date ('Ymd H: m', strtotime ($ event ['mis à jour'])) . '.'); putBlock ('Pour cet événement, vous devez '. $ event ['summary']. '.'); 

Même si cette méthode n’affiche que des informations, il faut garder à l’esprit que cela dépend de connaissances intimes sur la structure des $ événement. Mais ça va pour le moment. Pour le reste des méthodes, elles ont été séparées de la même manière.

Éliminer les déclarations longues si-sinon

La dernière chose qui me dérange dans notre code actuel est la déclaration longue if-else dans notre doUserAction () fonction, qui est utilisé pour décider quoi faire pour chaque action. Maintenant, PHP est assez flexible en matière de méta-programmation (appeler des fonctions par référence). Cette astuce nous permet de corréler les noms de fonction avec le $ _GET les valeurs de la variable. Donc, nous pouvons introduire un seul action paramètre dans le $ _GET variable et utiliser la valeur de celui-ci comme nom de fonction.

function doUserAction () putMenu (); if (! isset ($ _GET ['action'])) retourne; $ _GET ['action'] (); 

Sur la base de cette approche, notre menu sera généré comme ceci:

fonction putMenu () putLink ('? action = putHome', 'Home'); putLink ('? action = printCalendars', 'Afficher les calendriers'); putLink ('? déconnexion', 'Déconnexion'); impression('

');

Comme vous pouvez probablement le constater, cette réorganisation nous a déjà poussés vers une conception orientée objet. On ne sait pas quel genre d’objets on a et avec quel comportement exact, mais on a des indices ici et là.

Nous avons des présentations qui dépendent des types de données de la logique métier. Cela ressemble à l'inversion de dépendance dont nous parlions dans le chapitre d'introduction. Le flux du contrôle va toujours de la logique métier à la présentation, mais la dépendance au code source a commencé à se transformer en dépendance inversée. Je dirais que, à ce stade, cela ressemble plus à une dépendance bidirectionnelle.

Une autre astuce de conception orientée objet est le peu de méta-programmation que nous venons de faire. Nous appelons une méthode, dont nous ne savons rien. Cela peut être n'importe quoi et c'est comme si nous avions affaire à un faible niveau de polymorphisme.

Analyse de dépendance

Pour notre code actuel, nous pourrions dessiner un schéma, comme celui ci-dessous, pour illustrer les premières étapes de notre application. Tracer toutes les lignes aurait été trop compliqué.


Nous avons marqué avec des lignes bleues, les appels de procédure. Comme vous pouvez le constater, elles vont dans le même sens qu'auparavant. De plus, nous avons les lignes vertes marquant les appels indirects. Ce sont tous en passant par doUserAction (). Ces deux types de lignes représentent le flux de contrôle et vous pouvez constater qu’il est fondamentalement inchangé..

Les lignes rouges introduisent cependant un concept différent. Ils représentent une dépendance rudimentaire au code source. Je veux dire rudimentaire, parce que ce n’est pas si évident. le putMenu () La méthode inclut les noms des fonctions devant être appelées pour ce lien particulier. C'est une dépendance et la même règle s'applique à toutes les autres méthodes créant des liens. Ils dépendre sur le comportement des autres fonctions.

Un deuxième type de dépendance peut également être vu ici. La dépendance aux données. J'ai déjà mentionné $ calendrier et $ événement. Les fonctions d’impression doivent avoir une connaissance intime de la structure interne de ces baies pour faire leur travail..

Donc, après tout cela, je pense que nous avons de nombreuses raisons de passer à la dernière étape..


Une solution orientée objet

Quel que soit le paradigme utilisé, il n’existe pas de solution parfaite à un problème. Alors voici comment je propose d'organiser notre code de manière orientée objet.

Premier instinct

Nous avons déjà commencé à séparer les préoccupations de la logique métier et de la présentation. Nous avons même présenté notre doUserAction () méthode en tant qu’entité séparée. Donc, mon premier instinct est de créer trois classes Présentateur, Logique, et Routeur. Ceux-ci vont probablement changer plus tard, mais nous avons besoin d'un endroit pour commencer, juste?

le Routeur contiendra une seule méthode et restera assez similaire à la mise en œuvre précédente.

class Router function doUserAction () (new Presenter ()) -> putMenu (); if (! isset ($ _GET ['action'])) retourne; (new Logic ()) -> $ _ GET ['action'] (); 

Alors maintenant, nous devons appeler explicitement notre putMenu () méthode utilisant un nouveau Présentateur objet et le reste des actions seront appelées à l'aide d'un Logique objet. Cependant, cela pose immédiatement un problème. Nous avons une action qui n'est pas dans la classe logique. putHome () est dans la classe de présentateur. Nous devons introduire une action dans Logique qui déléguera au présentateur putHome () méthode. Rappelez-vous, pour le moment, nous voulons uniquement inclure notre code existant dans les trois classes que nous avons identifiées comme des candidats possibles pour une conception orientée objet. Nous voulons faire uniquement ce qui est absolument nécessaire pour que la conception fonctionne. Après avoir du code de travail, nous le changerons encore.

Dès que nous mettons un putHome () méthode dans la classe de logique, nous avons un dilemme. Comment appeler des méthodes de Presenter? Nous pourrions créer et transmettre un objet Presenter dans Logic afin qu’il ait toujours une référence à la présentation. Faisons cela depuis notre routeur.

class Router function doUserAction () (new Presenter ()) -> putMenu (); if (! isset ($ _GET ['action'])) retourne; (nouvelle logique (nouveau présentateur)) -> $ _GET ['action'] (); 

Maintenant, nous pouvons ajouter un constructeur dans Logic et ajouter la délégation vers putHome () en présentateur.

classe Logic private $ presenter; function __construct (présentateur $ présentateur) $ this-> présentateur = $ présentateur;  fonction putHome () $ this-> presenter-> putHome ();  […]

Avec quelques ajustements mineurs dans index.php et si Presenter encapsule les anciennes méthodes d'affichage, Logic encapsule les anciennes fonctions de la logique métier et Router encapsule l'ancien sélecteur d'action, nous pouvons réellement exécuter notre code et laisser l'élément de menu "Accueil" fonctionner.

require_once './google-api-php-client/src/Google_Client.php'; require_once './google-api-php-client/src/contrib/Google_CalendarService.php'; require_once __DIR__. '/… /ApiAccess.php'; require_once './functins_google_api.php'; require_once './Presenter.php'; require_once './Logic.php'; require_once './Router.php'; session_start (); $ client = createClient (); if (! authenticate ($ client)) return; (new Router ()) -> doUserAction ();

Et le voici en action.


Ensuite, dans notre classe de logique, nous devons changer correctement les appels pour afficher la logique, pour travailler avec $ this-> présentateur. Ensuite nous avons deux méthodes - isCurrentEvent () et retrieveEvents () - qui sont utilisés uniquement dans la classe Logic. Nous allons les rendre privés et changer les appels en conséquence.

Nous adopterons ensuite la même approche avec la classe Presenter. Nous allons changer tous les appels aux méthodes pour pointer vers $ this-> quelque chose et fais putTitle (), putLink (), et putBlock () privé, car ils ne sont utilisés que par Presenter. Découvrez le code dans le GoogleCalObjectOrientedInitial répertoire dans le code source joint si vous avez du mal à faire vous-même toutes ces modifications.

À ce stade, nous avons une application qui fonctionne. Il s’agit principalement de code de procédure encapsulé dans la syntaxe OO, qui utilise toujours le $ client variable globale et a des tonnes d'autres odeurs anti-objet, mais cela fonctionne.

Si nous dessinons le diagramme de classes avec des dépendances pour ce code, cela ressemblera à ceci:>


Les dépendances du contrôle de flux et du code source passent par le routeur, puis par la logique et enfin par la présentation. Ce dernier changement que nous avons fait atténue en fait un peu l'inversion de dépendance observée à l'étape précédente. Mais ne vous laissez pas berner. Le principe est là, il suffit de le rendre évident.

Rétablir la dépendance au code source

Il est difficile de dire qu'un principe SOLID est plus important qu'un autre, mais je pense que le principe d'inversion de dépendance a l'impact le plus important et le plus immédiat sur votre conception. Ce principe dit:

UNE: Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Les deux devraient dépendre des abstractions et B: Les abstractions ne doivent pas dépendre des détails. Les détails devraient dépendre des abstractions.

En termes simples, cela signifie que les implémentations concrètes doivent dépendre de classes abstraites. Plus vos classes deviennent abstraites, moins elles ont tendance à changer. Vous pouvez donc percevoir le problème comme suit: les classes changeant fréquemment devraient dépendre d’autres classes, beaucoup plus stables. Ainsi, la partie la plus volatile de toute application est probablement son interface utilisateur, qui serait la classe Presenter de notre application. Rendons évidente cette inversion de dépendance.

Premièrement, nous ferons en sorte que notre routeur utilise uniquement le présentateur et rompt sa dépendance à Logic.

class Router function doUserAction () (new Presenter ()) -> putMenu (); if (! isset ($ _GET ['action'])) retourne; (new Presenter ()) -> $ _GET ['action'] (); 

Nous changerons ensuite Presenter pour utiliser une instance de Logic et lui demander les informations qu’il doit présenter. Dans notre cas, j'estime qu'il est acceptable pour Presenter de créer l'instance de Logic, mais dans tout système de production, les usines créeront probablement les objets liés à la logique métier et les injecteront dans la couche de présentation..

Maintenant, la fonction putHome (), présents dans les classes Logic et Presenter, disparaîtront de Logic. C'est un bon signe, car nous éliminons les doublons. Le constructeur et la référence à Presenter disparaissent également de Logic. D'autre part, un constructeur créant un objet logique doit être écrit sur Presenter.

présentateur de classe private $ businessLogic; function __construct () $ this-> businessLogic = new Logic ();  function putHome () print ('Exemple d'utilisation de Google Agenda sur NetTuts');  […]

Après ces changements, en cliquant sur Afficher les calendriers produira cependant une belle erreur. Toutes nos actions depuis les liens pointant vers des noms de fonctions de la classe logique, nous devrons effectuer des modifications plus cohérentes pour inverser la dépendance entre les deux. Prenons cela une méthode à la fois. Le premier message d'erreur dit:

Erreur fatale: appel de la méthode non définie Presenter :: printCalendars () dans / […] /GoogleCalObjectOrientedFinal/Router.php à la ligne 9

Donc, notre routeur veut appeler une méthode qui n’existe pas sur Presenter, printCalendars (). Créons cette méthode dans Presenter et vérifions ce qu'elle a fait dans Logic. Il imprimait un titre puis parcourait des calendriers et appelait putCalendar (). Dans le présentateur le printCalendars () méthode va ressembler à ceci:

function printCalendars () $ this-> putCalendarListTitle (); foreach ($ this-> businessLogic-> getCalendars () en tant que $ calendar) $ this-> putCalendarListElement ($ calendar); 

En revanche, dans Logic, la méthode devient assez anémique. Juste un appel à la bibliothèque Google API.

fonction getCalendars () global $ client; retourne getCalendarList ($ client) ['items']; 

Cela peut vous amener à vous poser deux questions: "Avons-nous réellement besoin d'un cours de logique?" et "Notre application a-t-elle même une logique?". Eh bien, nous ne savons pas encore. Pour le moment, nous poursuivrons le processus ci-dessus jusqu'à ce que tout le code fonctionne et que Logic ne dépende plus de Presenter..

Donc, nous allons utiliser un printCalendarContents () méthode dans Presenter, comme celle ci-dessous:

function printCalendarContents () $ this-> putCalendarTitle (); foreach ($ this-> businessLogic-> getEventsForCalendar () en tant que $ event) $ this-> putEventListElement ($ event); 

Ce qui nous permettra à notre tour de simplifier la getEventsForCalendar () dans Logic, dans quelque chose comme ça.

fonction getEventsForCalendar () global $ client; return getEventList ($ client, htmlspecialchars ($ _GET ['showThisCalendar']))) ['items']; 

Maintenant, cela fonctionne, mais j'ai une préoccupation ici. le $ _GET La variable est utilisée dans les classes Logic et Presenter. La classe Presenter ne devrait-elle pas utiliser $ _GET? Je veux dire, le présentateur doit absolument savoir à propos de $ _GET car il doit créer des liens qui peuplent cette $ _GET variable. Donc, cela voudrait dire que $ _GET est strictement lié à HTTP. Désormais, nous souhaitons que notre code fonctionne avec une interface graphique ou une interface graphique de bureau. Nous voulons donc garder cette connaissance uniquement dans le présentateur. Cela rend les deux méthodes d'en haut, transforme en les deux ci-dessous.

fonction getEventsForCalendar ($ calendarId) global $ client; return getEventList ($ client, $ calendarId) ['items']; 
function printCalendarContents () $ this-> putCalendarTitle (); $ eventsForCalendar = $ this-> businessLogic-> getEventsForCalendar (htmlspecialchars ($ _GET ['showThisCalendar'])); foreach ($ eventsForCalendar as $ event) $ this-> putEventListElement ($ event); 

La dernière fonction que nous devons traiter est d’imprimer un événement spécifique. Pour cet exemple, supposons qu’il n’ya aucun moyen de récupérer directement un événement et que nous devons le trouver nous-mêmes. Maintenant, notre classe de logique est pratique. C'est un endroit idéal pour manipuler des listes d'événements et rechercher un identifiant spécifique:

function getEventById ($ eventId, $ calendarId) foreach ($ this-> getEventsForCalendar ($ calendarId) as $ event) si ($ event ['id'] == $ eventId) renvoie $ événement; 

Et ensuite, l'appel correspondant sur Presenter se chargera de l'imprimer:

function printEventDetails () $ this-> putEvent ($ this-> businessLogic-> getEventById ($ _GET ['showThisEvent'], $ _GET ['calendarId'])); 

C'est tout. Nous voilà. Dépendance inversée!


Le contrôle coule toujours de Logic vers Presenter. Le contenu présenté est totalement défini par Logic. Si, par exemple, nous souhaitons demain nous connecter à un autre service de calendrier, nous pouvons créer une au