PHP - Modifier l'objet courant dans la boucle foreach

111

Je me demandais s'il était possible de modifier l'objet actuel qui est géré dans une foreachboucle

Je travaille avec un tableau d'objets $questionset je veux parcourir et rechercher les réponses associées à cet objet question dans ma base de données. Donc, pour chaque question, allez chercher les objets de réponse et mettez à jour le courant $question dans ma foreachboucle afin que je puisse sortir / traiter ailleurs.

foreach($questions as $question){
    $question['answers'] = $answers_model->get_answers_by_question_id($question['question_id']);
}
Garbit
la source
Comme ArtjomKurapov et @topener l'ont suggéré, je cherchais «passer par référence» en utilisant le signe &. Merci les chaps :) bonne journée
Garbit

Réponses:

207

Il y a 2 façons de faire cela

foreach($questions as $key => $question){
    $questions[$key]['answers'] = $answers_model->get_answers_by_question_id($question['question_id']);
}

De cette façon, vous enregistrez la clé, vous pouvez donc la mettre à jour à nouveau dans la $questionsvariable principale

ou

foreach($questions as &$question){

L'ajout de &gardera le $questionsmis à jour. Mais je dirais que le premier est recommandé même s'il est plus court (voir commentaire de Paystey)

Selon la foreachdocumentation PHP :

Afin de pouvoir modifier directement les éléments du tableau dans la boucle, précédez $ value avec &. Dans ce cas, la valeur sera attribuée par référence.

René Pot
la source
32
Les références dans ne foreachsont vraiment pas recommandées, la façon dont les foreachpassages autour de la partie valeur de la boucle se traduisent par un comportement imprévisible. Cela peut être plus long, mais vous êtes beaucoup plus en sécurité en utilisant la méthode 1 ici.
Paystey
1
Je viens de passer une heure perplexe à déboguer un problème causé par l'utilisation d'une référence dans un foreach. J'ai réutilisé le même nom de variable pour un deuxième appel foreach - comme j'avais passé le premier par référence, il a continué à modifier le dernier élément du tableau! L'utilisation d'un index explicite n'aurait pas posé ce problème.
Hippyjim
7
@Paystey pouvez-vous citer vos sources ou donner une explication détaillée?
Nico
2
Pourquoi la manipulation des références serait-elle dangereuse? Le C / C ++ où vous devez manipuler des références partout est-il dangereux? C'est à vous de le sécuriser ou non, pas à la langue.
Kalzem le
2
@BabyAzerty: Paystey n'a pas dit de références "en général", mais à foreachpropos de l'horreur comme celle-ci: stackoverflow.com/questions/3307409 /... (@Nico, FYI, aussi.)
Sz.
6

Sûrement utiliser array_mapet si utiliser un conteneur implémenté ArrayAccesspour dériver des objets est juste un moyen plus intelligent et sémantique de procéder?

La sémantique de la carte de tableau est similaire dans la plupart des langages et implémentations que j'ai vus. Il est conçu pour renvoyer un tableau modifié basé sur l'élément du tableau d'entrée (haut niveau ignorant la préférence de type de compilation / d'exécution du langage); une boucle est censée exécuter plus de logique.

Pour récupérer des objets par ID / PK, selon que vous utilisez SQL ou non (cela semble suggéré), j'utiliserais un filtre pour m'assurer d'obtenir un tableau de PK valides, puis imploser avec une virgule et placer dans une IN()clause SQL pour renvoie l'ensemble de résultats. Il effectue un appel au lieu de plusieurs via SQL, optimisant un peu le call->waitcycle. Plus important encore, mon code se lirait bien à quelqu'un de n'importe quelle langue avec un certain degré de compétence et nous ne rencontrons pas de problèmes de mutabilité.

<?php

$arr = [0,1,2,3,4];
$arr2 = array_map(function($value) { return is_int($value) ? $value*2 : $value; }, $arr);
var_dump($arr);
var_dump($arr2);

contre

<?php

$arr = [0,1,2,3,4];
foreach($arr as $i => $item) {
    $arr[$i] = is_int($item) ? $item * 2 : $item;
}
var_dump($arr);

Si vous savez ce que vous faites, vous n'aurez jamais de problèmes de mutabilité (en gardant à l'esprit si vous avez l'intention d'écraser, $arrvous pouvez toujours $arr = array_mapet être explicite.

MrMesees
la source
2
Beaucoup plus intuitif que de faire un foreach - c'est exactement ce pour quoi cette fonction est conçue.
benjaminhull