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 :

conseil sur les requêtes SQL en MVC [PHP 5.3]


Sujet :

Langage PHP

  1. #1
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut conseil sur les requêtes SQL en MVC
    Bonjour,

    Je suis en train de développer un site en php5 sur le modèle MVC (mon premier) et je suis confronté à un problème que vous aurez certainement déjà rencontré et pour lequel j'espère que vous pourrez me conseiller.

    A certains moment, je dois exécuter cette requête :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM table

    et à d'autres, je dois exéctuer :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM table WHERE colonne1 = 'param1' AND colonne2 = 'param2' AND colonne3 = 'param3'

    Je me dis qu'une seule méthode devrait pouvoir me remonter les résultats d'une ou de l'autre requête et que je n'ai pas besoin de faire 2 méthodes (getAll() et getSpecific()). Mon idée est donc de faire un méthode avec un tableau de paramètre qui permettra de générer la requête adéquate (complète ou restreinte). Mes interrogations sont les suivantes dans la mesure où les éléments propres à la bd doivent rester dans la couche modèle, je ne vais pas utiliser les noms de colonne dans le controller (dans le tableau de paramètre) ! Dans ce cas, le lien entre mes valeurs de restrictions (définies dans mon controller) et les colonnes de ma table (utilisées dans le model) ? Evidemment, je pourrai faire le lien entre les index de mon tableau et les nom de colonnes, mais je ne trouve pas cela très fin ! Plus généralement, si ne dois pas utiliser les noms de mes colonnes ailleurs que dans les model, comment faire, lorsqu'un model renvoi un tableau de résultats, pour les utiliser dans mon controller correspondant, je vais être obligé de faire $r['nomcolonne'], non ?

    merci de vos lumières !

  2. #2
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Salut,

    A un moment ou à un autre il faudra bien lier tes données aux besoins d'un traitement.
    Pour ne pas utiliser les noms des colonnes tu peux utiliser des alias à la place. Il est tout à fait possible que l'alias corresponde au nom de la colonne mais ce n'est pas nécessaire.

    L'intérêt c'est que tu peux très bien avoir ce genre de requête :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT a.nom, b.nom FROM a INNER JOIN b...
    dans ce cas, tu devras avoir des alias pour repérer tes colonnes
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT a.nom AS nom_parent, b.nom AS nom_enfant FROM a INNER JOIN b...
    Vu que tu ne peux pas présupposer des ressources DB à ta disposition, il est préférable d'uniformiser le tout en utilisant toujours des alias et bâtir de système de génération auto SQL sur cette approche.

    J'ai codé un système de cet acabit (un exemple) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $data = Membre::select(ExecParams $params);
    La fonction select() expose en commentaire une liste d'alias et attend une classe de paramétrage, en fonction de son contenu, elle est capable de valider les paramètres un à un et d'écrire un SQL valide ou si la ressource SQL est trop complexe (style code SQL monstrueux, hyper optimisé) de remplir les blancs sans altérer le code SQL.

  3. #3
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    Merci de ton retour.
    Selon toi, les alias de colonnes sont donc le lien entre les controllers et les models ?

  4. #4
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Oui, c'est comme ça que je le vois.
    Tu conserves la flexibilité du côté du modèle d'organiser tes ressources comme tu l'entends et du côté du contrôleur tu n'exposes rien d'autre qui soit différent d'un simple paramétrage du modèle -> situation idéale

  5. #5
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    Humm, j'essaie de prendre un cas concret, histoire de voir si j'ai bien compris :

    voici une requête :

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SELECT
        t1.col1
        t2.col2
        t3.col3
    FROM
        tab1 t1 INNER JOIN
        tab2 t2 ON t1.id = t2.id LEFT JOIN
        tab3 t3 ON t2.id = t3.id
    GROUP BY
        col1

    Cette requête permet de remonter toutes les données. Elle est exécutée par l'appel : maclasse::getData();

    Dès lors que je ne veux récupérer qu'une partie des données, j'exécute la même requête mais avec la clause WHERE suivante :

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    WHERE
        col1 > 10 AND
        col2 = 300

    Ayant déjà une requête qui remonte toutes les infos, le but est la réutiliser - en l'affinant - pour ne récupérer que la partie souhaitée des données.

    Je paramètre donc un tableau dans mon controller avec de transmettre les valeurs WHERE a mon model :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $param[alias_colonne1] = '> 10';
    $param[alias_colonne2] = '= 300';
     
    // pour appeler le model de la façon suivante :
    maclasse::getData($param);
    Dans 2 constantes, j'aurai déclaré :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    define ('alias_colonne1', 'col1');
    define ('alias_colonne2', 'col2');
    et dans la fonction du model, je manipulerai le tableau de paramètre de la façon suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    foreach($param as $key => $value){
       $query .= $key.$value;
    }
    Est-ce que sur le principe j'ai bon ? Par contre la fonction de conversion du tableau de param en WHERE doit être chiadée parce que pour l'instant elle en prend pas en compte un truc du type :
    col1 > 10 AND (col2 < 20 OR col3 > 50) !

  6. #6
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Tes alias devraient apparaître ici
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT
        t1.col1 AS alias_colonne1,
        t2.col2 AS alias_colonne2,
        t3.col3 AS alias_colonne3
    FROM
    Tes alias n'ont strictement rien à voir avec des constantes globales (define(...)).

    Ensuite il est tout à fait possible de définir le signe de comparaison dans la valeur du paramètre (comme tu le fais '> 10'), sauf que cela va du coup légèrement gagner en complexité dans le traitement : tu vas devoir extraire le signe pour ensuite valider si la valeur de la variable correspond au type attendu par la colonne... Parce qu'il est hors de question dans un système de génération automatique de SQL de ne pas valider les données avant l'exécution de la requête.

    Et pour les clauses très complexes comme : col1 > 10 AND (col2 < 20 OR col3 > 50), ben là il y a pas de mystère : soit tu codes un parseur soit tu codes un système où la clause est créée étape par étape permettant ainsi la validation de chacune des valeurs du critère.

  7. #7
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    Citation Envoyé par rawsrc Voir le message
    Tes alias devraient apparaître ici
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT
        t1.col1 AS alias_colonne1,
        t2.col2 AS alias_colonne2,
        t3.col3 AS alias_colonne3
    FROM
    évidemment, c'était un oubli...

    Citation Envoyé par rawsrc Voir le message
    Tes alias n'ont strictement rien à voir avec des constantes globales (define(...)).
    Humm... Comment faire le lien entre l'alias et la colonne réelle du coup ? parce que si je fais :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT colonne1 AS alias_colonne1 FROM table1 WHERE alias_colonne1 = 10
    ça va pas marcher !

  8. #8
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    C'est parce que tu t'attaques à une montagne là, crois-moi.
    Tu vas arriver très vite à des situations bloquantes que tu devras résoudre et c'est pas simple : garder un système léger utilisable à la volée tout en ayant une base coté traitement et gestion SQL très solide.

    Cette problématique est loin d'être triviale.

    Il faudra abstraire tes tables, gérer la validation, les rattachement des données, les Statement sous forme de Clause et concevoir soigneusement les différents constructeurs de SQL.
    Et tout ça en évitant une usine à gaz qui plomberait les performances. Un vrai défi. Faisable mais compliqué tout de même. Utilise directement la POO, sinon t'es cuit.

    Le but c'est d'arriver à décharger complètement le codeur lambda des contraintes relatives à la base de données.
    Tu accèdes à un entrepôt de ressources DB : tu pioches dedans la requête qui va bien, tu ne te préoccupes pas du tout de son implémentation, tu passes tes paramètres, la ressource valide tout, remonte les erreurs pour chaque variable, sécurise ce qui est nécessaire et hop tu traites de ton côté uniquement le résultat. Un peu comme un semblant d'ORM sauf que ce n'en pas un : c'est un outil entre l'ORM et le tout fait main.

  9. #9
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    Donc au final, le meilleur rapport efficacité / simplicité c'est de faire 2 requêtes ? une générale et une spécifique avec génération du where via le tableau de paramètres passé généré dans le controller et passé au model ?

  10. #10
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Non, c'est la raison pour laquelle je t'ai parlé d'aborder le problème sous forme de clause (tu vas partager 99 % du code entre elles, pas la peine de faire de la redondance)
    Je t'explique :

    Tu vas avoir des Statement :
    • INSERT
    • UPDATE
    • SELECT
    • DELETE
    • CALL

    Et chacun d'eux va embarquer des Clause :
    Pour le SELECT :
    • select
    • from
    • where
    • having
    • group by
    • order by
    • limit

    Comme tu peux voir, certaines de ces clauses vont être réutilisées par d'autres Statement. Là, il faut faire appel à tes connaissances en SQL.

    Donc dans ton cas de figure, tu peux effectivement coder 2 requêtes (une avec et l'autre sans filtrage) mais cela n'est pas nécessaire, si ton système est judicieusement architecturé, cela n'est pas obligatoire.

  11. #11
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    tu pourrais me donner un exemple simple parce que là j'ai du mal à comprendre !

  12. #12
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Qu'est ce que tu ne comprends pas ?

    Par exemple le Statement DELETE, va utiliser les clauses :
    • table
    • where
    • order by
    • limit

    Ces clauses qui vont générer un bout du sql final, sont identiques à chaque statement. L'avantage c'est que tu suis la spec SQL et ensuite tu ne travailles que sur un petit bout du SQL.

  13. #13
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    Donc si je te suis, la partie commune de la requête est stockée dans l'attribut d'un objet ou dans une variable afin d'être partagée et complétée avec d'éventuelles autres clauses (selon le besoin) ? c'est ça ?

    par exemple :
    requête générale :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    $queryBase = '
    select 
    t1.col1 as al1, 
    t2.col2 as al2
    from
    table1 t1 inner join table2 t2 ON t1.id = t2.id';
    Pour ne récupérer qu'une partie des résultats, je récupère $query et lui ajoute les clauses nécessaires :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    public static function getspecific($params){
     
    $query = $queryBase.'
    WHERE
    t1.col1 = \''.$params[1].'\'';
     
    ...
     
    }
    C'est ça ?

  14. #14
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Non, la requête est représentée par un objet. Cet objet embarque une représentation complète et abstraite de la requête.
    Ainsi à l'exécution, la requête lira le paramétrage passé en argument et traduira le tout en SQL.
    En clair, le Statement attends un paramétrage et en fonction de celui-ci, il s'auto-paramétrera pour produire le SQL adéquate.

  15. #15
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    mais cet objet qui représente ma requête c'est à moi de l'implémenter ou existe-t-il des mécanismes tout prêt à paramétrer avec les statements et les clauses de mes requêtes ?

  16. #16
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Ben, c'est à toi à l'implémenter avec tes petites mimines.

    Sinon, tu peux te diriger vers des systèmes tout prêts proche des ORM.

  17. #17
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    ok ! j'ai donc compris que je dois implémenter les fonctions qui convertiront mon objet en requête sql (et qui interpréteront donc les clauses de mes statements...). Par contre, y'a un truc que je pige toujours pas ! Dans ma requête globale et la spécifique, toute la partie SELECT est commune ! Donc si elle est commune c'est qu'elle doit bien être partagée et donc stockée quelque part, non ? Dans un attribut de mon objet ? parce que si je dois redéfinir mon objet à chaque instanciation, à mon sens, c'est la même chose qu'écrire les 2 requêtes distinctement... il n'y a pas de partage de l'élément commun !

  18. #18
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    dans ton cas de figure, il n'y pas de partage : c'est la même ressource qui va d'un côté générer du sql sans la clause where et de l'autre avec (c'est fonction du paramétrage à l'exécution)

  19. #19
    Membre actif Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    707
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 707
    Points : 215
    Points
    215
    Par défaut
    ok, la différence est dans le fait de faire appel à la clause WHERE ou pas...

    Mais pour la liste des colonnes sléectionnées (SELECT colonne1, colonne2 FROM...), celle ci est la même dans les 2 requête (avec et sans clause WHERE), comment me conseilles-tu de l'implémenter ? A première vue, je dirais que ce doit être un attribut d'un objet représentant le statement SELECT (statement composée d'instance des clauses) ?

  20. #20
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Fais flexible :
    ta ressource mets à disposition une liste d'alias sélectionnables, cette information fait aussi parti du paramétrage à l'exécution de manière si demain tu n'as pas besoin de rapatrier par exemple l'alias alias_col2 et ben ton générateur de clause SQL ne devra produire pour cette partie que : select col1 as alias_col1 from ... parce que à l'exécution tu auras paramétré quelque chose dans ce genre ['select' => 'alias_col1']

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 6
    Dernier message: 22/08/2014, 15h57
  2. [Débutant] Question sur les requêtes SQL
    Par Genyuumaru dans le forum ASP.NET MVC
    Réponses: 4
    Dernier message: 08/10/2012, 08h43
  3. Réponses: 4
    Dernier message: 21/04/2012, 12h56
  4. [MySQL] [POO] Sécurité sur les requêtes SQL
    Par Sh4dow49 dans le forum PHP & Base de données
    Réponses: 3
    Dernier message: 24/10/2008, 10h51

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