JeuWeb - Crée ton jeu par navigateur
Exécuter une requête une seule fois dans une boucle - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Exécuter une requête une seule fois dans une boucle (/showthread.php?tid=6511)

Pages : 1 2 3


RE: Exécuter une requête une seule fois dans une boucle - Ereinion - 18-11-2012

C'est clair que niveau propreté et lisibilité, le code laisse à désirer. Je vais me replonger sur ça aujourd'hui. A vouloir coder vite, on ne fait pas vraiment attention à ça Smile

Sinon, je test ça cet après-midi Xenos, merci pour l'aide.

@php_addict: J'ai pas compris, quelle est la différence avec mon formulaire actuel ?


RE: Exécuter une requête une seule fois dans une boucle - php_addict - 18-11-2012

(18-11-2012, 11:47 AM)Ereinion a écrit : @php_addict: J'ai pas compris, quelle est la différence avec mon formulaire actuel ?

bah...la différence c'est ton champs name="" dans ton input, exemple:

au lieu de:

Code :
<input type="text" name="unite_1" >
<input type="text" name="unite_2" >
<input type="text" name="unite_3" >
<input type="text" name="unite_4" >

il vaut mieux

Code :
<input type="text" name="unite[1]" >
<input type="text" name="unite[2]" >
<input type="text" name="unite[3]" >
<input type="text" name="unite[4]" >

comme ca au lieu de te retrouver avec

$_POST[unite_1]
$_POST[unite_2]
$_POST[unite_3]
$_POST[unite_4]

tu te retrouves avec un array:

$_POST[unite] = array()

ceci dit je ne suis pas certain que cela ne soit pas dejà le cas dans ton code, mais niveau lisibilité c'est chaud ton code, c'est une remarque constructive, car si dans 2 mois tu retouche ton code tu risque de ne même plus le comprendre...

petits conseils:


$req_unites = mysql_query("SELECT * FROM unitesconstruites WHERE id_village = '" . $villageattaquant . "'");

ne SELECT pas tout mais juste ce dont tu as besoin et même si tu SELECT tout mieux vaut dire quoi comme ca tu n'a pas besoin de jeter un oeil à phpmyadmin pour savoir se que tu retourne...

dans

while ($unites = mysql_fetch_row($req_unites)) { ?>
<input type="text" name="<?php echo $unites[3]; ?>" /> (Max: <?php echo $unites[5]; ?>)<br />

ne fait pas $unites[3] mais plutot un truc dans le genre $unites['unit_id'] c'est 1000 fois plus clair...car ton index 3 de $unites personne à part toi peut savoir de quoi il s'agit

par convention souvent on met plutôt $i au lieu de $nbr_boucle


RE: Exécuter une requête une seule fois dans une boucle - Ereinion - 18-11-2012

J'ai remplacé mysql_fetch_row par mysql_fetch_assoc . C'est plus clair maintenant Smile

Sinon, j'ai eu un peu de temps pour me pencher sur le problème en reprenant la solution de Xenos et j'ai un petit problème. La requête s'exécute deux fois et avec de fausses valeurs. Par exemple, j'ai une unité de PA 2 et une autre de PA 5. Imaginons le joueur a choisit d'envoyer 10 unités de chaque type en attaque. Je dois me retrouver avec un PA de 20 et 50. Là je me retrouve avec un PA de 20 et 40 dans ma base de données. De plus, ces valeurs sont censés être additionnées.


RE: Exécuter une requête une seule fois dans une boucle - Akira777 - 18-11-2012

Salut !

Sans vouloir sur-enchérir sur ce qui a été dit.

- Les requêtes dans les boucles, évite.
Pour faire un INSERT par exemple, après traitement de ton formulaire, comme l'a dit Xenos, travaille différement. Voici ce que je te propose :

Code PHP :
<?php 
$query
= "INSERT INTO file_attaque VALUES ";

while (...)
{
// traitement divers...

$query .= "('', '" . $villagedefenseur . "', '" . $idjoueur . "', '" . $villageattaquant . "', '" . $pa_final . "'), ";

// traitement divers...
}

$query .= substr($query, 0, -2); // pour enlever la dernière virgule de $query

// reste du script

- Secondement, renseigne toi sur PDO, tu en sortira meilleur, tu pourras utiliser des requêtes préparées et surtout "quoter" proprement tes requêtes :

Code PHP :
<?php 
$query
.= "('', " . $pdo->quote($villagedefenseur) . ", " . $pdo->quote($idjoueur) . ", " . $pdo->quote($villageattaquant) . ", " . $pdo->quote($pa_final) . "), ";

// note la disparition des simple quotes dans la requête, mis à part la premiere colonne

- Dans tes inserts, veille à bien renseigner les colonnes dans ta requête, ici tu indiques la première que tu remplies avec du vide. Si tu dois insérer une ligne avec, indique les colonnes qui te sont réellement utiles. Avantages :
- ca t'évite de devoir penser à renseigner des champs vides
- si tu modifies ta table à l'avenir (ajout de colonne) tu seras pas obligé de venir mettre à jour ta requete
- tu profites du champs "valeur par défaut" de mysql. En envoyant un champs vide, MySQL va détecter que tu envoies quelque chose et n'insereras pas la valeur par défaut.

- Dernièrement, les valeurs villageattaquant et villagedefenseur dans des input hidden, c'est pas terrible. N'hésites pas à mettre ça en session. Car dans ton formulaire, n'importe qui est capable de changer ces valeurs...


RE: Exécuter une requête une seule fois dans une boucle - Xenos - 18-11-2012

La méthode du "$query.=" pose un soucis si la boucle est vide (non-exécutée).
Le SELECT * est parfaitement viable. Il est, après quelques tests et d'après mon expérience, plus rapide de faire un "SELECT *" que de faire un SELECT des N colonnes une à une (SELECT `col1`, `col2`,...`colN` est moins rapide que SELECT *) car le "*" permet à MySQL d'optimiser la récupération (il peut se dire "pas besoin de regarder les noms de colonne car je veux tout sélectionner).

En revanche, spécifier les noms de colonne dans un INSERT permet de s'assurer que, même si on ajoute ou modifie les colonnes de la table, il n'y aura pas de changement dans la requête qui sera toujours valable.
Le SELECT * permet de modifier la structure de la table, sans devoir refaire la requête d'ailleurs.

Le fetch_assoc est effectivement à recommander, mais fetch_array peut être tout à fait utilisable, à condition que le SELECT porte sur des colonnes nommées (un SELECT `col1`, col4` par exemple). Pourquoi? parce que dans ce cas là, le nom des colonnes est spécifié dans le SELECT donc, l'index 0 ou 1 du tableau retourné par le fetch_row est nommé, via ce sélect (si on a "SELECT `col1` FROM table, on sait que $result[0] réfère à la colonne `col1`).

La PDO, c'est +1. mysql_fetch_assoc, c'est un peu "old school". Regarde le manuel php de MySQLi par exemple http://php.net/manual/fr/book.mysqli.php ou de PDO http://fr2.php.net/manual/fr/class.pdostatement.php Perso, je préfère MySQLi.

SI ta première colonne de la table de la BDD est une colonne de type "AUTO_INCREMENT PRIMARY", utilise DEFAULT au lieu de ''. Cela indiquera à MySQL d'insérer la valeur par défaut dans cette colonne, cette valeur étant la valeur de l'AUTO_INCREMENT.

Pour le villageattaque villagedefense en formulaire, ca dépend des villages: si l'utilisateur peut choisir le village dans le formulaire, autant le laisser dans le formulaire, et non le déplacer en variable de session.

Effectivement, comme dit plus haut, ta structure "while & $nbr_boucle" est usuellement remplacée par un "for ($i=0;-condition-;$i++)".

Sort le SELECT de la boucle... Sélectionne le nombre de PA et l'id des unités nécessaires via une seule requète ("SELECT `Id`, `PA`FROM ... WHERE `Id` IN (1, 4, 76)") et stocke les résultats de la requète dans un tableau.
Si l'id est un nombre entier, en place de "id = '" . $var . "'", préfère "id=".((int)$var), ce qui utilisera le typage entier au lieu du typage string. Si ta colonne contient des entiers, cela évitera à MySQL de faire la conversion. Idem dans la requête d'insertion.

Enfin, pour ton soucis de calcul, dump tes variables au fil du code et tu trouveras immédiatement l'erreur de logique qui se cache (var_dump($var)).


RE: Exécuter une requête une seule fois dans une boucle - Ereinion - 18-11-2012

Pour le calcul, c'était une erreur de ma part. Concernant le fait de sortir la requête de la boucle, c'est impossible car la requête pour séléctionner le PA dépend de l'ID de l'unité qu'on récupère grâce au name du formulaire à savoir $unites[3] . Etant donné que ce tableau contient plusieurs valeurs, il faut obligatoirement le traiter dans une boucle, sinon j'aurai fait beaucoup plus simple.

Honnêtement, je suis perdu Sad

Sinon par rapport aux remarques concernant mes choix de développement, je sais que la PDO est conseillée et que ma façon de traiter les requêtes devient obsolète mais j'ai préféré pour une première version faire avec ce que je connais. Je connais un peu la PDO, mais je ne voulais pas prendre le risque. Après pour une nouvelle version de mon jeu, je compte me mettre à la POO également. C'est peut être pas très judicieux, mais c'est la méthode avec laquelle je me sent le plus en "sécurité".


RE: Exécuter une requête une seule fois dans une boucle - php_addict - 18-11-2012

ba non pas besoin de boucle:

- tu traite ton formulaire, par exemple tu en sort les id 1 2 3 et 4

- tu fait ton unique requête en récupérant les infos au sujet des unites ayant pour id 1 2 3 et 4, WHERE id IN (1,2,3,4) ou un truc du genre

- tu calcules

- et hop !


grosso modo tu traite ton formulaire de façon a ne faire qu'une seule requête puis tu calcules...easy!


RE: Exécuter une requête une seule fois dans une boucle - Xenos - 18-11-2012

Exact. Je t'ai présenté le principe du buffer, rien ne t'empêche de le réutiliser dans le cas rpésent.
Egalement, rien ne t'empêche de stocker les lignes retournées par MySQL quand tu fais ton fetch_assoc: le résultat retourné par fetch_assoc peut être réutilisé hors de la boucle (par exemple, tu peux faire ta requête, stocker dans un tableau tous les résultats issus de fetch_assoc, et ensuite n'utiliser que ce tableau de résultats pour faire tes calculs et traitements).

Si tu te sens plus "sûr" avec ta méthode, fais donc. Il vaut mieux arriver au bout de ton projet qu'essayer et laisser tomber en plein milieu pour finir dégouté à jamais.


RE: Exécuter une requête une seule fois dans une boucle - Ereinion - 18-11-2012

J'ai testé quelque chose de nouveau, mais je sais pas si c'est juste. Je vous le présente pour me dire si c'est valable ou pas:

Code PHP :
<?php 
$villageattaquant
= $_POST['attaquant']; //Village du joueur attaquant
$villagedefenseur = $_POST['defenseur']; //Village du joueur défenseur
$nbr_boucle = 0; //Nombre de boucles par défaut
$valeur = Array(); //Tableau qui contient la valeur des champs
$id = Array(); //Tableau contenant l'ID des unités choisis par le joueur

if (empty ($villageattaquant) OR empty ($villagedefenseur))
{
echo
'Vous devez passer par le formulaire avant de lancer une attaque';
}
else
{
$fetch_unites = mysql_query("SELECT * FROM unitesconstruites WHERE id_village = '" . $villageattaquant . "'"); //On récupère la valeur du tableau $unites[3]
while($unites = mysql_fetch_row($fetch_unites))
{
$valeur[] = $_POST[$unites[3]];
$valeur_implode = implode($valeur);

$id[] = $unites[3];
$id_implode = implode($id);
}
if (isset (
$valeur_implode))
{
if (!
is_numeric($valeur_implode))
{
echo
'Vous devez renseigner des valeurs numériques';
}
else
{
$req_pa = mysql_query("SELECT pa FROM unites WHERE id = '" . $id_implode. "'") or die;
$pa = mysql_fetch_array($req_pa);
$calcul_pa = $valeur_implode * $pa['pa'];
$pa_final += $calcul_pa;
}
}
}

Mon problème, c'est qu'après avoir fait des tests avec des echo tout fonctionne, mais je n'arrive pas à additionner les valeurs dans le sens où $pa_final et $calcul_pa retournent la même chose.

Donc je ne sais pas si ce que je viens de faire est valable ou pas.


RE: Exécuter une requête une seule fois dans une boucle - Akira777 - 18-11-2012

@Xenos

L'exemple que j'ai donné avec $query .= [...], est justement... Un exemple. C'est pourquoi j'ai mis en commentaire "// traitement divers", ce qui sous-entend un développement plus approfondie de la méthode, inexorablement, on compte les concaténations de $query afin de ne pas exécuter une requête vide.
Soit dit en passant, ce comptage est intéressant dans la mesure ou l'exec() en PDO derrière nous retournera le nombre de ligne enregistré et ce comptage nous permettra une vérification supplémentaire.

La où je parlais du nom des colonnes je faisais référence aux écritures, pas aux SELECTs. Mais pour revenir sur ta remarque, sache que là où tu penses gagner du temps via MySQL en faisant un SELECT *, tu risques de le perdre en bande-passante. Oui car pour un SELECT * sur une table contenant col1, [col N], col23, je doute que tu sois gagnant sur tous les plans à faire tout sélectionner, surtout si tu as besoin de col1 à col4. Enfin ça coule de source...

@Xenox, @Ereinion

En ce qui concerne MySQLi et PDO, sans dénigrer MySQLi, bien au contraire. Dans son cas, vaut mieux qu'il se jette dans PDO plutôt que s'embêter avec une extension intermédiaire.
Et puis PDO, c'est pas vraiment complexe...

// Pour les requêtes retournant un résultat : ->query()
Code PHP :
<?php 
$pdo
->query('SELECT ...');

// Pour les requêtes ne retournant pas de résultat : ->exec()
Code PHP :
<?php 
$pdo
->exec('INSERT ...');
$pdo->exec('UPDATE ...');
$pdo->exec('DELETE ...');

// Pour récupérer la valeur d'un auto-increment après un INSERT
Code PHP :
<?php 
$pdo
->lastInsertId();

// Equivalent de mysql_real_escape_string
Code PHP :
<?php 
$pdo
->quote($var);

// Ferme la connexion
Code PHP :
<?php 
$pdo
->closeCursor();

// Parser le résultat d'un ->query()
Code PHP :
<?php 
$result
= $pdo->query('SELECT ...');

while (
$foobar = $result->fetch(PDO::FETCH_ASSOC)) { echo $foobar['col1'] }

// ou bien

while ($foobar = $result->fetch(PDO::FETCH_OBJ)) { echo $foobar->col1 }

// ou encore

while ($foobar = $result->fetch(PDO::FETCH_NUM)) { echo $foobar[0] }

// Compter le nombre de résultat
Code PHP :
<?php 
$result
= $pdo->query('SELECT ...');

echo
$result->rowCount(); // par exemple : 5

En mode simple bien sûr, c'est une approche simple pour ceux qui sont pas à l'aise avec la POO. Mais on fait déjà beaucoup avec ça...