You must be registered for see images attach
Bonjour à tous , nous allons aujourd'hui apprendre à créer notre premier add-On CRUD (Create, Read, Update, Delete) sous le CMS XenForo.
Architecture
Insertion de données
Affichage de données
Modification de données
Suppression de données
Avant toute chose je vais vous demander d'activer le mode debug de votre forum XenForo (créez un environnement de développement, n'utilisez en aucun cas une production).Insertion de données
Affichage de données
Modification de données
Suppression de données
ACTIVER LE MODE DEBUG
- Ouvrez le fichier Config.php que vous trouverez dans le dossier Library de votre FTP.
- Ajoutez la ligne suivante : $config['debug'] = true; .
You must be registered for see images attach
Dans ce premier chapitre nous allons voir ensemble comment structurer son add-On mais également l'utilité des dossiers qui composeront l'architecture de notre add-On, dans ce tutoriel je vais prendre un exemple particulier : vous êtes lobbyman , vous possédez un forum et vous souhaitez créer un add-On qui permettra à vos membres de demander des lobbys.
- Un add-On XenForo se situera toujours dans le dossier library (mais pas que, nous verrons ceci par la suite), créons un dossier Lobbys qui sera le dossier parent de notre add-On.
- Dans ce dossier Lobbys nous allons créer un premier dossier Model qui permettra d'effectuer des requêtes de type : je veux récupérer telle demande de lobby, je veux récupérer toutes les demandes de lobby entre telle date et telle date etc.
- Créons un autre dossier nommé DataWriter, ce dossier nous permettra de créer, modifier et supprimer des demandes de lobby.
- Puis un dossier Route et un sous dossier Prefix, ces dossiers nous permettrons de déclarer notre routing c'est à dire les URLs à utiliser pour notre add-On (exemple:
Vous devez être inscrit pour voir les liens ! Inscrivez-vous ou connectez-vous ici.).
- Et enfin, terminons notre architecture avec un dossier ControllerPublic qui contiendra l'algorithme de notre add-On (si le membre soumet le formulaire alors j'enregistre....).
You must be registered for see images attach
(l'architecture finalisée)
ROUTING BASIQUE
- Rendez-vous sur votre panel d'administration à l'onglet Development puis Create Add-on.
Add-on ID : lobbys , c'est tout simplement un identifiant unique correspondant à notre add-on.
Title : Lobbys, explicitement c'est le titre de celui-ci.
Version String : 1.0.0.
Version ID : 1.
URL : (vous pouvez laisser ce champs vide, ça n'a aucune importance)
Installation Code & Uninstallation Code : (laissez également vide pour le moment).
Title : Lobbys, explicitement c'est le titre de celui-ci.
Version String : 1.0.0.
Version ID : 1.
URL : (vous pouvez laisser ce champs vide, ça n'a aucune importance)
Installation Code & Uninstallation Code : (laissez également vide pour le moment).
- Sauvegardez et créez un fichier Public.php dans le dossier Prefix (Route > Prefix) et insérez le code ci-dessous
PHP:
<?php
class Lobbys_Route_Prefix_Public implements XenForo_Route_Interface
{
/**
* Match a specific route for an already matched prefix.
*
* @see XenForo_Route_Interface::match()
*/
public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
{
//on précise que nos URLs (plus particulièrement nos actions) peuvent prendre un paramètre de base : l'identifiant du lobby
$action = $router->resolveActionWithIntegerParam($routePath, $request, 'lobby_id');
//on indique notre ControllerPublic que nous allons créer dans le chapitre suivant
return $router->getRouteMatch('Lobbys_ControllerPublic_Index', $action, 'lobby');
}
/**
* Method to build a link to the specified page/action with the provided
* data and params.
*
* @see XenForo_Route_BuilderInterface
*/
public function buildLink($originalPrefix, $outputPrefix, $action, $extension, $data, array &$extraParams)
{
return XenForo_Link::buildBasicLinkWithIntegerParam($outputPrefix, $action, $extension, $data, 'lobby_id');
}
}
?>
- Rendez-vous sur votre panel d'administration, à l'onglet Development puis Route Prefixes, pour créer un nouveau préfixe de routage.
- Remplissez & sauvegardez les champs indiqués avec les valeurs suivantes :
Route Prefix : lobby (c'est notre URL de base, exemple : members/{username}.{id}/follow , l'URL de base est members).
Route Type : Public .
Route Class : Lobbys_Route_Prefix_Public qui correspond évidemment à la class PHP de notre fichier que nous venons de créer.
Use class to build link : Only when data is provided
Add-on : Lobbys (techniquement nous lions ce préfixe de routage à notre add-On, ainsi à chaque exportation de notre add-On ce préfixe sera inscrit dans le fichier .XML de notre module)
Route Type : Public .
Route Class : Lobbys_Route_Prefix_Public qui correspond évidemment à la class PHP de notre fichier que nous venons de créer.
Use class to build link : Only when data is provided
Add-on : Lobbys (techniquement nous lions ce préfixe de routage à notre add-On, ainsi à chaque exportation de notre add-On ce préfixe sera inscrit dans le fichier .XML de notre module)
Nous nommons notre fichier "Public" car vous pouvez également créer un add-On qui concernera le panel d'administration dans ce cas vous devrez créer un fichier "Admin", vous devrez également modifier le préfixe de route en change le type de route de Public à Admin Control Panel.
You must be registered for see images attach
Passons au deuxième chapitre qui porte sur l'insertion de données nous allons interagir avec deux dossiers qui sont DataWriter et ControllerPublic.
INDEX.PHP (actionIndex())
PHP:
<?php
class Lobbys_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
/*
Action index, affichons "Hello World !"" tout bêtement.
*/
public function actionIndex()
{
echo "Hello World !";
}
}
Vous devez être inscrit pour voir les liens ! Inscrivez-vous ou connectez-vous ici.
!
You must be registered for see images attach
(nous avons bien notre Hello World ! d'affiché)
INDEX.PHP (actionIndex())
PHP:
//on instancie l'utilisateur courant.
$visitor = XenForo_Visitor::getInstance();
if(!$visitor['user_id'])
{
//si l'utilisateur n'est pas connecté on renvoit une erreur de permissions
return $this->responseNoPermission();
}
return $this->responseView('Lobbys_ViewPublic_Index', 'lobbys_index', $params = []);
Dans ce code nous utilisons une méthode nommée responseView qui provient de notre controller XenForo_ControllerPublic_Abstract , d'où l'importance d'hériter de cette classe, ainsi le premier paramètre correspond à une classe qui n'est pas importante pour la création de notre add-On, vous pouvez laisser ce paramètre vide, le deuxième paramètre est le nom de notre template et le dernier est un tableau de paramètres qui sera disponible dans notre template lobbys_index.
Rendez-vous sur notre ACP , à l'onglet Appearance puis Templates, créez une template avec le nom que nous lui avons donné précédemment et avec un contenu basique pour essayer le fonctionnement de notre méthode responseView.
You must be registered for see images attach
You must be registered for see images attach
LOBBYS_INDEX
HTML:
<xen:title>{xen:phrase lobbys_index_title}</xen:title>
<xen:description>{xen:phrase lobbys_index_description}</xen:description>
<xen:navigation>
<xen:breadcrumb source="$nodeBreadCrumbs" />
</xen:navigation>
<form action="{xen:link 'lobby/save'}" method="post" class="xenForm AutoValidator" data-redirect="yes">
<dl class="ctrlUnit">
<dt><label>{xen:phrase lobbys_index_games_label}:</label></dt>
<dd><select name="game">
<option value="BO3">{xen:phrase lobbys_index_games_bo3}</option>
<option value="BO2">{xen:phrase lobbys_index_games_bo2}</option>
<option value="MW3">{xen:phrase lobbys_index_games_mw3}</option>
</select>
</dd>
</dl>
<dl class="ctrlUnit">
<dt><label>{xen:phrase lobbys_index_plateforms_label}:</label></dt>
<dd><input type="radio" name="plateform" value="PS3"> {xen:phrase lobbys_index_plateforms_ps3}</br>
<input type="radio" name="plateform" value="XBOX360"> {xen:phrase lobbys_index_plateforms_xbox360}
</dd>
</dl>
<dl class="ctrlUnit">
<dt><label>{xen:phrase lobbys_index_games_id_label}:</label></dt>
<dd><input type="text" class="textCtrl" name="game_id">
</dd>
</dl>
<dl class="ctrlUnit submitUnit">
<dt></dt>
<dd><input type="submit" value="{xen:phrase lobbys_index_submit}" class="button primary" /></dd>
</dl>
<input type="hidden" name="_xfToken" value="{$visitor.csrf_token_page}" />
</form>
You must be registered for see images attach
(résultat après avoir modifié la template)
LOBBYS.PHP
PHP:
<?php
class Lobbys_DataWriter_Lobbys extends XenForo_DataWriter
{
protected function _getFields()
{
// nom de la table
return array('lobbys' => array(
// identifiant du lobby
'lobby_id' => array('type' => self::TYPE_UINT, 'autoIncrement' => true),
// identifiant du membre qui demande un lobby
'user_id' => array('type' => self::TYPE_UINT, 'required' => true),
// on définit des valeurs autorisées, ainsi impossible de glitcher le jeu sélectionné
'game' => array('type' => self::TYPE_STRING, 'required' => true, 'allowedValues' => array('BO3', 'MW3', 'BO2')),
'plateform' => array('type' => self::TYPE_STRING, 'required' => true, 'allowedValues' => array('PS3', 'XBOX360')),
// identifiant IG du membre
'identifiant' => array('type' => self::TYPE_STRING, 'required' => true),
// etat du lobby , par défaut il est en cours
'lobby_state' => array('type' => self::TYPE_STRING, 'required' => true, 'default' => 'cours', 'allowedValues' => array('cours', 'termine', 'refuse')),
// on note la date de demande sous forme de timestamp grâce à la classe XenForo_Application
'lobby_date' => array('type' => self::TYPE_UINT, 'default' => XenForo_Application::$time)
));
}
protected function _getExistingData($data)
{
if (!$id = $this->_getExistingPrimaryKey($data, 'lobby_id'))
{
return false;
}
return array('lobbys' => XenForo_Model::create('Lobbys_Model_Lobbys')->getLobbyById($id));
}
protected function _getUpdateCondition($table)
{
return 'lobby_id = ' . $this->_db->quote($this->getExisting('lobby_id'));
}
}
La prochaine étape consiste à créer un Installer, celui-ci va nous permettre d'ajouter ou supprimer des tables & colonnes à notre base de données à chaque installation ou désinstallation (et mise à jour) de notre add-on.
Créez un fichier Installer.php à la racine de votre dossier parent, il existe différente façon de créer un Installer je vous donne la plus simple.
INSTALLER.PHP
PHP:
<?php
class Lobbys_Installer
{
protected static $table = array(
'createQuery' => "CREATE TABLE IF NOT EXISTS `lobbys` (
`lobby_id` INT( 10 ) NOT NULL AUTO_INCREMENT,
`user_id` INT ( 10 ) NOT NULL,
`game` VARCHAR ( 255 ) NOT NULL,
`plateform` VARCHAR ( 255 ) NOT NULL,
`identifiant` VARCHAR ( 255 ) NOT NULL,
`lobby_state` ENUM ( `cours`, `termine`, `refuse` ) NOT NULL,
`lobby_date` VARCHAR ( 255 ) NOT NULL UNSIGNED,
PRIMARY KEY (`lobby_id`)
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci"',
'dropQuery' => 'DROP TABLE IF EXISTS `lobbys`'
);
/*
Lors de l'installation c'est cette fonction qui est appelée
*/
public static function install()
{
$db = XenForo_Application::get('db');
$db->query(self::$table['createQuery']);
}
/*
Lors de la désinstallation c'est cette fonction qui est appelée
*/
public static function uninstall()
{
$db = XenForo_Application::get('db');
$db->query(self::$table['dropQuery']);
}
}
Je vous invite à modifier votre add-on depuis votre ACP pour renseigner les classes et fonctions d'installation / désinstallation.
You must be registered for see images attach
Créez un fichier Lobbys.php dans le dossier Model, ce fichier contiendra pour le moment une fonction qui nous permettra de récupérer un lobby via son identifiant unique .
LOBBYS.PHP
PHP:
<?php
class Lobbys_Model_Lobbys extends XenForo_Model
{
public function getLobbyById($lobbyId)
{
return $this->_getDb()->fetchRow('SELECT * FROM lobbys WHERE lobby_id = ?', $lobbyId);
}
}
You must be registered for see images attach
Passons à l'insertion de données, pour se faire nous allons utiliser notre DataWriter dans notre ControllerPublic, pour sauvegarder nos données nous allons tout simplement créer une méthode, une action Save qui se chargera de les sauvegarder uniquement lorsque nous l'utilisons avec le type de requête HTTP : POST.
PHP:
public function actionSave()
{
//on instancie l'utilisateur courant.
$visitor = XenForo_Visitor::getInstance();
if(!$visitor['user_id'])
{
//si l'utilisateur n'est pas connecté on renvoit une erreur de permissions
return $this->responseNoPermission();
}
//on vérifie si la requête est en POST uniquement, dans le cas contraire une erreur automatique sera retournée.
$this->_assertPostOnly();
//on récupère nos valeurs de champs
$game = $this->_input->filterSingle('game', XenForo_Input::STRING);
$plateform = $this->_input->filterSingle('plateform', XenForo_Input::STRING);
$identifiant = $this->_input->filterSingle('game_id', XenForo_Input::STRING);
//on crée une instance de notre DW.
$dw = XenForo_DataWriter::create('Lobbys_DataWriter_Lobbys');
//on définit nos champs / valeurs de cette manière.
$dw->set('user_id', $visitor['user_id']); // on récupère l'identifiant du membre courant.
$dw->set('game', $game);
$dw->set('plateform', $plateform);
$dw->set('identifiant', $identifiant);
/*
Une fois que l'on a définit ce dont on a besoin, on sauvegarde nos données.
Nous n'avons pas besoin de sauvegarder l'état de notre lobby car il y a déjà une valeur par défaut, pareil pour la date.
*/
$dw->save();
//on effectue une redirection avec un message de succès.
return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS,
XenForo_Link::buildPublicLink('/'),
new XenForo_Phrase('lobbys_index_success'));
}
Tentez de remplir le formulaire, sauvegardez le et vous obtiendrez un message de succès ainsi qu'une redirection vers l'accueil.
You must be registered for see images attach
Nous avons déjà parcouru du chemin , passons à l'affichage des demandes de lobby nous allons utiliser notre ControllerPublic et notre Model pour se faire.
LOBBYS.PHP
PHP:
/*
* Il existe des manières bien plus propre de réaliser ceci mais je pars de 0 afin de vous expliquer les bases, inutile pour le moment de vous rendre la tâche trop compliquée.
*/
public function getAllLobbys()
{
return $this->_getDb()->fetchAll('SELECT * FROM lobbys INNER JOIN xf_user ON lobbys.user_id = xf_user.user_id');
}
Passons au ControllerPublic nous allons créer une action all qui va nous permettre de lister toutes les demandes de lobby automatiquement, en soit celle-ci ne fait que d'afficher une template avec un tableau de paramètre qui contient nos demandes.
INDEX.PHP (actionAll)
PHP:
public function actionAll()
{
//models, nous instancions notre model afin d'utiliser notre fonction getAllLobbys.
$lobbysModel = XenForo_Model::create('Lobbys_Model_Lobbys');
//les demandes
$lobbys = $lobbysModel->getAllLobbys();
return $this->responseView('Lobbys_ViewPublic_All', 'lobbys_all', $params = ['lobbys' => $lobbys]);
}
Enfin, crééons une template lobbys_all qui contiendra un tableau parcouru par chaque demande.
LOBBYS_ALL
HTML:
<xen:title>{xen:phrase lobbys_all_title}</xen:title>
<xen:description>{xen:phrase lobbys_all_description}</xen:description>
<xen:navigation>
<xen:breadcrumb source="$nodeBreadCrumbs" />
</xen:navigation>
<xen:if is="{$lobbys}">
<table class="dataTable">
<tr class="dataRow subtle">
<th width="20%">{xen:phrase user}</th>
<th width="38%">{xen:phrase lobbys_all_game}</th>
<th width="32%">{xen:phrase lobbys_all_plateform}</th>
<th width="10%">{xen:phrase lobbys_all_state}</th>
<th width="10%">{xen:phrase lobbys_all_date}</th>
</tr>
<xen:foreach loop="$lobbys" value="$lobby">
<tr class="dataRow">
<td><xen:username user="{$lobby}" rich="true" /></td>
<td>{$lobby.game}</td>
<td>{$lobby.plateform}</td>
<td><a href="{xen:link 'lobby/update', $lobby}"><xen:if is="{$lobby.lobby_state} == 'cours'">{xen:phrase lobbys_all_state_cours}<xen:elseif is="{$lobby.lobby_state} == 'refuse'" />{xen:phrase lobbys_all_state_refuse}<xen:elseif is="{$lobby.lobby_state} == 'termine'" />{xen:phrase lobbys_all_state_termine}</xen:if></a></td>
<td><xen:datetime time="{$lobby.lobby_date}"/></td>
</tr>
</xen:foreach>
</table>
<xen:else />
</xen:if>
Rendez-vous sur l'URL suivante :
Vous devez être inscrit pour voir les liens ! Inscrivez-vous ou connectez-vous ici.
!
You must be registered for see images attach
You must be registered for see images attach
Pour cet avant dernier chapitre nous allons créer une fonctionnalité qui va permettre de modifier l'état de la demande du lobby, pour se faire nous allons globalement créer une action dans notre Controller.
LOBBYS_UPDATE
HTML:
<xen:h1>{xen:phrase lobbys_update}</xen:h1>
<xen:navigation>
<xen:breadcrumb source="$nodeBreadCrumbs" />
</xen:navigation>
<form action="{xen:link 'lobby/update', $lobby}" method="post" class="xenForm AutoValidator" data-redirect="yes">
<dl class="ctrlUnit">
<dt><label>{xen:phrase lobbys_all_state}:</label></dt>
<dd><select name="lobby_state">
<option {xen:if '{$lobby.lobby_state} == "cours"', 'selected'} value="cours">{xen:phrase lobbys_all_state_cours}</option>
<option {xen:if '{$lobby.lobby_state} == "termine"', 'selected'} value="termine">{xen:phrase lobbys_all_state_termine}</option>
<option {xen:if '{$lobby.lobby_state} == "refuse"', 'selected'} value="refuse">{xen:phrase lobbys_all_state_refuse}</option>
</select>
</dd>
</dl>
<dl class="ctrlUnit submitUnit">
<dt></dt>
<dd>
<input type="submit" value="{xen:phrase save_changes}" accesskey="s" class="button primary" />
</dd>
</dl>
<input type="hidden" name="_xfToken" value="{$visitor.csrf_token_page}" />
</form>
Puis créons une action update dans notre Controller, si la requête est de type GET nous affichons simplement notre template pour choisir l'état de notre demande si c'est de type POST nous traitons la requête en utilisant notre DataWriter.
INDEX.PHP (actionUpdate())
PHP:
public function actionUpdate()
{
//input
$lobbyId = $this->_input->filterSingle('lobby_id', XenForo_Input::UINT);
$lobbysModel = XenForo_Model::create('Lobbys_Model_Lobbys');
$lobby = $lobbysModel->getLobbyById($lobbyId);
//on vérifie si la demande existe
if(!$lobby)
{
return $this->responseError(new XenForo_Phrase('requested_lobby_not_found'));
}
//si la requête est de type POST, donc soumission du formulaire
if($this->_request->isPost())
{
$lobbyState = $this->_input->filterSingle('lobby_state', XenForo_Input::STRING);
$dw = XenForo_DataWriter::create('Lobbys_DataWriter_Lobbys');
$dw->setExistingData($lobbyId);
$dw->set('lobby_state', $lobbyState);
$dw->save();
//on effectue une redirection avec un message de succès.
return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS,
XenForo_Link::buildPublicLink('lobby/all'),
new XenForo_Phrase('lobbys_update_success'));
}
return $this->responseView('Lobbys_ViewPublic_Update', 'lobbys_update', $params = ['lobby' => $lobby]);
}
Revenez sur notre liste de demandes, puis cliquez sur son état pour être redirigé vers la page d'édition de celui-ci, soumettez et observez !
You must be registered for see images attach
You must be registered for see images attach
Pour notre dernier chapitre nous allons tout simplement créer la fonctionnalité qui nous permettra de supprimer notre demande en cochant une case sur la page d'édition de l'état de celui-ci.

INDEX.PHP (actionUpdate())
PHP:
public function actionUpdate()
{
//input
$lobbyId = $this->_input->filterSingle('lobby_id', XenForo_Input::UINT);
$delete = $this->_input->filterSingle('delete', XenForo_Input::UINT);
$lobbysModel = XenForo_Model::create('Lobbys_Model_Lobbys');
$lobby = $lobbysModel->getLobbyById($lobbyId);
//on vérifie si la demande existe
if(!$lobby)
{
return $this->responseError(new XenForo_Phrase('requested_lobby_not_found'));
}
//si la requête est de type POST, donc soumission du formulaire
if($this->_request->isPost())
{
$lobbyState = $this->_input->filterSingle('lobby_state', XenForo_Input::STRING);
$dw = XenForo_DataWriter::create('Lobbys_DataWriter_Lobbys');
$dw->setExistingData($lobbyId);
if($delete)
{
$dw->delete();
}
else {
$dw->set('lobby_state', $lobbyState);
}
$dw->save();
//on effectue une redirection avec un message de succès.
return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS,
XenForo_Link::buildPublicLink('lobby/all'),
new XenForo_Phrase('lobbys_update_success'));
}
return $this->responseView('Lobbys_ViewPublic_Update', 'lobbys_update', $params = ['lobby' => $lobby]);
}
You must be registered for see images attach
You must be registered for see images attach