IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

[POO] Requêtes croisées


Sujet :

Langage PHP

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    117
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2007
    Messages : 117
    Points : 80
    Points
    80
    Par défaut [POO] Requêtes croisées
    Bonjour,

    Voici la situation dans laquelle je suis, l'état de mon raisonnement et mes questions ...

    J'aimerai considérer chacune de mes tables comme étant des objets et donc, pour chacune de mes tables, créer une classe contenant une fonction pour le SELECT, une fonction pour le INSERT, etc.

    Bien entendu, mes tables sont relationnelles. Exemple :

    TABLE langue est composée des champs :
    - id
    - code
    - langue
    - statut

    TABLE statut est composée des champs :
    - id
    - statut

    Comme vous l'aurez comprit, le champ statut dans langue contient un ID renvoyant vers l'id de la table statut. Je n'ai par contre pas envie de trop lier mes 2 tables dans une seule et même classe.

    Ma réflexion était donc la suivante :

    - je crée une classe LANGUE avec une fonction SELECT qui fait (en gros) un SELECT * FROM langue et qui stocke le tout dans un array ;
    - je crée une classe STATUT avec une fonction SELECT qui fait (en gros) un SELECT * FROM statut et qui stocke également le tout dans un array ;
    - je crée une troisième classe avec une fonction qui effectuerai un "croisement" entre ces 2 tableaux pour permettre de générer un nouveau tableau qui donnerait comme résultat l'équivalent d'une requête comme celle-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT langue.id AS id, langue.code AS code, langue.langue AS langue, statut.statut AS statut 
    FROM langue, statut 
    WHERE langue.statut = statut.langue
    et qui retournerait donc quelque chose comme :

    1 FR Français OK
    2 EN Anglais NOK
    3 NL Néerlandais OK
    ...

    Mes questions :

    1. est-ce que ma réflexion "orientée objet" vous semble cohérente ou est-il tout simplement préférable de faire, dans ma fonction SELECT de ma classe LANGUE, la query complète ?

    2. si ma réflexion est correcte, est-il possible de générer ce "croisement" de requête ?

    3. en travaillant de la sorte (en gros, faire une query globale et faire un tri ou un filtre éventuel sur les résultats par la suite) ; n'est-ce pas un risque de ralentir le site puisqu'on ne fait aucun filtre dans la requête SQL elle-même ?

    4. avez-vous une meilleure idée :-) ?

    Merci d'avance,
    Olivier

  2. #2
    Membre confirmé
    Inscrit en
    Février 2005
    Messages
    419
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Février 2005
    Messages : 419
    Points : 532
    Points
    532
    Par défaut
    Question fort intéressante, je suis confronté au même dilemme chaque fois que je créé un nouveau site.

    Une piste que j'ai peut être trouvé (mais pas encore exploitée à fond par manque de temps ) serait l'utilisation de requêtes écrites par un objet (voir le package Zend_Db_Select de Zend Framework).
    ça permettrait de détecter dans la méthode de chargement qu'on essaye de faire une jointure sur une autre table et qu'on a donc besoin de remplir des attributs supplémentaires.

    Bon dit comme ça ce n'est peut être pas très clair mais vu que c'est encore en réflexion dans ma tête j'arrive pas à faire mieux pour le moment

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    117
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2007
    Messages : 117
    Points : 80
    Points
    80
    Par défaut
    Mais donc l'idée en POO est meilleure de ne pas inclure dans le SELECT de ma classe LANGUE des éléments de requête d'une autre table ... histoire de ne pas lier ces 2 tables ensemble et donc de permettre une modif de la structure des tables sans devoir passer dans chacune des classes ou peut-on tout de même le faire ?

    Enoncé comme cela, ça me parait logique ...

    Mais je ne vois pas comment faire pour, au départ de 2 tableaux :

    - 1 contenant les infos de la table statut :
    ID STATUT
    1 OK
    2 NOK

    - 1 contenant les infos de la table langue
    ID CODE LANGUE STATUT
    1 FR Français 1
    2 EN Anglais 2

    Obtenir un seul tableau faisant la jointure ...

    Et puis, toujours ce problème de performance DB ! Dans ces exemple, on fait un SELECT global sur des tables contenant peu d'info donc pas de souci ... mais que donnerait un SELECT global dans une table contenant des miliers de records ?

    Doit-on dans ce cas, prévoir x fonctions de SELECT différente dans mes classes pour prévoir les différentes logiques applicatives (tantôt, j'aurai besoin de faire un filtre sur le code langue, tantôt sur le statut, etc.) ?

    Désolé pour toutes ces questions, mais venant du monde "procédural" ... c'est un grand changement pour moi :-)

  4. #4
    Membre confirmé
    Inscrit en
    Février 2005
    Messages
    419
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Février 2005
    Messages : 419
    Points : 532
    Points
    532
    Par défaut
    Oui c'est sûr que pour faire quelque chose de parfait, les éléments de ta table2 n'auraient rien à faire dans la classe correspondant à ta table1.

    J'avais fais quelque chose comme ça à une époque mais niveau performances j'explosais tout (pas dans le bon sens...)

    L'idée d'utiliser un composant tel que Zend_Db_Select pour l'écriture des requêtes permet justement de rendre celles-ci modulables. Ce qui peut déjà aider pour la réalisation de filtres simples.

    Ensuite pour l'histoire des jointures, il faudrait que ta table1 ait un moyen de connaitre le nom des attributs de la table2 ($table2->getColNames() ???). Comme ça si tu détectes une jointure sur la table2 dans ta requête, tu vas chercher la liste des attributs nécessaires automatiquement.

    Comme ça chaque classe conserve bien en son sein seulement ses attributs à elle et pas ceux des autres. Il faut juste qu'elle sache communiquer avec les autres pour leur demander des informations sur leur structure.

    C'est marrant dit comme ça ça parait super simple mais quand je vais rentrer chez moi ce soir et me mettre à coder un truc comme ça je suis sûr que je vais encore me poser 200 questions à la seconde

  5. #5
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 249
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 249
    Points : 1 565
    Points
    1 565
    Par défaut
    Zend_Db_Select a l'air d'avoir ses limites... j'ai du mal a voir comment générer quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    select count(*)
    from t1, t2
    where t1.champ1 = 1
    AND (t1.champ2 = 2 OR t1.champ3 = 3)
    OR (t2.champ1 = t1.champ4)
    Bref :

    * Le parenthesage des conditions AND OR
    * Les clauses de jointures dans un OR

    Il est possible de faire un framework permettant de gérer tout ca... j'suis en train d'en faire un ^^ mais c'est chaud en effet.
    Une idée intéressante c'est le concept de RELATION entre les objets.

    2 objets(=table) "liés" le sont au niveau structurel (par une contrainte dans la base) ou au niveau fonctionnel (dans l'esprit du developpeur) mais dans les deux cas, ce lien peut etre exprimé dans le code.
    Ainsi, il n'est pas necessaire d'indiquer explicitement la clause de jointure, on peut faire quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $aSelect = new SelectQuery($monObject1);
    $aSelect2 = new SelectQuery($monObject2);
    $aSelect->join($monObject2);
    voir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $aSelect->join($monObject2, $monObject1::RELATION_1_WITH_OBJECT2);
    dans le cas ou les objets ont plusieurs liens différents entre eux.

    Le framework peut alors ajouter automatiquement la clause de jointure.

    On peut aussi imaginer quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $aSelect->WhereAnd($monObject1, $monObject2, NOM_RELATION);
    pour gérer les clauses de jointures dans un OR

  6. #6
    Membre confirmé Avatar de SphynXz
    Développeur Web
    Inscrit en
    Mars 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Âge : 38

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Mars 2008
    Messages : 439
    Points : 547
    Points
    547
    Par défaut
    je pense qu'avec une très bonne notion des fonctions de tableaux tu pourra arriver à tes fins très aisément .

    La création d'une classe abstraite qui sera décliner pour toute tes tables. une jointures de deux select ne se ferait qu'avec un array_intersect() par exemple

  7. #7
    Membre confirmé
    Inscrit en
    Février 2005
    Messages
    419
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Février 2005
    Messages : 419
    Points : 532
    Points
    532
    Par défaut
    Oui ça je te l'accorde Zend_Db_Select a ses limites. D'ailleurs je ne l'utilise pas, j'ai mon truc perso pour ça (qui n'est pas forcément mieux mais plus adapté à mes besoins). Je l'ai juste donné à titre d'exemple vu qu'il est assez répandu et qu'il y a une bonne doc dessus pour expliquer.

    Ensuite pour le reste on est d'accords, ta réflexion semble rejoindre la mienne dans le fond mais tu as eu plus de courage que moi et tu es plus rentré dans les détails

    Donc en gros si je comprend bien, tes objets seraient capables de fournir des informations sur la structure de leurs tables correspondantes (attributs, clé étrangères ...) et tu aurais un objet "SelectQuery" qui te monterait une requête à partir de ça. C'est ça ?

    L'idée me semble pas mal mais la question que je me pose c'est qu'est ce que va te retourner ton objet SelectQuery après son exécution ? Un objet "ligneTable1", un objet "ligneTable2" ?

  8. #8
    Membre confirmé
    Inscrit en
    Février 2005
    Messages
    419
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Février 2005
    Messages : 419
    Points : 532
    Points
    532
    Par défaut
    Une petite chose au passage : ce qu'on est en train d'essayer de faire ne correspond il pas tout simplement au design pattern "active record" ?

  9. #9
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 249
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 249
    Points : 1 565
    Points
    1 565
    Par défaut
    en fait j'ai des select spécialisés du genre (les classes sont générées automatiquement avec un meta fichier contenant le schema ;o) :

    $aSelect = new SelectObject1();
    $aSelect2 = new SelectObject2();

    // cas "simple"
    $aSelect->whereAnd($aSelect->getChamp1(), OPERATEUR_EGAL, $aSelect2->getChamp1());

    // join automatique
    $aSelect->whereAndJoin($aSelect2->getRelation1());

    Pour ce qui est du type de retour, ca depend de la methode utilisée pour interroger la base.
    Ca peut etre un objet (completement ou partiellement chargé)
    Ca peut etre un tableau indexé (si les données du select proviennent de plusieurs tables, ou contiennent des count(), max(), etc...)

    Enfin rien ne m'empeche de "reconstruire" facilement un objet a partir du tableau indexé ensuite ;o)

    Les parentheses sont gérés par l'ajout de select :

    $aSelect = new SelectObject1();
    $aSousSelect = new SelectObject1();
    $aSousSelect->whereOr(..., ..., ...); // condition1
    $aSousSelect->whereOr(..., ..., ...); // condition2
    $aSelect->whereAnd(..., ..., ...); // condition3
    $aSelect->whereAnd($aSousSelect);

    va générer
    WHERE condition3 AND (condition1 OR condition2)

    je ne connais pas le pattern "active record", faudrait que je me renseigne ;o)
    Edit : oui, c'est un peu ca (apres m'etre renseigné ^^)

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    117
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2007
    Messages : 117
    Points : 80
    Points
    80
    Par défaut
    et par rapport au fait de faire une query globale et d'ensuite faire un tri dans le tableau ...

    qu'en penser au niveau performance ?

    est-il préférable de créer des fonctions de SELECT différentes selon la logique applicative désirée ou peut-on imaginer taper dans un array, le résultat d'une query globale (genre : SELECT * FROM langue) et ensuite faire des recherches et des filtres dans le tableau ?

    merci encore :-)

  11. #11
    Membre confirmé
    Inscrit en
    Février 2005
    Messages
    419
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Février 2005
    Messages : 419
    Points : 532
    Points
    532
    Par défaut
    Comme je l'ai dis, un query object est bien utile pour faire ce genre de chose. Parce que tu peux très bien imaginer une méthode de chargement qui prend en paramètre des "parts de requête" contenant des instructions de filtre ou de tri.

    Par exemple quelque chose dans le style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $options = new SelectPart();
    $options->orderBy('nom', 'DESC');
    $options->limit(0, 10);
     
    $mesObjets = MaClasse::getList($options);
    ça offre un peu plus de modularité. Bon ensuite je ne suis pas sûr que ce soit conceptuellement correct d'avoir des traitements direct sur la couche données au milieu du reste comme ça. Donc il va peut être falloir rajouter quelque chose entre tout ça, mais dans les grandes lignes ça doit ressembler à mon petit bout de code là.

    En tout cas tout charger dans un tableau et faire les filtres après je pense que c'est très très mal. Si tu as 10 enregistrements voire 1000 ça va, mais si tu as une base de données gigantesque ça va faire très mal

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    117
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2007
    Messages : 117
    Points : 80
    Points
    80
    Par défaut
    Bon j'avoue, j'utilise quasi pas les tableaux et j'ai dur dur au début ...

    j'ai ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    class Database {
     
    	var $query;
    	var $result;
    	var $result_arr;
     
    	function query($q) {
    		$this -> query = mysql_query($q);
     
    		return $this -> query;
    	}
     
    	function result($r) {
    		while ($result = mysql_fetch_array($r)) {
    			$result_arr = $result;
    		}
     
    		return $result_arr;
    	}
     
    }
    et ceci est mon fichier test :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    $database = new Database();
     
    $select = "SELECT * FROM langue";
    $result = $database -> result($database -> query($select));
     
    print_r($result);
    le résultat du print_r donne ceci :

    Array ( [0] => 1 [id] => 1 [1] => FR [code] => FR [2] => Français [langue] => Français [3] => Y [defaut] => Y [4] => 1 [statut] => 1 )

    Pq ai-je chaque fois les trucs en double ... j'ai donc [0] => 1 et ensuite [id] => 1 ... idem, j'ai d'abord [1] => FR et ensuite [code] => FR

    comment faire pour avoir uniquement le [id] => 1, [code] => FR, etc. ?

    merci

  13. #13
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 249
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 249
    Points : 1 565
    Points
    1 565
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    	function result($r) {
    		while ($result = mysql_fetch_array($r)) {
    			$result_arr = $result;
    		}
     
    		return $result_arr;
    	}
    Tu écrase $result_arr a chaque fois, ce n'est pas correct.
    fetch_array renvoie 2 fois le resultat, une fois avec un index numérique, une fois avec un index correspondant au nom de la colonne. _fetch_assoc est mieux ;o) (uniquement le nom de la colonne)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    	function result($r) {
    		$result_arr = array();
    		while ($result = mysql_fetch_assoc($r)) {
    			$result_arr[] = $result;
    		}
     
    		return $result_arr;
    	}

  14. #14
    Membre confirmé
    Inscrit en
    Février 2005
    Messages
    419
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Février 2005
    Messages : 419
    Points : 532
    Points
    532
    Par défaut
    Bon alors je vois carrément pas le lien entre ton bout de code et le sujet mais c'est pas grave

    Remplace :
    mysql_fetch_array

    par
    mysql_fetch_assoc

  15. #15
    Expert éminent
    Avatar de GrandFather
    Inscrit en
    Mai 2004
    Messages
    4 587
    Détails du profil
    Informations personnelles :
    Âge : 54

    Informations forums :
    Inscription : Mai 2004
    Messages : 4 587
    Points : 7 103
    Points
    7 103
    Par défaut
    Citation Envoyé par Fladnag Voir le message
    Zend_Db_Select a l'air d'avoir ses limites... j'ai du mal a voir comment générer quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    select count(*)
    from t1, t2
    where t1.champ1 = 1
    AND (t1.champ2 = 2 OR t1.champ3 = 3)
    OR (t2.champ1 = t1.champ4)
    Ca ne pose pas de soucis particuliers, le framework prend soin de placer des parenthèses autour de chaque composant de la clause where. Ta requête générant un Zend_Db_Select s'écrira donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $select = $db->select()
                 ->from(array('t1', 't2'),
                        array('COUNT(*)'))
                 ->where('t1.champ1 = 1')
                 ->where('t1.champ2 = 2 OR t1.champ3 = 3')
                 ->orWhere('t2.champ1 = t1.champ4');
    La façon dont Zend a implémenté le pattern ActiveRecord dans Zend Framework vaut le coup qu'on y regarde précisément avant de se lancer dans le développement de son propre framework DB, car c'est tout sauf une tâche triviale.

  16. #16
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    117
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2007
    Messages : 117
    Points : 80
    Points
    80
    Par défaut
    Voila ...

    Ce point est résolu pour ma part en tout cas ;-)

    J'ai développé 2 classes qui me permettent de gérer et de générer des requêtes SELECT de manière "automatisée" et surtout qui sont capables de prendre en compte la structure de ma DB.

    Un peu long à expliquer ici mais j'essayerai de prendre le temps un jour ;-)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 05/10/2005, 16h07
  2. Pb sur Requête croisée Access
    Par Proview dans le forum Access
    Réponses: 3
    Dernier message: 04/10/2005, 17h33
  3. [HIBERNATE] - Débutant - Requête croisées
    Par charlot44 dans le forum Hibernate
    Réponses: 19
    Dernier message: 14/06/2005, 16h06
  4. Besoin d'aide sur requête croisée
    Par keawee dans le forum Access
    Réponses: 7
    Dernier message: 18/11/2004, 09h46
  5. Requète croisée et affichage
    Par BakaOnigiri dans le forum SQL
    Réponses: 2
    Dernier message: 31/10/2002, 11h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo