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

Contribuez / Téléchargez Sources et Outils PHP Discussion :

PDOStatemnt & Iterator


Sujet :

Contribuez / Téléchargez Sources et Outils PHP

  1. #1
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut PDOStatemnt & Iterator
    Bonjour à tous.

    Je vous propose la classe PDOStatementIterator. J'ai été amené à créer cette classe pour faciliter l'usage des Iterators avec les PDOStatement car bien que ces derniers implémentent l'interface Traversable, il n'est pas possible de l'utiliser comme un Iterator (du moins pas directement). Il est vrai que l'usage de IteratorIterator nous permet de contourner ce problème, mais on aimerai grandement pouvoir se positionner dans le PDOStatement (SeekableIterator) et compter le nombre de lignes retournées (Countable), c'est là que PDOStatement entre en scène.

    Voici le code source de la classe:
    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
     
    class PDOStatementIterator extends IteratorIterator implements SeekableIterator, Countable {
     
        protected $_statement;
     
        protected $_count;
     
        public function __construct (PDOStatement $statement) {
            parent::__construct($this->_statement = $statement);
        }
     
        public function seek ($position) {
            if ($position > $this->count() || $position < $this->key())
                throw new OutOfBoundsException("Cannot seek to $position");
     
            for ($i = $this->key(); $i < $position; $i++)
                $this->next();
        }
     
        public function count () {
            if (!isset($this->_count))
    		    return $this->_count = $this->_statement->rowCount();
    	    return $this->_count;
        }
    }
    Cette classe est ni plus ni moins qu'un IteratorIterator seekable et coutable... On conserve une référence sur le PDOStatement à cet effet.
    L'usage de cette classe est indépendant du fetch_mode choisi dans le PDOStatement, ce qui la rends bien pratique pour récupérer des collections en LazyLoading.
    Exemple:
    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 { 
      protected $_data;
     
      public function __get ($key) {
        return isset($this->_data[$key]) ? $this->_data[$key] : null;
      }
     
      public function __set ($key, $value) {
    	$this->_data[$key] = $value;
      }
    }
     
    // ...
     
    $user = new User;
     
    $stmt = $pdo->prepare('SELECT * FROM `users`');
    $stmt->setFetchMode(PDO::FETCH_INTO, $user);
    $stmt->execute();
     
    $it = new PDOStatementIterator($stmt);
    $itit = new LimitIterator($it, 10, 20);
    foreach ($itit as $user) {
      var_dump($user);
    }
    Une amélioration possible serait d'étendre NoRewindIterator au lieu de IteratorIterator, mais je n'ai pas encore réussi à la faire marcher dans ce sens...

    Voir également la discussion d'origine: http://www.developpez.net/forums/d10...oo-orm-entite/

    Vos commentaires sont les bienvenus.

  2. #2
    Invité
    Invité(e)
    Par défaut
    A vue de nez, je dirai nikel.

    Traversable est compliquée, elle déclare un itérateur interne, donc on peut utiliser foreach() (ça appel fetch()), mais l'itérateur n'est pas visible depuis PHP, pour personnaliser le comportement, faut l'encapsuler dans IteratorIterator, comme tu l'as fait.

    NoRewind ne fonctionne pas ?

  3. #3
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    Cette implémentation ne fonctionne pas correctement:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class PDOStatementIterator extends NoRewindIteratro implements SeekableIterator, Countable {
     
        protected $_statement;
     
        protected $_count;
     
        public function __construct (PDOStatement $statement) {
            parent::__construct(new IteratorIterator($this->_statement = $statement));
        }
     
        // ...

  4. #4
    Invité
    Invité(e)
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    parent::__construct($this->_statement = $statement);

  5. #5
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    Non car le constructeur de NoRewindIterator attends un Iterator. La solution consiste à wrapper le PDOStatement dans un IteratorIterator avant de le wrapper à son tour par un NoRewindIterator, mais pour une raison qui m'échappe, ça ne fonctionne pas dans le contexte du constructeur... L'objet construit ne se traverse plus.

  6. #6
    Invité
    Invité(e)
    Par défaut
    NoRewindIterator extends IteratorIterator

  7. #7
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    La signature de leurs constructeurs est différentes. Essaie tu verras.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Ahh oui les vilains,ils ont typés sur Iterator et non pas Traversable.

    Pas bien ça

    Du coup je sais pas, j'ai pas testé le IteratorIterator , mais ça devrait fonctionner.
    Bug possible ?

  9. #9
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    J'ai testé à la main avec ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $stmt = $pdo->prepare('machin truc');
    $it = new IteratorIterator($stmt); // traverser cet objet fonctionne
    $itit = new NoRewindIterator($it); // ne se traverse pas...

  10. #10
    Invité
    Invité(e)
    Par défaut
    C'est plutot louche

  11. #11
    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
    malheureusement on ne pas pas utiliser ce genre de classe direct dans PDO::ATTR_STATEMENT_CLASS sans passer par un pont

  12. #12
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    Non en effet, à moins de réécrire un iterateur en partant de Iterator.

  13. #13
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par stealth35 Voir le message
    malheureusement on ne pas pas utiliser ce genre de classe direct dans PDO::ATTR_STATEMENT_CLASS sans passer par un pont
    Yes, il type sur PDOStatement. C'est une limite interne

  14. #14
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Benjamin Delespierre Voir le message
    Non en effet, à moins de réécrire un iterateur en partant de Iterator.
    Ouai ?

    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
     
    class MySt extends PDOStatement implements IteratorAggregate
    {
        public function getIterator() {
            return new ArrayIterator($this->fetchAll());
    }
    }
     
    $p = new PDO('sqlite::memory:');
    $p->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('MySt'));
    $p->exec('CREATE TABLE foo (txt TEXT);');
    $p->exec("INSERT INTO foo VALUES ('foobar');");
     
    var_dump(iterator_to_array($p->query("SELECT * FROM foo")));
     
    /*
    array(1) {
      [0]=>
      array(2) {
        ["txt"]=>
        string(6) "foobar"
        [0]=>
        string(6) "foobar"
      }
    }*/
    Ecrit en 2min, utilise fetchAll() à l'arrache. En implémentant Iterator, on peut appeler seulement fetch() à chaque tour.

  15. #15
    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
    Ça perd de beaucoup de son intérêt la, je vois pas comment tu peux implémenter Iterator puisque c'est pas possible de le faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class Bug38592 extends PDOStatement implements Iterator {}
    //Fatal error: Class Bug38592 could not implement interface Iterator in Unknown on line 0

  16. #16
    Invité
    Invité(e)
    Par défaut
    Yes, c'est parcequ'il implémente Traversable en interne et ne peut pas implémenter Iterator.

    Bloqué !

  17. #17
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    On revient donc sur notre wrapper de départ... Aucune piste en ce qui concerne le NoRewindIterator.
    Pour rappel les tests de wrap d'un PDOStatement dans un IteratorIterator ne posent aucun problème, or lorsqu'on met cet IteratorIterator dans un NoRewindIterator, plus rien ne marche...

    Voici un exemple:
    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
     
    $db = "backoffice";
    $db_type = "mysql";
    $db_user = "root";
    $db_password = "";
    $db_host = "localhost";
     
    $pdo = new PDO("$db_type:dbname=$db;host=$db_host", $db_user, $db_password);
    $query = "SELECT * FROM users";
    $stmt = $pdo->query($query);
     
    $it = new IteratorIterator($stmt);
    $itit = new NoRewindIterator($it);
    foreach ($itit as $row) {
     var_dump($row);
    }
     
    var_dump("done");

  18. #18
    Invité
    Invité(e)
    Par défaut
    Faut regarder si ya pas un rapport de bug à ce sujet.

    A tout hasard, tester avec un NoRewindIterator(IteratorIterator) sur autre chose que PDOStatement, pour prouver que le bug supposé vient bien de PDOStatement (pas sûr)

  19. #19
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    Chose étrange, il semble que NoRewindIterator nécéssite un iterateur rembobiné pour fonctionner correctement... Je vais voir la doc à ce sujet.

    Voici un exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    $a = new ArrayObject(array(1,2,3,4,5));
     
    $it = new IteratorIterator($a);
    $it->rewind(); // Si on commente cette ligne, le NoRewindIterator ne se traverse plus...
     
    $itit = new NoRewindIterator($it);
    foreach ($itit as $val)
      echo $val;
    A noter que cette forme fonctionne parfaitement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    $a = new ArrayIterator(array(1,2,3,4,5));
     
    $itit = new NoRewindIterator($a); // On a besoin d'un Iterator ici
    foreach ($itit as $val)
      echo $val;

  20. #20
    Invité
    Invité(e)
    Par défaut
    Idem sur le Caching ; ça , c'est un bug ! (déja vu, relevé, mais pas corrigé il me semble)

Discussions similaires

  1. Iteration VS recursivité
    Par yacinechaouche dans le forum C
    Réponses: 40
    Dernier message: 16/11/2012, 11h52
  2. [débutant][struts] iterate imbriquée
    Par muim dans le forum Struts 1
    Réponses: 6
    Dernier message: 19/02/2004, 15h13
  3. [debutant]iterator
    Par Wis dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 05/05/2003, 10h49
  4. vInt::iterator
    Par Monstros Velu dans le forum C++
    Réponses: 19
    Dernier message: 05/04/2003, 15h06

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