2

Estoy haciendo un sistema de medallas y cuando trato de listar las medallas me muestra una sola. En este caso, el usuario prueba tiene dos medallas.

Este es el script

<?php

$user = $conn->query("SELECT * FROM usuario INNER JOIN medallas ON usuario.idu = medallas.idus WHERE idu = '".$userid."'");
$row = $user->fetch();

?>
<h1>Hola <?=$row['nombre'];?></h1>

Tus medallas
<?php
    while ($rows = $user->fetch()) {
?>

<img src="<?=$rows['medalla'];?>" width="48"><br>

<?php
}
?>
Sebastian
  • 1,859
  • 12
  • 31

2 Answers2

3

El problema es que tienes dos fetch.

Es preciso entender que query devuelve un puntero y cada vez que aplicas fetch el puntero se mueve una fila, por eso, cuando aplicas el segundo fetch el puntero ya está en la 2ª fila de datos.

Una solución no óptima sería esta:

<?php
    $sql="SELECT * FROM usuario INNER JOIN medallas ON usuario.idu = medallas.idus WHERE idu = '$userid'";
    $user = $conn->query($sql);
        #Guardamos todos los datos en $mUser
        $mUser=array();
        while ($row = $user->fetch()) {
            $mUser[]=$row;
        }

    $html="";
    $html.="<h1>Hola $mUser[nombre]</h1>";
    $html.="<p>Tus medallas:</p>";
        foreach ($mUser as $row) {
            $html.="<img src=\"$row[medalla]\" width=\"48\"><br>";
        }
    echo $html;
?>

La solución es no óptima porque en tu consulta el nombre de usuario se repite una y otra vez, deberías implementar una consulta de agrupación, para traer el nombre del usuario una sola vez y su lista de medallas.

He escrito un código más claro, evitando tantas aperturas y cierres de bloque.

A. Cedano
  • 86,578
  • 19
  • 122
  • 221
  • 2
    Ufff Cedano, muchas gracias. Tu siempre me haces pensar mucho, gracias nuevamente y voy quitar el `while` para no repetir el usuario, creo que debe estar por ahi la solucion – Sebastian Feb 14 '20 at 22:55
  • @STN no, no se trata de quitar el `while`, sino de **optimizar la consulta** por ejemplo con `GROUP_CONCAT` y `GROUP BY`, si estás usando `MySQL`. Lo del `while` está bien, pues sirve para vaciar el recurso en una variable o mostrar los datos in situ. Lo que pasa es que aquí si el usuario tiene 1000 medallas tendrás en los resultados su nombre 1000 veces sin necesidad. Es por eso que digo que la solución es **no óptima**. Y no me metí en esa optimización porque se aleja demasiado del problema inicial que es que aplicas `fetch` dos veces. – A. Cedano Feb 14 '20 at 22:57
  • Entiendo, si, el fin es que tengas al menos 20 medallas por cada 15 puntos. Entonces voy a centrarme en la consulta como tu dices. Muchas gracias – Sebastian Feb 14 '20 at 23:01
  • @STN puedes traer las medallas en una sola fila con un separador y luego usar `explode` para mostrarlas o sustituir el separador por `
    ` en la lectura de los datos. [Aquí hay un ejemplo de uso](https://es.stackoverflow.com/a/158848/29967) basado en `GROUP-CONCAT` y `GROUP BY`. Ahí se traen todas las respuestas separadas por `|` no una fila por respuesta y la pregunta viene una sola vez. Sería lo mismo para tu caso: si cambias `pregunta` por `usuario` y `respuestas` por `medallas`.
    – A. Cedano Feb 14 '20 at 23:06
  • Gracias Cedano, lo voy a probar, seria la mejor opcion ya que se que como maximo son 20 medallas. Voy a tener mejor respuesta de datos. A favoritos! – Sebastian Feb 14 '20 at 23:13
1

El problema es que usas fetch() antes del while por eso solo te sale una medalla. Intenta asi:

<?php

$user = $conn->query("SELECT * FROM usuario INNER JOIN medallas ON usuario.idu = medallas.idus WHERE idu = '".$userid."'");
$row = $user->fetch();

?>
<h1>Hola <?=$row['nombre'];?></h1>

Tus medallas
<img src="<?=$row['medalla'];?>" width="48"><br>
<?php
    while ($rows = $user->fetch()) {
?>

<img src="<?=$rows['medalla'];?>" width="48"><br>

<?php
}
?>
alanfcm
  • 20,427
  • 11
  • 17
  • 34