Passons sans plus attendre au code, cela vaut mieux qu’un long discours…![]()
Définir les relations entre modèles
Il s’agit d’établir ici une relation simple : à une marque sont associés n produits.
Il peut exister d’autres relations avec le modèle "Produit", de même type comme ici le modèle Gamme, ou d’autres types d’association [1]…
Extrait de "models/produits.php"
class Produit extends AppModel {
public $name = 'Produit';
[...]
//DÉFINITION DES RÈGLES DE VALIDATION DU MODÈLE
public $validate = array(
'name' => array(
'nom' => array(
// PETITE REGLE PERSONNALISÉE :
// APHANUM (ACCENT + APOSTROPHE) 3 CARACTÈRES MINIMUM
'rule' => "/^[^0-9°_%:().\/\*\^\?#!@$%+=,\"><~\[\]{}]{3,}$/i",
'message' => 'Vous devez préciser la dénomination du produit.',
'allowEmpty' => false,
'required' => false
),
),
[...]
);
[...]
//ASSOCIATION PRODUIT ET MARQUE : belongsTo (plusieurs vers un)
public $belongsTo = array(
'Marque' => array(
'className' => 'Marque',
'foreignKey' => 'marque_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Gamme' => array(
'className' => 'Gamme',
'foreignKey' => 'gamme_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
[...]
);
[...]
}
Extrait de "models/marques.php"
class Marque extends AppModel {
public $name = 'Marque';
[...]
//ASSOCIATION MARQUE ET PRODUIT : hasMany (un vers plusieurs)
public $hasMany = array(
'Produit' => array(
'className' => 'Produit',
'foreignKey' => 'marque_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}Récupérer les données issues de la relation
La consultation d’une marque doit permettre l’affichage d’une liste paginée des produits associés. Il faut récupérer les informations liés :
- à la marque courante ;
- aux produits liés ;
- aux autres modèles liés à ces produits.
Extrait de "controllers/marques_controller.php" :
public function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Marque inexistante', true));
$this->redirect(array('action' => '/'));
}
$this->set('marque', $this->Marque->read(null, $id));
$this->helpers[] = 'Number';
//ON CHARGE LE MODÈLE PRODUIT
$this->loadModel('Produit');
//POUR RÉCUPÈRER LES INFOS DES AUTRES MODÈLES LIÉS AU PRODUIT
$gammes = $this->Produit->Gamme->find('list',array('order' => array('id ASC')));
[...]
$this->set(compact('gammes',[...]));
//ON RÉCUPÉRE LES PRODUITS ASSOCIÉS À LA MARQUE,
$this->paginate = array('Produit'=> array(
'conditions' => array('`Produit`.`marque_id`'=>$id),
'limit' => 10,
'order' => '`Produit`.`name` ASC'
));
$this->set('produits', $this->paginate('Produit'));
}Mise en oeuvre d’URLs conviviales
Nous souhaitons mettre en place des urls de la forme :
"/nos-produits", pour afficher le listing de tous les produits ;
"/nos-produits/1-titre-du-premier-produit" , pour consulter la page d’un produit spécifique ;
"/nos-produits/marques", pour afficher le listing de toutes les marques ;
"/nos-produits/marques/1-titre-de-la-premiere-marque" , pour consulter la page d’une marque en particulier et le listing des produits associés.
Pour ce dernier cas la pagination des listings sera du type :
"/nos-produits/marques/1-titre-de-la-premiere-marque/pageX" où "X" représentera le numéro de page ;
"/nos-produits/marques/1-titre-de-la-premiere-marque/pageX/trier-par-YYYYY" ou "YYYYY" représentera un critère tri dans ordre croissant.
On ajoutera "/inverse" à l’url pour obtenir un ordre décroissant.
Complexe ? Non, c’est juste une demande courante…![]()
Grâce à CakePHP tout ou presque va se réaliser au niveau de la configuration des routes du framework.
Extrait de "config/routes.php"
//PRODUITS
Router::connect('/nos-produits/:id-:suffixe',
array('controller' => 'produits', 'action' => 'view'),
array('pass' => array('id', 'suffixe'),'id' => '[0-9]+','suffixe' => '[a-z0-9\-]+'))
);
Router::connect('/nos-produits/:id-:suffixe',
array('controller' => 'produits', 'action' => 'view'),
array('pass' => array('id', 'suffixe'),'id' => '[0-9]+','suffixe' => '[a-z0-9\-]+'))
);
Router::connect('/nos-produits',
array('controller' => 'produits', 'action' => 'index')
);
//MARQUES
//MARQUES TRI ASC
Router::connect('/nos-produits/marques/:marque_id-:suffixe/page:page/trier-par-:sort',
array('controller' => 'marques', 'action' => 'view','direction'=>'asc'),
array('pass' => array('marque_id', 'suffixe','page','sort','direction'),'marque_id' => '[0-9]+','suffixe' => '[a-z0-9\-]+')
);
//MARQUES TRI DESC
Router::connect('/nos-produits/marques/:marque_id-:suffixe/page:page/trier-par-:sort/inverse',
array('controller' => 'marques', 'action' => 'view', 'direction'=>'desc'),
array('pass' => array('marque_id', 'suffixe','page','sort','direction'),'marque_id' => '[0-9]+','suffixe' => '[a-z0-9\-]+')
);
//MARQUES PAGINATION
Router::connect('/nos-produits/marques/:marque_id-:suffixe/page:page',
array('controller' => 'marques', 'action' => 'view'),
array('pass' => array('marque_id', 'suffixe'),'marque_id' => '[0-9]+','suffixe' => '[a-z0-9\-]+')
);
//MARQUES ALL
Router::connect('/nos-produits/marques/:marque_id-:suffixe',
array('controller' => 'marques', 'action' => 'view'),
array('pass' => array('marque_id', 'suffixe'),'marque_id' => '[0-9]+','suffixe' => '[a-z0-9\-]+')
);
//MARQUES INDEX
Router::connect('/nos-produits/marques',
array('controller' => 'marques', 'action' => 'index')
);Hum, et la pagination au sein de la vue ? ![]()
Il faut passer la jolie url au ’Helper Paginator’ [2]
Extraits de "views/marques/view.ctp"
$this->Paginator->options(array('url' => array('marque_id' => $marque['Marque']['id'],'suffixe'=>Inflector::slug($marque['Marque']['name'],'-'))));Le dernier point concerne le tri.
Dans la vue il suffit d’utliser la méthode "sort" du Paginator :
echo $this->Paginator->sort('Dénomination','name',array('title'=>'Trier par dénomination'));
echo $this->Paginator->sort('Gamme','gamme_id',array('title'=>'Trier par gamme'));
[...]Ainsi si critère de tri le champ "name" des produits nous allons obtenir en l’état l’url : "/nos-produits/marques/1-titre-de-la-premiere-marque/pageX/trier-par-name" et par gamme on aurait "/nos-produits/marques/1-titre-de-la-premiere-marque/pageX/trier-par-gamme_id"
c’est pas beau…
Comment faire autrement ?
On va définir au sein du modèle produit les "intitulés conviviaux" pour les attributs souhaités, grâce aux "virtualFields [3] de CakePHP.
Ajoutons cela à notre modèle ("models/produits.php")
public $virtualFields = array(
'denomination' => 'Produit.name',
'marque' => 'Produit.marque_id',
'gamme' => 'Produit.gamme_id',
[...]
);
Notre vue devient (extrait de "views/marques/view.ctp")
echo $this->Paginator->sort('Dénomination','denomination',array('title'=>'Trier par dénomination'));
echo $this->Paginator->sort('Dénomination','gamme',array('title'=>'Trier par gamme'));Piti bonus, la cerise du Cake [4]
Nos résultats sont affichés au sein d’un tableau dont les entêtes contiennent les critères de tri.
<table id="results">
<tr>
<th class="trierpar">Tri :</th>
<th><?php echo $this->Paginator->sort('Dénomination','denomination',array('title'=>'Trier par dénomination'));?></th>
<th><?php echo $this->Paginator->sort('Gamme','gamme',array('title'=>'Trier par gamme'));?></th>
[...]Le système de pagination du framework ajoute au lien de tri une classe css ("asc" ou "desc") pour discriminer l’ordre de tri. Sachant cela et que nous avons définit un attribut "id" à notre tableau de résultats, il devient trivial d’ajuster via javascript (ici jQuery) le titre du lien, rajoutons ce code à notre vue et le tour est joué.
$this->Html->script('jquery/jquery.min.js', array('inline'=>false));
$this->Html->scriptBlock("
$(document).ready(function() {
$(\"#results th a\").each(function(){
if ($(this).hasClass(\"asc\")){
$(this).attr(\"title\",($(this).attr(\"title\")+', dans l\'ordre décroissant'));
} else {
$(this).attr(\"title\",($(this).attr(\"title\")+', dans l\'ordre croissant'));
}
});
});
",
array('inline'=>false));Conclusion
CakePHP c’est à la mode, accessible, plein de bonnes idées, alors pourquoi pas… essayez !!!
Tags
Infos