CONNEXION
  • RetourJeux
    • Tests
    • Soluces
    • Previews
    • Sorties
    • Hit Parade
    • Les + attendus
    • Tous les Jeux
  • RetourActu
    • Culture Geek
    • Astuces
    • Réalité Virtuelle
    • Rétrogaming
    • Toutes les actus
  • RetourHigh-Tech
    • Actus JVTECH
    • Bons plans
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTECH
  • 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
    • Xbox Series
    • Overwatch 2
    • FUT 23
    • League of Legends
    • Genshin Impact
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • PS4
  • One
  • Switch
  • Wii U
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Etoile Abonnement RSS

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

DébutPage précedente
1
Page suivantePage suivante
vava740 vava740
MP
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
MP
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
MP
Niveau 13
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
MP
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`.

DébutPage précedente
1
Page suivantePage suivante
Répondre
Prévisu
?
Victime de harcèlement en ligne : comment réagir ?
Infos 0 connecté(s)

Gestion du forum

Modérateurs : Thymotep
Contacter les modérateurs - Règles du forum

Sujets à ne pas manquer

La vidéo du moment