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

PHP & Base de données Discussion :

Comprendre PDO [Tutoriel]


Sujet :

PHP & Base de données

  1. #41
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    Citation Envoyé par Madfrix Voir le message
    Jamais utilisé FETCH_CLASS alors je suis pas contre un tuto

    Demain pour 10h c'est possible ?

    J'ai maintenant l'habitude d'utiliser le module Zend_Db pour le mapping et je dois dire que parfois on devient un peu fainéant avec et qu'on oublie "les bases"
    j'en avais fait un petit sur FETCH_GROUP :

    PDO offre une option intéressante dans son fetchMode : PDO::FETCH_GROUP qui permet de grouper des entrées par catégorie.

    exemple :

    Code php
    +----+-----------+----------+-------+
    | id | firstname | lastname | city |
    +----+-----------+----------+-------+
    | 1 | Jean | Machin | Paris |
    | 2 | Jean | Truc | Nice |
    | 3 | Paul | Bla | Nice |
    +----+-----------+----------+-------+

    on a envie de trier les utilisateur par ville pour avoir une sortie type :
    Paris
    Jean Machin
    Nice
    Jean Truc
    Paul Bla
    plusieurs solutions :

    Faire une requête DISTINCT sur les villes, et a chaque tour de boucle allé chercher les personnes qui correspondes
    Créer un array temporaire et a chaque tour de boucle ajouter la personne ($tmp[$user['city']][] = $user) , et reboucler ensuite pour créer son tableau
    A chaque tour de boucle vérifier via un buffer si la ville a été mise et afficher les personnes, sinon afficher ville


    la solution PDO :

    Grâce au fetchAll et son option PDO::FETCH_GROUP, on a en sortie directement un array formateé, avec en clé la catégorie et en valeur les entrées correspondantes
    (par contre attention contraiement a PDO::FETCH_COLUMN, on ne choisie pas qui est la catégorie, ca sera uniquement la première colonne)

    exemple de code :

    Code php
    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
    try
    {
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'root');
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
    }
    catch (Exception $e)
    {
        exit($e->getMessage());
    }
     
    $query = $pdo->query("SELECT u.city, u.id, u.firstname, u.lastname FROM test_user u");
     
    if($query)
    {
        $result = $query->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_GROUP);
        print_r($result);
    }
    en sortie
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    Array
    (
        [Paris] => Array
            (
                [0] => Array
                    (
                        [id] => 1
                        [firstname] => Jean
                        [lastname] => Machin
                    )
     
            )
     
        [Nice] => Array
            (
                [0] => Array
                    (
                        [id] => 2
                        [firstname] => Jean
                        [lastname] => Truc
                    )
     
                [1] => Array
                    (
                        [id] => 3
                        [firstname] => Paul
                        [lastname] => Bla
                    )
     
            )
     
    )
    tout simplement

    Si on veux que la catégorie sois un autre champs il suffis de le mettre en premier (le prénom par exemple)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT u.firstname, u.id, u.lastname, u.city FROM test_user u
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    Array
    (
        [Jean] => Array
            (
                [0] => Array
                    (
                        [id] => 1
                        [lastname] => Machin
                        [city] => Paris
                    )
     
                [1] => Array
                    (
                        [id] => 2
                        [lastname] => Truc
                        [city] => Nice
                    )
     
            )
     
        [Paul] => Array
            (
                [0] => Array
                    (
                        [id] => 3
                        [lastname] => Bla
                        [city] => Nice
                    )
     
            )
     
    )
    et on peux bien sur doubler les valeurs pour avoir des entrées complètes, ce qui sera d'ailleurs plus simple, au niveau portabilité et pour différentes sorties

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT u.city, u.id, u.firstname, u.lastname, u.city FROM test_user u
    -- ou version raccourcis
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT u.city, u.* FROM test_user u
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    Array
    (
        [Paris] => Array
            (
                [0] => Array
                    (
                        [id] => 1
                        [firstname] => Jean
                        [lastname] => Machin
                        [city] => Paris
                    )
     
            )
     
        [Nice] => Array
            (
                [0] => Array
                    (
                        [id] => 2
                        [firstname] => Jean
                        [lastname] => Truc
                        [city] => Nice
                    )
     
                [1] => Array
                    (
                        [id] => 3
                        [firstname] => Paul
                        [lastname] => Bla
                        [city] => Nice
                    )
     
            )
     
    )
    il peux y avoir pas mal de combinaison pour les fetch, mais certaine ne marche qu'avec fetch(), d'autre qu'avec fetchAll et d'autre avec setFetchMode, y'en a plein mais beaucoup ne sont pas documentés : http://www.php.net/manual/fr/pdo.constants.php

  2. #42
    Membre émérite Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Points : 2 566
    Points
    2 566
    Par défaut
    plusieurs solutions :

    * Faire une requête DISTINCT sur les villes, et a chaque tour de boucle allé chercher les personnes qui correspondes
    * Créer un array temporaire et a chaque tour de boucle ajouter la personne ($tmp[$user['city']][] = $user) , et reboucler ensuite pour créer son tableau
    * A chaque tour de boucle vérifier via un buffer si la ville a été mise et afficher les personnes, sinon afficher ville
    Tu avais aussi la solution SQL mais le PDO::FETCH_GROUP n'avait plus d'intérêt ^^

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT city, GROUP_CONCAT( firstname, lastname )
    FROM uneTable
    GROUP BY city

  3. #43
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    tu dois retraiter quand même, et faire un explode pour avoir ton tableau sachant que tu perds le nom de tes colonnes

  4. #44
    Membre émérite Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Points : 2 566
    Points
    2 566
    Par défaut
    On a rien sans rien ^^

    par contre on fait qu'une seule requête

  5. #45
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    Citation Envoyé par Madfrix Voir le message
    On a rien sans rien ^^

    par contre on fait qu'une seule requête

    tu fais qu'un requêtes pour la solution 2 et 3, faut aussi trouver un séparateur qui ne sois pas dans les resultats aussi

  6. #46
    Membre émérite Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Points : 2 566
    Points
    2 566
    Par défaut
    je parle pour le cas de figure enoncé, j'ai encore jamais vu de virgule dans le nom ou le prénom

    Mais c'est vrai que PDO::FETCH_GROUP est plus clean, reste à voir le temps de traitement sur grosses volumétries

  7. #47
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    si tu veux un petit exemple simplifié avec FETCH_CLASS

    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
    23
    24
    25
    26
    class User
    {
        public $id; 	
        public $firstname;
        public $lastname;
        public $city;
     
        public function getFullname()
        {
            return sprintf('%s, %s', $this->firstname, $this->lastname);
        }
    }
     
    $stmt = $dbh->query("SELECT id, firstname, lastname, city FROM test_user");
    $users = $stmt->fetchAll(PDO::FETCH_CLASS, 'User');
     
    foreach($users as $user)
    {
        var_dump($user->getFullname());
    }
     
    /*
    string(13) "jean, machine"
    string(10) "jean, truc"
    string(9) "paul, bla"
    */
    on peux biensur le couplé FETCH_GROUP

    on peu le faire direct aussi, mais la ca marche qu'avec setFetchMode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $stmt = $dbh->query("SELECT id, firstname, lastname, city FROM test_user");
    $stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
     
    while($user = $stmt->fetch())
    {
        var_dump($user->getFullname());
    }

  8. #48
    Membre émérite Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Points : 2 566
    Points
    2 566
    Par défaut
    Merci l'ami, c'est simple et facilement compréhensible

  9. #49
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    un autre exemple avec FETCH_CLASSTYPE, qui lui va prendre le nom de la premier colonne pour créer l'objet a la classe du même nom. (en rajoutant la colonne rôle, qui est sois admin sois guest
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    class Guest
    {
        public $id; 	
        public $firstname;
        public $lastname;
        public $city;
     
        public function getFullname()
        {
            return sprintf('%s, %s', $this->firstname, $this->lastname);
        }
    }
     
    class Admin extends Guest
    {
        public function getUsers()
        {
            //blabla
        }
    }
     
    $stmt = $dbh->query("SELECT role, id, firstname, lastname, city FROM test_user");
    $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);
     
    while($user = $stmt->fetch())
    {
        var_dump(get_class($user));
    }
    /*
    string(5) "Admin"
    string(5) "Guest"
    string(5) "Guest"
    */
    c'est pas très percutant comme exemple mais ca montre mais ca pourrais servir dans le cas d'un UNION par exemple avec des tables différentes

  10. #50
    Membre confirmé
    Avatar de FMaz
    Inscrit en
    Mars 2005
    Messages
    643
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 643
    Points : 640
    Points
    640
    Par défaut
    RunCodePHP m'a fait remarqué une importante erreur dans l'article. En effet, à plusieurs endroits (les joies du copier coller) j'avais ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $pdo->prepare($query);
    $pdo->bindValue(1, 6, PDO::PARAM_INT);
    $prep = $pdo->execute();
    Prepare() retourne un PDOStatement, ce qui n'est pas le cas de la méthode execute().
    J'ai donc corrigé les exemples en question afin qu'il soient tel que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $prep = $pdo->prepare($query);
    $prep->bindValue(1, 6, PDO::PARAM_INT);
    $prep->execute();

    Voilà, désolé pour cette erreur, et encore merci à RunCodePHP

  11. #51
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par FMaz Voir le message
    Hum-hum...
    Je n'avais pas vu la chose sous cet angle...
    Mais c'est vrai qu'il y a un manque à combler à ce niveau.
    Pour ma part, je constates que le TRY est utilisé avec
    $bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    chez les rares qui demandent le renvoie des exeptions. lors de la connection.

    Alors encore plus rare sont ceux qui craient un TRY pour les requétes
    Bien sur l'attribut est acquis a $bdd quand au retour d'exeptions,
    mais encore faut'il un CATCH qui les reçoivent.

    Et la c'est le floux absolu, deux écoles :
    mettre les requétes dans le TRY de connection, donc un seul CATCH
    Ou un TRY par requéte

  12. #52
    Membre confirmé
    Avatar de FMaz
    Inscrit en
    Mars 2005
    Messages
    643
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 643
    Points : 640
    Points
    640
    Par défaut
    Maurisier:

    Normalement les gens se font des gestionnaire de connection et d'erreur afin de centraliser la gestion des erreurs.

    Par exemple, sur les gros projets, les erreurs de requêtes seront forcément traitées (loggué quelque part). Ainsi, on se doute bien que les développeurs ne copie-collent pas le code d'ouverture et d'écriture dans un fichier à chaque fois qu'ils ont une requête.

    L'avantage de l'orienté objet est que les classes PDO peuvent être étendues.
    De plus, si une exception n'est pas capturée, elle est transmise à la fonction parent. Une erreur fatale d'uncatched exception arrivera uniquement si au terme de la récursion il n'y a pas eu de capture.

    En somme, pour tracer une image grossière, si tu as un index.php comme front-controlleur unique, il te suffirait de créer un gros try-catch et l'ensemble de ce qui est chargé par ce fichier (c'est à dire tout ton code) sera capturé.

  13. #53
    Invité
    Invité(e)
    Par défaut
    FMaz:
    Oui j'entends bien, mais il reste désagréable d'incruster dans un seul TRY
    tout un code ... et perdu a la fin du php le Catch ...
    Ton exemple a ce sujet est trop sommaire.
    Tu vas te retrouver avec des includes etc ... pffff en plus c'est pas beau a lire.

  14. #54
    Membre confirmé
    Avatar de FMaz
    Inscrit en
    Mars 2005
    Messages
    643
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 643
    Points : 640
    Points
    640
    Par défaut
    Heu ?? Je suis pas certain de comprendre ce que tu essaie d'exprimer. D'ailleurs de quel "exemple" parle-tu exactement ?
    En ce qui me concerne, c'est un article sur PDO, pas sur la gestion des exceptions, et encore moins sur la façon de gérer ses inclusions de fichiers.

    De plus si tu regarde les exemples du manuel PHP, tu verra qu'ils placent la connexion dans un try catch aussi, car la connexion n'utilise que les exceptions.

    Après si tu ne veux pas utiliser les exceptions, tu n'es pas obligé. Mais on est en 2010 avec PHP 5 (pour ne pas dire 5.3), alors je ne vais pas recommander de gérer ses erreurs en PHP 4... mais je n'ai rien contre ca, et l'article indique bien comment ne pas utiliser les exceptions.

  15. #55
    Invité
    Invité(e)
    Par défaut
    FMaz:
    Bien sur bien sur je comprends tout cela
    Je parlais de récupérer les exeption sur les requétes PDO elles mêmes,
    bien sur pas sur du PHP
    Mais c'est bon n'en parlons plus.

  16. #56
    Membre confirmé
    Avatar de FMaz
    Inscrit en
    Mars 2005
    Messages
    643
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 643
    Points : 640
    Points
    640
    Par défaut
    Oui, je parlais de ca aussi.

    Tu dois mettre en place un mécanisme de gestion des exceptions. Si c'est bien fait, c'est transparent. Dans mon cas j'ai un connexion manager qui instancie une classe étendue de PDO, qui elle est en lien avec un error manager.

    De cette facon, j'instancie ma connexion, puis la gestion des erreurs et des statistiques est prises en charge en arrière plan sans que ca soit visible.

    Mais c'est hors du spectre de l'article, j'en ai donc pas parlé... pour le moment.

  17. #57
    Membre habitué
    Inscrit en
    Avril 2003
    Messages
    397
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 397
    Points : 133
    Points
    133
    Par défaut
    Citation Envoyé par FMaz Voir le message
    De plus, j'aimerais faire une mise en garde que l'utilisation des requêtes préparée n'est pas une protection absolue contre les injections. Vous devez tout de même valider vos entrés. (Donc si tu les a bien validée comme tu le dis, il ne devrait pas y avoir de problème).
    Que faut-il faire alors pour s'assurer que la requête est protégée ?

  18. #58
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    Envoyé par FMaz
    De plus, j'aimerais faire une mise en garde que l'utilisation des requêtes préparée n'est pas une protection absolue contre les injections. Vous devez tout de même valider vos entrés. (Donc si tu les a bien validée comme tu le dis, il ne devrait pas y avoir de problème).
    moi j'aimerai bien un exemple.

  19. #59
    Membre éclairé

    Profil pro
    Inscrit en
    Juin 2004
    Messages
    772
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Juin 2004
    Messages : 772
    Points : 872
    Points
    872
    Par défaut
    Je pense que ce qu'il veut dire, c'est que requête préparée n'est pas synonyme de protection contre les injections. Encore faut-il savoir les utiliser.

    Je te renvoie vers un de tes posts ou tu expliques à quelqu'un comment les utiliser : http://www.developpez.net/forums/d10...e/#post5747039

    a mon avis la remarque de FMaz vise à ne pas mettre de fausses certitudes dans l'esprit des débutants notamment.
    • Mon blog PHP : http://blog.alterphp.com
    • "Peace cannot be kept by force, it can only be achieved by Understanding" -- Albert Einstein

  20. #60
    Membre confirmé
    Avatar de FMaz
    Inscrit en
    Mars 2005
    Messages
    643
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 643
    Points : 640
    Points
    640
    Par défaut
    Exactement.

    L'article à été écris pour être une base d'apprentissage saine à PDO. J'ai donc essayé de casser certains "mythes". Par la suite, la lecture d'article plus poussée devrait être facilité.

    Quoi qu'il en soit, de ce que je sais, et sous toute réserve, l'utilisation d'une requête préparée et le passage des variables par les fonctions BIND est une protection suffisante concernant --seulement-- l'injection SQL.

    Par contre, ca ne garanti pas la sécurité de la donnée pour les autres failles. Par exemple si le champs du nom d'utilisateur permet de stocker du code javascript, il est possible que ca permette d'exploiter d'autres failles qui ne sont pas spécifiquement liées à la base de données.

Discussions similaires

  1. [PDO] Problème de requête préparée (à n'y rien comprendre?)
    Par waldo2188 dans le forum Bibliothèques et frameworks
    Réponses: 3
    Dernier message: 20/03/2007, 21h53
  2. [Concept][JavaBeans] Comprendre ?
    Par nicoo dans le forum Langage
    Réponses: 15
    Dernier message: 08/12/2004, 08h01
  3. [Procédure][java] A n'y rien comprendre
    Par Stessy dans le forum SQL
    Réponses: 2
    Dernier message: 18/03/2004, 15h05

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