Microblogging avec le MkFramework

Créez votre propre twitter-like en moins d'une heure

Michael Bertocchi

Dans le précédent tutoriel, vous avez découvert ce framework. Je vous propose ici d'apprécier sa simplicité et sa productivité.

Commentez

- Titre : Microblogging avec le MkFramework

- Auteur : Michael Bertocchi

- Parution : 10 août 2013

- Mise à jour : 10 août 2013

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Michael Bertocchi. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Voir également :

- Introduction au framework

Sommaire

- I. Présentation du tutoriel

- II. Le modèle de données

- III. L'application de base

- III-A. Création de l'application

- III-B. Présentation de l'arborescence

- III-C. Génération de la couche modèle

- IV. Les modules

- IV-A. Introduction

- IV-B. Module public et d'authentification

- IV-B-1. Introduction

- IV-B-2. Création d'un module d'authentification

- IV-B-3. Test du module d'authentification

- IV-C. Module profil

- IV-C-1. Introduction

- IV-C-2. Un peu de ménage

- IV-C-3. Visualisation de son profil

- IV-C-4. L'édition de son profil

- IV-C-5. Comment forcer ce module lorsque l'on se connecte

- IV-D. Module posts

- IV-D-1. Introduction

- IV-D-2. Amélioration du modèle model_posts

- IV-D-3. Visualisation de son fil (avec post des abonnements + les siens)

- IV-D-4. Poster

- IV-E. Module hashtags

- IV-E-1. Récupération du hashtag par son nom

- IV-E-2. Modification du modèle hashtags pour récupérer les hashtags d'un post

- IV-E-3. Ajout d'une méthode pour prendre en compte les hashtags

- IV-E-4. À l'enregistrement d'un post, enregistrer les hashtags

- IV-E-5. Visualisation des hastags les plus populaires

- IV-E-6. Affichage des hashtags et surtout des posts associés

- IV-F. Module mentions

- IV-F-1. Modification du modèle mention pour récupérer les mentions d'un post

- IV-F-2. Ajout d'une méthode pour prendre en compte les mentions

- IV-F-3. À l'enregistrement du post, enregistrement de la/des mention(s)

- IV-F-4. Modification de notre modele posts pour lister les posts mentionnant notre membre

- IV-F-5. Ajout d'une méthode au module posts pour retourner une vue des posts par mention

- IV-F-6. Visualisation de nos mentions

- IV-G. Module abonnements

- IV-G-1. Liste des membres

- IV-G-2. Ajout d'un autre membre pour pouvoir tester nos abonnements

- IV-G-3. Permettre de « suivre » un autre membre

- IV-G-4. Modification de notre modèle model_followers pour ajouter un abonnement

- IV-G-5. Modification de notre modèle members pour y ajouter le calcul des abonnés et celui des abonnements

- IV-G-6. Affichage du nombre d'abonnements et d'abonnés sur notre profil

- V. Création de notre menu

- VI. Modification du module profil pour afficher celui des autres membres

- VI-A. Ajout d'une action pour afficher le profil d'un autre membre

- VI-B. Modification du modèle posts pour récupérer les derniers posts d'un membre

- VI-C. Modification du module posts pour afficher les derniers posts d'un membre

- VI-D. Création d'une vue pour afficher un profil

- VII. Cosmétique

- VIII. Conclusion

- IX. Remerciements

I - Présentation du tutoriel

Dans ce tutoriel, nous allons apprendre à utiliser le framework pour créer une application de microblogging (comme le célèbre oiseau).

II - Le modèle de données

Voici les tables à créer pour notre tutoriel.

Ci-dessous le script SQL de création de la base de données « microblogging » :

CREATE TABLE members (
id int(11) NOT NULL AUTO_INCREMENT,
login varchar(50) NOT NULL,
pass varchar(50) NOT NULL,
picture varchar(50) NOT NULL,
description varchar(200) NOT NULL,
nb_posts int(11) NOT NULL,
nb_followers int(11) NOT NULL,
nb_subscriptions int(11) DEFAULT NULL,
shortname varchar(50) DEFAULT NULL,
PRIMARY KEY (id)
);

CREATE TABLE posts(
id int(11) NOT NULL auto_increment,
text varchar(300) NOT NULL,
member_id int(11) NOT NULL,
dateCreation datetime NOT NULL,
post_id_parent int(11) NULL,
nb_retweets int(11) NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE hashtags (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(20) NOT NULL,
nb_posts int(11) NOT NULL,
dateLastUse datetime NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE hashtags_posts (
id int(11) NOT NULL AUTO_INCREMENT,
post_id int(11) NOT NULL,
hashtag_id int(11) NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE mentions (
id int(11) NOT NULL AUTO_INCREMENT,
member_id int(11) NOT NULL,
post_id int(11) NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE followers (
id int(11) NOT NULL AUTO_INCREMENT,
member_id int(11) NOT NULL,
follower_member_id int(11) NOT NULL,
PRIMARY KEY (id)
);

III - L'application de base

III-A - Création de l'application

Rendez-vous sur l'adresse du builder : http://localhost/mkframework

Renseignez « microblogging » puis validez avec le bouton « Créer ».

III-B - Présentation de l'arborescence

Éditez le fichier conf/connexion.ini.php.

Renseignez votre profil de connexion :

microblogging.dsn="mysql:dbname=microblogging;host=localhost"
microblogging.sgbd=pdo_mysql
microblogging.hostname=localhost
microblogging.database=microblogging
microblogging.username=root
microblogging.password=root

Connexion à une base de données « microblogging » via « pdo_mysql », avec l'utilisateur « root » et le mot de passe « root » également (à modifier en fonction des paramètres de votre base de données).

III-C - Génération de la couche modèle

Cliquez sur « générer la couche modèle ».

Sélectionnez votre connexion à la base de données (« microblogging ») :

Cochez les tables créées précédemment.

Validez avec le bouton « générer ».

Un fichier par table va être créé dans le répertoire « model » de votre projet.

IV - Les modules

IV-A - Introduction

Pour cette application, nous allons créer plusieurs modules afin de gérer les différentes fonctionnalités.

Un module, si l'on compare aux autres frameworks (Zend framework, Symfony…), est composé d'un contrôleur et de ses vues.

IV-B - Module public et d'authentification

IV-B-1 - Introduction

Cette application va nécessiter une authentification, c'est-à-dire un formulaire avec un nom d'utilisateur/mot de passe pour identifier le membre. Pour cela nous allons créer un module qui contiendra trois pages (aussi appelées « actions ») (login/logout/inscription).

IV-B-2 - Création d'un module d'authentification

Le « builder » contient un formulaire pour créer des modules et des modules d'authentification, celui-ci créé :

- un sous-répertoire dans le répertoire « module » de votre projet ;

- un fichier contrôleur main.php ;

- un sous-répertoire « view » contenant les fichiers de vues nécessaires.

Cliquez sur « Créer un module d'authentification avec inscription ».

Vous allez voir s'afficher une page d'erreur vous indiquant que vous ne possédez pas de classe modèle éligible pour créer ce module, et vous demandant d'ajouter deux méthodes à la classe modèle contenant vos comptes de connexion.

Une méthode getListAccount() qui retournera l'ensemble des comptes dans un tableau indexé ainsi qu'une méthode hashPassword() qui hachera votre mot de passe.

Ajoutez ces deux méthodes à votre classe modèle model_members.php puis retournez sur ce menu : il n'y a plus d'erreur.

Saisissez dans le champ « module » : « auth », sélectionnez vers quel module diriger une fois authentifié, et indiquez via les menus déroulants les champs de login et de mot de passe.

Cliquez ensuite sur « Générer ».

Le builder va créer :

- un répertoire « module/auth » ;

- un fichier module/auth/main.php (contenant le contrôleur du module) ;

- un sous répertoire « module/auth/view » ;

- et deux fichiers de vues login.php et inscription.php dans le répertoire « module/auth/view ».

Vous allez également lire que vous devez modifier le fichier de paramétrage pour activer l'authentification sur l'ensemble du site en passant la variable « enabled » à 1 dans la section [auth] du fichier conf/site.ini.php.

IV-B-3 - Test du module d'authentification

Profitez-en pour tester ce départ : cliquez sur le lien inscription, saisissez un nom d'utilisateur et un mot de passe et enregistrez-le.
Par exemple « login » comme nom d'utilisateur et « pass » comme mot de passe.
Vous pouvez désormais vous authentifier avec votre nouveau compte.

IV-C - Module profil

IV-C-1 - Introduction

Actuellement, lorsque l'on se connecte, on arrive sur une page vide, on va donc créer un module de profil qui nous permettra de voir et modifier notre profil (avatar, informations…). Pour cela, nous allons utiliser le builder.
Cliquez sur « Editer le projet » en face de votre projet.
Cliquez ensuite sur « Créer un module CRUD », puis sur la classe modèle « model_members.php ».

Vous avez un formulaire de création de CRUD, modifiez « members » (nom de la table) par « profil ».
Décochez les cases « Formulaire d'ajout », « Formulaire de suppression » (nous n'en avons pas besoin).
Décochez ensuite les champs « nb_posts », « nb_followers », « nb_subscriptions », « login » et « pass ».
Pour le champ « picture », dans le menu déroulant, sélectionnez « upload ».
Puis cliquez sur « Créer ».

Le builder va générer un répertoire « profil » dans le répertoire « module », un fichier contrôleur main.php ainsi qu'un sous-répertoire « view » contenant quatre fichiers de vue : list.php, show.php, edit.php et delete.php.

IV-C-2 - Un peu de ménage

Nous allons supprimer l'action et la vue inutile de ce module, éditez le fichier module/profil/main.php et supprimez la méthode _list().

idem pour le fichier module/profil/view/list.php.

IV-C-3 - Visualisation de son profil

Nous souhaitons par défaut que ce module affiche notre profil en lecture, pour cela on va modifier notre module CRUD.
Éditez le fichier module/profil/main.php.

On remplace notre action _index() (page par défaut) :

public function _index(){
//on considere que la page par defaut est la page de listage
$this->_list();
}

Par :

public function _index(){
//on considere que la page par defaut est la page d'affichage de profil
$this->_show();
}

On édite ensuite la méthode _show(), où l'on force l'id du membre affiché par celui de notre membre connecté.

L'appel à _root::getAuth()>getAccount() nous retourne l'objet du membre connecté.

public function _show(){
//recuperation de l'id de notre membre connecte
$member_id=_root::getAuth()->getAccount()->id;
$oMembers=model_members::getInstance()->findById( $member_id );
$oView=new _view('profil::show');
$oView->oMembers=$oMembers;
$this->oLayout>add('main',$oView);
}

On modifie ensuite la vue pour y ajouter un bouton d'édition du profil.

Fichier module/profil/view/show.php.

<table class="tb_show">
<tr>
<th>shortname</th>
<td><?php echo $this->oMembers->shortname ?></td>
</tr>
<tr>
<th>picture</th>
<td><?php echo $this->oMembers->picture ?></td>
</tr>
<tr>
<th>description</th>
<td><?php echo $this->oMembers->description ?></td>
</tr>
</table>
<p style="textalign:right"> <a href="<?php echo $this>getLink('profil::edit')?>">Editer</a></p>

IV-C-4 - L'édition de son profil

Nous allons ensuite, comme pour la méthode d'affichage, forcer le membre édité par celui connecté. Éditez la méthode _edit() toujours dans module/profil/main.php.

Remplacez :

public function _edit(){
$tMessage=$this->save();
$oMembers=model_members::getInstance()->findById( _root::getParam('id') );
$oView=new _view('profil::edit');
$oView->oMembers=$oMembers;
$oView->tId=model_members::getInstance()->getIdTab();
$oPluginXsrf=new plugin_xsrf();
$oView->token=$oPluginXsrf->getToken();
$oView->tMessage=$tMessage;
$this->oLayout->add('main',$oView);
}

Par :

public function _edit(){
$tMessage=$this->save();
//recuperation de l'id de notre membre connecte
$member_id=_root::getAuth()->getAccount()->id;
$oMembers=model_members::getInstance()->findById( $member_id );
$oView=new _view('profil::edit');
$oView->oMembers=$oMembers;
$oView->tId=model_members::getInstance()->getIdTab();
$oPluginXsrf=new plugin_xsrf();
$oView->token=$oPluginXsrf->getToken();
$oView->tMessage=$tMessage;
$this->oLayout->add('main',$oView);
}

Il faut également forcer la redirection post-modification vers la page d'affichage du profil.

Et forcer à toujours modifier (supprimer l'ajout de membre).

Remplacez dans la méthode save() :

$iId=_root::getParam('id',null);
if($iId==null){
$oMembers=new row_members;
}else{
$oMembers=model_members::getInstance()->findById( _root::getParam('id',null) );
}

Par :

//recuperation de l'id de notre membre connecte
$member_id=_root::getAuth()->getAccount()->id;
$oMembers=model_members::getInstance()->findById( $member_id );

Et :

//une fois enregistre on redirige (vers la page liste)
_root::redirect('profil::list');

Par :

//une fois enregistre on redirige (vers la page d'affichage)
_root::redirect('profil::show');

Ici vous pouvez éditer votre profil, choisir une image, définir un shortname et une description et valider.
Mais lorsque le profil s'affiche, on voit le chemin de l'image et non l'image, modifions un peu la vue.

Éditez le fichier module/profil/view/show.php.

<div style="float:right;border:2px solid gray"><img style="width:100px" src="<?php echo $this->oMembers->picture ?>"/></div>
<table class="tb_show">
<tr>
<th>shortname</th>
<td><?php echo $this->oMembers->shortname ?></td>
</tr>
<tr>
<th>description</th>
<td><?php echo $this->oMembers->description ?></td>
</tr>
</table>
<div style="clear:both"></div>
<p style="textalign:right"> <a href="<?php echo $this->getLink('profil::edit')?>">Editer</a></p>

IV-C-5 - Comment forcer ce module lorsque l'on se connecte

Éditez le fichier module/default/index.php et remplacez dans la méthode _index() :

public function _index(){
$oView=new _view('default::index');
$this->oLayout->add('main',$oView);
}

Par :

public function _index(){
_root::redirect('profil::index');
}

Nous allons rediriger par défaut sur la page de profil.

IV-D - Module posts

IV-D-1 - Introduction

Passons aux posts : la partie principale de l'application, pour faire simple nous allons d'abord créer un module CRUD que nous modifierons pour répondre à notre besoin.

Rendez-vous sur le builder et cliquez sur « Créer un module CRUD », sélectionnez le modèle model_post.php .

Déselectionnez les cases « Formulaire de modification », « Formulaire de suppression » et « Page affichage de détail ».

Désélectionnez tous les champs sauf « ext ».

IV-D-2 - Amélioration du modèle model_posts

Ajoutez la méthode suivante dans le fichier model_post.php.

Dans la classe model_posts :

public function findAllOwnerAndFollowed($members_id){
return $this->findMany('SELECT posts.* FROM '.$this->sTable.' LEFT OUTER JOIN followers ON
followers.member_id=posts.member_id WHERE posts.member_id=? OR followers.follower_member_id=? ORDER BY posts.id DESC',$members_id,$members_id);
}

Dans la classe row_posts on va ajouter une méthode pour récupérer son auteur.

Ajoutez la méthode suivante dans la classe row_posts :

public function findMember(){
return model_members::getInstance()->findById($this->member_id);
}

Modifiez également la méthode d'enregistrement pour ajouter la date à l'insertion en base. Dans la méthode save() de la classe row_posts juste avant l'appel à la méthode save() de la classe parente.

Ajoutez :

$this->dateCreation=date('Y-m-d H:i:s');

Ce qui donne :

public function save(){
if(!$this->isValid()){
return false;
}
$this->dateCreation=date('Y-m-d H:i:s');
parent::save();
return true;
}

IV-D-3 - Visualisation de son fil (avec post des abonnements + les siens)

Il faut utiliser la méthode précédemment ajoutée pour afficher uniquement les posts de l'utilisateur.

Modifiez le fichier main.php du module posts pour remplacer dans la méthode _list() :

$tPosts=model_posts::getInstance()->findAll();

Par :

$tPosts=model_posts::getInstance()->findAllOwnerAndFollowed(_root::getAuth()->getAccount()->id);

Modifions la vue liste.

Éditez le fichier module/posts/view/list.php :

<h1>Tweets</h1>
<table class="tb_posts">
<?php if($this->tPosts):?>
<?php foreach($this->tPosts as $oPosts):?>
<?php
$oMember=$oPosts->findMember();
$oDate=new plugin_datetime($oPosts->dateCreation);
$sDate=$oDate->toString('d/m/Y &\a\g\r\a\v\e; H\hi');
$sText=$oPosts->text;
?>
<tr <?php echo plugin_tpl::alternate(array('','class="alt"'))?>>
<td style="width:22px"><img style="width:20px" src="<?php echo $oMember->picture?>"/></td>
<td>
<div style="float:right;color:gray"><?php echo $sDate?></div>
<strong><?php echo $oMember->shortname?></strong> <a style="color:#444"
href="<?php echo _root::getLink('profil::showother',array('name'=>$oMember->login))?>">@<?php echo $oMember->login?></a><br/><?php echo $sText ?></td>
</tr>
<?php endforeach;?>
<?php else:?>
Aucun posts
<?php endif;?>
</table>
<p><a href="<?php echo $this->getLink('posts::new') ?>">Nouveau message</a></p>

IV-D-4 - Poster

Si vous utilisez le lien « new », vous allez ajouter un enregistrement dans la table posts, mais sans indiquer son auteur, on va forcer l'auteur à l'enregistrement.

Modifions la méthode save() dans le fichier main.php du module « posts ».

Vous allez ajouter une ligne pour forcer le member_id avant l'enregistrement :

//on force l'id du membre
$oPosts->member_id=_root::getAuth()->getAccount()->id;

Elle ressemblera à ceci :

public function save(){
if(!_root::getRequest()->isPost() ){ //si ce n'est pas une requete POST on ne soumet pas
return null;
}
$oPluginXsrf=new plugin_xsrf();
if(!$oPluginXsrf->checkToken( _root::getParam('token') ) ){ //on verifie que le token est valide
return array('token'=>$oPluginXsrf->getMessage() );
}
$iId=_root::getParam('id',null);
if($iId==null){
$oPosts=new row_posts;
}else{
$oPosts=model_posts::getInstance()->findById( _root::getParam('id',null) );
}
$tId=model_posts::getInstance()->getIdTab();
$tColumn=model_posts::getInstance()->getListColumn();
foreach($tColumn as $sColumn){
if(isset($_FILES[$sColumn]) and $_FILES[$sColumn]['size'] > 0){
$sNewFileName=_root::getConfigVar('path.upload').$sColumn.'_'.date('Ymdhis');
$oPluginUpload=new plugin_upload($_FILES[$sColumn]);
$oPluginUpload->saveAs($sNewFileName);
$oPosts->$sColumn=$oPluginUpload->getPath();
continue;
}else  if( _root::getParam($sColumn,null) === null ){
continue;
}else if( in_array($sColumn,$tId)){
continue;
}
$oPosts->$sColumn=_root::getParam($sColumn,null) ;
}
//on force l'id du membre
$oPosts->member_id=_root::getAuth()->getAccount()->id;
if($oPosts->save()){
//une fois enregistre on redirige (vers la page liste)
_root::redirect('posts::list');
}else{
return $oPosts->getListError();
}
}

IV-E - Module hashtags

IV-E-1 - Récupération du hashtag par son nom

Ajoutez une méthode dans model_hashtags.php.

public function findByName($uId){
return $this->findOne('SELECT * FROM '.$this>sTable.' WHERE name=?',$uId );
}

IV-E-2 - Modification du modèle hashtags pour récupérer les hashtags d'un post

Éditez le fichier model_hashtags.php.

Et ajoutons une méthode pour récupérer les hashtags :

public function findAllHashtags($sText){
$sText.=' ';
preg_match_all('/#([0-9a-zA-Z]*) /',$sText,$tHashtag);
if(isset($tHashtag[1])){
return $tHashtag[1];
}
return null;
}

IV-E-3 - Ajout d'une méthode pour prendre en compte les hashtags

Toujours le même fichier modèle model_hashtags.

Nous allons ajouter une méthode qui recevra un tableau de hashtags et créera les liens SQL entre ceux-ci et notre post :

public function addForPost($sHashtag,$post_id){
//recherche du hashtag par son nom
$oHashtag=$this->findByName($sHashtag);
if($oHashtag){
//si on trouve le hashtag en base, on incremente sa notoriete
$oHashtag->nb_posts=$oHashtag->nb_posts+1;
}else{
//si on ne le trouve pas on créé ce hashtag
$oHashtag=new row_hashtags;
$oHashtag->name=$sHashtag;
$oHashtag->nb_posts=1;
}
//dans les deux cas on indique au hashtag sa dernière utilisation
$oHashtag->dateLastUse=date('Ymd H:i:s');
$oHashtag->save();
//enfin on sauvegarde le lien post/hashtag
$oHashtagPost=new row_hashtags_posts;
$oHashtagPost->post_id=$post_id;
$oHashtagPost->hashtag_id=$oHashtag->id;
$oHashtagPost->save();
}

IV-E-4 - À l'enregistrement d'un post, enregistrer les hashtags

On va prendre en compte ici les méthodes ajoutées précédemment.

Dans la classe model_post, profitons de la méthode save() pour prendre en compte les hashtags.

Éditez la classe row_posts :

public function save(){
if(!$this->isValid()){
return false;
}
$this>dateCreation=date('Ymd H:i:s');
parent::save();
//on en profite pour enregistrer les tags et les mentions
//les hashtags
$tHashtags=model_hashtags::getInstance()->findAllHashtags($this->text);
if($tHashtags){
foreach($tHashtags as $sHashtag){
$sHashtag=trim($sHashtag);
model_hashtags::getInstance()->addForPost($sHashtag,$this->id);
}
}
return true;
}

IV-E-5 - Visualisation des hastags les plus populaires

Créons un module pour voir les hashtags.
Passez par le builder pour créer un module « hashtags » avec deux actions « list » et « show ».
Éditez ensuite le fichier module/hashtags/main.php pour afficher le menu.

Modifiez la méthode before() :

public function before(){
$this->oLayout=new _layout('template1');
$this->oLayout->addModule('menu','menu::list');
}

Modifiez également la méthode _list() pour récupérer les hashtags et les ajouter à la vue :

public function _list(){
//recuperons la liste des hashtags
$tHastags=model_hashtags::getInstance()>findAll();
$oView=new _view('hashtags::list');
//assignons le tableau d'hashtag a la vue
$oView->tHastags=$tHastags;
$this->oLayout->add('main',$oView);
}

Modifiez également la vue list module/hashtags/view/list.php.

Pour lister les hashtags :

<h1>Hashtags</h1>
<table class="tb_posts">
<?php if($this->tHastags):?>
<?php foreach($this->tHastags as $oHastags):?>
<tr <?php echo plugin_tpl::alternate(array('','class="alt"'))?>>
<td><strong><a href="<?php echo _root::getLink('hashtags::show',array('name'=>$oHastags->name))?>">#<?php echo $oHastags->name ?></a></strong>
<br /><strong><?php echo $oHastags->nb_posts ?></strong> TWEETS <br/>
<i>dernier tweet le <?php echo $oHastags->dateLastUse ?></i>
</td>
</tr>
<?php endforeach;?>
<?php endif;?>
</table>

IV-E-6 - Affichage des hashtags et surtout des posts associés

Occupons-nous de la page pour afficher les hashtags et ses posts associés.
Au départ on ajoute une méthode dans le modèle posts pour lister les posts associés à un hashtag.

Ouvrez le fichier model_posts.php :

public function findByHashtag($uId){
return $this->findMany('SELECT posts.* FROM '.$this->sTable.',hashtags_posts WHERE posts.id=hashtags_posts.post_id AND hashtag_id=?',$uId );
}

Ensuite ajoutez une méthode pour retourner les posts liés à un hashtag.

Dans le module module/posts/main.php :

public function listByHashtag($hashtag_id){
$tPosts=model_posts::getInstance()->findByHashtag($hashtag_id);

$oView=new _view('posts::list');
$oView->tPosts=$tPosts;

return $oView;
}

IV-F - Module mentions

IV-F-1 - Modification du modèle mention pour récupérer les mentions d'un post

Comme pour les hashtags, nous ajoutons une méthode dans la couche modèle pour trouver les mentions contenues dans un post.

Dans le modèle model/model_mentions.php :

public function findAllMention($sText){
preg_match_all('/@([0-9a-zA-Z]*) /',$sText,$tMention);
if(isset($tMention[1])){
return $tMention[1];
}
return null;
}

IV-F-2 - Ajout d'une méthode pour prendre en compte les mentions

Également comme pour les hashtags, on ajoute une méthode pour enregistrer ces mentions :

public function addForPost($sLogin,$post_id){
//recuperons le membre indique par la mention par son nom
$oMember=model_members::getInstance()->findByLogin($sLogin);

if($oMember){
//si on trouve le membre, on cree une mention
$oMention=new row_mentions;
//le lien avec le membre de la mention
$oMention->member_id=$oMember->id;
//le lien vers le post a l'origine de la mention
$oMention->post_id=$post_id;
$oMention->save();
}
}

IV-F-3 - À l'enregistrement du post, enregistrement de la/des mention(s)

Toujours comme pour les hashtags, on va profiter de l'enregistrement du post pour lier celui-ci à ses mentions.
Éditez model/model_posts.php.
Et dans la méthode save().
En ajoutant la partie suivante :

//les mentions
$tMentions=model_mentions::getInstance()->findAllMention($this->text);
if($tMentions){
foreach($tMentions as $sLogin){
model_mentions::getInstance()->addForPost($sLogin,$this->id);
}
}

Ce qui donnera :

public function save(){
if(!$this->isValid()){
return false;
}

$this->dateCreation=date('Y-m-d H:i:s');

parent::save();

//on incremente notre nombre de posts
$oMember=$this->findMember();
$oMember->nb_posts=$oMember->nb_posts+1;
$oMember->save();

//on en profite pour enregistrer les tags et les mentions

//les tags
$tHashtags=model_hashtags::getInstance()->findAllHashtags($this->text);
if($tHashtags){
foreach($tHashtags as $sHashtag){
$sHashtag=trim($sHashtag);
model_hashtags::getInstance()->addForPost($sHashtag,$this->id);
}
}

//les mentions
$tMentions=model_mentions::getInstance()->findAllMention($this->text);
if($tMentions){
foreach($tMentions as $sLogin){
model_mentions::getInstance()->addForPost($sLogin,$this->id);
}
}

return true;
}

IV-F-4 - Modification de notre modele posts pour lister les posts mentionnant notre membre

Ajoutons une méthode pour lister les posts par rapport à une mention.

Éditez le fichier modèle model_posts.php et ajoutez la méthode :

public function findAllByMention($uId){
return $this->findMany('SELECT posts.* FROM '.$this->sTable.',mentions WHERE posts.id=mentions.post_id AND mentions.member_id=?',$uId );
}

IV-F-5 - Ajout d'une méthode au module posts pour retourner une vue des posts par mention

Ajoutons une méthode au module qui retournera une vue des posts par mention :

public function listByMention($member_id){
$tPosts=model_posts::getInstance()->findAllByMention($member_id);

$oView=new _view('posts::list');
$oView->tPosts=$tPosts;

return $oView;
}

IV-F-6 - Visualisation de nos mentions

Pour cela créons un module « mentions »: utiliser le générateur web pour créer un module avec une action « list » seulement.

Ajoutons la prise en compte du menu.

Dans la méthode before() du module mentions module/mentions/main.php :

$this->oLayout->addModule('menu','menu::list');

Ensuite modifions la vue _list() pour afficher les mentions :

public function _list(){
//recuperons le compte utilisateur de l'utilisateur connecte
$oMember=_root::getAuth()->getAccount();

$oView=new _view('mentions::list');
//assignons a la vue notre objet membre
$oView->oMember=$oMember;

$this->oLayout->add('main',$oView);

//recuperons la liste des posts par mention (a nous meme)
$oModulePost=new module_posts;
$oViewPost=$oModulePost->listByMention($oMember->id);

$this->oLayout->add('main',$oViewPost);
}

Éditons la vue module/mentions/view/list.php :

<h1>Mentions </h1>
<p style="padding-left:20px"><strong><?php echo $this->oMember->shortname?></strong> @<?php echo $this->oMember->login?></p>

IV-G - Module abonnements

IV-G-1 - Liste des membres

Dans le module profil, on va modifier la méthode _show().

En ajoutant la récupération de tous les membres :

//listons les membres du site
$tMember=model_members::getInstance()->findAll();
$oView->tMember=$tMember;

Ce qui donnera :

public function _show(){
//recuperation de l'id de notre membre connecte
$member_id=_root::getAuth()->getAccount()->id;
$oMembers=model_members::getInstance()->findById( $member_id );

$oView=new _view('profil::show');
$oView->oMembers=$oMembers;

//listons les membres du site
$tMember=model_members::getInstance()->findAll();
$oView->tMember=$tMember;

$this->oLayout->add('main',$oView);
}

Modifions également la vue pour lister ces membres avec un lien pour les suivre.

Éditez la vue module/profil/view/show.php :

<table class="tb_subscriptions">
<?php foreach($this->tMember as $oMember):?>
<?php if($oMember->id==$this->oMembers->id) continue ?>
<tr>
<td style="width:22px"><img style="width:20px" src="<?php echo $oMember->picture?>" /></td>
<td><strong><?php echo $oMember->shortname?></strong>
@<?php echo $oMember->login?>
</td>
<td style="width:100px;text-align:right">
<a class="btn" href="<?php echo _root::getLink('profil::follow',array('member_id'=>$oMember->id))?>">Suivre</a>
</td>
</tr>
<?php endforeach;?>
</table>
<div style="clear:both;height:10px"></div>

Ici on boucle sur les membres et on affiche un lien « suivre » uniquement si le compte n'est pas le nôtre.

IV-G-2 - Ajout d'un autre membre pour pouvoir tester nos abonnements

Via la page d'inscription, créez un second compte, ici dupot/pass.

Ainsi sur la page précédente, vous pouvez voir l'utilisateur ajouté, et on peut cliquer sur le bouton « suivre », dont on va écrire le code.

IV-G-3 - Permettre de « suivre » un autre membre

Nous allons ajouter cette méthode _follow dans notre module profil qui va ajouter un abonnement entre notre membre et un autre.

Éditez le fichier module/profil/main.php.

Et ajoutez deux méthodes pour suivre et arrêter de suivre un membre :

public function _follow(){
//on s'abonne au membre
model_followers::getInstance()->followForMember(_root::getParam('member_id'),_root::getAuth()->getAccount()->id);
//on recalcule son nombre d'abonnes
model_members::getInstance()->calculFollower(_root::getParam('member_id'));
//on calcul notre nombre d'abonnement
model_members::getInstance()->calculSubscription(_root::getAuth()->getAccount()->id);

_root::redirect('profil::index');
}
public function _unfollow(){
//on se desabonne du membre
model_followers::getInstance()->unfollowForMember(_root::getParam('member_id'),_root::getAuth()->getAccount()->id);
//on recalcule son nombre d'abonnes
model_members::getInstance()->calculFollower(_root::getParam('member_id'));
//on recalcule notre nombre d'abonnements
model_members::getInstance()->calculSubscription(_root::getAuth()->getAccount()->id);

_root::redirect('profil::show');
}

IV-G-4 - Modification de notre modèle model_followers pour ajouter un abonnement

Éditez le fichier modèle model_followers.php, et ajoutez-y une méthode « followForMember » :

public function followForMember($member_id,$follower_member_id){
$this->unfollowForMember($member_id,$follower_member_id);

$oFollow=new row_followers;
$oFollow->member_id=_root::getParam('member_id');
$oFollow->follower_member_id=_root::getAuth()->getAccount()->id;
$oFollow->save();
}

public function unfollowForMember($member_id,$follower_member_id){
$this->execute('DELETE FROM followers WHERE member_id=? AND follower_member_id=?',$member_id,$follower_member_id);
}

IV-G-5 - Modification de notre modèle members pour y ajouter le calcul des abonnés et celui des abonnements

Modifiez le fichier modèle model_members.php et ajoutez-y deux méthodes « calculFollower » et « calculSubscription ».

public function calculFollower($member_id){
$oRow=$this->findOne('SELECT COUNT(*) as total FROM followers WHERE member_id=?',$member_id);
$nb=$oRow->total;

$this->execute('UPDATE members SET nb_followers=? WHERE id=?',$nb,$member_id);
}
public function calculSubscription($member_id){
$oRow=$this->findOne('SELECT COUNT(*) as total FROM followers WHERE follower_member_id=?',$member_id);
$nb=$oRow->total;

$this->execute('UPDATE members SET nb_subscriptions=? WHERE id=?',$nb,$member_id);
}

IV-G-6 - Affichage du nombre d'abonnements et d'abonnés sur notre profil

Modifions notre vue profil/show.

Et ajoutez dans module/profil/view/show.php :

<table class="profil">
<tr>
<td>
<strong><?php echo $this->oMembers->nb_posts?></strong>
<br />POSTS</td>
<td>
<strong><?php echo $this->oMembers->nb_followers?></strong>
<br />ABONNES</td>
<td style="width:450px">
<strong><?php echo $this->oMembers->nb_subscriptions?></strong>
<br />ABBONNEMENTS</td>
</tr>
</table>

V - Création de notre menu

Le builder permet facilement de générer un menu à partir de vos modules existants.

Cliquez sur « Créer un module menu ».

Vous voyez listé vos modules ainsi que les méthodes « actions » disponibles.

Cochez la méthode _list() pour le module « posts » et indiquez dans la colonne libellée « Votre fil ».

Faites de même pour la méthode _list() du module « hashtags » avec comme libellé « Hashtag », idem pour la méthode _list() du module « mentions » avec « Mention » et enfin « Votre profil » pour le couple _index / profil.

Cliquez sur « Générer le menu ».

Le builder va créer un module menu, il ne vous reste plus qu'à l'inclure dans vos modules « posts », « hashtags »,  « mentions » et « profil ».

Éditez les fichiers main.php de ces quatre modules et ajoutez dans les méthodes before() :

$this->oLayout->addModule('menu','menu::list');

VI - Modification du module profil pour afficher celui des autres membres

VI-A - Ajout d'une action pour afficher le profil d'un autre membre

Modifiez le module profil module/profil/main.php et ajoutez-y :

public function _showother(){
$oMembers=model_members::getInstance()->findByLogin(_root::getParam('name'));

if($oMembers){
$oView=new _view('profil::showother');
$oView->oMembers=$oMembers;

$oModulePosts=new module_posts;
$oViewPosts=$oModulePosts->listLastByMember($oMembers->id);

$oView->oViewPosts=$oViewPosts;
}else{
$oView=new _view('profil::showothernotfound');
}

$this->oLayout->add('main',$oView);

}

VI-B - Modification du modèle posts pour récupérer les derniers posts d'un membre

Éditez le fichier modèle model_posts et ajoutez la méthode :

public function findAllLastByMember($member_id){
return $this->findMany('SELECT * FROM '.$this->sTable.' WHERE member_id=? ORDER BY id DESC',$member_id);
}

VI-C - Modification du module posts pour afficher les derniers posts d'un membre

Ajoutons une action à notre module posts pour qu'il retourne une vue contenant les posts d'un membre.

Éditez le fichier module/posts/main.php :

public function listLastByMember($member_id){
$tPosts=model_posts::getInstance()->findAllLastByMember($member_id);

$oView=new _view('posts::list2');
$oView->tPosts=$tPosts;

return $oView;
}

Créez une nouvelle vue pour lister les posts d'un autre membre.

Créez un fichier module/posts/view/list2.php :

<table class="tb_posts" style="width:620px">

<?php if($this->tPosts):?>
<?php foreach($this->tPosts as $oPosts):?>
<?php
$oMember=$oPosts->findMember();
$oDate=new plugin_datetime($oPosts->dateCreation);
$sDate=$oDate->toString('d/m/Y &\a\g\r\a\v\e; H\hi');
$tHashtags=model_hashtags::getInstance()->findAllHashtags($oPosts->text);
$sText=$oPosts->text;
foreach($tHashtags as $sHashtags){
$sLink='<a href="'._root::getLink('hashtags::show',array('name'=>$sHashtags)).'">#'.$sHashtags.'</a>';
$sText=preg_replace('/#'.$sHashtags.'/',$sLink,$sText);
}

$tMentions=model_mentions::getInstance()->findAllMention($oPosts->text);
foreach($tMentions as $sMention){
$sLink='<a href="'._root::getLink('profil::showother',array('name'=>$sMention)).'">@'.$sMention.'</a>';
$sText=preg_replace('/@'.$sMention.'/',$sLink,$sText);
}
?>

<tr <?php echo plugin_tpl::alternate(array('','class="alt"'))?>>

<td style="width:22px"><img style="width:20px" src="<?php echo $oMember->picture?>"/></td>
<td>
<div style="float:right;color:gray"><?php echo $sDate?></div>
<strong><?php echo $oMember->shortname?></strong> <span style="color:#444">@<?php echo $oMember->login?></span><br/><?php echo $sText ?></td>

</tr>
<?php endforeach;?>
<?php else:?>
Aucun posts
<?php endif;?>
</table>

VI-D - Création d'une vue pour afficher un profil

Modifiez le module module/profil/view/showother.php.

<div class="popup">
<h1 class="close"><a href="<?php echo _root::getLink('posts::list')?>">Fermer</a></h1>
<div style="margin:auto;width:100px;background:#fff;margin-top:20px"><img style="width:100px" src="<?php echo $this->oMembers->picture ?>"/></div>
<p style="text-align:center;font-weight:bold;font-size:14px"><?php echo $this->oMembers->shortname ?></p>
<p style="text-align:center;font-size:14px">@<?php echo $this->oMembers->login ?></p>
<p style="text-align:center"><?php echo $this->oMembers->description ?></p>
<table class="profil" style="width:640px">
<tr>
<td>
<strong><?php echo $this->oMembers->nb_posts?></strong>
<br />POSTS</td>
<td>
<strong><?php echo $this->oMembers->nb_followers?></strong>
<br />ABONNES</td>
<td style="width:450px">
<strong><?php echo $this->oMembers->nb_subscriptions?></strong>
<br />ABBONNEMENTS</td>
</tr>
</table>

<?php echo $this->oViewPosts->show()?>
</div>

Créez également une vue dans le cas où l'on ne trouve pas le membre.

Créez un fichier de vue module/posts/view/showothernotfound.php.

<div class="popup">
<h1 class="close"><a href="<?php echo _root::getLink('posts::list')?>">Fermer</a></h1>
<div style="margin:auto;width:100px;background:#fff;margin-top:20px;background:#444;text-align:center;color:white;font-size:14px;font-weight:bold">?</div>
<p style="text-align:center;font-size:14px">@<?php echo _root::getParam('name')?> introuvable :(</p>
</div>

VII - Cosmétique

Juste en modifiant la feuille de style, on obtient un résultat plus sympathique.

Le CSS :

*{
font-family:arial;
font-size:12px;
}
img{
border:none;
}
a{
color:darkblue;
}
body{
background:#bbb;
margin:0px;
}
.main{
margin:0px auto;
width:700px;
background:#fff;
border:2px solid #444;
box-shadow: 1px 1px 12px #555;
}
.main .menu{
background:#444;
height:30px;
}
.main .menu ul{
margin:0px;
padding:0px;
height:30px;
}
.main .menu a{
text-decoration:none;
color:white;
font-weight:bold;
}
.main .menu li{
padding:0px 20px;
margin:0px;
list-style:none;
display:inline-block;
height:23px;
padding-top:7px;
}
.main .menu .selectionne{
background:#222;
}
.main .content form .textarea, .main .content form .input{
width:100%;
}
.tb_list{
border-collapse: collapse;
}
.tb_list th,.tb_list td{
border:1px solid gray;
}
.tb_list th{
text-align:left;
}
.tb_list td{
vertical-align:top;
}
.tb_list .alt td{
background:#eee;
}
.profil{
border-collapse:collapse;
width:680px;
margin:10px;
}
.profil td{
border:1px solid #777;
padding:8px;
color:#777;
}
.profil td strong{
color:#000;
}
.tb_posts{
margin:4px;
margin-top:20px;
width:680px;
border-collapse:collapse;
}
.tb_posts td{
padding:6px;
padding-bottom:20px;
border-top:1px solid #ccc;
}
.tb_posts strong{
font-size:14px;
}
.tb_posts a{
text-decoration:none;
}
.tb_subscriptions{
margin:4px;
margin-top:20px;
width:680px;
border-collapse:collapse;
}
.tb_subscriptions td{
padding:6px;
padding-bottom:20px;
border-top:1px solid #ccc;
}
.profil{
border-collapse:collapse;
width:678px;
margin:10px;
margin-top:5px;
}
.profil td{
border:1px solid #777;
padding:8px;
color:#777;
}
.profil td strong{
color:#000;
}
.btn, .btnDisabled{
border:2px solid #196160;
background:#2a9190 ;
display:inline-block;
text-decoration:none;
padding:2px 6px;
color:white;
font-weight:bold;
text-align:center;
}
.btnDisabled{
border-color:#444;
background:#2a9190 ;
}
.popup{
margin:20px;
width:650px;
border:3px solid gray
}
.popup .close{
background:gray;
margin-top:0px;
text-align:right;
}
.popup .close a{
color:white;
text-decoration:none;
}
h1{
font-size:14px;
padding-left:8px;
color:#444;
}
.tbCenter{
margin:auto;
}

VIII - Conclusion

Dans ce tutoriel vous avez pu voir qu'il était assez simple de créer un site aussi complexe qu'une application de microblogging.

Le MkFramework, depuis sa publication en 2009 sur le site de Developpez, n'a cessé d'évoluer et de s'améliorer. Je prends en compte toutes les remarques et axes d'améliorations que l'on me soumet via le forum de Developpez.

Une barre de debug a fait son apparition, ainsi que des améliorations du générateur web…

De plus, vous pouvez maintenanttélécharger des applications (sous licence LGPLv3) développées en utilisant le framework.

J'espère vous avoir convaincus de donner une chance à ce framework qui se veut simple à utiliser.

IX - Remerciements

Je souhaiterais remercier zoom61 pour son soutien, et f-leb pour sa correction orthographique.