belongsTo : génération de la liste déroulante des données du modèle associé
Imaginons un classement de documents par type de document : le formulaire de création / édition d’un document doit faire apparaître la liste déroulante des types de document disponibles. Pour ce faire, nous utilisions la méthode generateList, mais celle-ci est dépréciée dans la version 1.2.0.6311-beta. Voyons comment générer la liste déroulante avec la méthode find('list').
Soit le modèle de données suivant :
Type hasMany Document
Document belongsTo Type
1. Les tables
1.1 Table types
CREATE TABLE `types` ( `id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT, `libelle` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM;
1.2 Table documents
CREATE TABLE `documents` ( `id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT, `type_id` mediumint(8) UNSIGNED NOT NULL, `titre` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`), KEY `type_id` (`type_id`), ) ENGINE=MyISAM;
2. Les modèles et leurs relations
21. Modèle Type
1 2 3 4 5 6 7 8 | // {app}/models/type.php class Type extends AppModel { var $name = 'Type'; var $useTable = 'types'; var $displayField = 'libelle'; var $hasMany = 'Document'; } |
Notez la définition de la variable $displayField, qui va être utilisée par la méthode Model::find('list') pour savoir quel champ afficher dans la liste déroulante. Si cette variable n’est pas définie, CakePHP va chercher un champ title ou name (qui n’existent pas dans notre définition de table en français) et en dernier recours affiche simplement l’id du type.
2.2 Modèle Document
1 2 3 4 5 6 7 | // {app}/models/document.php class Document extends AppModel { var $name = 'Document'; var $useTable = 'documents'; var $belongsTo = 'Type'; } |
3. Le Contrôleur DocumentsController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // {app}/controllers/documents_controller.php class DocumentsController extends AppController { function edit($id = null) { if(isset($this->data)) { $this->Document->set($this->data); $this->Document->save(); $this->redirect(array('action' => 'index')); } $this->data = $this->Document->read(null, $id); // Génération de la liste de types de document : $types = $this->Document->Type->find('list'); // Passage de la liste à la Vue : this->set('types', $types); } } |
Pas besoin d’en dire plus, CakePHP se charge du reste !
4. La vue edit
Voyons maintenant la vue correspondant à l’actionedit du Contrôleur DocumentsController.
1 2 3 4 5 6 7 8 9 | // {app}/views/documents/edit.ctp
<?php
e($form->create('Document', array('action' => 'edit')));
?>
<div class="input">
<label>Type :</label>
<?php e($form->select('type_id', $types, null, null, false)); ?>
</div>
// Puis le champ titre, le bouton submit, etc. |
Nous constatons que la liste déroulante des types disponibles est bien présente et correctement remplie. Si nous sommes en train d’éditer un document existant, le type du document est préselectionné dans la liste.
Commentaires
12 février 2008 à 12:03
Eh bien si il faudrait peut-être en dire plus. Quelle est la méthode qui retourne la liste des éléments dans le contrôleur ? Quel est l’effet de la fonction e() ?
12 février 2008 à 12:48
C’est la méthode
find('list')qui retourne la liste des éléments dans le contrôleur :$this->Document->Type->find(‘list’);La fonction e() n’est qu’un substitut à l’instruction echo. Ainsi,
echo "Bonjour";est équivalent àe("Bonjour");en CakePHP.D’autres petites fonctions de ce genre sont inclues dans le framework, c’est parfois pratique et plus rapide à écrire, voici un lien vers la section du manuel qui les énumère : http://manual.cakephp.org/chapter/constants
18 février 2008 à 15:51
Salut !
Je viens de passer 3 heures sur un find(‘list’) qui ne me renvoyait que le id dans le Select… alors que tu donnais ici l’explication : « Si cette variable n’est pas définie, CakePHP va chercher un champ title ou name (qui n’existent pas dans notre définition de table en français) et en dernier recours affiche simplement l’id du type. »
Alors juste une question : où as-tu pêcher cette information ? Car je ne l’ai vu nulle part dans l’API ou la tempdocs…
18 février 2008 à 16:28
J’ai lu le code de model.php dans la librairie de CakePHP. J’ai décidé d’écrire ce sujet justement parce que cette information n’était pas disponible ailleurs…
18 février 2008 à 16:59
Ah oui, le code source… c’est la meilleure source d’info il est vrai !
J’essaie le plus souvent d’éviter une plongée dans le coeur de Cake, surtout si j’utilise des « fonctions de bases » comme « find », en me disant qu’elles doivent être éprouvées et documentées. Belle erreur de fainéant !
En tout cas, bravo pour la pertinence de tes articles !
16 avril 2008 à 18:18
Bonjour, Tout d’abord merci pour ces tutoriels très bien expliqués.
Il y a deux points qui peuvent être améliorés : Dans le controlleur des documents, vous faites appel à $this->Document->save(); sans en tester le code de retour, si bien qu’en cas de problème à la sauvegarde (ou à la validation) des données, vous revenez systématiquement sur la page d’index. Voici une portion de code que j’utilise et qui résoud ce problème : if($this->Document->save($this->data['Document'])) { $this->flash(htmlentities(« Le document a bien été mis à jour. »), ‘/documents/index’); }
Deuxième point bien moins génant : vous ne faites pas appel à $form->end pour ajouter la balise dans votre vue. Je suppose que dans vos fichiers cet appel est bien présent mais il peut être utile de le préciser ici.
16 avril 2008 à 21:48
1) Par souci de concision, je « zappe » certaines étapes classiques afin de mettre l’accent sur ce qu’il y a d’important, mais effectivement il faudrait vérifier que l’enregistrement s’est bien passé. 2) Effectivement il ne s’agit ici que d’un extrait, mais on peut bien entendu fermer le formulaire avec :
$form->submit("Valider"); $form->end();Ou encore plus rapidement :$form->end("Valider");23 avril 2009 à 13:22
c’est quand même un peu documenté ! http://book.cakephp.org/view/438/displayField on peut via cette propriété de la classe Model changer le champ utilisé pour le libellé dans les listes, on peut même, dans app_model.php qui est hérité par tous les modèles re-positionner ce champ à ‘titre’ ou ‘nom’ !
23 avril 2009 à 14:55
Effectivement mais ça ne l’était lors de la publication de ce tutoriel. La documentation officielle s’étoffe et c’est heureux !
13 janvier 2010 à 9:45
Bonjour, Cette exemple est très bien cependant je voulais savoir si il y avais une solution de personnaliser l’affichage dans la liste déroulante.
Du genre: ‘-’.$id.’: ‘.$libelle
Car je voudrai concaténé deux champs de base dans mon cas pour rentre la liste déroulante plus complete.
Cordialement Jean-Baptiste.
13 janvier 2010 à 10:39
Pour CakePHP 1.2 il faut ajouter un nouveau type de find comme expliqué ici : find(‘list’) with 3 or combined fields Et pour CakePHP 1.3 on peut utiliser les nouveaux « champs virtuels » : 1.3 new feature : virtual fields Bonne lecture !
13 janvier 2010 à 11:08
Merci bcp, vue que j’ai cakephp 1.2 j’ai opté pour la deuxième solution! c’est vraiment ce que je cherchais.
En plus c’est une solution qui est réutilisable pour tout les models donc parfait.
Que dire à part merci !
Cordialement Jean-Batiste