Travail de fin d’études Développement d’un micro-CMS Olivier Wenders Section Web Developer Décembre 2014
nstall I , d a lo Down
G g O o L l B y g oD g ATE C I N U COMM SINCE
2014
École Industrielle et Commerciale de la Ville de Namur Rue Pépin 2b, 5000 Namur 081/25.74.00 - 081/25.74.09 www.epsnamur.be
Travail de fin d’études
Développement d’un micro-CMS Olivier Wenders École Industrielle et Commerciale de la Ville de Namur Section Web Developer Décembre 2014
Remerciements Je remercie tout d’abord du fond du cœur ma femme Cécile et mes enfants Loïc et Nathan, éternelles sources de joie et d’inspiration, qui subissent mes absences depuis plusieurs années déjà mais qui malgré cela ont été d’un soutien absolu dans ma démarche de formation en cours du soir. À ma femme, en plus de ma profonde reconnaissance, je lui promets de ressortir à nouveau les poubelles et de vider le lave-vaisselle. Quant à mes enfants je leur promets de rejouer aux tortues ninja et...que... ? Une erreur s’est produite lors de la conversion du fichier depuis l’UTF-8 vers ISO-8859-1. Le fichier reste non-sauvegardé. Durant cette formation j’ai découvert que le web est un domaine plus vaste que je ne le pensais et qui permet d’aborder des sujets plus passionnants les uns que les autres. Je remercie donc les professeurs de l’EICVN de nous avoir ouvert la voie vers ces chemins multiples, il ne tient plus qu’à nous de les explorer davantage.
Sommaire Introduction............................................................... 5
Le système de route............................................................23
Le référencement.................................................................36
Le projet..................................................................................... 6
Le Backcontroller................................................................. 25
Live view................................................................................36
Objectifs et méthodes.............................................................. 7
La classe PDOFactory......................................................... 25
Conclusions..............................................................................37
La landing page......................................................... 9
Exécution de la méthode du contrôleur............................ 25
Sur le projet...........................................................................37
Affichage de la page............................................................26
Sur le plan personnel............................................................37
La classe ApplicationComponent.......................................27
Les annexes..............................................................39
Construction des formulaires...............................................28
La charte graphique................................................................40
Ajout de commentaires.......................................................28
Les couleurs..........................................................................40
La méthode executeInsertComment................................28
Le style graphique...............................................................40
Les validateurs.....................................................................29
Le cahier des charges.............................................................. 41
La création de la vue du formulaire...................................30
Glossaire...................................................................................44
Le traitement des données du formulaire........................30
Webographie........................................................................... 45
Description de la conception.................................................10 Design....................................................................................10 Mise en place.........................................................................10 Code.......................................................................................10 Compatibilité avec les navigateurs.................................... 13 Live view................................................................................. 13
L’application Doggyblog.......................................15 Entrée en matière.....................................................................16 Principe de l’application......................................................16 Le Tutoriel de référence........................................................16 Principales différences entre les deux applications.........17 La modélisation........................................................................19 Modèle UML de l'application.................................................20 Description du fonctionnement............................................22 Le démarrage de l’application............................................22 Le système d’Autoload........................................................22 L’instanciation de FrontendApplication............................23
Modification d’un commentaire......................................... 31 Gestion des utilisateurs.........................................................32 Les modules Subscription et Profile.................................32 Le module Connection........................................................32 Le Backend..............................................................................33 La classe BackendApplication...........................................33 Le panneau d’administration..............................................33 Intégration sur le site hôte.................................................... 35 Le principe............................................................................ 35 Le responsive design........................................................... 35
1 Introduction
Le projet Depuis l’arrivée des réseaux sociaux les blogs semblent être passés de mode mais proposer un blog sur son site web reste encore un formidable moyen de communiquer, d’attirer du public et par conséquent d’améliorer son référencement. Il existe déjà de nombreuses plateformes pour déployer rapidement un blog (Skyblog, Blogger, Skynet, etc.) mais elles ne permettent pas au webmaster d’en installer un sur son propre site web, sur son propre domaine, afin de bénéficier directement de l’afflux des visiteurs. Ainsi est née l’idée de développer un blog modulable, pratique et installable sur n’importe quel site web. Le nom du projet, Doggyblog, fait référence au doggy bag permettant d’emporter son plat pour le consommer chez soi. Ici on fait une parallèle avec un blog à télécharger puis à «emporter chez soi». L’application se présente sous forme d’une bibliothèque à installer à la racine du site web hôte et qui est activée en appelant un fichier css et un fichier javascript. Une connexion à une base de données est nécessaire. Ce projet s’adresse aux webmasters qui souhaitent augmenter l’interactivité sur leur site web afin notamment d’augmenter le nombre de visiteurs. L’application est téléchargeable librement sur GitHub, via la landing page (voir page suivante) ou directement sur GitHub. Il n’est pas prévu qu’elle génère un revenu, bien qu’une bannière publicitaire sur la landing page n’est pas exclue si la visibilité de Doggyblog devient suffisante. Un cahier des charges plus fourni est présenté en annexe ainsi que la charte graphique.
6
Objectifs et methodes L’objectif de ce travail a été de développer un produit complet, de l’application Doggyblog elle-même à la landing page. Pour cette raison, le projet a été subdivisé en deux volets. D’une part, la création de la landing page qui est une page web dédiée sur laquelle le visiteur peut télécharger l’application et trouver toute l’info nécessaire à l’installation et à l’utilisation de Doggyblog. Et d’autre part, le développement de l’application Doggyblog en tant que telle. Ces deux volets sont abordés en profondeur dans les sections suivantes. Plutôt que de créer une application à partir d’un framework ou d’un CMS, j’ai voulu travailler à un plus bas niveau, plus exactement en PHP orienté objet. En effet, cette méthode de programmation vue au cours a suscité chez moi pas mal de curiosité et j’ai voulu en savoir davantage, y compris sur le fonctionnement profond d’un framework. Il me manquait cependant une structure et une méthodologie pour aborder sereinement cette tâche. J’ai donc fait des recherches sur le web afin de trouver un maillon technique qui puisse faire le lien entre le cours de POO et la réalisation d’une telle application et j’ai trouvé assez rapidement un tutoriel décrivant la réalisation d’un site de news en POO (voir références). Cette optique de développement «from scratch» a permis d’obtenir en fin de compte une bibliothèque plus légère que si elle avait été construite à partir d’un framework. Comme d’autres ont utilisé le code existant d’un framework ou d’un CMS pour créer leur application, j’ai choisi quant à moi de m’appuyer sur la structure de base du tutoriel. Evidemment, j’ai modifié le code pour l’adapter à mes besoins et je l’ai fait évoluer en lui ajoutant des modules. Concernant la méthodologie, le tutoriel initie à une technique de modélisation: l’UML. L’usage élémentaire et simple de cette méthodologie qui en est fait m’a suffit amplement pour mon travail. Avant de commencer à coder j’ai donc entièrement modélisé l’application me permettant de structurer le travail et de visualiser globalement l’application. Le modèle UML est présenté dans la partie consacrée au développement de l’application. Un autre aspect de la méthodologie est l’utilisation du pattern MVC que j’apprécie particulièrement pour organiser une application, en séparant le visuel de la logique du code ainsi que du traitement des données (interactions avec la base de données). Avec tout ça j’avais les outils et la méthodologie qu’il faut pour démarrer sur de bonnes bases.
7
8
2 La landing page
Description de la conception Design J’ai commencé par réaliser une maquette du site dans Photoshop. Comme la plupart des landing pages, le site est une page unique sur laquelle le visiteur navigue en scrollant et en utilisant les boutons de navigation. La charte graphique est décrite dans le cahier des charges en annexe. Un aperçu de la maquette est visible à la page ci-contre. Evidemment, les couleurs ne reflètent pas la réalité à l’écran puisque l’on passe d’un espace colorimétrique RVB à l’écran vers un espace colorimétrique CMJN sur cette page. Le vert est en outre la couleur où la différence entre les espaces RVB et CMJN se marque le plus, particulièrement pour un vert lumineux.
Mise en place Avant de coder j’ai préparé la structure de base avec Initializr, un générateur de templates basés sur HTML5 Boilerplate, Bootstrap et quelques polyfills dont Respond.js qui permet l’utilisation des media queries pour le responsive design dans IE 6-8. Le package à télécharger peut être assemblé par l’utilisateur luimême sur le site web d’Initializr. La librairie Modernizr, qui permet de détecter les propriétés CSS3 et HTML5 prises en charge par le navigateur, y est intégrée mais je l’ai remplacée par un assemblage personnalisé sur le site de Modernizr afin de l’optimiser à mes besoins c-à-d principalement la propriété CSS3 transform (pour la rotation des éléments HTML). Quand Modernizr détecte une absence de prise en charge de cette propriété, une classe CSS est prévue pour une dégradation gracieuse, c-à-d une accessibilité minimale mais fonctionnelle pour l’agent utilisateur concerné. Les fontes web choisies et la librairie jQuery ont également été mises en place en les appelant dans le fichier index.html.
Code Les animations sur la page étant très présentes j’ai choisi une alternative plus performante à la méthode animate de jQuery, Velocity.js. Il s’agit d’une librairie javascript dont l’utilisation ressemble à celle d’animate. Son principal attrait est sa rapidité par rapport à la méthode jQuery. De plus, elle apporte des fonctionnalités supplémentaires telles que le pack UI qui permet des 10
effets animés sur les éléments d’interaction de la page. J’ai donc simplement téléchargé la librairie et je l’ai appelée dans le fichier index.html. J’ai également voulu installer la librairie javascript cssSandpaper qui est un polyfill de certaines propriétés CSS3 (transform, box-shadow, etc.) dans les navigateurs qui ne les supportent pas nativement. J’ai dû cependant la désinstaller car un problème de compatibilité est apparu avec Respond.js. En effet, les éléments HTML sur lesquels la propriété CSS3 transform appliquait une rotation se figeaient et n’étaient plus fluides. J’ai ensuite optimisé les animations grâce au forcefeeding1 de Velocity.js qui précise, en second élément d’un tableau, la valeur de départ de l’élément DOM à animer afin que le navigateur n’ait pas à la calculer lui-même. Je souhaitais un effet de parallaxe sur deux sections de la page, j’ai donc essayé de mettre en place la librairie Jarallax mais le type de mise en page créait des dysfonctionnements de celle-ci. Je me suis donc tourné vers le plugin jQuery Parallax. Pour l’animation des textes de la section mégaphone, j’ai utilisé la méthode jQuery position2 pour connaître et enregistrer dans une variable la position des éléments définie dans les CSS et donc qui varie grâce aux media queries (c’est parce qu’elle varie que j’ai utilisé du javascript pour la connaître). Puis j’ai initialisé la position des textes aux extrémités de l’écran avec offset2. La position d’arrivée de l’animation est donc celle enregistrée précédemment dans la variable. La position du curseur de la barre de scroll pour le déclenchement des animations a été déterminée par jQuery. Une autre librairie javascript qui m’ a été utile est Prism.js qui permet une coloration syntaxique du texte que l’on veut présenter comme du code sur la page web3. La coloration syntaxique peut se faire selon plusieurs langages et cela est paramétré via Prism.js. Le travail s’est poursuivi en ajustant les animations, principalement celle plus difficile des volets supérieurs et inférieurs qui viennent entourer le logo, en corrigeant les petits bugs, en optimisant l’affichage et finalement en améliorant la compatibilité avec les différents navigateurs. C’est ce que nous allons voir au point suivant.
12
1. Forcefeeding de Velocity.js $element.velocity({ /* Two-item array format. */ translateX: [ 500, 0 ], /* Three-item array format with a per-property easing.*/ opacity: [ 0, “easeInSine”, 1 ] }); 2. Aperçu du script jQuery var megaphonetop = $(“#megaphone”).offset().top-100; var bigfont = $(“#megaphone .bigfont”); var bigfontleft = bigfont.position().left; bigfont.offset( {left: 0} ); var slogan = $(“#megaphone .slogan”); var sloganleft = slogan.position().left; slogan.offset( {left: 2000} ); var abouttop = $(“#about”).offset().top; $(window).scroll(function() { // Megaphone section if($(this).scrollTop() > megaphonetop) { $(bigfont).velocity({ opacity: 0.4, left: bigfontleft }, { duration: 1000, easing: [ 0, 0.98, 0.12, 0.99 ], begin: function () { $(slogan).velocity({ opacity: 1, left: sloganleft }, { delay: 400, duration: 1000, easing: [ 0, 0.98, 0.12, 0.99 ] }); } }); } ... 3. Coloration syntaxique du code par Prism.js
Compatibilité avec les navigateurs Afin qu’elle soit accessible par un maximum de personnes, la landing page a été rendue compatible avec IE à partir de sa version 8. D’où la mise en place de Respond.js et Modernizr mentionnés précédemment. Quand Respond.js est entré en conflit avec cssSandpaper, j’ai préféré désinstaller cette dernière afin de privilégier l’aspect responsive du site. Par conséquent, les éléments ne pouvant plus être mis en rotation sur IE8, une feuille de style particulière à IE8 et appelée via les commentaires conditionnels dans index.html1 a permis la dégradation gracieuse. Travaillant sur un iMac, j’ai testé chaque changement dans Modern IE, une machine virtuelle windows 7 avec IE8 mise à disposition par Microsoft sur https://www.modern.ie/fr-fr.
1. Commentaires conditionnels ... <!--[if gt IE 8]><!--> <script src=”js/plugins.js”></script> <script src=”js/main.js”></script> <!--<![endif]--> <!--[if lte IE 8]> <script src=”js/main-ie.js”></script> <![endif]--> </body> </html>
Un autre problème est apparu avec la propriété overflow-x:hidden dans IE8 (propriété cachant, sur l’axe x, tout ce qui sort de la boîte sur laquelle s’applique l’overflow). Il considérait cette propriété comme un overflow:hidden (x et y) et il a été résolu en utilisant un préfixe (-ms-overflow-x:hidden). Dans iOS, overflow-x n’avait pas le comportement attendu (les éléments sortaient de leur boîte malgré le overflow-x:hidden). Après lecture de forums, la solution a été de mettre cette propriété à la fois sur la balise html et à la fois sur la balise body en utilisant les classes générées par Modernizr pour ne pas cibler tous les navigateurs.
Live View Voir sur http://www.01artdesign.be/doggyblog/
13
14
3 Lâ&#x20AC;&#x2122;application Doggyblog
Entree en matiere Principe de l’application Doggyblog est un moteur de blog dont l’originalité n’est pas d’exister de manière indépendante mais d’exister en symbiose avec un site web hôte afin de lui ajouter de l’interactivité, ce qu’un blog fait très bien. En vue d’être parfaitement intégrée dans le site qui l’accueille, l’application est modifiable aussi bien au niveau du contenu qu’au niveau de l’aspect. C’est pourquoi le principe du CMS s’est imposé, un blog étant déjà d’un point de vue sémantique un système de gestion de contenu. Le webmaster qui installe l’application sur son site peut donc personnaliser le contenu et l’aspect du blog via un panneau d’administration, sans connaissance de programmation. Evidemment, on parle ici plutôt d’un micro-CMS puisque sa fonction est limitée à un blog et que les paramètres de personnalisation sont encore peu nombreuses. Cela peut éventuellement être amené à évoluer.
Le Tutoriel de référence Comme expliqué précédemment, l’application a été développée en PHP orienté objet et le maillon technique manquant pour faire le lien entre le cours de POO et la réalisation complète d’une application en POO a été trouvé sur openclassrooms.com (voir références). Le tutoriel «Programmez en orienté objet en PHP», écrit par Victor Thuillier, explique en profondeur cette façon de programmer inventée dans les années 1970 et qui prend de plus en plus de place aujourd’hui grâce aux avantages qu’elle apporte, comme l’organisation plus structurée du code, une maintenance plus facile et une distribution du code plus aisée. Les sujets abordés et illustrés par quelques travaux pratiques sont, outre les connaissances de base, la modélisation UML, les design patterns, les méthodes magiques, l’API de réflexivité, etc. C’est à la fin du tutoriel que l’on trouve le travail pratique sur lequel je me suis appuyé pour mon projet. Il explique comment réaliser un site web complet avec système de news et espace d’administration. Le fonctionnement de base de Doggyblog se calque sur celui du tutoriel, il sera d’ailleurs expliqué en détail dans la suite de ce travail, cependant l’objectif à atteindre n’étant pas le même, de nombreuses différences existent entre les deux applications. Les principales d’entre-elles sont mentionnées aux pages suivantes. Le code du tutoriel ne fut donc qu’un commencement, un socle pour bâtir le reste. 16
Principales différences entre les deux applications
1. Modélisation complète de l’application en UML avec toutes les relations entre les classes.
2. Il n’y a pas de gestion d’autres API que PDO pour la communication avec la DB et donc tous les managers spécifiques à d’autres API n’ont pas été intégrés.
3. Il n’y a pas de module News mais un module Post. L’entité News a donc été adaptée en une entité Post. Le contrôleur et le manager associés ont aussi été adaptés.
4. Le code est beaucoup plus documenté et l’est en anglais. La syntaxe PHPDoc est utilisée.
5. Création d’un module Profile pour la gestion des utilisateurs et de leur profil avec gestion des erreurs. Les classes FormHandler et FormBuilder sont modifiées et adaptées.
6. L’administrateur ne peut pas modifier un commentaire d’un autre utilisateur mais seulement le supprimer. L’auteur du commentaire a quant à lui la possibilité de supprimer son commentaire mais aussi de
le modifier. Les méthodes executeUpdateComment et executeDeleteComment sont donc ajoutées au frontend et supprimées du backend. Le backend, c-à-d l’accès admin, ne gère que les posts et le frontend gère
les commentaires. Les droits associés à chaque action sont évidemment fonction de l’utilisateur connecté. Toutes les méthodes liées sont donc modifiées.
7. Les entités (classes Entity, Post, Comment) sont modifiées car il n’y a plus besoin de gestion interne des erreurs grâce aux validateurs de formulaires plus
nombreux. Les constantes, la méthode isValid et le check interne dans les setters sont donc supprimés.
8. L’attribut errorMessage de la classe Field est converti en array au lieu de string afin de conserver plusieurs messages d’erreur possibles pour un champ de formulaire (au cas où plusieurs validateurs pour
un champ sont non-valides). Par conséquent, la méthode isValid de la classe Field et l’affichage de ces erreurs dans les classes filles de Field sont aussi adaptés.
9. Création de la méthode executeLogOut dans la classe ConnectionController pour se délogger.
10. Création de nouveaux validateurs de formulaires: MinLengthValidator, SameValueValidator, EmailValidator, IsIntValidator. Il y a des modifications dans la méthode getField de la classe Form.
11. Création du module Subscription pour permettre l’inscription d’utilisateurs souhaitant laisser des commentaires.
12. Le module de connexion est modifié car la connexion ne se base plus sur le fichier XML de configuration mais sur la DB.
13. Création de la méthode setType et de l’attribut type dans la classe Stringfield afin de pouvoir préciser le type de champ (email, text, date, etc.) lors de la création d’un formulaire. La méthode
buildWidget est modifiée pour éviter un rappel de la valeur si le type de champ est password.
14. Création de la méthode processSubscription dans la classe FormHandler pour vérifier si l’email et le login ne se trouvent pas déjà dans la DB. Par conséquent, il y a également de nouvelles méthodes dans
la classe UsersManager (loginIsAvailable et emailIsavailable) ainsi que dans la classe Field (addErrorMessage) afin de pouvoir ajouter un message d’erreur au champ.
15. Correction des liens en liens absolus et pas relatifs en les faisant commencer par dirname($_SERVER[‘PHP_ SELF’] pour que l’application s’adapte à des serveurs où le dossier Public de Doggyblog n’est pas paramétré comme
racine du domaine. Par conséquent, la méthode match de la classe Route a été modifiée pour adapter l’expression régulière qui va chercher les routes dans le fichier XML associé.
16. La classe User est devenue la classe Session qui gère plus largement les sessions de l’application. La méthode isAdmin a été ajoutée ainsi que d’autres pour obtenir des infos sur les utilisateurs connectés.
17. Afin de centraliser les paramètres, la DB est paramétrée dans le fichier de configuration app.xml du frontend avec pour conséquence une modification de la classe PDOFactory.
18. L’entité Config est créée. La classe Config devient ConfigHandler pour ne pas la confondre avec l’entité et création de la méthode set pour sauvegarder les changements dans le fichier XML.
19. Création de la classe ConfigFormBuilder, de la classe NumberField et de la méthode processConfig de la classe FormHandler.
20. Les classes HTTPRequest et HTTPResponse sont simplifiées pour ne garder que les méthodes ayant une utilité dans le projet.
21. Les classes HTTPRequest et User (devenue Session) n’héritent plus de la classe ApplicationComponent car inutile.
22. Utilisation du paramètre PDO::MYSQL_ATTR_INIT_ COMMAND => ‘SET NAMES \’UTF8\’ pour la connexion à la DB avec PDO. Sans ce paramètre, il n’y a pas de
problème d’affichage dans le navigateur mais les termes sont enregistrés avec des caractères ISO dans la DB.
17
23. La méthode match de la classe Route a un type de valeur en retour mixed (plusieurs types possibles) et pas bool (booléen).
24. Certaines valeurs par défaut des attributs et certains paramètres des classes BackController, Managers et Field sont supprimés car inutiles.
25. Suppression de fonctions inutiles de la classe Entity (offset methods) car je n’implémente pas \ArrayAccess qui permet de parcourir un objet comme un tableau.
26. Le type de données de la méthode send de la classe HTTPResponse est string (chaîne de caractère) et pas void (aucune valeur en retour).
27. Suppression du paramètre HTTPRequest passé aux méthodes execute de la classe BackController car elle l’a à disposition via l’instance de l’application dans ses attributs.
28. Modification du nom de la méthode add dans la classe Form en setter (setFields).
29. Ajout du préfixe ‘get’ au nom de certaines méthodes pour signifier qu’il s’agit de getters.
30. Le dossier Web est renommé Public car plus explicite et intuitif. En effet, on rencontre aussi ce type de dénomination pour le dossier contenant les css, javascript, images dans d’autres frameworks.
31. Raccourci dans l’appel des classes par utilisation de l’instruction use.
32. Relation créée entre les tables post et comment de la DB.
33. Modification de la méthode setFields de la classe Form pour s’adapter au nom des getters des entités.
34. Ajout du type de paramètre à passer dans certaines méthodes, ex: array pour le paramètre $varsNames de la méthode setVarsNames de la classe
Route afin de limiter le passage de paramètres inadéquats et faciliter ainsi le débuggage.
35. Amélioration du code de la méthode buildWidget de la classe Field pour le rendre conforme aux spécifications HTML5.
36. Méthode length de la classe Field supprimée car elle n’est pas utile dans le projet.
37. Ajout de la méthode getDao dans la classe Managers afin de rendre disponible le DAO à d’autres classes pour des méthodes telles que lastInsertId dans le contrôleur du module d’inscription. Cela lui permet
d’envoyer l’info d’ID du user qui vient d’être inscrit dans la variable de session. Ainsi, l’administrateur peut être distingué des autres utilisateurs loggés.
38. Ajout du setter et du getter de l’auteur du post (ID) dans l’entité Post.
39. Ajout de la méthode getAuthorLogin dans la classe PostsManager afin de récupérer le login de l’auteur dans la table user via jointure.
40. Modification de la méthode executeDeleteComment dans la classe PostController du frontend et de la route associée, afin de lui donner deux paramètres, l’ID du commentaire pour savoir
lequel supprimer et l’ID du post afin de savoir vers quel post il faut être redirigé après la suppression.
41. Modification de la méthode delete de la classe PostsManager afin qu’elle supprime d’abord tous les commentaires associés au post sinon les contraintes de clés étrangères empêchent la suppression.
42. Protection des méthodes de gestion de la classe PostController du frontend (celles du backend sont déjà protégées à la base) afin que les utilisateurs non-connectés n’y aient pas accès via l’URL, par exemple
qu’ils ne puissent pas insérer un commentaire en tapant simplement comment-8.html (insertion d’un commentaire pour le post 8). De même, grâce à cette protection,
un utilisateur ne pourra réaliser aucune action sur un commentaire d’un autre via l’URL.
43. La méthode getAuthorId a été ajoutée dans la classe CommentsManager et est nécessaire pour vérifier si l’utilisateur connecté est l’auteur du commentaire.
44. Ajout de la pagination, modification de la page d’accueil index.php, de la méthode executeIndex de la classe PostController et ajout d’une route à route.xml.
45. Cryptage des mots de passe par les méthodes password_* de PHP 5.5
18
La modelisation J’ai commencé le développement non pas en codant directement mais en passant par une étape préliminaire de construction du modèle UML de l’application. La première raison de construire ce modèle était d’avoir à disposition un aperçu graphique de l’application au fur et à mesure de son développement afin de faciliter l’intégration de nouvelles «briques». La deuxième raison était que le logiciel de modélisation Dia et son extension uml2php5 permettent d’exporter le modèle UML construit en code PHP dans lequel les classes (vides) sont construites et placées chacune dans un fichier distinct. De plus, le code exporté est déjà commenté selon la syntaxe PHPDoc1. Chaque classe est représentée dans le modèle par un tableau contenant un volet supérieur pour les attributs et constantes et un volet inférieur pour les méthodes. Les relations entre classes sont représentées par différents types de flèches donnant une direction. Le modèle est visible aux pages suivantes. On y voit que l’application démarre par la classe FrontendApplication ou par la classe BackendApplication selon que l’on veuille atteindre la page d’accueil (FrontendApplication) ou le panneau d’administration (BackendApplication). Grâce aux relations modélisées entre les classes, on peut «naviguer» dans l’application. On y aperçoit également deux zones, la zone regroupant les classes permettant la création et la gestion des formulaires, et la zone regroupant les classes jouant un rôle dans la gestion des entités.
1. Exemple de fichier créé par le logiciel Dia et son extension uml2php5 <?php /** * Represents a number field * Code skeleton generated by dia-uml2php5 plugin * written by KDO kdo@zpmag.com * @see Field */ require_once(‘Field.class.php’); class NumberField extends Field { /** * Sends back the HTML code to the Form object * @access public * @return string */ public } ?>
function buildWidget() {
}
2. Hiérarchie des dossiers de l’application (dans un dossier Doggyblog)
L’application telle qu’elle existe actuellement est composée de 44 classes. Le modèle UML de départ comportait environ 30 classes et donc après exportation dans Dia une trentaine de fichiers PHP ont été générés1, les autres ont été ajoutés manuellement ensuite. On aperçoit dans l’exemple ci-contre que les fichiers requis sont appelés via la fonction PHP require_once, mais on verra plus loin qu’elle sera remplacée par un système d’autoload bien plus pratique. J’ai organisé ces fichiers en une structure de dossiers2 comprenant quatre dossiers principaux: Applications, Errors, Library et Public. Le dossier Applications contient lui-même les dossiers Frontend et Backend et chacun de ceux-ci est composé des fichiers de configuration y compris les routes, des modules et leurs vues et contrôleur associés ainsi que du template c-à-d le layout de l’application. Le dossier Errors contient les pages d’erreur (ex: erreur 404). Le dossier Library contient les fichiers constituant le noyau de l’application ainsi que les entités, les modèles, les constructeurs de formulaire et les librairies externes. Enfin, le dossier Public contient les javascript, css, etc. organisés en thèmes.
19
Modele UML de l'application
FORMS 20
ENTITIES MANAGEMENT
START
Frontend
)
Backend Frontend and Backend 21
Description du fonctionnement Le démarrage de l’application La structure de dossiers et les fichiers de classe ayant été générés par Dia et uml2php5, j’ai poursuivi le travail en complétant fichier après fichier le code nécessaire au fonctionnement de chaque classe, en suivant la chronologie et la logique de l’application. C’est donc dans cet ordre d’idée que j’explique ci-après comment le code que j’ai écrit fonctionne. L’accès à l’application, autrement dit le lien qui lance l’application, doit passer par le dossier Public qui contient à sa racine les fichiers frontend.php, backend.php et .htaccess. Ce dernier est essentiel pour la navigation car il permet de rediriger tous les liens de l’intérieur de l’application mais aussi ceux provenant de l’extérieur vers frontend.php, excepté ceux qui commencent par admin/ qui sont redirigés eux vers backend.php. Le moteur de redirection du serveur est donc activé dans .htaccess et les règles et conditions de redirection sont fixées1. Ainsi, un lien vers Doggyblog/Public/ est redirigé vers frontend.php . Il s’agit en quelque sorte du fichier d’accueil. Les librairies tierces y sont tout d’abord appelées, dans le cas présent une seule librairie, password.php, qui rend utilisable les fonctions password_* de PHP 5.5 à partir de PHP 5.3.7. Elle permet un cryptage efficace des mots de passe. 2
A côté de .htaccess, l’autre fichier fondamental dans la navigation à l’intérieur de l’application est le fichier autoload.php, inclus ici, qui va permettre d’appeler automatiquement une classe dans le bon fichier quand elle est instanciée, sans devoir utiliser à chaque fois les fonctions require* ou include*. La classe FrontendApplication est ensuite instanciée et l’application démarrée via la méthode run. Toutes les méthodes appelées dans la foulée le seront donc au sein de frontend.php.
Le système d’Autoload Le fichier autoload.php situé dans le dossier Library de l’application fonctionne sur base des namespaces (espaces de noms) et de la fonction spl_autoload_register3. Cette dernière enregistre la fonction autoload dans la pile __autoload( ) et elle va donc être appelée chaque fois qu’une classe est 22
1. Le fichier .htaccess RewriteEngine On # Each url starting with ‘admin/’ is rewritten to backend. php RewriteRule ^admin/ backend.php [QSA,L] # Each url is rewritten to frontend.php only if the file that we want to access doesn’t exist (e.g. if it is an existing image in the Public folder, the url is not rewritten) RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ frontend.php [QSA,L] 2. Le fichier frontend.php <?php // Vendor library(ies) activation require ‘../Library/Vendor/password.php’; // Autoload of the classes require ‘../Library/autoload.php’; $app = new Applications\Frontend\FrontendApplication; $app->run(); 3. Le fichier autoload.php <?php function autoload($class) { require ‘../’.str_replace(‘\\’, ‘/’, $class).’.class. php’; } spl_autoload_register(‘autoload’);
instanciée. Le namespace dans lequel se trouve chaque classe est important car le paramètre passé dans la fonction autoload est le nom de la classe précédé du namespace qui la contient. La fonction autoload va convertir le namespace en chemin d’accès vers le fichier contenant la classe (les antislashes sont remplacés par des slashes). C’est pourquoi il faut que le namespace de la classe corresponde au chemin d’accès et que le nom du fichier soit du type nom_classe.class.php. Avec ce système, chaque fois qu’une classe va être instanciée dans la suite du code, le bon fichier la contenant sera inclus.
L’instanciation de FrontendApplication FrontendApplication1 étant une classe fille de la classe Application, son instanciation amène son constructeur à stocker dans les attributs de sa classe mère, le nom de l’application (Frontend), un objet contenant les méthodes relatives à la session (objet Session), un objet qui permet de gérer les paramètres de configuration du fichier app.xml du backend (objet ConfigHandler), et les objets relatifs aux requêtes et réponses HTTP (objets HTTPRequest et HTTPResponse).
Le système de route La méthode getController de la classe Application est ensuite appelée2. Elle est chargée d’envoyer le contrôleur adéquat. Pour ce faire elle va solliciter le routeur. Elle commence par instancier la classe Router puis elle charge le fichier contenant les routes du frontend ($this->name = Frontend) via l’objet DOMDocument, bibliothèque native de PHP. Les routes sont stockées dans un fichier XML3. Chacune d’entre-elles possède un attribut url dont la valeur est une expression régulière qui est comparée à l’URL, un attribut module qui indique dans quel module l’application doit aller chercher le contrôleur et un attribut action qui donne la méthode correspondante du contrôleur à appeler. Un attribut facultatif vars contient le nom des variables GET de l’URL et dont les valeurs sont extraites via les parenthèses capturantes de l’expression régulière.
1. La classe FrontendApplication class FrontendApplication extends Application { /** * @access public */ public function __construct() { parent::__construct(); $this->name = ‘Frontend’; } /** * Runs the frontend application and sends the response * @access public * @return string */ public function run() { $controller = $this->getController(); $controller->execute(); $this->httpResponse->setPage($controller ->getPage()); $this->httpResponse->send(); } } ?> 2. La méthode getController de la classe Application public function getController() { $router = new Router; $xml = new \DOMDocument; $xml->load(__DIR__.’/../Applications/’.$this->name. ’/Config/routes.xml’); $routes = $xml->getElementsByTagName(‘route’); foreach ($routes as $route) { $vars = array(); if ($route->hasAttribute(‘vars’)) { $vars = explode(‘,’, $route ->getAttribute(‘vars’)); } $router->setRoutes(new Route($route ->getAttribute(‘url’), $route->getAttribute(‘module’), $route->getAttribute(‘action’), $vars)); } try { $matchedRoute = $router->getRoute($this ->httpRequest->uri()); } catch (\RuntimeException $e) { if ($e->getCode() == Router::NO_ROUTE) { $this->httpResponse->redirect404(); } }
23
Le routeur va parser chacune des routes du fichier XML grâce à DOMDocument et emmagasiner celles-ci sous formes d’objets Route contenant les valeurs des attributs XML. Si la route XML parsée contient un attribut vars, les valeurs seront stockées dans l’objet Route sous forme de tableau. La méthode getController fait ensuite appel à la méthode getRoute du routeur qui va renvoyer l’objet Route correspondant à l’URL par une comparaison de celle-ci avec l’expression régulière de chaque route. Si la route XML contient un attribut vars, la méthode getRoute extrait également les valeurs des paramètres GET de l’URL et les stocke dans l’objet Route. L’URL est obtenue par la méthode uri de l’objet HTTPRequest. Si aucune route n’est trouvée, une erreur 404 est renvoyée via l’objet HTTPResponse. Les variables de l’URL sont fusionnées avec la variable superglobale $_GET et le contrôleur approprié est déterminé grâce à l’information sur le module et l’action que contient l’objet Route envoyé par la méthode getRoute. Le contrôleur est alors instancié et retourné. L’instance de l’application, le nom du module et le nom de l’action sont passés en paramètre. Exemple: Soit l’URL étant Doggyblog/Public/page-1.html, l’expression régulière la vérifiant étant ”/page-([0-9]+)\.html”, la route retournée est celle correspondant à la route XML suivante: <route url=”/page-([0-9]+)\.html” module=”Post” action=”index” vars=”page” /> et donc le contrôleur envoyé est Applications/ Frontend/Modules/Post/PostController, la méthode appelée dans PostController est index et la variable $_GET[‘page’] est créée et a pour valeur le contenu de la parenthèse capturante. Grâce à ce système il est donc très facile d’ajouter une route à l’application, il suffit de rajouter une ligne dans le fichier XML contenant l’expression régulière à tester, le nom du module correspondant, le nom de la méthode du contrôleur associé et éventuellement le nom des variables GET à extraire de l’URL.
24
$_GET = array_merge($_GET, $matchedRoute->getVars()); $controllerClass = ‘Applications\\’.$this->name.’\\ Modules\\’.$matchedRoute->getModule().’\\’.$matchedRoute ->getModule().’Controller’; return new $controllerClass($this, $matchedRoute ->getModule(), $matchedRoute->getAction()); } 3. Le fichier routes.xml du frontend <?xml version=”1.0” encoding=”utf-8” ?> <routes> <route url=”/” module=”Post” action=”index” /> <route url=”/page-([0-9]+)\.html” module=”Post” action=”index” vars=”page” /> <route url=”/subscription\.html” module=”Subscription” action=”index” /> <route url=”/logout\.html” module=”Connection” action=”logOut” /> <route url=”/connection\.html” module=”Connection” action=”index” /> <route url=”/profile.html” module=”Profile” action=”index” /> <route url=”/post-([0-9]+)\.html” module=”Post” action=”show” vars=”id” /> <route url=”/comment-([0-9]+)\.html” module=”Post” action=”insertComment” vars=”post” /> <route url=”/comment-update-([0-9]+)\.html” module=”Post” action=”updateComment” vars=”id” /> <route url=”/comment-delete-id([0-9]+)-post([0-9]+)\. html” module=”Post” action=”deleteComment” vars=”id,post” /> </routes>
Le backcontroller L’application poursuit ensuite son chemin dans FrontendApplication en exécutant la méthode execute1 du contrôleur retourné précédemment. Cette méthode est une méthode de la classe BackController qui est la classe mère de tous les contrôleurs de l’application. Cette classe permet le stockage dans ses attributs des paramètres passés lors de l’instanciation du contrôleur mais aussi de la vue, d’une instance de la classe Managers qui enregistre les différents managers de l’application et le DAO (via un pattern Factory pour PDO -> voir ci-dessous), et enfin d’une instance de la classe Page qui sera chargée plus tard d’afficher la page générée avec sa vue et ses variables dans le layout/template. Pour en revenir à la méthode execute, celle-ci appelle la méthode du contrôleur correspondant à l’action (ex: si action = ‘index’, méthode = ‘executeIndex’) et un objet HTTPRequest est passé en paramètre. Si l’action ne correspond à aucune méthode du contrôleur une erreur est lancée.
La classe PDOFactory Cette classe est un pattern Factory pour PDO2 qui instancie les classes nécessaires à la connexion à la DB qui est donc centralisée ici, c’est pourquoi on parle de classe usine. Elle commence par lire les paramètres de connexion à la DB dans le fichier de configuration app.xml du backend grâce un objet DOMDocument déjà mentionné précédemment, puis instancie un objet PDO. Elle finit par le retourner à l’endroit du code où on y a fait appel.
Exécution de la méthode du contrôleur Si on considère que c’est la page d’accueil qui est visitée donc que l’URL est vérifiée par l’expression régulière ‘/’, le module correspondant est Post et l’action correspondante est index. La méthode executeIndex du PostController (Frontend) est ainsi exécutée3.
1. Méthode execute du BackController public function execute() { $method = ‘execute’.ucfirst($this->action); if (!is_callable(array($this, $method))) { throw new \RuntimeException(‘The action “’. $this->action.’” is not defined in this module’); } $this->$method($this->app->getHttpRequest()); } 2. La classe PDOFactory class PDOFactory { public static function getMysqlConnexion() { $xml = new \DOMDocument; $xml->load(__DIR__.’/../Applications/Backend/Config/ app.xml’); $parameters = $xml->getElementsByTagName (‘database’); $dbvars = array(); foreach ($parameters as $parameter) { $dbvars[$parameter->getAttribute(‘var’)] = $parameter->getAttribute(‘value’); } $db = new \PDO(‘mysql:host=’.$dbvars[‘host’] [‘host’].’;dbname=’.$dbvars[‘dbname’], $dbvars[‘login’], $dbvars[‘password’], array (\PDO::MYSQL_ATTR_INIT_COMMAND => ‘SET NAMES \’UTF8\’’)); $db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_ EXCEPTION); return $db; } } 3. La méthode executeIndex du PostController (Frontend) public function executeIndex() { $postsPerPage = $this->app->getConfig()->get(‘posts_ number’); $charactersNumber = $this->app->getConfig() ->get(‘characters_number’); $manager = $this->managers->getManagerOf(‘Posts’); $totalOfPosts = $manager->count(); $numberOfPages = ceil($totalOfPosts/$postsPerPage); if($this->app->getHttpRequest()->dataGetExists(‘page’)) { $page = $this->app->getHttpRequest() ->dataGet(‘page’); } else { $page = 1; } $messageToDisplay = ($page - 1) * $postsPerPage; $postsList = $manager->getList($messageToDisplay,
25
Elle commence par récupérer les paramètres de configuration du nombre de posts à afficher sur la page d’accueil ainsi que du nombre de caractères de l’aperçu du post. La méthode getManagerOf de la classe Managers récupère et stocke éventuellement dans un tableau, s’il n’est pas déjà enregistré, le manager lié aux posts c-à-d PostsManager. Ensuite, la pagination est créée en comptant le nombre total de posts et en calculant le nombre de pages nécessaires en fonction du nombre de posts à afficher par page. Si un paramètre $_GET[‘page’] n’existe pas c’est qu’on se trouve sur la première page. La méthode getList de PostsManager permet ensuite de récupérer les posts de la base de données par ordre décroissant d’id (le plus récent en premier)4. Le premier paramètre qui lui est passé correspond à l’instruction offset de la requête SQL c-à-d le décalage calculé pour la sélection des posts dans la base de données en fonction de la page dans laquelle on se trouve. Le deuxième paramètre correspond quant à lui à l’instruction limit de la requête SQL à qui on attribue tout simplement la valeur du paramètre du nombre de posts à afficher par page. Grâce à PDO::FETCH_CLASS les posts sont récupérés sous forme d’entités/objets Post (définis dans le dossier Entities par Post.class.php), quoi de plus normal en effet que de manipuler les données comme des objets en POO. Lors de ce processus, les valeurs de champ de chaque post ‘hydratent’ les attributs de l’objet pour y être stockés. Les dates sont reformatées par l’objet DateTime et enfin les entités/objets Post sont retournés sous forme de tableau. Dans executeIndex, le contenu des posts est ensuite traité pour que ne s’affiche que le nombre de caractères paramétré, en prenant soin de ne pas couper un mot mais en s’arrêtant à l’espace le précédant. Pour finir, les paramètres à passer dans la vue sont ensuite stockés dans l’objet Page, notamment le titre de la page, le tableau des posts, le nombre de pages, etc.
Affichage de la page On revient dans FrontendApplication où l’application poursuit sa route. C’est maintenant la méthode setPage de l’objet HTTPResponse qui est appelée et qui va stocker dans ses attributs l’objet Page préparé par le contrôleur. Et pour finir, la méthode send est exécutée5. Elle affiche le contenu de la parenthèse de la fonction exit avant de clôturer le script en cours. Ce contenu est donné par la méthode getGeneratedPage de l’objet Page mentionné juste avant.
26
$postsPerPage); foreach ($postsList as $post) { if (strlen($post->getContent()) > $charactersNumber) { $debut = substr($post->getContent(), 0, $charactersNumber); $debut = substr($debut, 0, strrpos($debut, ‘ ‘)) . ‘...’; $post->setContent($debut); } } $this->page->setVars(‘title’, ‘Doggyblog - last ‘ .$postsPerPage.’ posts’); $this->page->setVars(‘postsList’, $postsList); $this->page->setVars(‘numberOfPages’, $numberOfPages); $this->page->setVars(‘page’, $page); $this->page->setVars(‘manager’, $manager); } 4. La méthode getList de la classe PostsManager public function getList($debut = -1, $limit = -1) { $sql = ‘SELECT id, title, content, publishDate, modifDate, author FROM post ORDER BY id DESC’; if ($debut != -1 || $limit != -1) { $sql .= ‘ LIMIT ‘.(int) $limit.’ OFFSET ‘.(int) $debut; } $request = $this->dao->query($sql); $request->setFetchMode(\PDO::FETCH_CLASS | \PDO::FETCH_ PROPS_LATE, ‘\Library\Entities\Post’); $postsList = $request->fetchAll(); foreach ($postsList as $post) { $post->setPublishDate(new \DateTime($post ->getPublishDate())); $post->setModifDate(new \DateTime($post ->getModifDate())); } $request->closeCursor(); return $postsList; } 5. La méthode send de la classe HTTPResponse public function send() { exit($this->page->getGeneratedPage()); }
Cette méthode1 débute en testant l’existence du fichier pointé par l’attribut contentFile, c-à-d la vue définie précédemment par le BackController lors de l’instanciation du contrôleur associé à la route. Dans le cas de la page d’accueil, la vue est le fichier index.php situé dans le module Post du Frontend. S’il n’existe pas, une erreur est lancée. Ensuite, l’objet Session est passé dans une variable et les variables de l’objet Page sont appelées. Afin d’afficher le contenu de différents fichiers, plus exactement de la vue et du layout, les fonctions de tamporisation sont utilisées. La vue est d’abord passée dans la variable $content lors d’une première séquence de tamporisation, puis le layout est envoyé en retour lors d’une deuxième séquence de tamporisation. Le layout est donc affiché avec la vue et le script est clôturé.
1. La méthode getGeneratedPage de la classe Page public function getGeneratedPage() { if (!file_exists($this->contentFile)) { throw new \RuntimeException(‘The specified view doesn\’t exist’); } $session = $this->app->getSession(); extract($this->vars); ob_start(); require $this->contentFile; $content = ob_get_clean(); ob_start(); require __DIR__.’/../Applications/’.$this->app ->getName().’/Templates/layout.php’; return ob_get_clean(); }
La classe ApplicationComponent Une petite parenthèse est faite ici pour expliquer l’utilité de cette classe. De nombreuses classes ont besoin d’une instance de la classe Application pour fonctionner, c’est pourquoi la classe ApplicationComponent a été créée. En effet, elle stocke dans un attribut une instance de la classe Application. Ainsi, toutes les classes ayant besoin de cette instance vont hériter de la classe ApplicationComponent.
27
Construction des formulaires Ajout de commentaires Je vais illustrer le principe de construction des formulaires par la page d’ajout de commentaire. On accède à cette page via la vue show.php du module Post du Frontend. Cette vue affiche le contenu complet d’un post ainsi que tous les commentaires associés. En fin de page, un lien permet d’accéder à la page d’ajout de commentaire1. L’URL de l’hyperlien contient une partie dynamique déterminée par l’ID du post concerné et donc si l’ID du post est 10 par exemple, l’hyperlien pointera vers comment-10.html. Dans le fichier routes.xml, on peut identifier la route correspondante2. Selon le principe de fonctionnement vu précédemment, c’est donc le PostController qui va être renvoyé par le BackController et c’est la méthode executeInsertComment qui va être exécutée3. De plus, comme un attribut vars existe la valeur de la parenthèse capturante (10 dans l’exemple ci-dessus) va être attribuée à la variable post.
La méthode executeInsertComment La première vérification est réalisée grâce à l’objet Session et elle permet de savoir si un utilisateur est loggé. Dans le cas contraire il est redirigé vers la page de connexion. On verra plus loin la gestion des utilisateurs. Ensuite, l’objet HTTPRequest permet de déterminer si la requête est une requête POST autrement dit que des données de formulaire sont envoyées. Comme ce n’est pas le cas quand on arrive sur la page, on continue et c’est une entité Comment vide qui est instanciée. Cette entité est passée ensuite comme paramètre dans l’instanciation d’un constructeur de formulaires spécifique aux commentaires, CommentFormBuilder.
28
1. Lien d’accès vers la page d’ajout de commentaire dans show.php <p><a href=”comment-<?php echo $post->getId() ?>.html”>Add a comment</a></p> 2. Route correspondante à la page d’ajout de commentaire <route url=”/comment-([0-9]+)\.html” module=”Post” action=”insertComment” vars=”post” /> 3. La méthode executeInsertComment du PostController (Frontend) public function executeInsertComment() { if ($this->app->getSession()->isConfirmed()) { if ($this->app->getHttpRequest()->method() == ‘POST’) { $comment = new Comment(array( ‘post’ => $this->app->getHttpRequest() ->dataGet(‘post’), ‘author’ => $this->app->getSession() ->getUserId(), ‘content’ => $this->app->getHttpRequest() ->dataPost(‘content’) )); } else { $comment = new Comment; } $formBuilder = new CommentFormBuilder($this->app, $comment); $formBuilder->build(); $form = $formBuilder->getForm(); $formHandler = new FormHandler($this->app, $form, $this->managers->getManagerOf(‘Comments’)); if ($formHandler->process()) { $this->app->getSession()->setFlash(‘The comment has been added, thanks!’); $this->app->getHttpResponse()->redirect(‘post’.$this->app->getHttpRequest()->dataGet(‘post’).’.html’); } $this->page->setVars(‘title’, ‘Addition of a comment’); $this->page->setVars(‘form’, $form->createView()); } else { $this->app->getHttpResponse()->redirect(dirname($_ SERVER[‘PHP_SELF’]).’/connection.html’); } }
Les constructeur de formulaires Les constructeurs de formulaires héritent tous de la classe FormBuilder dont le constructeur va instancier un objet Form en passant l’entité envoyée en paramètre, dans ce cas-ci une entité Comment. L’objet Form est enregistré dans un attribut de la classe FormBuilder. La méthode build de CommentFormBuilder est ensuite appelée dans la méthode executeInsertComment1. La méthode setFields de l’objet Form permet de stocker sous forme de tableau le champ passé en paramètre et de retourner le résultat afin que d’autres champs puissent y être ajoutés, mais ce n’est pas le cas ici car un seul champ est défini. Les champs sont tous des objets héritant de la classe Field et les paramètres passés lors de l’instanciation de l’objet (TextField ici), tels que le label et le nom, vont être stockés dans la classe Field et les paramètres spécifiques au champ, tels que rows pour TextField, seront stockés dans la classe fille.
1.La méthode build de la classe CommentFormBuilder public function build() { $this->form->setFields(new TextField(array( ‘label’ => ‘Content’, ‘name’ => ‘content’, ‘rows’ => 7, ‘validators’ => array( new NotNullValidator(‘Please insert a comment before submitting the form’) ) ))); } 2. La classe NotNullValidator class NotNullValidator extends Validator { public function isValid($value) { return $value != ‘’; } }
Les validateurs Un tableau de validateurs de champ est également passé en paramètre lors de l’instanciation du champ. Il peut donc y avoir un ou plusieurs validateurs par champ. Ces validateurs sont également des objets et ils sont instanciés en passant le message d’erreur associé. Le validateur du champ TextField du constructeur de formulaires spécifique aux commentaires est du type NotNullValidator2. Les validateurs possèdent une méthode isValid qui va tester la valeur du champ et retourner un booléen. La méthode isValid de NotNullValidator teste simplement si la valeur n’est pas nulle. Les autres validateurs sont SameValueValidator (teste si une valeur est égale à une autre), MinLengthValidator (teste si une valeur est supérieure à une valeur minimale), MaxLengthValidator (teste si une valeur est inférieure à une valeur maximale), IsIntValidator (teste si une valeur est un entier) et EmailValidator (teste si une valeur a le format d’un email).
29
La gestion des formulaires Un objet FormHandler est ensuite instancié et sa méthode process mise en œuvre. Elle va en fait tester si la requête est de type POST et elle va appeler la méthode isValid de l’objet Form. Cette dernière va à son tour appeler la méthode isValid de chaque champ qui le constitue. Si toutes ces méthodes retournent true, la méthode isValid de l’objet Form retournera elle aussi true. Si les deux conditions sont remplies, la méthode process va sauvegarder le commentaire dans la base de données et retourner true. Dans le cas présent, en arrivant sur la page d’insertion de commentaire, ces conditions ne sont pas remplies donc on va plus loin.
La création de la vue du formulaire Les variables sont enregistrées dans l’objet Page, dont la vue du formulaire. Cette dernière est créée par la méthode createView de l’objet Form qui ellemême fait appel à la méthode buildWidget de chaque champ constituant l’objet Form. Dans le cas d’un champ TextField c’est un élément textarea qui est construit avec le label correspondant. Le ou les éventuels messages d’erreur provenant du ou des validateurs sont placés avant le champ. Si l’attribut value de Field n’est pas vide, la valeur est incorporée au champ en prenant soin de l’échapper avec htmlspecialchars.
Le traitement des données du formulaire Quand le formulaire est rempli et soumis on revient dans la méthode executeInsertComment du PostController mais cette fois-ci avec une requête POST. L’entité Comment passée en argument de l’objet n’est donc plus vide mais complétée avec la valeur de la variable $_GET[‘post’] définie par la parenthèse capturante de l’expression régulière (10 dans notre exemple, c-à-d l’ID du post
30
1. La méthode buildWidget de la classe TextField public function buildWidget() { $widget = ‘’; if (!empty($this->errorMessage)) { $widget .= ‘<p class=”error”>’.implode(‘ / ‘,$this ->errorMessage).’</p>’; } $widget .= ‘<label for=”’.$this->name.’” class=”textarea-label”>’.$this->label.’</label><textarea id=”’.$this->name.’” name=”’.$this->name.’”’; if (!empty($this->cols)) { $widget .= ‘ cols=”’.$this->cols.’”’; } if (!empty($this->rows)) { $widget .= ‘ rows=”’.$this->rows.’”’; } $widget .= ‘>’; if (!empty($this->value)) { $widget .= htmlspecialchars($this->value); } return $widget.’</textarea>’; }
associé au commentaire), avec l’ID de l’utilisateur connecté qui correspond à l’auteur et avec le contenu de la variable $_POST correspondant au contenu du commentaire. Si la validation du formulaire passe, l’objet Session va stocker un message flash dans la session qui s’affichera dans la page et qui confirme que le commentaire a bien été introduit. L’utilisateur est ensuite redirigé vers la page du post correspondant.
Modification d’un commentaire La modification d’un commentaire implique la méthode executeUpdateComment du PostController qui ressemble à executeInsertComment mais avec quelques différences. Notamment l’ID du post n’est plus transmise par la méthode GET mais elle est d’abord placée dans un champ caché du formulaire puis envoyée par méthode POST.
31
Gestion des utilisateurs Les modules subscription et profile La gestion des utilisateurs se fait notamment grâce aux modules Subscription et Profile. Le module Subscription permet à un utilisateur de s’inscrire. La page d’accueil est un formulaire généré dans la méthode executeIndex du contrôleur de ce module. L’entité associée est l’entité User qui possède des attributs login, email, password et passwordConfirm. La gestion du formulaire se fait par l’objet FormHandler qui exécute la méthode processUser1. Elle va mettre en oeuvre les méthodes emailIsAvailable et loginIsAvailable pour vérifier si l’utilisateur n’est pas déjà dans la base de données (ou s’il s’agit de l’utilisateur connecté dans le cas d’une mise à jour du profil). Dans ce cas il sera sauvegardé dans la base de données (ou la base de données sera mise à jour). Dans le cas contraire, un ou des messages d’erreur seront joints au champ concerné. Les mots de passe sont d’abord hashés avec la méthode password_hash avant d’être entrés dans la base de données. Ensuite, si processUser retourne true, l’objet Session génère un message en session et enregistre également en session l’ID de l’utilisateur, son login et son email. Il se retrouve donc connecté. Enfin, il est redirigé vers l’accueil. Le module Profile ressemble au module Subscription sauf qu’il faut être connecté pour pouvoir y accéder. Il permet de mettre à jour le profil.
Le module connection La méthode executeIndex de ce module prépare le formulaire de connexion et exploite la méthode connectionIsValid du manager de l’entité User2. Cette méthode fait une requête dans la base de données et compare le mot de passe hashé de la base données avec le mot de passe entré dans le formulaire. Si c’est bon elle retourne l’ID, le login et le mot de passe et l’utilisateur est connecté grâce à la méthode setAuthenticated de l’objet Session3. Quand un utilisateur est connecté, ses ID, login et email sont enregistrés en session, cela permet donc de gérer via des conditions, quelle action il peut faire sur tel commentaire, post, etc. 32
1. La méthode processUser de la classe FormHandler public function processUser() { if($this->app->getHttpRequest()->method() == ‘POST’ && $this->form->isValid()) { $emailIsAvailable = $this->manager ->emailIsAvailable($this->form->getEntity()->getEmail()); $loginIsAvailable = $this->manager ->loginIsAvailable($this->form->getEntity()->getLogin()); if($emailIsAvailable && ($loginIsAvailable || ($this->form->getEntity()->getLogin() === $this->app ->getSession()->getUserLogin()))) { $this->manager->save($this->form->getEntity()); return true; } if(!$emailIsAvailable) { $this->form->getField(‘email’) ->addErrorMessage(‘The email is already used, please choose another one’); } if(!$loginIsAvailable) { $this->form->getField(‘login’) ->addErrorMessage(‘The login is already used, please choose another one’); } return false; } return false; } 2. La méthode connectionIsValid de la classe UsersManager public function connectionIsValid($login, $password) { $request = $this->dao->prepare(‘SELECT id, password, login, email FROM user WHERE login = :login’); $request->bindValue(‘:login’, $login); $request->execute(); $result = $request->fetchAll(\PDO::FETCH_ASSOC); if(empty($result) || !password_verify($password, $result[0][‘password’])) { return false; } return [‘id’ => (int)$result[0][‘id’],’login’ => $result[0][‘login’], ‘email’ => $result[0][‘email’]]; } 3. La méthode setAuthenticated de la classe Session public function setAuthenticated(array $userData) { $_SESSION[‘auth’] = true; $_SESSION[‘userData’] = $userData; }
Le Backend La classe BackendApplication Le Backend représente tout ce qui est géré par l’administrateur, donc la configuration de l’application et la gestion de posts. Tout cela doit être sécurisé afin que seul l’administrateur y ait accès. Alors que dans le Frontend, seules certaines méthodes étaient protégées des utilisateurs non-connectés, ici c’est tout le Backend qui est protégé des utilisateurs non-connectés et/ou qui ne sont pas administrateur. Il est en effet protégé à la source, dans la classe BackendApplication1. C’est la méthode isAdmin de la classe Session qui vérifie que ce soit l’administrateur qui est connecté2.
Le panneau d’administration Le panneau d’administration inclut la gestion des posts et la gestion des paramètres de configuration actuellement peu nombreux (nombre de posts par page et nombre de caractères pour l’aperçu des posts sur la page d’accueil). L’affichage du panneau d’administration est lié à l’exécution de la méthode executeIndex du PostController du Backend. Elle commence par instancier l’entité Config qui est hydratée soit par les données de formulaire (si la méthode est POST) soit par les données du fichier de configuration app.xml envoyées par la méthode get de la classe ConfigHandler3. Le traitement du formulaire de configuration est particulier parce que les données ne sont pas enregistrées dans la base de données mais dans le fichier app.xml. L’avantage c’est que l’on peut modifier la configuration de l’application par le panneau d’administration mais aussi directement et facilement dans le fichier XML. C’est la méthode set de la classe ConfigHandler qui se charge de sauvegarder les données si les données de formulaire sont valides4. Ce sont de nouveau les méthodes de la classe DOMDocument qui permettent ces manipulations et notamment la modification du fichier XML par la méthode save. En ce qui concerne l’ajout, la suppression et la modification des posts, les méthodes et processus ressemblent à ce qui a été vu précédemment pour les commentaires sauf en ce qui concerne la suppression d’un post car il faut d’abord supprimer tous les commentaires associés avant de supprimer le post lui-même pour éviter une erreur liée à une contrainte de clé étrangère.
1. La classe BackendApplication class BackendApplication extends Application { /** * @access public */ public function __construct() { parent::__construct(); $this->name = ‘Backend’; } /** * Runs the backend application and sends the response * @access public * @return string */ public function run() { if ($this->session->isConfirmed() && $this->session ->isAdmin()) { $controller = $this->getController(); } else { $this->getHttpResponse()->redirect(dirname($_ SERVER[‘PHP_SELF’]).’/connection.html’); } $controller->execute(); $this->httpResponse->setPage($controller->getPage()); $this->httpResponse->send(); } } 2. La méthode isAdmin de la classe Session public function isAdmin() { return $_SESSION[‘userData’][‘id’] === 1; } 3. La méthode get de la classe Confighandler public function get($var) { if (!$this->vars) { $xml = new \DOMDocument; $xml->load(__DIR__.’/../Applications/Backend/Config/ app.xml’); $elements = $xml->getElementsByTagName(‘config’); foreach ($elements as $element) { $this->vars[$element->getAttribute(‘var’)] = $element->getAttribute(‘value’); } } if (isset($this->vars[$var])) { return $this->vars[$var];}return null;}
33
4. La méthode set de la classe ConfigHandler public function set($var, $value) { $xml = new \DOMDocument; $xml->load(__DIR__.’/../Applications/Backend/Config/app. xml’); $elements = $xml->getElementsByTagName(‘config’); foreach ($elements as $element) { if($element->getAttribute(‘var’) == $var) { $element->setAttribute(‘value’, $value); $xml->save(__DIR__.’/../Applications/Backend/ Config/app.xml’); } } }
34
Integration sur le site hote Le principe Le webmaster qui souhaite installer l’application sur son site web commence par télécharger le dossier de l’application, puis il place le dossier Doggyblog à la racine de son site et il met les fichiers doggyblog.css et doggyblog.js à des emplacements qui lui conviennent. Il active ensuite ces deux fichiers en faisant un lien vers ceux-ci dans la page d’accueil et il crée la base de données avec doggyblog.sql. Enfin, il règle les paramètres de connexion à la DB dans le fichier app.xml situé dans le dossier Config du Backend. Doggyblog est à ce moment prêt à être démarré lors du prochain rafraîchissement de la page. Le principe de l’affichage de Doggyblog sur un site hôte est basé sur une iframe qui embarque dans la page un autre document HTML c-à-d celui généré par Doggyblog. Cette iframe est donc une vision de Doggyblog dans un cadre dont l’aspect est réglé par doggyblog.css et doggyblog.js. Le conteneur de l’iframe se présente d’abord sous forme d’un bouton dans le coin supérieur droit et quand on clique dessus il s’ouvre en s’animant par un morphing. Un overlay sombre apparaît également en-dessous permettant de se focaliser sur le blog ouvert. Aucun élément HTML ne doit être ajouté à la page d’accueil du site hôte car tous les éléments sont créés par javascript dans doggyblog.js, y compris l’iframe. L’attribut src de l’iframe pointe vers Doggyblog/Public c’est pourquoi il faut installer le dossier Doggyblog à la racine du site. La librairie externe classie est utilisée pour l’ouverture et la fermeture aisée de l’iframe par ajout et retrait de la classe open sur le conteneur de l’iframe1. La classe noscrollbar, activée également par classie, permet de supprimer la barre de scroll quand Doggyblog est ouvert puis de la remettre à sa fermeture afin d’éviter d’avoir deux barres de scroll et de bloquer la navigation dans la page hôte. La fenêtre de l’application Doggyblog peut-être fermée en cliquant sur la croix dans le coin supérieur droit, en cliquant à l’extérieur de cette fenêtre ou encore en appuyant sur la touche esc du clavier2.
1. Utilisation de la librairie classie dans doggyblog.js // Show/hide the Doggyblog container var doggycontainer = document. getElementById(‘doggycontainer’), doggyclose = document.getElementById(‘doggyclose’), doggyoverlay = document.getElementById(‘doggyoverlay’), hosthtml = document.getElementsByTagName(‘html’)[0]; isOpen = false; toggleContainer = function() { if(isOpen) { classie.remove(doggycontainer, ‘open’); // Puts back the scrollbar of the host page setTimeout(function() { classie.remove(hosthtml, ‘noscrollbar’); }, 200); } else { classie.add(doggycontainer, ‘open’); // Removes the scrollbar of the host page setTimeout(function() { classie.add(hosthtml, ‘noscrollbar’); }, 500); } isOpen = !isOpen; }; 2. Fermeture de la fenêtre de l’application par la touche esc du clavier addEvent(document, ‘keydown’, function(e) { var keyCode = e.keyCode || e.which; if( keyCode === 27 && isOpen) { toggleContainer(); } });
Le responsive design Le design de l’application tient compte de la variabilité de la taille d’écran chez les utilisateurs et est donc prévu pour que Doggyblog soit lisible sur plusieurs formats. 35
Le référencement Le référencement naturel du contenu de l’application est assuré par un hyperlien pointant vers Doggyblog/Public, placé entre les balises d’ouverture et de fermeture de l’iframe. Cet hyperlien est invisible pour les visiteurs, par contre il est emprunté par les robots d’indexation.
Live View Voir sur http://www.01artdesign.be/doggyblog/ L’application est accessible via le bouton Demo dans le coin supérieur droit. Voici les infos de connexion pour se connecter en tant qu’administrateur: login: admin password: admin Pour se connecter en tant qu’utilisateur, je vous invite à créer un compte d’essai (attention, le site est servi en http et non pas en https). Vous pouvez également tester l’application en l’installant vous-même, elle est disponible au téléchargement via le bouton correspondant sur la landing page ou directement sur Github.
36
Conclusions Sur le projet Le résultat de ce travail de fin d’études est un produit complet et fonctionnel, depuis la landing page qui donne des informations aux visiteurs sur l’application et son fonctionnement jusqu’à l’application elle-même, le tout dans un design attractif. Le cahier des charges a été globalement respecté et donc les objectifs sont atteints. La programmation orienté objet en PHP a été mise en œuvre selon les standards et conventions actuels. L’application basée sur un système de gestion de contenu et d’aspect est présentée comme micro-CMS car ses fonctionnalités et ses possibilités de personnalisation sont minimales. Bien sûr, puisque son intérêt réside principalement dans ses capacités à s’adapter à son site hôte et à s’y fondre, elle doit être amenée à évoluer et à acquérir plus de paramètres de personnalisation. C’est là que le bénéfice de la POO se fait sentir, en effet l’ajout de «briques» supplémentaires est pensé dès la conception et son évolution s’en trouve ainsi facilitée. Un module de notification qui fonctionnerait sur base d’emails et/ou de fils RSS, un module de conversation, etc. sont quelques exemples des ajouts possibles. Le panneau d’administration pourra également s’étoffer, par exemple, par un choix de couleur, de fonte, de l’aspect du bouton d’accès à l’application, etc.
Sur le plan personnel Ce travail m’a permis d’approfondir la POO, une matière vue au cours qui avait éveillé ma curiosité. Afin de mener ce projet à bien, il fallait un lien entre la théorie assimilée au cours et la pratique du développement d’une application fonctionnelle en POO. Ce lien je l’ai trouvé dans un tutoriel d’openclassrooms.com. A travers le cours de POO, le tutoriel et l’expérience gagnée en développant cette application, j’ai pu augmenter mes compétences au-delà de ce que je pouvais espérer. Le développement n’a pas été de tout repos, je suis parfois resté bloqué sur certains bugs, mais la structure organisée d’une application en POO et la lecture de forums m’ont à chaque fois aidé à surmonter les difficultés. Je clôture donc ce TFE avec une énorme satisfaction du travail accompli et des connaissances acquises.
37
38
4 Les annexes
La charte graphique Les couleurs
1. Comparaison des gamuts CMJN et RVB
La palette de couleurs choisie est une palette de tonalités vertes. Le vert symbolise la croissance et l’application Doggyblog est justement destinée à évoluer, à gagner en fonctionnalités et en possibilités de personnalisation, le vert est donc parfait pour représenter cela. Il symbolise en même temps la stabilité et inspire ainsi la confiance. Le vert est la couleur pour laquelle la différence entre le mode colorimétrique CMJN et RVB se marque le plus1 et donc j’ai choisi deux palettes différentes pour le RVB et le CMJN car les couleurs vertes vives à l’écran seraient apparues comme complètement délavées et peu différenciées sur papier si j’avais conservé les couleurs RVB pour l’impression (voir ci-dessous). Un blanc cassé pour les textes complète également la palette RVB. Palette RVB (couleurs non représentatives de la réalité sur écran):
R: 29 V: 128 B: 13 #1d800d
R: 29 V: 229 B: 55 #1de537
R: 120 V: 255 B: 84 #78ff54
R: 178 V: 250 B: 111 #b2fa6f
R: 255 V: 252 B: 184 #fffcb8
Palette CMJN:
C: 100 M: 0 J: 100 N: 0
C: 70 M: 0 J: 100 N: 0
C: 40 M: 0 J: 100 N: 0
Le style graphique Le design a été conçu dans un style minimaliste et vintage bien ancré dans son époque. L’identité visuelle est complétée par des lignes obliques que l’on retrouve un peu partout et qui ajoutent du dynamisme, de la cohérence et de l’originalité à l’ensemble. 40
Le Cahier des charges // PROJET WEB DE FIN D’ÉTUDES
DÉVELOPPEMENT D’UN MICRO-CMS École Industrielle et Commerciale de la ville de Namur Section Web Developer
Auteur Olivier Wenders
Promoteur Matthieu Thémans
1. Projet 1.a. Objectifs Il s’agira de développer un micro-CMS, possédant la fonctionnalité d’un blog. Il se présentera sous la forme d’une librairie open source à incorporer dans un site web existant et il permettra d’ajouter de l’interactivité à un site qui n’a pas été développé dès le départ avec un blog. Une fois la librairie installée, un bouton sera affiché pour accéder au blog. Il sera géré par l’administrateur du site et celui-ci pourra incorporer les articles et choisir son aspect visuel parmi des thèmes. La librairie pourra être téléchargée sur une page web dédiée à celle-ci.
1.b. Cible Ce micro-CMS ciblera le gestionnaire de site web souhaitant ajouter rapidement un blog à son site, celui-ci n’ayant pas été développé dès le départ avec un tel outil. Etant donné la conception sous forme de CMS, le gestionnaire pourra facilement implémenter et administrer le blog même s’il ne possède que très peu de compétences techniques.
1.c. Rentabilité Le projet étant open source, il ne vise pas à une rentabilité immédiate mais si la fréquentation est suffisante, il sera envisagé d’y ajouter une bannière publicitaire afin de récolter des revenus provenant de la publicité. Grâce à l’augmentation de la visibilité du concepteur sur le web il pourrait générer éventuellement par la suite des revenus indirects via la commande de nouveaux projets.
1.d. Moyens financiers Ils seront réduits au minimum. Il y aura au moins l’achat d’un nom de domaine et l’hébergement du site web sur un serveur mutualisé.
1.e. Moyens humains La conception et la maintenance du micro-CMS et du site web de présentation seront réalisées par une seule personne.
Page 1
41
1.f. Contenu Le site web dédié inclura le contenu suivant: • présentation du micro-CMS et lien de téléchargement de la librairie, • manuel d’installation et d’utilisation de la librairie, • présentation brève du concepteur et info de contact. La librairie téléchargée contiendra les fichiers css, javascript, la base de données et les autres fichiers nécessaires au bon fonctionnement du micro-CMS.
2. Mise en oeuvre 2.a. Nom de domaine Le nom de domaine sera défini après le choix du nom du micro-CMS qui n’est pour l’instant pas encore connu.
2.b. Hébergeur Le choix de l’hébergeur se fera en fonction du prix de l’hébergement mutualisé et des services offerts. OVH, PlanetHoster ou HostGator sont trois possibilités qui semblent intéressantes.
2.c. Système de gestion de contenu et framework L’utilisation d’un CMS existant, Wordpress par exemple, sera envisagée si la tache est facilitée pour développer ce projet. Bootstrap servira de framework CSS.
2.d. Editeur de site web L’IDE PHPStorm sera utilisé comme éditeur de texte avancé pour écrire le code.
3. Conception 3.a. Structure Le site web de présentation sera constitué des pages suivantes: • Page d’accueil: elle présentera le micro-CMS et proposera un lien de téléchargement. • Page de documentation: elle contiendra le manuel de l’utilisateur: comment installer et comment utiliser la librairie. • Page about: elle présentera brièvement le concepteur du micro-CMSet fournira des informations de contact pour un support technique.
3.b. Charte graphique Les couleurs du site de présentation seront choisies dans des tonalités de vert. Le style développé sera à la fois vintage et minimaliste. Les thèmes du micro-CMS varieront du style contemporain au style rétro, avec plusieurs variantes de couleur. Ceux-ci devront couvrir une large gamme afin de s’harmoniser au mieux avec n’importe quel site.
Page 2
42
4. Réalisation 4.a. Conception des pages Le code sera écrit dans PHPStorm sur une base Bootstrap pour les pages du site web de présentation et éventuellement à partir d’un CMS existant tel que Wordpress pour le microCMS.
4.b. Intégration de la charte graphique La charte graphique sera mise en place dans les éléments graphiques conçus dans Photoshop et Illustrator mais aussi dans les styles CSS. La réalisation du projet tiendra compte des standards du web en terme de présentation en séparant le contenu de la mise en forme.
4.c. Développement dynamique Le développement d’un blog implique qu’il faudra intégrer une base de données et une gestion dynamique de celle-ci.
4.d. Référencement Le site de présentation du micro-CMS devra être «SEO-friendly» afin d’être naturellement bien référencé.
5. Suivi 5.a. Mise en ligne Une fois le projet développé il sera transféré sur le serveur de l’hébergeur via FTP.
5.b. Maintenance Une maintenance visant à optimiser le micro-CMS afin de le rendre compatible avec un maximum de plate-formes et de navigateurs sera envisagé par la suite. Celle-ci permettra également de faire évoluer la librairie en parallèle avec les technologies du web. Un support technique pourrait également être fourni.
5.c. Annonce Les réseaux sociaux et les forums serviront de relai pour annoncer la disponibilité du microCMS.
6. Constituants • • • •
Structure et présentation: HTML5, CSS3 Aspect dynamique: javascript, jQuery, AJAX, PHP Base de données: MySQL Eléments graphiques: JPEG, PNG
Page 3
43
Glossaire API
CMJN
CMS
CSS
DB
Cyan, Magenta, Jaune et Noir - les quatre couleurs primaires du processus en quadrichromie
Content Management System - logiciel destiné à la conception et à la mise à jour dynamique de sites web
Cascading Style Sheet - langage qui permet de gérer la présentation d’une page web
Database - base de données pour le stockage des informations
Getter
HTML5
HTTP
Hypertext Markup Langage version 5 - dernière révision majeure d’HTML, langage de balisage
Hypertext Transfer Protocol - protocole de communication clientserveur
Parser
Pattern Factory
PHP
ou analyseur syntaxique - logiciel qui va permettre d’analyser la syntaxe d’un code
C’est un design pattern (masque de conception) qui charge une classe usine d’instancier des classes
Langage de programmation libre principalement utilisé pour produire des pages web dynamiques
Application Programming Interface - ensemble normalisé de classes, de méthodes ou de fonctions qui sert
de façade par laquelle un logiciel offre des services à d’autres logiciels
DAO
DOM
Data Access Object - objet contenant la connexion à la base de données
Document Object Model - API permettant d’accéder ou de mettre à jour le contenu, la
IE
MVC
Internet Explorer - navigateur web développé par Microsoft
Modèle-Vue-Contrôleur - modèle informatique visant à séparer le modèle (modèle de données) de la vue
PHPDoc
POO
Polyfill
Standard dérivé de javadoc, formalisé pour commenter le code PHP
Programmation Orientée briques logicielles appelées objets Objet - paradigme de programmation qui consiste en la définition et l’interaction de
Ensemble de fonctions, le plus souvent écrites en javascript ou en flash, permettant de simuler sur un navigateur ancien
des fonctionnalités qui ne sont pas nativement disponibles
Setter
SQL
UML
XML
ou mutateur - méthode d’une classe qui permet de modifier un attribut de cette classe
Structured Query Language - langage informatique normalisé servant à exploiter
Unified modeling Language - langage de modélisation graphique à base de pictogrammes
Extensible Markup Language - langage informatique de balisage générique qui dérive du SGML
44
structure ou le style de ou accesseur - méthode documents XML et HTML d’une classe qui permet de récupérer un attribut de cette classe
(présentation, interface utilisateur) et du contrôleur (logique de contrôle et gestion des événements)
des bases de données relationnelles
RVB Rouge, Vert et Bleu - les trois couleurs primaires des supports multimédia
Webographie http://fr.openclassrooms.com/informatique/cours/programmez-en-oriente-objet-en-php http://openclassrooms.com/courses/dynamisez-vos-sites-web-avec-javascript http://php.net/manual/fr/index.php http://forum.webrankinfo.com/iframe-referencement-t1637.html http://tympanus.net/codrops/2014/11/04/simple-morphing-search/ https://developer.mozilla.org/fr/ http://www.w3schools.com/ http://fr.wikipedia.org/wiki/WikipĂŠdia:Accueil_principal
45
46
Depuis l’arrivée des réseaux sociaux, les blogs semblent être passés de mode mais en proposer un sur son site web reste encore un formidable moyen de communiquer, d’attirer du public et par conséquent d’améliorer le référencement de son site web. Doggyblog est un moteur de blog or il existe déjà de nombreuses plateformes pour déployer rapidement un blog (Skyblog, Blogger, Skynet, etc.) mais l’idée de ce projet est de permettre à tout webmaster qui souhaite gérer un blog, non pas d’en ouvrir un énième sur skyblog.com, blogs.skynet.be, etc. mais d’en installer un directement et facilement sur son propre site web existant, sur son propre domaine. Ainsi, un site web auparavant peu dynamique, uniquement contemplatif, peut soudainement retrouver un regain d’intérêt aux yeux du visiteur par cet apport d’interactivité et générer ainsi le trafic tant attendu par le webmaster. L’objectif de ce travail a été de développer un produit complet, de l’application Doggyblog elle-même à la landing page. Cette dernière est une page web dédiée sur laquelle le visiteur peut trouver toute l’info nécessaire à l’installation et à l’utilisation de Doggyblog. L’application se présente sous forme d’une bibliothèque à installer à la racine du site web. Une connexion à une base de données est nécessaire. Elle a été développée en PHP et la méthode choisie a été la programmation orientée objet afin de faciliter sa maintenance et son évolution mais aussi dans un but pédagogique car cette méthode vue au cours a suscité chez moi de la curiosité, je voulais en savoir davantage. Le produit issu de ce projet est fonctionnel mais sa vie, je l’espère, ne fait que commencer car l’application est appelée à acquérir plus de fonctionnalités, notamment la personnalisation avancée de son aspect afin d’améliorer son intégration dans le site hôte.