I. Introduction▲
Vous développez en PHP de zéro pour chaque projet et vous ne cessez d'entendre parler de frameworks à tout va, mais lorsque vous commencez à regarder les principaux frameworks du marché, vous êtes effrayé/découragé par la courbe d'apprentissage. Ces frameworks qui vous promettent monts et merveilles vont d'abord nécessiter un temps d'apprentissage plus ou moins important…
J'ai commencé l'aventure des frameworks PHP il y a 5-6 ans avec symfony (version 1 bêta à l'époque) puis Zend framework 1.5,1.7, 1.8… et 1.11, ceci tout en continuant de développer en parallèle mon propre framework.
II. Pourquoi un framework de plus ?▲
En utilisant divers frameworks, j'ai appris au fil des années à apprécier leurs avantages (cadre de travail, bibliothèques facilitant certaines tâches, générateur…) et pester contre leurs inconvénients (courbe d'apprentissage, temps à rechercher dans la documentation, performances de l'ORMObject-Relational Mapping (Mapping objet-relationnel), verbosité importante, perte de compatibilité plus ou moins importante entre les versions…).
J'ai recommencé de zéro ce framework quatre fois : je développais une version, je l'utilisais, me rendais compte des limites, erreurs, comparais avec les autres frameworks puis quelques mois après, je mettais tout à la poubelle en prenant en compte les erreurs à ne pas commettre et j'avais une meilleure idée de ce que je voulais faire. Dans cette quatrième version, je pense bien avoir appris des précédentes, et j'espère que mes choix pour ce framework plairont aux développeurs qui l'utiliseront.
J'ai voulu créer un framework le plus simple possible (une structure de framework très légère utilisant peu de fichiers), facile à prendre en main, en évitant d'avoir des choses induites qu'il faudrait apprendre ainsi qu'en mettant à disposition un générateur permettant de démarrer une application avec des exemples facilitant la prise en main.
Ce framework utilise le « pattern » MVC (Modèle-Vue-Contrôleur) : il y a une séparation entre la couche modèle (accès à la base de données), la vue (mise en forme de la page ou d'une partie de celle-ci) et le contrôleur (sorte de chef d'orchestre requêtant la couche modèle, enrichissant la vue et ordonnant d'afficher l'ensemble ainsi construit).
III. Citons quelques avantages▲
III-A. Un générateur web▲
Pas besoin d'utiliser la ligne de commande, de connaître les divers paramètres à utiliser, il suffit de sélectionner et de cliquer. Le builder, un générateur web, permet en effet en 2-3 clics de créer un nouveau site, générer sa couche modèle ses CRUDCreate Read Update Delete: pages permettant de lister, éditer et supprimer les élements d'une base de donnée…
Et ceci en prenant en compte la sécurité (XSS, CSRF, null byte…).
III-B. Extrêmement paramétrable▲
Plusieurs fichiers de configuration permettent de configurer bon nombre d'éléments du framework : de la politique de cache aux répertoires, en passant par le choix du plugin de routing (URL rewriting) à utiliser.
III-C. Pas de magie, tout est écrit▲
Dans beaucoup de frameworks, la partie Vue du MVC est induite : on crée un contrôleur, on définit une méthode « action », et lorsqu'on appelle ce couple contrôleur/action, le framework déduit un nom de fichier de vue à utiliser avec les problèmes que cela peut entraîner (ex. : problème de casse ou de substitution de caractères).
Ici, pas de magie, ni de déduction : on choisit de créer un « layout » (gabarit de site), on décide d'utiliser une vue particulière, on lui assigne les variables nécessaires et enfin on indique d'afficher l'ensemble.
À partir du moment où l'on décide que l'on a la possibilité de faire ce que l'on veut et que l'on manipule ces objets : on peut déléguer certaines parties, se passer des objets de vues d'une méthode à l'autre. La gestion du cache suit le même principe : on choisit de mettre en cache un objet de vue rempli, pour le récupérer via le gestionnaire de cache plus tard.
III-D. DRY au possible (Don't Repeat Yourself)▲
Ce framework est conçu pour utiliser des modules et non des contrôleurs : chaque module est constitué de la manière suivante (par exemple pour un module article) :
? un répertoire « article » ;
? à sa racine un fichier « main.php » contenant la classe du module intitulée module_article
(c'est le contrôleur du MVC) ;
? un sous répertoire « view » contenant les différentes vues à utiliser.
Ce mode d'organisation permet simplement de récupérer un module d'un projet à l'autre en copiant juste un répertoire contenant à la fois le contrôleur et les vues dont il a besoin.
On peut ensuite très facilement instancier ce module et récupérer ses vues remplies.
IV. Fini le blabla, passons à la pratique▲
IV-A. D'abord on télécharge le framework▲
Après s'être connecté au site du framework, on télécharge le ZIP à l'adresse http://mkdevs.com/telecharger.html que l'on va désarchiver dans le répertoire web de notre serveur apache (LAMP, WAMP, MAMP…).
IV-B. Ensuite faisons connaissance avec le builder▲
IV-B-1. Créons le projet▲
Comme je le disais plus tôt, pas de ligne de commande ici. Il vous suffit d'ouvrir votre navigateur à l'adresse où vous avez installé le framework.
Exemple : http://localhost/mkframework_v4_XX_XX_rXX.
Si vous êtes sur Linux ou Mac, vous pouvez voir le message en rouge : il faut commencer par changer les droits du répertoire « date/genere » (un chmod 777). C'est dans ce répertoire que le builder générera votre site.
Une fois ce changement de droits fait, vous n'avez plus qu'à taper le nom de votre site par exemple « blog » puis cliquer sur le bouton « Créer ».
Par défault, le builder crée un nouveau site avec quelques exemples, ici nous décocherons cette case. Ensuite, il redirige vers l'onglet de listage des projets présents dans le répertoire « data/genere ».
IV-B-2. Administrons ce projet▲
En cliquant sur le bouton « Éditer le projet » on peut voir les actions possibles via le builder.
IV-B-3. Générons la couche modèle▲
Avant de cliquer sur le premier bouton de création de la couche model, il faut éditer le fichier de configuration des connexions « conf/connexion.ini.php » (dans le répertoire du site nouvellement créé dans data/genere : data/genere/votresite/conf/connexion.ini.php).
Dans notre exemple pour une connexion :
[db]
mysql.dsn="mysql:dbname=blog;host=localhost"
mysql.sgbd=pdo_mysql
mysql.hostname=localhost
mysql.database=blog
mysql.username=root
mysql.password=pass
Exemple avec plusieurs connexions (une pour les membres « membreDatabase », une autre pour les commandes) :
[db]
membre.dsn="mysql:dbname=membreDatabase;host=localhost"
membre.sgbd=pdo_mysql
membre.hostname=localhost
membre.database=membreDatabase
membre.username=root
membre.password=pass
commande.dsn="mysql:dbname=commandeDatabase;host=localhost"
commande.sgbd=pdo_mysql
commande.hostname=localhost
commande.database=commandeDatabase
commande.username=root
commande.password=pass
Après avoir paramétré votre ou vos connexions, vous pouvez cliquer sur « Créer couche model »
Vous voyez listés ci-dessous les différents profils présents dans votre fichier précédemment édité.
Créons trois tables dans notre serveur MySQL pour l'exemple :
CREATE
TABLE
`article`
(
`id`
int
(
11
)
NOT
NULL
auto_increment
,
`titre`
varchar
(
50
)
NOT
NULL
,
`resume`
text
NOT
NULL
,
`auteur_id`
int
(
11
)
NOT
NULL
,
PRIMARY
KEY
(
`id`
)
)
;
CREATE
TABLE
`auteur`
(
`id`
int
(
11
)
NOT
NULL
auto_increment
,
`nom`
varchar
(
30
)
NOT
NULL
,
`prenom`
varchar
(
30
)
NOT
NULL
,
PRIMARY
KEY
(
`id`
)
)
;
CREATE
TABLE
`comment`
(
`id`
int
(
11
)
NOT
NULL
auto_increment
,
`text`
text
NOT
NULL
,
`article_id`
int
(
11
)
NOT
NULL
,
PRIMARY
KEY
(
`id`
)
)
;
Il vous suffit de cliquer sur le ou les profils renseignés.
Vous voyez se lister les tables accessibles via ce profil de connexion.
Pour chaque table, il faut préciser la clé primaire (le premier champ de table est sélectionné par défaut) qui sera utilisée pour créer la classe modèle de la table.
Vous avez également la possibilité de générer une méthode getSelect() retournant un tableau des éléments indexés. Par exemple, pour la table auteur, on peut demander à générer une méthode getSelect() avec l'id en guise de clé et le nom en guise de valeur.
En générant cette méthode, on y gagnera par la suite lors de la génération du CRUD.
IV-B-4. Génération du CRUD (Create Read Update Delete)▲
Note : un message en rouge indiquant que le module existe déjà peut apparaître afin d'éviter d'écraser un module existant, il suffit d'indiquer dans ce cas-là un autre nom.
Cette partie liste les classes modèles précédemment générées et vous permet, en sélectionnant une des classes, de générer le CRUD pour la table de la classe indiquée.
Au moment de générer le CRUD d'une table, vous avez un menu déroulant vous permettant de sélectionner le type de champ de formulaire à utiliser (text, textarea ou date).
Mais vous pouvez également voir « select » s'appuyant sur la méthode getSelect de la classe auteur (précédemment créée lors de la génération de la couche modèle).
En sélectionnant pour auteur_id cette ligne getSelect(), on va :
1. Permettre d'avoir un menu déroulant à la place d'un champ texte ;
2. Dans la page de listage, le tableau sera utilisé pour remplacer la clé étrangère par sa valeur.
Ici on voit le builder créer un module (dans le répertoire module) du nom de la classe pour le CRUD :
Avec :
- un répertoire ;
- un fichier main.php (le contrôleur) ;
- un répertoire view ;
- ses fichiers vues de listage, affichage, édition et suppression.
Comme vous le voyez sur la capture du dessus, on peut voir un lien permettant d'accéder à la page nouvellement générée.
Pour la page CRUD article, le lien sera du type « index.php?:nav=article::list ».
Ce qui donnera pour la page liste :
Pour la page new :
Ici on a préalablement renseigné des auteurs.
V. Petite explication du fonctionnement du framework▲
V-A. Commençons par l'URL▲
Les URL du framework ressemblent à ceci: « index.php?:nav=module::action ».
La variable GET « :nav » contient le couple « module :: action » à appeler.
Note : vous pouvez changer ce nom de variable « :nav » dans le fichier conf/site.ini.php.
[navigation]
scriptname=index.php
var=:nav
Il vous suffit de remplacer var=:nav par autre chose. Par exemple, en remplaçant par « var=url », « index.php?:nav=article::list » deviendrait: « index.php?url=article::list »
Le framework utilise par défaut « :nav » pour ne pas contraindre le développeur dans le choix de ses variables GET. De plus, vous pourrez lors du passage à l'URL rewriting avoir des URL finales plus claires par exemple articles.html pour la liste, ou article_1.html l'affichage d'un article en particulier.
V-B. Scénario d'appel du framework▲
Lors de l'appel d'une page/action de module, le framework va appeler (si elles existent)
Pour le module :
1. Sa méthode before() ;
2. Sa méthode before_action() ;
3. Sa méthode _action() ;
4. Sa méthode after_action() ;
5. Sa méthode after().
Ce qui donnera pour l'exemple article::list :
1. Sa méthode before() ;
2. Sa méthode before_list() ;
3. Sa méthode _list() ;
4. Sa méthode after_list() ;
5. Sa méthode after().
Exemple d'un module article :
<?php
class
module_article extends
abstract_module{
public
function
before(){
//toujours appelée en début de module
}
public
function
before_list(){
//appelée seulement lors de l'appel de l'action « list »
}
public
function
_list(){
//la méthode de l'action/page « list »
}
public
function
after_list(){
//appelée seulement lors de l'appel de l'action « list »
}
public
function
after(){
//toujours appelée en fin de module
}
}
Ces méthodes peuvent être utilisées pour les rôles décrits dans ce qui suit.
La méthode before() étant appelée toujours au départ du module, on peut ici définir ce qui est commun au module : créer un layout, définir la politique d'authentification de la page…
La méthode before_action() étant elle appelée juste avant la page demandée, on peut l'utiliser pour vérifier le cache (s'il est encore valable), décider d'afficher la page en cache et de s'arrêter là.
La méthode after() étant appelée toujours à la fin du module, on peut l'utiliser pour afficher la totalité de la page (layout + vues).
Voici un schéma résumant ces appels :
V-C. Les vues/layout▲
Ce framework respectant le pattern MVC utilise également des vues.
Quand vous êtes dans la partie module (ou contrôleur), vous pouvez créer une vue, indiquer un fichier à utiliser, lui assigner des valeurs et l'ajouter à votre layouttemplate principal du site.
Ceci grâce à la classe _view.
Par exemple, dans votre module article pour créer la vue utilisant le fichier view/list.php :
//création d'un layout, en utilisant le template site/layout/template1.php
$this
->
oLayout =
new _layout(&
#8216;template1');
//création d'une vue utilisant le fichier module/article/view/list.php
$oView
=
new _view(&
#8216;article::list');
$oView
->
variable =
'
valeur
'
;
//on ajoute cette vue à notre layout à l'emplacement « main »
$this
->
oLayout->
add(&
#8216;main',$oView);
//on affiche la page (layout + vue)
$this
->
oLayout->
show();
Le fichier de vue list (module/article/view/list.php) peut ressembler à cela :
Ma vue liste, <br />
ma valeur : <?php echo $this->variable ?>
Et le fichier de template du site (site/layout/template1.php) à cela :
<
html>
<
head>
<
title>
Mon site<
/title
>
<
/head
>
<
body>
<?php
echo $this
->
load('main'
) ?>
<
/body
>
<
/html
>
Un schéma illustrant le fonctionnement simple du couple vues/layout :
V-D. La couche modèle▲
Pour accéder aux données, on utilise la « couche modèle » : on a généré via le builder une classe par table dans notre base de données, il suffit d'utiliser ces classes pour récupérer, modifier et ajouter des données.
Voici un exemple de classe modèle :
<?php
class
model_article extends
abstract_model{
protected
$sClassRow
=
'row_article'
;
protected
$sTable
=
'article'
;
protected
$sConfig
=
'mysql'
;
protected
$tId
=
array
('id'
);
public
static
function
getInstance(){
return
self
::
_getInstance(__CLASS__
);
}
public
function
findById($uId
){
return
$this
->
findOne('SELECT * FROM '
.
$this
->
sTable.
' WHERE
id=?'
,
$uId
);
}
public
function
findAll(){
return
$this
->
findMany('SELECT * FROM '
.
$this
->
sTable);
}
}
class
row_article extends
abstract_row{
protected
$sClassModel
=
'model_article'
;
public
function
findListComment(){
return
model_comment::
getInstance()->
findByArticle($this
->
id);
}
/*exemple jointure
public function findAuteur(){
return model_auteur::getInstance()->findById($this->auteur_id);
}
*/
}
On utilise les singletons des classes commençant par « model_ » pour exécuter les requêtes, et elles nous retournent des instances de classe commençant par « row_ ». Il y a deux méthodes de requête de type « select » : celle retournant un tableau d'objets et celle retournant un objet unique : findMany et findOne.
Dans notre exemple, on voit que l'on définit pour chaque requête nécessaire une méthode prenant ou non des paramètres, et c'est dans cette méthode que nous exécutons la requête.
Pour récupérer tous les articles (un tableau d'objets row_article) :
$tArticle = model_article::getInstance()->findAll();
Pour récupérer un article en particulier (un objet row_article) :
$oArticle = model_article::getInstance()->findById( 1 );
V-E. Utilisation du modèle dans un module▲
Dans notre module article :
(&
#8230;)
public function _list(){
//la méthode de l'action/page « list »
//on utilise la couche modèle pour récupérer un tableau d'objets article
$tArticle
=
model_article::
getInstance()->
findAll();
//création d'une vue utilisant le fichier module/article/view/list.php
$oView
=
new _view(&
#8216;article::list');
//on assigne à notre vue ce tableau d'articles
$oView
->
tArticle =
$tArticle
;
//on ajoute cette vue à notre layout à l'emplacement « main »
$this
->
oLayout->
add(&
#8216;main',$oView);
}
(&
#8230;)
Et dans la vue :
<
/ul
>
<?php
foreach
($this
->
tArticle as
$oArticle
):
?>
<
li>
<?php
echo $oArticle
->
titre?>
<
/li
>
<?php
endforeach
;
?>
<
/ul
>
Ce qui donnerait finalement pour notre module_article :
<?php
class
module_article extends
abstract_module{
public
function
before(){
//toujours appelé en début de module
//création d'un layout, en utilisant le template site/layout/template1.php
$this
->
oLayout =
new
_layout(&
#8216;template1');
}
public
function
_list(){
//la méthode de l'action/page « list »
//on utilise la couche modèle pour récupérer un tableau d'objets article
$tArticle
=
model_article::
getInstance()->
findAll();
//création d'une vue utilisant le fichier module/article/view/list.php
$oView
=
new
_view(&
#8216;article::list');
//on assigne à notre vue ce tableau d'articles
$oView
->
tArticle =
$tArticle
;
//on ajoute cette vue à notre layout à l'emplacement « main »
$this
->
oLayout->
add(&
#8216;main',$oView);
}
public
function
after(){
//toujours appelé en fin de module
//on affiche la page (layout + vue)
$this
->
oLayout->
show();
}
}
VI. Conclusion▲
Ainsi se termine notre brève introduction au MkFramework, en espérant avoir éveillé votre curiosité :).
J'ai tenté de vous initier aux concepts principaux de ce framework.
Nous avons ainsi découvert le builder, qui a été créé pour simplifier la tâche des développeurs désireux de débuter de nouveaux projets.
Enfin, nous connaissons désormais la structure d'un projet MkF avec son architecture MVC ainsi que le fonctionnement du MkFramework.
Sur le site vous trouverez la documentation sous trois formes : des tutoriels, une FAQ ainsi qu'une documentation générée par Doxygen.
Le MkFramework est hébergé sur developpez.com à l'adresse suivante : https://projets.developpez.com/projects/mkframework.
Son site officiel (contenant la documentation complète) : http://mkdevs.com.