Bonjour,

Je développe actuellement une petite application web en CakePHP 1.3 (C'est un peu vieux, oui)
et j'ai un peu de mal à comprendre le fonctionnement des "requêtes avancées". Je débute.

J'appelle "requete avancé", une requete qui comporte au minium une jointure.

Voilà mon problème,

J'ai 3 tables : reunions, actions, pilotes.
Une réunion comporte zéro ou plusieurs actions.
Une réunion est dirigé par un et un seul pilote.
Une action comporte un et un seul pilote.

Un petit schéma de récap'.

Réunion 1 - pilote : Mr pilote
--Action 1 - pilote : Mr X
--Action 2 - pilote : Mr Y

A partir du controller Reunion, je voudrais récupérer le nom et le prénom du pilote qui s'occupe l'action $idAction , puis passer le résultat dans ma vue reunion/voir/1
Actuellement, j'arrive a récupérer le pilote_id d'une action à partir du controller Réunion, mais pas le nom et prenom de la table "actions"

Est-ce techniquement faisable ? (Nothing is impossible)

J'espère être à peu près clair.

Je vous remercie par avance

Mowe.

7 réponses


Pakito
Réponse acceptée

Il me semble que containable prenne le dessus sur recursive. Normal donc que l'effet soit limité.

Tente un contain vraiment complet, du type :

function voir($id) {
    $this->Reunion->contain('Pilote', 'Action', 'Action.Pilote'); // Attention, toujours en majuscule les noms de requête
    $this->set('Reunion', $this->Reunion->find('all'));
}

Et essaie de voir quelles sont les requêtes effectuées.

Bonjour,

Avant tout, on va recentrer un peu le sujet en collant les bons termes aux bons endroits d'après les liaisons que tu nous expose.

  • Reunion hasOne Pilote, hasMany Action
  • Action belongsTo Reunion, hasOne Pilote

Puisque si j'ai bien compris, les actions ne peuvent appartenir qu'à une seule réunion. Sinon, il faudrait partir sur un hasAndBelongsToMany.

La question à se poser à ce moment là, c'est s'il n'y a pas un principe d'héritage ? De ce que je vois en l'état actuel, si la réunion a un pilote, alors les actions liées à la réunion vont appartenir au même pilote. Du coup, est-ce bien nécessaire d'établir à nouveau cette liaison dans le model Actions ? Tu pourrais simplement te baser sur le pilote de la réunion. Mais c'est de l'optimisation, ça n'est pas fondamental.

De là tu devrais donc avoir les tables suivantes :
reunions : id, name, pilote_id (et d'autres champs nécessaire à ton model)
pilotes : id, firstname, lastname (et d'autres champs nécessaire à ton model)
actions : id, name, reunion_id, pilote_id (ceci si tu conserve l'idée selon laquelle une action appartient à un pilote qui peut être différent de celui de la réunion) et d'autres champs nécessaire à ton model

En procédant comme cela, tes liaisons sont prêtes à fonctionner.

A partir de là, il va falloir que tu utilises le comportement containable afin de récupérer les models liés à ton model.

Du coup, le find de ta function voir() dans ton ActionsController pourrait ressembler à ça :

function voir($id) {
    $this->Reunion->contain('Pilote', 'Action'); // Cette ligne peut être facultative si tu as un haut niveau de récursivité dans ton containable.
    $this->set('Reunion', $this->Reunion->find('all'));
}

Ainsi, tu auras directement ta réunion, ton pilote, et toutes les actions liées à ta réunion.
Dans ton contain tu pourrais même ajouter 'Action.Pilote' pour récupérer en plus le pilote de chacune des actions.

Bon courage ;)

Mowe
Auteur

Bonjour Pakito,

Tout d'abord merci d'avoir pris le temps d’écrire une réponse aussi complète.

Mon modèle de données colle bien à ce que tu as pu écrire. Ainsi que ma récursivité qui est de 2 sur le modèle afin d'être sur de récupérer toutes les infos que j'ai besoin. J'adapterai par la suite.

Je pense que mon problème vient de mes liaisons;
J'ai effectivement testé celles que tu m'as donné (qui semblent être pourtant correctes)

Seulement voilà le soucis :

SQL Error: 1054: Unknown column 'Pilote.reunion_id' in 'on clause'

car la query essaye de faire :

JOIN `pilotes` AS `Pilote` ON (`Pilote`.`reunion_id` = `Reunion`.`id`)

Ce qui devrait être fait, serait plutôt :

JOIN `pilotes` AS `Pilote` ON (`Pilote`.`id` = `Reunion`.`pilote_id`)

Si l'on respecte les liaisons énoncées, cela me parait plus logique.

Comment corriger cette erreur ? As-tu déjà rencontré ce problème ?

Erreur de ta part, autant que de la mienne d'ailleurs !

Je me suis planté dans les liaisons. Il faudrait plutôt faire :

  • Reunion belongsTo Pilote, hasMany Action
  • Action belongsTo Reunion, belongsTo Pilote
  • Pilote hasMany Reunion, hasMany Action

Mais le pire c'est qu'en me relisant aujourd'hui, ça m'a sauté aux yeux. Et ta query SQL va dans ce sens également.

Mowe
Auteur

Effectivement !
Il y a du mieux mais c'est pas encore ça. On est pas loin.

J'arrive a récupérer le pilote d'une réunion, et seulement le pilote_id d'une action. :/

J'obtiens cela :

Array
(
    [0] => Array
        (
            [Reunion] => Array
                (
                    [id] => 1
                    ...
                    [pilote_id] => 2
                )
            [Pilote] => Array
                (
                    [id] => 2
                    [nom] => Lorem
                    [prenom] => Ipsum
                )
            [Action] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            ...
                            [pilote_id] => 2
                            [reunion_id] => 1
                        )
                    [1] => Array
                        (
                            [id] => 2
                            ...
                            [pilote_id] => 3
                            [reunion_id] => 1
                        )
                )

Il manque donc un niveau à cette arborescence. Il faudrait que pilote_id soit un array du modele pilote.

J'ai pourtant régler ma variable

$recursive = 2;

D'ailleurs que je le mette à -1, 1 ou 2 n'impacte rien du tout. Ce qui n'est pas normal.

J'ai aussi essayer de faire :

$this->Reunion->find('all', array('contain' => 'Action.pilote');

Mais je n'obtiens pas le niveau inférieur.

Mowe
Auteur

Genius !

Je récupère bien d'une part le pilote (nom, prenom) d'une réunion et d'autre part le niveau inférieur, c'est à dire le pilote de n'importe quelle action (qui peut être différent)

Je n'ai pas d'exemple concret pour tester si cela permettrait de descendre encore un niveau plus bas, mais du moment que les liaisons sont faites correctement, cela doit être réalisable.

En tout cas, Merci beaucoup pour ton aide, je vais pouvoir corriger pas mal de choses dans mon code. :)

PS : J'ai quand même regarder un peu les requêtes que retournait mon contain.

Je valide ta réponse.

Oh c'est tout à fait réalisable. Sur un projet je suis descendu jusqu'au 8ème niveau. C'était lourd niveau requêtes, mais comme on était sur une navigation full Ajax, on récupérait tout d'un bloc avec un chargement en asynchrone, donc c'était pas très dérangeant au final. Et en pensant bien le cache, ça passe tout seul.

Ravi si ça a pu t'aider. Je t'avoue que j'ai lâche le recursive très tôt pour me baser sur contain à la place, qui offre une souplesse sur chaque action.