CONNEXION
  • RetourJeux
    • Sorties
    • Hit Parade
    • Les + populaires
    • Les + attendus
    • Soluces
    • Tous les Jeux
    • Gaming
  • RetourActu Gaming
    • News
    • Astuces
    • Tests
    • Previews
    • Toute l'actu gaming
  • RetourBons plans
    • Bons plans
    • Bons plans Smartphone
    • Bons plans Hardware
    • Bons plans Image et Son
    • Bons plans Amazon
    • Bons plans Cdiscount
    • Bons plans Decathlon
    • Bons plans Fnac
    • Tous les Bons plans
  • RetourJVTech
    • Actus High-Tech
    • Intelligence Artificielle
    • Smartphones
    • Mobilité urbaine
    • Hardware
    • Image et son
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTech
  • RetourCulture
    • Actus Culture
    • Culture
  • RetourVidéos
    • A la une
    • Gaming Live
    • Vidéos Tests
    • Vidéos Previews
    • Gameplay
    • Trailers
    • Chroniques
    • Replay Web TV
    • Toutes les vidéos
  • RetourForums
    • Hardware PC
    • PS5
    • Switch 2
    • Xbox Series
    • Switch
    • Pokemon pocket
    • FC 25 Ultimate Team
    • League of Legends
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • Switch 2
  • PS4
  • One
  • Switch
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Liste des sujets

[PHP] Itération sur one-to-many

vava740
vava740
Niveau 10
18 août 2014 à 11:38:24

Bonjour,

Je suis confronté à un problème qui m'avait l'air trivial mais qui me pose plus de problèmes que prévu (du moins en PHP).

J'ai un jeu de résultats potentiellement très grand, provenant d'une jointure SQL one-to-many (triée par ID de la partie "one"), dans un itérateur de sorte à ce que tout ne soit pas chargé en mémoire. Un exemple de résultat : https://wall.deblan.org/x1baa/php/1/

L'idée est de créer un itérateur PHP qui (dans l'exemple) aggrège les colonnes `name` dans un tableau `names` jusqu'à un changement d'`id`.

Le résultat devrait être le suivant après un `iterator_to_array` : https://wall.deblan.org/x1bab/php/1/

J'ai implémenté ça sans problèmes avec un générateur PHP 5.5, et ça marche très bien: https://wall.deblan.org/x1bac/php/1/

Mais le serveur de production est en PHP 5.4, et faut pas compter avoir PHP 5.5 dessus avant que ce soit la version par défaut sur Debian stable. Je me retrouve donc à devoir implémenter une logique similaire dans une classe qui implémente `Iterator`, et là je bloque. Y'a beaucoup plus de contraintes "inutiles" que je n'avais pas avec le générateur, et je ne trouve pas de solution qui me semble logique et évidente.

J'ai essayé d'observer comment fait Laravel, mais il s'avère qu'ils chargent le résultat entier en mémoire pour gérer les associations : https://github.com/illuminate/database/blob/8de434dbdd352b271201eb537df5f629060f4ea7/Eloquent/Relations/BelongsToMany.php#L390-L439

J'ai aussi regardé du côté de Doctrine qui a une méthode `iterate` pour ne pas tout charger en mémoire, mais cette méthode est justement incompatible avec les jointures de ce type (voir la note en jaune sur la doc) : http://docs.doctrine-project.org/en/2.0.x/reference/batch-processing.html#iterating-results

Enfin, j'ai regardé des solutions équivalentes en JavaScript, en particulier une réponse sur StackOverflow qui monte un exemple de "simulation de yield" avec Traceur, mais le résultat compilé est incompréhensible et dépend en plus sur le runtime de Traceur : http://stackoverflow.com/a/14095322

J'ai pas l'impression que le fait d'itérer sur des relations one-to-many est quelque chose d'exceptionnel, c'est apparemment la méthode la plus efficace pour éviter le problème des N+1 requêtes : http://use-the-index-luke.com/sql/join/nested-loops-join-n1-problem

Ce qui est peut-être moins commun, c'est de factoriser la logique dans un itérateur pour rendre l'aggrégation transparente au code final qui va itérer sur les résultats.

Avez-vous déjà rencontré ce problème ? Quelles solutions, patterns, outils, librairies utiliser pour résoudre le problème sans les générateurs PHP 5.5 ?

vava740
vava740
Niveau 10
18 août 2014 à 20:14:20

Bon, j'ai réussi à m'en sortir en implémentant `Iterator`, autant le générateur m'a pris une dizaine de minutes, autant j'ai passé plusieurs heures (pas compté précisément) pour faire un truc à peu près propre et fonctionnel en itérateur. :pf:

https://github.com/valeriangalliat/php-one-to-many-iterator

Dans `src` y'a une classe pour le générateur (~50 lignes), et une classe pour l'itérateur (~150 lignes), pour donner une idée.

Need PHP 5.5 ! :(

deepblue
deepblue
Niveau 16
19 août 2014 à 02:00:18

Ça fonctionne presque :o)) Si tes résultats ne sont pas rassemblés par ta `commonKey`, ton code tombe à l'eau. Il y a de fortes chances que ce soit le cas d'ailleurs. Je n'ai pas de solution par contre, sauf tout charger en mémoire (cf la conversation IRC).

vava740
vava740
Niveau 10
19 août 2014 à 09:26:54

Il faut absolument que les résultats soient triés par la `commonKey` en effet, c'est d'ailleurs précisé dans le readme. :oui:

Sachant que les résultats proviennent d'une requête SQL, ça demande juste de rajouter un `ORDER BY`.

Faudrait que je prenne le temps de faire un bench, entre 1. faire N+1 requêtes, 2. tout charger en mémoire, 3. streamer après un `ORDER BY`.

Sous forums
  • Aide à l'achat Mac
  • Création de Jeux
  • Linux
  • Création de sites web
  • Programmation
  • Internet
  • Steam Deck
  • Macintosh
  • Hardware
La vidéo du moment