Bonjour,

J'ai un petit problème mon lier mes models, je m'explique.

J'ai un controller equipements, crafts (ou recette comme vous préféré) et ressources.

Voici mes différentes tables
Les images ne fonctionnent donc je met des liens à la place

Table Equipements
Table Crafts ou recette
Table Ressources

Mon but est que j'affiche les équipements avec leurs crafts et les ressources du crafts (recette)

J'ai réussi à récupérer le craft d'un item
Model Equipement

<?php
class Equipement extends AppModel{

    public $belongsTo = [
        'pano' => [
            'className' => 'panoplie',
            'foreignKey' => 'panoplie_id'
        ],
        'craft' => [
            'className' => 'craft',
            'foreignKey' => 'id'
        ],

    ];
}

Mais je ne vois pas comment lier ma table craft et ressource, en sachant que je dois lier :
Craft.ressource1 = Ressource.id
Craft.ressource2 = Ressource.id
et ainsi de suite...

Je vous remercie d'avance pour votre aide.

34 réponses


Tchoupi
Auteur
Réponse acceptée

Mhh d'accord c'est bien ce que je me disais.
Du coup j'ai dupluquer ma table équipement, seul inconvénient c'et qu'à chaque ajout d'un nouvel équipements / ressources / consommables, je devrais le save dans les deux tables.

J'attends voir si j'ai d'autres réponses mais sinon sujet résolu !

Il faudrais que tu passes par un HasAndBelongsToMany donc par une table d'association ou sur cette table tu retrouverais l'id de ta ressource et l'id de ton craft, et pour chaque association resource 1 ressource 2 etc... ca te créé un enregistrement

du genre:
Table: Crafts_ressources
id: 1
Craft_id : 1
Ressource_id: 1

id:2
Craft_id:1
Ressource_id:2

etc...

Regarde du coté de HasAndBelongsToMany dans la doc ;) C'est la seule solution, et la meilleure "Enfin euh je crois ^^"

class Craft extends AppModel {

    public $hasAndBelongsToMany = array(
        'Resources' => array(
            'className' => 'Ressource',
        )
    );
}

class Ressource extends AppModel {

    public $hasAndBelongsToMany = array(
        'Craft' => array(
            'className' => 'Craft',
        )
    );
}

Et n'oublie pas de créer la table Crafts_Ressources

Tchoupi
Auteur

Salut,

Merci pour ta réponse.
Mais je ne vois pas pourquoi utiliser hasAndBelongsToMany ?

Et pourquoi créer une autre table Crafts_Ressources.

En gros tu veux que j'exporte tous les crafts & ressources dans cette table.

Personnellement, je ne comprends pas ou tu veux en venir. Quand je vois la structure de ta base la première chose qui me saute aux yeux c'est une redondance au niveau de ta table craft avec des "ressource1", "quantité1" etc. Tu devrais plutôt avoir une table craft_data par exemple qui regroupe les paramètres de tes recettes (genre une table qui contient id, recette_id, ressource_id et quantiité).

Je suis à fond sur la modélisation de base de données ces jours ci donc ne m'enveux pas de voir ça partout xD

Tchoupi
Auteur

Je viens justement de restructurer ma bdd car mes crafts étaient comme ceci avant :
Colonne : id et craft

id : 39
craft : 4731;4411

le problème c'est que comme ça, il m'est impossible de faire des associations car il faut que je traite toutes les données avec des explode et autres. Chose que je suis capable, le seul problème c'est que j'arrive à un nombre de requête inconsidérable à la fin ...

Si je comprends bien tu passes d'une table qui dispose de plusieurs colonnes ressources et quantités à une seule colonne qui regroupe le tout que tu gères avec un explode ?

Tchoupi
Auteur

Oui c'est ça mais justement j'ai fais l'inverse avant j'avais tout en une colonne que je gèrais avec plusieurs explodes.
Mais du coup pour faire des associations avec ma tables ressources c'était beaucoup trop compliqué je suis donc passer à 16 colonne (8 colonnes ressources et 8 colonnes quantité) car un craft peux contenir au maximum 8 ressources et donc 8 quantité :D.

Maintenant ce qu'il me faut, c'est que je veux associé ressource1, ressource2, ressource3 ... avec l'id dans la table ressources pour pouvoir récupérer le nom de la ressource, le niveau de la ressource, la catégorie de la ressource et autres. Et malheureusement je ne vois pas comment faire.

Ok, si j'ai tout capté : tu as une table équipements (dans ton exemple, la petite amullete qui a l'ID 39), à qui tu associes une table crafts qui liste la recette pour faire cet équipement (dans ton exemple toujours, la petite amulette a besoin d'une unité de la ressource 473 qui est du bois de Chataignier) liste de ressources qui se trouve dans une dernière table ressources.

Ce que tu souhaites faire, c'est lorsque tu récupères un élément, tu récupères avec lui sa recette ainsi que l'ensemble des ressources associées à cette recette.

Je sais j'insiste mais cherche vraiment à comprendre ton cas pour t'aider du mieux que je peux !

Pour revenir au HasAndBelongsToMany qui est la solution, ca te donnerais 3 tables:

Table 1: Craft // Tu stocke tout tes Craft
Table 2: Ressource // Tu stocke toutes tes ressources potentielles
Table 3: Crafts_Ressources // Tu stocke uniquement la relation des deux

C'est comme dans un blog pour des Tags par exemple, tu as un article qui peut avoir plusieurs Tag, mais un Tag peut appartenir à plusieurs article, dans ton cas c'est exactement la même chose. Un craft peut avoir plusieurs ressource, et tes ressources peuvent appartenir à plusieurs Craft.

En déclarant cette relation, et la table de liaison "Crafts_Ressources", Cake lors des enregistrement de tes crafts ou tu indique qu'ils ont en relations des ressources va enregistrer uniquement la relation dans la fameuse table Crafts_Ressources. Lorsque tu feras un Find il va rechercher dans cette table toute les relations qu'il trouve avec Ressources

La table Crafts_Ressources doit être composé seulement de:

id // Clé primaire
Craft_id
Ressource_id

Du coup à la place de te limiter à 8 associations comme tu le fais, tu pourras en associer un nombre infini. Pour gérer le nombre de la même Ressource nécessaire pour un craft, il suffira de faire une boucle sur le save() du nombre de ressource requise.

Grafikart à fais un très bon tuto la dessus dans la formation de cakephp tu devrais jeter un oeil.
http://www.grafikart.fr/formations/cakephp/associations-model

la doc: http://book.cakephp.org/2.0/fr/models/associations-linking-models-together.html#hasandbelongstomany-habtm

Si toutefois tu penses toujours que ce n'est pas la solution, c'est qu'il faut que tu repense entièrement tes tables, envoi nous un descriptif de toutes tes tables et relation, je suis sûr qu'il y a des petites erreurs de conception, et que tu peut optimiser grandement tes requêtes, et te faciliter le boulot par la suite. Fais nous un plan sur PS ou simplement un descriptif écris détaillé s'il te plait que l'on puisse comprendre et aider plus simplement.

C'est toujours désagréable de se faire entendre dire que l'on fais des erreurs ou que l'on a mal commencer, mais justement c'est en acceptant cette aide que les autres proposent que l'on avance ;)

Tchoupi
Auteur

Merci pour ton aide ;)

Je vais regarer ça de plus près ce soir après le boulot.
J'accepte les erreurs, justement. Si tout était parfait ça serai moin marrant et c'est en faisant des erreurs que l'on comprends mieux.
Je vais regarder le tutoriel ce soir aussi.

Tchoupi
Auteur

Pour repondre a Wa3aR oui cest tout a fais ca :).

Tchoupi
Auteur

J'ai créer la table crafts_ressources tel quel :
Voir la table

Je l'ai rempli, elle contient du coup le craft et la ressource

Tu nous donneras le résultat. Normalement si tu as bien suivis les conventions Cake "Nom de table, Relations dans les models" lorsque tu feras un find sur Craft contain Ressources, il te donneras un tableau de toute les associations.

Tchoupi
Auteur

Heu je viens de comprendre oulah...
Tu as mis plus haut ceci :
Du coup à la place de te limiter à 8 associations comme tu le fais, tu pourras en associer un nombre infini. Pour gérer le nombre de la même Ressource nécessaire pour un craft, il suffira de faire une boucle sur le save() du nombre de ressource requise.

J'ai surement pas précisé qu'une ressource peut-être demandé 500 fois
Sur l'exemple ci-dessous il demande simplement une fois la ressource 473 et une fois la ressource 441
Exemple ici

Mais Sur cette exemple
il demande 500 fois la ressource 384, 300 fois la ressources 881 etc...

ça va faire un peu beaucoup de save non ?

Pour ma part, je dirais que tu dois avoir une table equipements et une table ressources. Tu auras ensuite une table equipements_ressources qui sera ta table crafts.

Je structurerai la table equipements_ressources de cette manière :

equipement_id, ressource_id, quantite

Au niveau de tes modèles tu auras ça :

// Ton model Equipement.php 
class Equipement extends AppModel {
    public $hasMany = array("Craft"); 
    public $hasAndBelongsToMany = array("Ressource");      
}
// Ton model Ressource.php 
class Ressource extends AppModel {
    public $hasMany = array("Craft"); 
    public $hasAndBelongsToMany = array("Equipement"); 
}
// Un model Craft.php qui utilisera la table de liaison 
class Craft extends AppModel {
    public $useTable = "equipements_ressources"; 
    public $belongsTo = array("Equipement", "Ressource"); 
}

Ensuite pour récupérer le craft d'un équipement, tu n'auras qu'à mettre en condition l'ID de l'équipement. Par exemple si tu es au niveau de ton controller EquipementController.php :

$this->Equipement->Craft->find("all", array(
    "conditions" => array("equipement_id" => "ton_id"), 
)); 

Et le tour est joué :) faut juste savoir que cette configuration te permet d'avoir un seul craft par équipement. Si tu veux en avoir plusieurs (on ne sait jamais !) tu n'auras qu'à ajouter une colonne "nom" ou quelque chose du genre à ta table equipements_ressources.

Voila j'espère que ça t'aidera ^^

Tchoupi
Auteur

J'ai pensé à ça aussi Wa3aR.

Je vais vous montrer ou va toutes ces infos et pourquoi je l'ai veux.
Image supprimée

Et la requête pour les afficher :

$equipements = $this->Equipement->find('all', [
            'conditions' => ['Equipement.type' => 1]
            'order' => ['Equipement.level DESC']
            ]
        );

Ca ne change rien à ce que tu veux faire, avec le $this->Equipement->Craft->find("all") tu récupères à la fois les infos sur ton équipement (l'image, la description, etc.) et l'ensemble des ressources qui lui sont associées. Si tu stockes la requête dans une variable $data par exemple, tu auras $data["Equipement"] et $data["Ressources"] que tu pourras exploiter avec un foreach.

Tchoupi
Auteur

Ah ouais d'accord, j'ai encore pas mal à apprendre alors...
Merci énormément pour votre aide, je vais tester tout ça
Faut que je retranscris avant toutes les quantités dans ma table de laision.

La solution de Wa3aR est très bonne, j'avais un peu lu en diagonale les précédents post mais c'est exactement ce qu'il faut. Ne t'en fais pas pour le nombre d'entrée, les base de donnée sont justement faites pour ça ;)

Tchoupi
Auteur

Par contre je récupérais aussi la panoplie qui était associé à mon équipement avec un belongsTo.
Dans ma table equipement j'ai une colonne panoplie avec l'id de la panoplie à laquelle il est associé.
Et du coup je récupérais le nom de la pano et d'autres info maintenant avec la requête

$this->Equipement->Craft->find('all")

Comment je pourrais faire pour retrouver l'association entre mes deux models equipement et panoplie ?
Je cherche actuellement et je regarde la doc aussi.

J'ai remarquer aussi que dans ma vue Exemple de la page Amulette (ça c'était avant)
Car ma vue est englober par un foreach :

$equipements = $this->Equipement->Craft->find('all', [
            'conditions' => ['Equipement.type' => 1],
            'order' => ['Equipement.level DESC']
            ]
        );

Car ce n'est pas une vue pour afficher uniquement un seul équipement.

Maintenant mes items se répète autant de fois que j'ai de nombre de craft :/

Edit :
Je pense que le mieux est que je fasse deux requêtes à mon avis, je vais tester.

Edit2 :

En fait non il n'y a rien qui va xD, enfin si ça fonctionne mais uniquement quand on affiche un seul item.

Il faut que tu gères tes associations dans tes find() grâce au comportement containable je m'explique:

//Dans le Model Equipement

class Equipement extends AppModel {
    public $actsAs = array('Containable');
}
//Dans ton controller Equipements
$this->Equipement->recursive = 1;  // 1 ou 2 Le récursive gére la profondeur d'association pour ton find ou -1 pour aucune assoc
$this->Equipement->contain('Craft');
$equipements = $this->Equipement->find('all', [
            'conditions' => ['Equipement.type' => 1],
            'order' => ['Equipement.level DESC']
            ]
        );

Normalement ça va te sortir un tableau des equipement dans lequel il y aura un tableau des Crafts associés et suivant le niveau de recursivité "1ou2" les Ressources associées au Craft. Je suis depuis un petit moment sur Cakephp3 et je dois dire que les requêtes sont bien plus facile à maitriser meme si elle paraisse plus compliquée à aborder.

Tchoupi
Auteur

Je vais tester. CaiePHP 3 apporte vraiment de changement ou pas ? elle n'est pas encore en version pour le developpement poue l'instant.

d'ici quelques semaines la 1.0 finale sortira

Le comportement Containable est disponible dans la version 2 de CakePHP, ce n'est pas la peine de passer à la version 3 pour l'utiliser.
Pour information, il n'est pas nécessaire de définir la récursivité lorsque l'on utilise le Behavior Containable, étant donné qu'il ne fera les associations que lorsque l'on défini la clé contain dans les requêtes de sélection.
Donc dans ton fichier AppModel, tu peux mettre ceci :

class AppModel extends Model {

    public $recursive = -1;

    public $actsAs = ['Containable'];
}

De cette manière, CakePHP ne fera les associations que lorsque tu définiras la clé contain dans tes requêtes avec les modèles passées dans le tableau de celle-ci.

Je n'ai pas dis qu'il devais passer sur Cake3, mais simplement que les requêtes sont plus lisibles et mieux gérable. Je n'ai repris que le code de la doc et juste changer certains nom pour la récursivité, mais je pense que c'était surtout le moment de lui parler de ce qu'est la recursivité et la notion de profondeur comme dans le titre de son sujet.

Tchoupi
Auteur

Je comprends un peu mieux. Je vais commencer à tester tout ça.

Est ce que quand Cake 3 sortira, il sera recommandé de passer dessus ?

Tchoupi
Auteur

J'ai tester pas mal de truc en vain...

Voici la requête que j'ai actuellement :

        $equipements = $this->Equipement->find('all', [
            'conditions' => ['Equipement.type' => 1],
            'contain' => ['Panoplie', 'Ressource'],
            'limit' => 2,
            'order' => ['Equipement.level DESC']
            ]
        );

la limit => 2 est uniquement là pour les test j'ai juste pour éviter que je récupère les 200 équipements de type 1.

Voici le ce que cela me retourne sous forme de tableau pour mieux comprendre.

Array
(
    [0] => Array
        (
            [Equipement] => Array
                (
                    [id] => 9464
                    [id_image] => 1142
                    [type] => 1
                    [name] => Kralamansion
                    [level] => 194
                    [statsTemplate] => 7d#fb#15e#0#1d100+250,7c#1f#28#0#1d10+30,b2#6#a#0#1d5+5,8a#15#1e#0#1d10+20,75#2#0#0#0d0+2,b0#15#1e#0#1d10+20,b6#1#2#0#1d2+0,6f#1#0#0#0d0+1,73#4#5#0#1d2+3,70#b#f#0#1d5+10
                    [pod] => 30
                    [panoplie_id] => 97
                    [condition] => 
                    [description] => Cette effrayante amulette est longtemps restée cachée dans un manoir perdu. Autrefois vivante, elle voulait conquérir le monde. Finalement elle n'aura que votre cou. La pauvre.
                )

            [Panoplie] => Array
                (
                    [id] => 97
                    [name] => Panoplie Ventouse
                    [items] => 8877, 8991, 9464
                    [bonus] => 124:20;124:20,111:1;;;;;;
                )

            [Ressource] => Array
                (
                    [0] => Array
                        (
                            [id] => 8801
                            [type] => 55
                            [name] => Etoffe de Ouassingue
                            [level] => 80
                            [statsTemplate] => 
                            [pod] => 1
                            [panoplie_id] => -1
                            [condition] => 
                            [EquipementsRessource] => Array
                                (
                                    [equipement_id] => 9464
                                    [ressource_id] => 8801
                                    [quantite] => 45
                                )

                        )

                    [1] => Array
                        (
                            [id] => 8808
                            [type] => 55
                            [name] => Etoffe du Roissingue
                            [level] => 200
                            [statsTemplate] => 
                            [pod] => 1
                            [panoplie_id] => -1
                            [condition] => 
                            [EquipementsRessource] => Array
                                (
                                    [equipement_id] => 9464
                                    [ressource_id] => 8808
                                    [quantite] => 28
                                )

                        )

                    [2] => Array
                        (
                            [id] => 8807
                            [type] => 15
                            [name] => Jus de Ouassingue
                            [level] => 80
                            [statsTemplate] => 
                            [pod] => 1
                            [panoplie_id] => -1
                            [condition] => 
                            [EquipementsRessource] => Array
                                (
                                    [equipement_id] => 9464
                                    [ressource_id] => 8807
                                    [quantite] => 13
                                )

                        )

                    [3] => Array
                        (
                            [id] => 8813
                            [type] => 15
                            [name] => Ventouse du Kralamoure géant
                            [level] => 999
                            [statsTemplate] => 
                            [pod] => 1
                            [panoplie_id] => -1
                            [condition] => 
                            [EquipementsRessource] => Array
                                (
                                    [equipement_id] => 9464
                                    [ressource_id] => 8813
                                    [quantite] => 4
                                )

                        )

                    [4] => Array
                        (
                            [id] => 8809
                            [type] => 15
                            [name] => Culotte à l'envers du Roissingue
                            [level] => 200
                            [statsTemplate] => 
                            [pod] => 1
                            [panoplie_id] => -1
                            [condition] => 
                            [EquipementsRessource] => Array
                                (
                                    [equipement_id] => 9464
                                    [ressource_id] => 8809
                                    [quantite] => 1
                                )

                        )

                    [5] => Array
                        (
                            [id] => 8812
                            [type] => 15
                            [name] => Encre du Kralamoure géant
                            [level] => 999
                            [statsTemplate] => 
                            [pod] => 1
                            [panoplie_id] => -1
                            [condition] => 
                            [EquipementsRessource] => Array
                                (
                                    [equipement_id] => 9464
                                    [ressource_id] => 8812
                                    [quantite] => 1
                                )

                        )

                )

        )

... et ainsi de suite pour les autres équipements

Mon but c'est d'afficher tous les équipements et comme on peux voir chaque équipement se craft.

Du coup dans ma vu j'ai fais un foreach de $equipements pour afficher mes items. Et il faudrait du coup que j'arrive à afficher pour chaque items leurs crafts qui à mon avis je dois les mettre dans un foreach.

Quand tu dis ça: Et il faudrait du coup que j'arrive à afficher pour chaque items leurs crafts, tu veut dire que tu voudrais afficher pour tout les équipements chaque ressources nécessaire ainsi que la quantité, si je comprend bien?

Si c'est bien ça, dans ta vue il ne te reste plus qu'a faire ça:

<?php foreach($equipements as $equipement):?>
    <?php foreach($equipement['Ressource'] as $ressource):?>

        <div class='craft-item'>Nom de la ressource : <?= $ressource;?> quantité : <?= $ressource['EquipementsRessource']['quantite'];?></div>

    <?php endforeach;?>
<?php endforeach;?>
Tchoupi
Auteur

Ah d'accord, je comprends mieux...
ça a l'air de fonctionné à merveille, j'en reviens pas ...
ça fais une semaine que je suis là dessus...

Je vous remercie tous pour votre aide.
Je ne met pas encore le sujet en résolu car je n'ai pas fini de traiter mes données.

Edit : Je me suis permis de supprimer quelques images que j'avais mis dans des messages précédent, car le projet est encore en développement.

Tchoupi
Auteur

Bon j'ai réussi ce que je voulais faire, mais je viens de remarquer que j'ai un petit problème dans un endroit je m'explique :

Dans ma base de donnée j'ai plusieurs tables qui sont 'consommables', 'equipements', 'ressources'.

Chaque équipement à un craft.
Les crafts sont des ressources, mais j'avais oublié que certain crafts avait aussi des équipements et des consommables.

Donc en faite, quand il tombe sur l'id d'un équipement ou d'un consommable, il va le rechercher dans la table ressources qui bien sûr sera un échec car il sera pas, il faudrait aller chercher aussi dans équipements et dans consommables.

Du coup j'ai modifier ma base ressources consommable et equipements et je les aient rassembler dans la même tables qui s'appelle maintenant équipements, jusqu'à là tout fonctionne sauf qu'il ne fais plus du coup les liaisons avec les crafts car la table ressources n'existent plus.

Pour que l'affichage des crafts fonctionne j'ai donc dupliquer ma table équipements et je les nommée ressources.
Du coup maintenant, même si le craft est composé d'un équipement, d'une ressources ou encore d'un consommable il le trouvera, mais ceci m'oblige lors de l'ajout d'un nouvel équipements deux faire deux save. L'une dans la table équipements et l'autre dans la table ressources.

Je ne sais pas si j'ai réussi à me faire comprendre lol, c'est assez chaud à expliquer à l'écrit.

Ce que je fais bcp dans mes table d'association, c'est ça:

id
... // autre champ nécessaire par exemple la quantité dans ton cas
ref
ref_id
created

Apres dans les models je déclare mes relations en y passant une condition:[ 'ref' => 'equipement'] par exemple comme ça dans cette table je peut y enregistrer une autre association par exemple dans ton cas les consommable et en déclarant encore une condition ['ref' => 'consommable']

Tu peut mettre des conditions dans tes associations, c'est souvent aussi très utile quand tu fais une association sur la meme table du style dans une structure en arbre avec un champ 'Parent_id' .

Pour prendre un exemple concret je suis entrain de dev, un cms de forum sur cake3 en ce moment, j'ai une table notifications qui me sert à stocker toutes les notifications lors d'un envoi de message à un user, ou que quelqu'un a répondu à un topic, ou à aimer un message, etc.... J'en ai une autre avec des ratings ou je stocke les j'aime/j'aime pas et autre à la fois sur des topic/message/article. Je pense que ça pourrais être pareil dans ton cas une seule table qui stockerais les associations:

id :1
craft_id:1
ref: 'equipement'
ref_id: 10
quantite: 2

id:2
craft_id:3
ref: 'equipement'
ref_id: 12
quantite: 20

id:3
craft_id:1
ref: 'consommable'
ref_id: 123
quantite: 5

J'ai un peu de mal à comprendre ton cas perso, mais est-ce que cette structure pourrais aller?

Tchoupi
Auteur

Je vois ce que tu veux dire, je pense que sa pourait aller mais pas sûr.

J'aurais juste une question, je fais actuellement dans mon model equipement un HABTM ressource.
Est-ce-que se serait possible de faire directement un equipement HABTM equipement ? toujours via la table de relation equipements_ressources ?

Bonjour.
Techniquement, je ne pourrais pas te dire si c'est faisable ou non, mais logiquement, il ne faut pas faire de liaison HABTM sur la même table.