Bonjour,

J'utilise CakePHP 3.
Pour commencer, voici ma table Profils (je me suis dis que des images seraient plus parlantes que du blabla ^^ ) :

Ensuite, voici mon tableau sur mon site :

Voici ma fonction edit() de mon controller ProfilsController :

public function edit($id = null)
{
    $profil = $this->Profils->get($id, [
        'contain' => []
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $profil = $this->Profils->patchEntity($profil, $this->request->data);
        if ($this->Profils->save($profil)) {
            $this->Flash->success(__('Le profil a bien été modifié.'));
            return $this->redirect(['action' => 'index']);
        } else {
            $this->Flash->error(__('Le profil n\'a pas été modifié. Veuillez réessayer.'));
        }
    }
    $this->set(compact('profil'));
    $this->set('_serialize', ['profil']);
}

Et mon tableau HTML :

<table cellpadding="0" cellspacing="0">                        
    <thead>
        <tr>
            <th class="actions">

                <button class="widget-update"><?php echo $this->html->image('update.gif') ?></button>                                    
                <button class="widget-back planqueW"><?php echo $this->html->image('back.gif') ?></button>
                <?= $this->Form->create($profils, ['url' => ['controller' => 'profils', 'action' => 'edit']]) ?>
                <button class="widget-save planqueW" type="submit"><?php echo $this->html->image('save.gif') ?></button>      
            </th>
            <th>DP</th>
            <th>CP</th>
            <th>Référent</th>
            <th>Sachant</th>
            <th>Intervenant</th>
            <th>MOE</th>
        </tr>
    </thead>
    <tbody>
        <?php   $i = 0; 
                $j = 10; ?>                            
        <tr id="news">
            <th>News</th>
            <?php foreach ($profils as $profil): ?>                            
                <?php if($i>0) { ?>
                <td id="<?= $i ?>">                                        
                     <span class="cellTexteW"><?= h($profil->role_news); ?></span>
                    <?= $this->Form->input('role_news', ['label' => false, 'class' => 'planqueW champSaisiW', 'default' => $profil->role_news, 'options' => ['aucun accès' => 'Aucun accès', 'consultatif' => 'Consultatif', 'contributeur' => 'Contributeur', 'administrateur' => 'Administrateur']]); ?>                                        
                </td> 
                <?php } ?>                       
            <?php $i++; ?>                                
            <?php endforeach; ?>
        </tr>
        <tr id="agenda">
            <th>Agenda</th>
            <?php foreach ($profils as $profil): ?>                            
                <?php if($j>10) { ?>
                <td id="<?= $j ?>">                                        
                     <span class="cellTexteW"><?= h($profil->role_calendar); ?></span>
                    <?= $this->Form->input('role_calendar', ['label' => false, 'class' => 'planqueW champSaisiW', 'default' => $profil->role_calendar, 'options' => ['aucun accès' => 'Aucun accès', 'consultatif' => 'Consultatif', 'contributeur' => 'Contributeur', 'administrateur' => 'Administrateur']]); ?>                            
                </td> 
                <?php } ?>                       
            <?php $j++; ?>                                
            <?php endforeach; ?>
        </tr>
        <?= $this->Form->end() ?>
    </tbody>                       
</table>

Maintenant, voici mon problème :
Lorsque l'on clique sur le bouton update dans mon tableau (sur le site), tous mes champs deviennent des select où l'on peut choisir le rôle que l'on veut. Toutefois, c'est une modification sur plusieurs enregistrements de la table en même temps, on ne choisit pas d'enregistrement spécifique.
Ma colonne DP correspond à mon enregistrement 2, CP au 3, etc. Mais ils sont modifiables tous en même temps.

Je sais que ma fonction edit() est fausse car elle demande un id spécifique.
Mais je ne vois pas comment la modifier pour que tous mes enregistrements soient modifiables en même temps :/

Pourriez-vous m'aider s'il vous plait ? J'avoue que je bloque totalement, et je dois absolument terminer cette fonction pour vendredi :(
Merci par avance !

18 réponses


PhiSyX
Réponse acceptée

Yop. Il faut faire en sorte que les données retournées par ton formulaire $this->request->data() aient ce format-ci:

Dans ta méthode edit il faut:

  • Passer tes données dans un newEntity($data)
  • Parcourir le résultat de ce dernier à l'aide d'un foreach et utiliser les méthodes dédiées aux sauvegardes des données (patchEntity($entity, $data),save($patchEntity)) dans le foreach.

Aussi pour que cela puisse fonctionner, il faut que ton Entité Profil accepte que les assignements en masse puisse s'executer sur l'ID. Pour cela soit:

  • Tu changes la propriété $_accessible en modifiant 'id' et le mettre à true. (Normalement à false si le code est généré par CakePHP via la cosnole)
  • Tu ajoutes aux options du newEntity($data, $options) la clé 'accessibleFields' avec la liste des champs à assigner en masse, ici: ['id' => true].
PhiSyX
Réponse acceptée

J'ai ceci comme methode edit.

public function edit()
{
    if ($this->request->is(['patch', 'post', 'put'])) {
        $entities = $this->Profils->newEntities($this->request->data(), [
            'accessibleFields' => ['id' => true],
        ]);

        foreach ($entities as $entity) {
            $profil = $this->Profils->patchEntity($entity, $this->request->data());
            $this->Profils->save($profil);
        }

        return $this->redirect(['action' => 'index']);
    }
}

coucou, CakePHP 3 ?

Anju
Auteur

Oui désolée, j'ai oublié de préciser ! Je vais éditer pour le dire !

oki, et bien je ne saurais pas t'aider, je suis sous CakePHP 2 et ma fonction edit ne ressemble en rien à la tienne, je suis désolé :( mais je devrais bientôt m'y attaquer !

Anju
Auteur

Pas de soucis, merci quand même :)

Tu dis que tous les champs deviennent des select, et juste après tu dis qu'on ne choisit pas de champs spécifique. J'ai pas compris la problématique dsl.

Anju
Auteur

Et bien, tous les champs du tableau du site deviennent des selects. En même temps.
Mais chaque select correspond à un champ de la table et d'un enregistrement. Par exemple, sur l'image de mon tableau du site, le "administrateur" entre DP et News, correspond à l'enregistrement 2 et au champ role_news. Alors que le "administrateur" entre CP et Agenda correspond à l'enregistrement 3 et au champs role_calendar. Donc ça touche plusieurs champs de plusieurs enregistrements en même temps. Et ils doivent être modifiables en même temps.

J'espère que je me suis mieux expliquée :/

Anju
Auteur

Bonjour PhiSyX ! Merci pour ta réponse :)

J'ai commencé une nouvelle fonction edit(), après avoir mis l'id à true dans le $_accessible :

public function edit($id = null)
    {
       if($this->request->is(['patch', 'post', 'put']))
        {

            $data = $this->request->data;
            $profil = $this->Profils->newEntity($data);

            debug($profil);

            foreach($profil as $p)
            {
                $p = $this->Profils->patchEntity($profil, $data);
                debug($p);

                $this->Profils->save($p);
            } 
        }
}

Pour le moment, ça me renvoie ceci :

object(App\Model\Entity\Profil) {

    'role_news' => 'aucun accès',
    'role_calendar' => 'aucun accès',
    '[new]' => true,
    '[accessible]' => [
        '*' => true,
        'id' => true
    ],
    '[dirty]' => [
        'role_news' => true,
        'role_calendar' => true
    ],
    '[original]' => [],
    '[virtual]' => [],
    '[errors]' => [
        'name' => [
            '_required' => 'This field is required'
        ]
    ],
    '[repository]' => 'Profils'

}

Ce qui correspond à la 7ème colonne, je sais pas trop pourquoi d'ailleurs... Mais il n'entre pas dans le foreach en tout cas :/
Je ne savais pas trop s'il fallait que je mette un if($this->request->is(['patch', 'post', 'put'])), mais que je le mette ou non, ça ne change rien, ça renvoie la même chose.
Mais est-ce que tu peux me dire si je suis bien partie ? :) Et m'aider en ce qui concerne le foreach, pourquoi n'entre-t-il pas dedans ? Et je ne vois pas comment donner le format dont tu as parlé au $this->request->data :/

Encore merci pour ton aide !

P.S : Je suis tombée là dessus aussi : http://book.cakephp.org/3.0/fr/orm/saving-data.html Tu penses que ça pourrait servir ?

Yop.

Ha oui. Il faut désactiver la validation (qui est faites par défaut avec newEntity (et patchEntity)).
On le voit d'ailleurs:

'[errors]' => [
        'name' => [
             '_required' => 'This field is required'
        ]
]

Et oui le lien peut te servir, surtout la partie "Convertir des Enregistrements Multiples" dans ce cas-ci. (Ce que j'ai expliqué, en gros.)

Pour les données, il y a plusieurs façon de faire, la plus simple est de passer à l'helper $this->Form->input("{$keyProfil}.role_{$keyField}").
N'oublie pas d'ajouter un input type hidden "{$keyProfil}.id" à ta vue (pour que la sauvegarde se fasse de manière "mise à jour" et non d'un ajout à de nouvelles entités) ^^
$keyProfil correspond à la clé foreach ($profils as $keyProfil => $profil) dans ta vue. (a été ajoutée par moi même ^^)
Chez toi $keyField n'existe pas parce que tu l'as fait manuellement mais c'est égal à 'news' et/ou 'calendar'.

Anju
Auteur

J'ai enlevé la validation ! Mais par contre, il entre toujours pas dans le foreach.

Pour le reste je suis désolée, mais je ne comprends pas ce que tu veux dire :s
{$keyProfil}.role_{$keyField} c'est pas du jquery ça ? Je comprends pas la syntaxe, désolée :/

Ok, non ça n'est pas du jQuery.

La syntaxe est celle de PHP, lorsque l'on met une {$variable} ou tout simplement $variable au sein d'une chaîne de caractère avec double guillemets, la variable sera interprétée. (Mais c'est la base des bases ^^)

$foo = 'bar';
echo "Je suis $foo"; // Je suis bar
echo "Je suis {$foo}"; // Je suis bar
echo 'Je suis $foo'; // Je suis $foo

Quand on sait ça, on peut comprendre ce que veut dire "{$keyProfil}.role_{$keyField}".
"{$keyProfil}.role_{$keyField}" sera transformé en "0.role_news","1.role_news"/"0.role_calendar","1.role_calendar", etc...
De cette manière on défini l'envoi de plusieurs données, en tableau.

En bref, le code généré par l'helper form donnera ceci: <select name"0[role_news]">...</select>/<select name"0[role_calendar]">...</select>, etc... .

Lors de l'envoi du formulaire les données te seront retournées comme telles: $this->request->data

[
    0 => [
        'id' => 1,
        'role_news' => 'sans aucun accès',
        'role_calendar' => 'sans aucun accès',
    ],
    1 => [
        'id' => 2,
        'role_news' => 'admin',
        'role_calendar' => 'contrib',
    ],
];
Anju
Auteur

Aaaah d'accord ! Je crois que je comprends mieux ! C'est parce que j'utilise jamais cette syntaxe en fait ^^

Je vais essayer de faire ça !
Si j'ai bien compris :

  • Ceci se met dans la vue : $this->Form->input("{$keyProfil}.role_news") . A la place de l'input que j'ai déjà créé, ou je le mets en plus ?
  • Ceci dans ma vue également, en créant un input caché : {$keyProfil}.id ?
  • Et je dois rajouter ceci $keyProfil dans les foreach de ma vue ? C'est bien ça ?

Par contre, désolée si je t'embête hein ^^', mais à quel moment le $keyProfil prend la valeur de l'id ?

Edit : Cette dernière question étant stupide, puisque tu y as déjà répondu, oublie la xD je vais essayer tout ça !

Edit 2 :
j'ai réussi :D
Ca me renvoie ceci :

[
    (int) 1 => [
        'id' => '2',
        'role_news' => 'administrateur',
        'role_calendar' => 'administrateur'
    ],
    (int) 2 => [
        'id' => '3',
        'role_news' => 'administrateur',
        'role_calendar' => 'administrateur'
    ],
    (int) 3 => [
        'id' => '4',
        'role_news' => 'contributeur',
        'role_calendar' => 'contributeur'
    ],
    (int) 4 => [
        'id' => '5',
        'role_news' => 'contributeur',
        'role_calendar' => 'contributeur'
    ],
    (int) 5 => [
        'id' => '6',
        'role_news' => 'consultatif',
        'role_calendar' => 'contributeur'
    ],
    (int) 6 => [
        'id' => '7',
        'role_news' => 'aucun accès',
        'role_calendar' => 'aucun accès'
    ]
]

Maintenant faut que je passe à l'enregistrement des modifications :o
Parce qu'il ne rentre toujours pas dans mon foreach :(

Anju
Auteur

Ca fonctiooooonne :D
Merci beaucoup, vraiment :D

Juste une question, si j'ai mis id => true dans mon entité Profil, est-ce que c'est vraiment utile de le remettre dans la fonction ? :o

Coooool! Non, ça n'est pas utile ^^
Par contre tu peux améliorer ton index.ctp niveau repetition (Et formulaire \ö/)

<?= $this->Form->create(null, ['id' => 'profil-form', 'url' => ['controller' => 'profils', 'action' => 'edit']]) ?>
<?= $this->Form->end() ?>

<table cellpadding="0" cellspacing="0">
    <thead>
        <tr>
            <th class="actions">
                <button class="widget-update"><?php echo $this->html->image('update.gif') ?></button>
                <button class="widget-back planqueW"><?php echo $this->html->image('back.gif') ?></button>
                <button class="widget-save planqueW" type="submit" form="profil-form"><?php echo $this->html->image('save.gif') ?></button>
            </th>

            <th>DP</th>
            <th>CP</th>
            <th>Référent</th>
            <th>Sachant</th>
            <th>Intervenant</th>
            <th>MOE</th>
        </tr>
    </thead>
    <tbody>
        <?php $fields = ['news' => 'News', 'calendar' => 'Agenda'] ?>
        <?php foreach ($fields as $keyField => $field): ?>
            <tr id="<?= $keyField ?>">
                <th><?= $field ?></th>

                <?php foreach ($profils as $keyProfil => $profil): ?>
                    <td>
                        <span class="cellTexteW"><?= h($profil->{'role_' . $keyField}) ?></span>
                        <?= $this->Form->input("{$keyProfil}.id", [
                            'form' => 'profil-form',
                            'id' => false,
                            'type' => 'hidden',
                            'value' => $profil->id,
                        ]) ?>
                        <?= $this->Form->input("{$keyProfil}.role_{$keyField}", [
                            'form' => 'profil-form',
                            'id' => false,
                            'label' => false,
                            'class' => 'planqueW champSaisiW',
                            'default' => $profil->{'role_' . $keyField},
                            'options' => [
                                'aucun accès' => 'Aucun accès',
                                'consultatif' => 'Consultatif',
                                'contributeur' => 'Contributeur',
                                'administrateur' => 'Administrateur'
                            ]
                        ]) ?>
                    </td>
                <?php endforeach ?>
            </tr>
        <?php endforeach ?>
    </tbody>
</table>
Anju
Auteur

Oh je vais essayer ça, merci :)
Mais si mon bouton submit ne se trouve pas dans le formulaire, ça va pas envoyer la réponse, non ?

Sisi, grâce à l'attribut html form="idduformulaire"!

Anju
Auteur

Du coup, j'ai testé, ça fonctionne bien !

Petit problème pourtant, tu as enlevé mes variables $i et $j :/
En effet, en fait dans ma table Profils, j'ai 7 profils ! Mais moi je n'en affiche que 6.
Mes variables $i et $j permettent de ne pas afficher le <td id=0> et <td id=10> qui représente donc le premier enregistrement de ma table Profils :/

Du coup, j'ai juste rajouté un if dans ton code, et c'est parfait \o/