Boucle de requêtes

Par Codekiller49, il y a 11 ans


Bonjour,

Je suis confronté à un problème pour la création de mon site. Je souhaiterais faire une boucle de requêtes (très limitées en nombre pour récupérer une liste de chapitres contenus dans des parties elles mêmes contenues dans un cours.

Je récupère correctement les données de mon cours
Je récupère aussi correctement les données de mes parties
Je ne récupère que les chapitres de ma dernière partie ....

<?php $course = $this->Course->find('first', array( 'conditions' =>array('Course.id' => $id_course), //On recherche toutes les informations )); $this->loadModel('Part'); $parts = $this->Part->find('all', array( 'conditions' =>array('Part.course_id' => $id_course), //On recherche toutes les parties )); $this->loadModel('Chapter'); foreach ($parts AS $part) { $chapters = $this->Chapter->find('all', array( 'conditions' =>array('Chapter.part_id' => $part['Part']['id']), //On recherche tous les chapitres )); } $this->set(compact('course')); // On envoit la liste à la vue. $this->set(compact('parts')); // On envoit la liste à la vue. $this->set(compact('chapters')); // On envoit la liste à la vue. ?>

Pourriez-vous éclairer ma lanterne SVP ? :)

17 réponses

Inspirat, il y a 11 ans

Pourquoi ne pas utiliser les liaisons de modèle ? Ca semble parfaitement correspondre à ton besoin ;)

// app/Controller/CoursesController.php $courses = $this->Courses->find('first', array( 'conditions' => array('Course.id' => $id_course), 'contain' => array( 'Part' => array('Chapter') ) )); $this->set(compact('courses')); // app/Model/Course.php public $hasMany = array('Part'); // app/Model/Part.php public $hasMany = array('Chapter');

En suite je t'invite à debug la variable "$courses" sur ta vue pour voir ce qu'elle contient ;)
J'en profite pour préciser que tu n'as pas besoin de faire une ligne pour chaque variable envoyé à la vue, il suffit de les mettres les unes à la suite des autres.

$this->set(compact('courses', 'parts', 'chapters'));
Codekiller49, il y a 11 ans

En effet ça serait plus utile, seulement je ne sais pas comment intéragir les données récupérées. Aurais-tu un exemple pour afficher le titre d'un chapitre bien précis ? :)

PS: Merci oui, j'avais oublié que je pouvais tout envoyer à la vue d'un seul coup :)

Inspirat, il y a 11 ans

Je t'invite à utiliser la fonction debug sur la variable envoyé à la vue, elle sera constitué de la façon suivante (si je ne fais pas d'erreur):

// app/View/Crouses/view.ctp <?= debug($courses); ?> // Ce qui va monter un tableau du type: $courses = array( 'Course' => array( 'id' => 1, 'title' => '...', 'content' => '...', 'etc' => '...' ), 'Part' => array( 0 => array( 'id' => 56, 'title' => '...', 'content' => '...', 'etc' => '...', 'Chapter' => array( 0 => array( 'id' => 33, 'title' => '...', 'content' => '...' ), 1 => array(...) // Dans le cas où il y a plusieurs chapitres associés ) ), 1 => array(...) // Dans le cas où il y a plusieurs parties associées ) );
Codekiller49, il y a 11 ans

Merci de ta réponse cependant c'est bien ce que j'avais fait et j'obtiens le cours, les parties mais aucunement les chapitres. Voilà ce que j'obtiens:

array( 'Course' => array( 'id' => '47', 'title' => 'Essai', 'description' => 'Essai nouveau cours', 'created' => '2014-12-16 21:39:21', 'difficulty' => '1', 'online' => '0', 'lastUpdate' => '2014-12-16 21:39:21', 'duration' => '1 semaine', 'type' => '1' ), 'Part' => array( (int) 0 => array( 'id' => '11', 'title' => 'Titre de votre première partie.', 'online' => '0', 'course_id' => '47', 'position' => '1' ), (int) 1 => array( 'id' => '12', 'title' => 'Titre de votre première partie.', 'online' => '0', 'course_id' => '47', 'position' => '2' ), (int) 2 => array( 'id' => '13', 'title' => 'Titre de votre première partie.', 'online' => '0', 'course_id' => '47', 'position' => '3' ) ) )
Inspirat, il y a 11 ans

Tu as bien ajouté l'association du modèle Part au modèle Chapter ?

// app/Model/Part.php public $hasMany = array('Chapter');

Ensuite tu utilises bien comme clé de liaison la colonne "part_id" dans ta table "chapters" ?
Si oui, est-ce que tu as bien des chapitres associés aux parties récupérées ?

Codekiller49, il y a 11 ans

J'ai bien ajouté l'association du modèle Part au modèle Chapter
Ensuite, oui j'ai bien part_id dans ma table chapter.
Et oui j'ai bien des chapitres associés aux parties récupérées.

Si j'écris:

$this->loadModel('Part'); $parts = $this->Part->find('all', array( 'conditions' => array('Part.course_id' => $id_course), ));

Alors j'obtiens :

array( (int) 0 => array( 'Part' => array( 'id' => '11', 'title' => 'Titre de votre première partie.', 'online' => '0', 'course_id' => '47', 'position' => '1' ), 'Chapter' => array( (int) 0 => array( 'id' => '8', 'content' => 'Ecrivez le contenu de votre tout premier chapitre.', 'title' => 'Titre de votre premier chapitre', 'online' => '0', 'part_id' => '11', 'position' => '1' ) ) ), (int) 1 => array( 'Part' => array( 'id' => '12', 'title' => 'Titre de votre première partie.', 'online' => '0', 'course_id' => '47', 'position' => '2' ), 'Chapter' => array() ), (int) 2 => array( 'Part' => array( 'id' => '13', 'title' => 'Titre de votre première partie.', 'online' => '0', 'course_id' => '47', 'position' => '3' ), 'Chapter' => array( (int) 0 => array( 'id' => '9', 'content' => 'Ecrivez le contenu de votre tout premier chapitre.', 'title' => 'Titre de votre premier chapitre', 'online' => '0', 'part_id' => '13', 'position' => '1' ), (int) 1 => array( 'id' => '10', 'content' => 'Ecrivez le contenu de votre tout premier chapitre.', 'title' => 'Titre de votre premier chapitre', 'online' => '0', 'part_id' => '13', 'position' => '2' ) ) ) )

Pour le coup je n'obtiens pas les informations sur le Cours ( ce qui est normal si on regarde ma requête). J'ai l'impression que CakePHP refuse de descendre plus bas dans la hiérarchie.

Inspirat, il y a 11 ans

Dans ton model AppModel ajoute les choses suivantes:

// app/Model/AppModel.php public $actsAs = array('Containable'); public $recursive = -1;

En suite c'est-à-dire "J'ai bien ajouté l'association du modèle Part au modèle Chapter.around" ? Chapter.around ?

Codekiller49, il y a 11 ans

Hum, c'est encore pire car je n'ai pas accès aux enfants du coup ... (parts pour course et chapters pour part).

Excuse-moi, lorsque j'ai dis "Chapter.around" je voulais dire "Chapter". Le balisage de Grafikart.fr a automatiquement marqué le ".around", je ne sais pas pourquoi.

Inspirat, il y a 11 ans

Pourtant, pour ce qui est du recursive c'est pour éviter par défaut de prendre les enfants (pour pas surcharger inutilement les requêtes, mais si tu précise quels enfants tu veux, ils sont inclus dans la requête...).
Pour la coup je bloque... Théoriquement ça devrait fonctionner, je ne suis plus disponible ce soir mais demain soir je pourrais essayer de t'aider d'avantage (teamviewer, skype) sauf si quelqu'un trouve la solution avant moi.
Vérifie bien que les associations sont faites dans les bons modèles, voici le lien de la documentation en attendant (en français): CakePHP Containable

Codekiller49, il y a 11 ans

Très bien, merci de ton aide. Je vais essayer de creuser de mon côté pourvoir si je peux trouver quelque chose ...

Bonne soirée à toi et merci beaucoup d'avoir pris du temps pour te pencher sur mon problème.

Daprod, il y a 11 ans

Salut,

Je pense que le problème du premier code viens de la boucle foreach; la variable $chapters est écrasée a chaque itération, du coup seuls les chapitres de la dernière partie survivent.
Il faudrait la déclarer avant la boucle, et lui greffer ensuite le resultat de chaque requête :
...

$this->loadModel('Chapter'); $chapters = array(); foreach ($parts AS $part) { $part_chapters = $this->Chapter->find('all', array( 'conditions' =>array('Chapter.part_id' => $part['Part']['id']), //On recherche tous les chapitres )); $chapters[] = $part_chapters; }

...

Inspirat, il y a 11 ans

Oups ! J'avais totalement oublié hier, excuse-moi :D
Sinon, pour résoudre ton problème, une solution très simple, définir la récursivité à la volée juste avant ta requête:

// app/Controller/CoursesController.php $this->Course->recursive = 2; // Soit une profondeur de 2 (part > chapter) $courses = $this->Course->find('first', array(...));

Je connaissais cette technique, mais sur l'un de mes anciens projets j'ai plusieurs niveaux de récursivités alors que je n'ai pas eu à modifier ça, peut-être une mise à jour entre temps...

Lartak, il y a 11 ans

Bonsoir.
Sinon, pour résoudre ton problème, une solution très simple, définir la récursivité à la volée juste avant ta requête:
S'il a mit le recursive à -1, qu'il défini le behavior Containable dans l'AppModel et qu'il utilise correctement la clé contain dans ses requêtes, il n'a nullement besoin de définir un récursive dans ses controllers.

Azorgh, il y a 11 ans

Bonjour,

L'inconvénient avec une telle récursivité, c'est que l'on risque de récupéré plein de données qui ne vont pas servir au final.
Je pense qu'il vaut mieux passer par un contain ! Au moins, c'est toi qui définit qu'est ce que tu veux récupérer, dans quel ordre, tout ça quoi.

Donc comme le disais Lartak11 et Inspirat, ajoute dans ton model Course :

public $actAs = ('Containable'); public $hasMany = ['Part'];

Ainsi que dans le model Part :

public $haMany = ['Chapter'];

Et ensuite tu peux réaliser ta requete :

$this->Course->contain( 'Part' => [ 'Chapter' => [ 'fields' => ['id', 'name', 'slug', '....'] ] ] ); $this->Course->findById($id_course);

Après c'est la meilleur chose pour moi, peut être que je me trompe ... :)

Inspirat, il y a 11 ans

Sans offense c'est exactement ce que je lui ai donné tout au long de mes réponses, mais écrit d'une façon différente qui fonctionne tout autant :D

Azorgh, il y a 11 ans

Excuse moi Inspirat, en effet, à la première réponse c'est la même que moi.
Acceptez mes excuses sur ce coup là !