Bonjour à tous,
Je bosse un petit projet de site collaboratif sur cakephp 2.6.2.
Tout ce passait plutôt bien jusqu'à ce que je tombe sur un os !
Le problème est simple : un utilisateur (user) peut choisir 0 à N genres musicaux favoris (styles). Un style peut appartenir à 0 à N users. On est donc, si je ne m'abuse, dans le cas d'une relation HABTM. Dans ma base j'ai donc les tables 'users', 'styles' et 'styles_users' dans laquelle j'ai un id, style_id et user_id.
Dans mon StylesController j'ai ma fonction edit comme ceci :
public function edit($user_id){
// je récupère ma liste des styles
$styles = $this->Style->find('list');
$this->set(compact('styles'));
// je vérifie mon user
if(empty($user_id)){
throw new Exception("Utilisateur invalide");
}
if($user_id != $this->Auth->user('id')){
$this->Session->setFlash("Vous ne pouvez pas éditer cet utilisateur", "flash", array('type' => 'alert'));
}
// le moment critique : je fais ma sauvegarde
if($this->request->is('post') || $this->request->is('put')){
if($this->Style->saveAll($this->request->data)){
$this->Session->setFlash("Votre données ont bien été sauvegardées","flash", array('type' => 'success'));
}
}
}
Dans ma vue 'edit', j'ai mon petit formulaire :
<?= $this->Form->create('Style'); ?>
<?= $this->Form->input('User.id', array('type' => 'hidden', 'value' => $this->Session->read('Auth.User.id'))); ?>
<?= $this->Form->input('Style.Style', array('label' => 'Modifier vos genres musicaux', 'multiple' => true, 'options' => $styles)); ?>
<?= $this->Form->end(); ?>
Pour finir, quand je debug $this->request->data j'obtiens par exemple :
array(
'User' => array(
'id' => '2'
),
'Style' => array(
'Style' => array(
(int) 0 => '2',
(int) 1 => '3'
)
)
)
Dans ma BDD, un seul enregistrement est créé dans ma table 'styles_users' quelque soit le nombre de 'styles' renseigné par l'utilisateur. Dans cet enregistrement, il y a le user_id qui est ok et le style_id qui est toujours == 0. Ca marche pas quoi !
J'ai lu la doc' concernant les sauvegardes de données dans les HABTM mais vraisemblablement quelque chose m'échappe ... !
Je ne dois pas être très loin de la solution mais là je sèche un peu ... Alors si quelqu'un pouvait me donner un petit coup de patte ;)
Enfin je m'en suis sorti !
Ma démarche était bonne mais j'étais dans le mauvais controller. Donc je pouvais essayer longtemps ...
Je laisse une copie de mon code, si cela peut aider quelqu'un :
Dans le UserController :
[code] public function style($id){
$this->User->contain('Style');
$styles = $this->User->Style->find('list');
$this->set(compact('styles'));
if(empty($id)){
throw new Exception("Utilisateur invalide");
}
if($id != $this->Auth->user('id')){
$this->Session->setFlash("Vous ne pouvez pas éditer cet utilisateur", "flash", array('type' => 'alert'));
}
if($this->request->data){
$data = $this->request->data;
if($this->User->save($data, true, array('deep' => true))){
$this->Session->setFlash("Vos données ont été sauvegardées", "flash", array('type' => 'success'));
debug($data);
}
}
}[/code]
Dans la vue :
[code]<div class="row">
<?= $this->Form->create('User'); ?>
<?= $this->Form->input('User.id', array('type' => 'hidden', 'value' => $this->Session->read('Auth.User.id'))); ?>
<?= $this->Form->input('Style', array('label' => 'Modifier vos genres musicaux', 'multiple' => true, 'options' => $styles)); ?>
<br/>
<?php $options = array('label' => 'Valider', 'class' => 'button radius', 'div' => false); ?>
<?= $this->Form->end($options); ?>
</div>[/code]
Encore merci pour l'aide qui m'a fait gagner beaucoup de temps dans l'appréhension de mon petit problème ! :D
J'oubliais une chose.
J'ai déclaré les relations entre mes models comme suit:
Model User :
public $hasAndBelongsToMany = array('Style');
public $hasMany = array('StylesUser');
Model Style :
public $hasAndBelongsToMany = array('User');
public $hasMany = array('StylesUser');
Model StylesUser :
public $useTable = 'styles_users';
public $belongsTo = array('User', 'Style');
En espérant que ça vous permettent d'y voir un peu plus clair !
Bonjour.
Les tables HABTM, doivent avoir la structure suivante : id, model_id, model_id.
Donc dans ton cas : id, style_id, user_id.
Prends l'habitude d'ajouter la méthode submit ou button, car dans la version 3 de CakePHP, la méthode end du helper form, ne fait plus que fermer le formulaire et ne génère plus un bouton submit, donc si tu comptes passer sur la version 3 un jour, ça t'évitera de faire des modifications supplémentaires dans tes habitudes avec CakePHP.
Merci de ta réponse Lartak.
J'ai essayé ta méthode qui me paraît parfaitement logique mais je me mange une erreur :
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Array' in 'field list'
En gros, il aime pas trop recevoir un array...
Cakephp me génère cette requête, si ça peut aider :
UPDATE poprock_demo
.styles
SET id
= Array WHERE poprock_demo
.styles
.id
= '2'
Merci du conseil pour la méthode submit. Je compte bientôt passer à la version 3 alors autant prendre de bonnes habitudes tout de suite !
Un autre exemple utile est lorsque quand vous souhaitez sauver de nombreusex Tags dans un Post. Vous devez transmettre les données HABTM associeés dans le format de tableau HABTM suivant. Notez que vous devez passer uniquement l’id du modèle HABTM associé mais il doit être imbriquées à nouveau:
Array
(
[0] => Array
(
[Post] => Array
(
[title] => 'Saving HABTM arrays'
)
[Tag] => Array
(
[Tag] => Array(1, 2, 5, 9)
)
)
[1] => Array
(
[Post] => Array
(
[title] => 'Dr Who's Name is Revealed'
)
[Tag] => Array
(
[Tag] => Array(7, 9, 15, 19)
)
)
[2] => Array
(
[Post] => Array
(
[title] => 'I Came, I Saw and I Conquered'
)
[Tag] => Array
(
[Tag] => Array(11, 12, 15, 19)
)
)
[3] => Array
(
[Post] => Array
(
[title] => 'Simplicity is the Ultimate Sophistication'
)
[Tag] => Array
(
[Tag] => Array(12, 22, 25, 29)
)
)
)
Passer le tableau ci-dessus à la fonction saveAll($data, array('deep' => true)) remplira la table jointe posts_tags avec l’association Tag vers Post.
Par exemple, nous allons construire un formulaire qui crée un nouveau tag et génèrerons le tableau de données approprié pour l’associer à la volée avec certaines recipies.
Le formulaire le plus simple ressemblerait à ceci (nous supposerons que $recipe_id est déjà définie à une valeur):
<?php echo $this->Form->create('Tag');?>
<?php echo $this->Form->input(
'Recipe.id',
array('type' => 'hidden', 'value' => $recipe_id)); ?>
<?php echo $this->Form->input('Tag.name'); ?>
<?php echo $this->Form->submit('Add Tag'); ?>
<?php echo $this->Form->end(); ?>
Dans cet exemple, vous pouvez voir le champ caché Recipe.id dont la valeur est définie selon l’ID de la recette que nous voulons lier au tag.
Les autres façons possibles pour présenter nos données associées peuvent inclure une liste déroulante. Les données peuvent être envoyées d’un model en utilisant la méthode find('list') et assignées à une variable de vue du nom du model. Une entrée avec le même nom va automatiquement envoyer ces données dans un <select>:
// dans le controller:
$this->set('tags', $this->Recipe->Tag->find('list'));
// dans la vue:
$this->Form->input('tags');
Un scénario plus probable avec une relation HABTM incluerait un <select> défini pour permettre des sélections multiples. Par exemple, un Recipe peut avoir plusieurs Tags lui étant assignés. Dans ce cas, les données du model sont triées de la même façon, mais l’entrée du formulaire est déclarée légèrement différemment. Le nom du Tag est défini en utilisant la convention ModelName:
// dans le controller:
$this->set('tags', $this->Recipe->Tag->find('list'));
// dans la vue:
$this->Form->input('Tag');
En utilisant le code précédent, un liste déroulante est créée, permettant aux multiples choix d’être automatiquement sauvegardés au Recipe existant en étant ajouté à la base de données.
Je pense que tu as tout là. ! :)
Il ne te reste plus qu'à l'adapter à ton cas.
Encore merci pour ta réponse ! J'ai bien suivi l'explication mais ça ne passe toujours pas. J'ai quoi que je fasse un enregistrement unique dans styles_users avec le bon user_id et le style_id qui prend pour valeur 0.
Le plus déroutant c'est que le debug de $this->request ->data me renvoie ce que j'attends.
array(
'User' => array(
'id' => '2'
),
'Style' => array(
'Style' => array(
(int) 0 => '2',
(int) 1 => '3'
)
)
)
J'ai aussi modifié mon saveAll($this->request->data, array('deep' => true)), mais rien n'y fait.
Pour le coup, je me demande si le problème ne vient pas d'autre part...