19. Creating a method display file data using id

 

19. Creación de un método para mostrar datos de archivos usando ID

¡Hola y bienvenido de nuevo!

En esta lección vamos a crear la funcionalidad de visualización individual de archivos. Hasta ahora teníamos una lista de archivos, pero al hacer click en uno, necesitamos mostrar ese archivo específico con su reproductor y contenido. ¡Es como abrir un archivo específico en tu computadora!


🎯 ¿Qué vamos a crear?

Vamos a crear el método getFileById() que:

  1. Recibe un ID de archivo

  2. Consulta la base de datos

  3. Retorna TODOS los datos de ese archivo específico

  4. Nos permite mostrar el reproductor y contenido correcto


🔍 Flujo de navegación completo

text

┌─────────────────────────────────────────────────────────────┐

│            FLUJO: LISTA → CLICK → VISUALIZACIÓN             │

├─────────────────────────────────────────────────────────────┤

│ 1. LISTA de archivos:                                       │

│    • view.php (sin parámetros)                             │

│    • Muestra TODOS los archivos                            │

│                                                             │

│ 2. USUARIO hace click:                                      │

│    • <a href="view.php?file=25">                           │

│    • Navega a: view.php?file=25                            │

│                                                             │

│ 3. view.php CON PARÁMETRO:                                 │

│    • Detecta $_GET['file'] = 25                            │

│    • Llama: getFileById(25)                                │

│    • Muestra SOLO el archivo 25                            │

│    • Sigue mostrando lista completa                       │

└─────────────────────────────────────────────────────────────┘


🛠️ Método getFileById()

php

/**

 * Obtiene un archivo específico por su ID

 * 

 * @param int $fileID ID del archivo a buscar

 * @return object|false Objeto con datos del archivo o false si no existe

 */

public function getFileById($fileID)

{

    // 1. Preparamos consulta con placeholder para seguridad

    $stmt = $this->DB->prepare("

        SELECT * FROM `files` 

        WHERE `ID` = :fileID

    ");

    

    // 2. Vinculamos el parámetro (evita SQL injection)

    $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);

    

    // 3. Ejecutamos la consulta

    $stmt->execute();

    

    // 4. Retornamos UN solo registro (fetch, no fetchAll)

    return $stmt->fetch(PDO::FETCH_OBJ);

}


📝 Diferencia clave: fetch() vs fetchAll()

php

// getRecentList() → MUCHOS registros

$files = $stmt->fetchAll(PDO::FETCH_OBJ);

// Retorna: Array de objetos

// Uso: foreach($files as $file)


// getFileById() → UN solo registro

$file = $stmt->fetch(PDO::FETCH_OBJ);

// Retorna: Un solo objeto (o false)

// Uso: echo $file->content

Analogía:

  • fetchAll() → Como pedir todas las pizzas del menú (array)

  • fetch() → Como pedir solo la pizza número 3 (objeto individual)


🔧 Implementación en view.php

1. Lógica PHP al inicio del archivo:

php

<?php 

    include 'backend/init.php';

    

    // Variable para almacenar el archivo actual

    $file = null;

    

    // Verificamos si llegamos por GET (click en enlace)

    if($_SERVER['REQUEST_METHOD'] === "GET"){

        

        // Verificamos si hay parámetro 'file' en la URL

        if(isset($_GET['file'])){

            

            // Obtenemos el archivo por ID

            $file = $whisperObj->getFileById($_GET['file']);

            

            // Si no existe el archivo, redirigimos al inicio

            if(!$file){

                header('Location: index.php');

                exit(); // Importante: detener ejecución

            }

        }

    }

?>


🔍 Explicando $_GET['file']

¿Qué es $_GET?

  • Superglobal de PHP que contiene parámetros de la URL

  • Ejemplo: view.php?file=25&lang=es

    • $_GET['file'] = "25" (como string)

    • $_GET['lang'] = "es" (como string)

¿De dónde viene el ID?

php

// En getRecentList() generamos enlaces con ID:

echo '<a href="view.php?file='.$file->ID.'">';


// Cuando usuario hace click, navega a:

// view.php?file=25


// PHP captura este valor:

$fileID = $_GET['file']; // "25" (string)


// Nuestro método lo convierte a int con bindParam

$stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);


🎨 Renderizado condicional en HTML

1. Reproductor según tipo (audio/video):

php

<div>

    <?php if($file->type === "audio"): ?>

        <!-- Audio Player -->

        <audio controls style="width:100%;border-radius: 20px;">

            <source src="<?php echo $file->fileUrl; ?>" type="audio/mpeg">

            Your browser does not support the audio element.

        </audio> 

    <?php else: ?>

        <!-- Video Player -->

        <video style="height:240px; width:100%;" controls>

            <source src="<?php echo $file->fileUrl; ?>">

            Your browser does not support the video tag.

        </video> 

    <?php endif; ?>

</div>

Explicación de sintaxis alternativa:

php

<?php if(condición): ?>

    HTML si verdadero

<?php else: ?>

    HTML si falso  

<?php endif; ?>

2. Contenido del archivo:

php

<div id="scroll" class="py-10 px-5 overflow-y-scroll w-full" style="height:500px;">

    <ul class="w-full">

        <li class="flex flex-col my-5 w-full">

            <h3 class="text-2xl">Text</h3>

            <div>

                <?php echo $file->content; ?>

            </div>

        </li>

    </ul>

</div>

3. Contenido traducido (si existe):

php

<?php if($file->translated !== NULL): ?>

    <li id="translateContent" class="flex flex-col my-5 w-full">

        <h3 class="text-2xl">[LANG]</h3> <!-- Futuro: mostrar idioma -->

        <div>

            <?php echo $file->translated; ?>

        </div>

    </li>

<?php endif; ?>

Importante: !== NULL es más seguro que != NULL


📊 Diagrama de estados de la página

text

┌─────────────────────────────────────────────────────────────┐

│                 ESTADOS DE VIEW.PHP                         │

├─────────────────────────────────────────────────────────────┤

│ CASO 1: view.php (sin parámetros)                          │

│   • $file = null                                           │

│   • Reproductor: NO muestra                                │

│   • Contenido: NO muestra                                  │

│   • Lista: SÍ muestra                                      │

│                                                             │

│ CASO 2: view.php?file=25 (archivo existe)                  │

│   • $file = objeto con datos del ID 25                     │

│   • Reproductor: SÍ muestra (audio/video según tipo)       │

│   • Contenido: SÍ muestra texto                            │

│   • Traducción: muestra si $file->translated !== NULL      │

│   • Lista: SÍ muestra                                      │

│                                                             │

│ CASO 3: view.php?file=999 (archivo NO existe)              │

│   • $file = false                                          │

│   • Redirecciona a: index.php                              │

│   • Usuario vuelve al inicio                               │

└─────────────────────────────────────────────────────────────┘


💡 Mejoras de seguridad y validación

1. Validar que ID sea numérico:

php

if(isset($_GET['file'])){

    // Verificar que sea número

    if(is_numeric($_GET['file'])){

        $fileID = (int)$_GET['file']; // Convertir a entero

        $file = $whisperObj->getFileById($fileID);

    } else {

        header('Location: index.php');

        exit();

    }

}

2. Sanitizar output:

php

<!-- Siempre escapar output para prevenir XSS -->

<div>

    <?php echo htmlspecialchars($file->content, ENT_QUOTES, 'UTF-8'); ?>

</div>

3. Verificar permisos (futuro):

php

// Si implementas usuarios, verificar propiedad

if($file->user_id !== $_SESSION['user_id']){

    header('Location: index.php');

    exit();

}


🔍 Ejemplo práctico paso a paso

Paso 1: Base de datos tiene:

text

ID  fileUrl              content              type   translated

---------------------------------------------------------------

25  files/abc123.mp3     "Hola mundo"         audio  NULL

26  files/def456.mp4     "Buenos días"        video  "Good morning"

Paso 2: Usuario navega a view.php?file=25

php

// $_GET['file'] = "25"

$file = $whisperObj->getFileById(25);


// $file ahora es un objeto con:

// $file->ID = 25

// $file->fileUrl = "files/abc123.mp3"

// $file->content = "Hola mundo"

// $file->type = "audio"

// $file->translated = NULL

Paso 3: HTML generado:

html

<!-- Como type === "audio", muestra reproductor de audio -->

<audio controls>

    <source src="files/abc123.mp3" type="audio/mpeg">

</audio>


<!-- Muestra contenido -->

<div>Hola mundo</div>


<!-- Como translated === NULL, NO muestra sección de traducción -->

Paso 4: Usuario navega a view.php?file=26

html

<!-- Como type === "video", muestra reproductor de video -->

<video controls>

    <source src="files/def456.mp4">

</video>


<!-- Muestra contenido -->

<div>Buenos días</div>


<!-- Como translated !== NULL, SÍ muestra traducción -->

<div>Good morning</div>


🎨 Visualización del resultado final

text

┌─────────────────────────────────────────────────────────────┐

│                   PANTALLA DEL USUARIO                       │

├─────────────────────────────────────────────────────────────┤

│  ┌───────────────────────────────────────────────────────┐  │

│  │ ▶ files/abc123.mp3              [ 0:00 / 3:24 ]      │  │

│  └───────────────────────────────────────────────────────┘  │

│                                                             │

│  ┌──────────────┐  ┌───────────────────────────────────┐   │

│  │ 🔊 Archivo 25│  │ TEXT:                             │   │

│  │   "Hola mundo"│  │ Hola mundo, esto es una prueba   │   │

│  │               │  │ de audio convertida a texto...   │   │

│  │ 🎥 Archivo 26│  │                                   │   │

│  │   "Buenos..."│  │ [Select Language] [Translate]     │   │

│  └──────────────┘  └───────────────────────────────────┘   │

└─────────────────────────────────────────────────────────────┘

Características:

  • Reproductor activo según tipo de archivo

  • Contenido visible del archivo seleccionado

  • Lista lateral con todos los archivos (clickeables)

  • Sección de traducción solo visible si existe traducción


⚠️ Errores comunes y soluciones

Error: "Trying to get property 'content' of null"

php

// PROBLEMA: $file es null pero intentamos acceder $file->content

// SOLUCIÓN: Verificar antes de usar

<?php if($file && isset($file->content)): ?>

    <div><?php echo $file->content; ?></div>

<?php endif; ?>

Error: "Undefined index: file"

php

// PROBLEMA: $_GET['file'] no existe

// SOLUCIÓN: Usar isset() antes de acceder

if(isset($_GET['file']) && !empty($_GET['file'])){

    // Código seguro

}

Error: Archivo no se reproduce

php

// PROBLEMA: Ruta incorrecta

// SOLUCIÓN: Verificar que files/ existe y tiene permisos

echo $file->fileUrl; // Debería mostrar: files/abc123.mp3

// Verificar que el archivo existe físicamente

if(file_exists($file->fileUrl)){

    // Archivo existe

}


📊 Comparación de métodos de la clase

Método

Parámetros

Retorna

Uso típico

getRecentList()

Ninguno

Void (echo directo)

Mostrar lista completa

getFileById($id)

ID (int)

Objeto o false

Obtener un archivo específico

save()

$file, $content, $type

ID insertado

Guardar nuevo archivo

convert()

Ninguno (usa propiedades)

Array de OpenAI

Procesar audio a texto


🎯 Puntos Clave para Recordar

  1. $_GET['file'] → Obtiene parámetros de la URL

  2. fetch() → Para UN registro, fetchAll() → Para MUCHOS registros

  3. PDO::PARAM_INT → Especifica que el parámetro es entero

  4. header('Location: ...') → Redirecciona al usuario

  5. $file->translated !== NULL → Verifica si hay traducción (estricto)

  6. Sintaxis alternativa PHP → <?php if(): ?> HTML <?php endif; ?>


📋 Cuestionario de Repaso

Pregunta 1:

¿Cuál es la diferencia principal entre fetch() y fetchAll() en PDO?
a) fetch() es más rápido que fetchAll()
b) fetch() retorna un solo registro, fetchAll() retorna todos
c) fetch() solo funciona con SELECT, fetchAll() con cualquier consulta
d) No hay diferencia significativa

Pregunta 2:

¿Qué hace bindParam(":fileID", $fileID, PDO::PARAM_INT)?
a) Convierte el string a número automáticamente
b) Vincula el valor asegurando que sea tratado como entero
c) Valida que el ID exista en la base de datos
d) Encripta el ID para mayor seguridad

Pregunta 3:

¿Por qué usamos !== NULL en lugar de != NULL para verificar traducción?
a) Porque es más corto de escribir
b) Porque != NULL no funciona en PHP
c) Porque !== verifica tipo y valor (comparación estricta)
d) Porque es una convención de programación

Pregunta 4:

¿Qué ocurre si un usuario accede a view.php?file=999 (ID no existe)?
a) Muestra una página en blanco
b) Muestra un error de PHP
c) Redirige a index.php gracias a la validación
d) Intenta crear un nuevo archivo con ID 999

Pregunta 5:

¿Por qué necesitamos exit() después de header('Location: ...')?
a) Para mejorar el rendimiento
b) Para detener la ejecución del script inmediatamente
c) Para cerrar la conexión a la base de datos
d) Para limpiar la memoria del servidor


📝 Respuestas del Cuestionario

  1. b) fetch() retorna la siguiente fila como objeto/array, mientras que fetchAll() retorna todas las filas como un array de objetos/arrays. Para un solo registro, fetch() es más eficiente.

  2. b) bindParam() con PDO::PARAM_INT no solo vincula el valor, sino que le dice a PDO que debe tratarlo como un número entero, lo que ayuda a prevenir ciertos tipos de inyección SQL y asegura el tipo correcto.

  3. c) El operador !== es una comparación estricta que verifica que el valor NO sea NULL y que además sea del mismo tipo. != solo verifica valor, lo que puede causar falsos positivos con otros valores "falsy" como 0, "", o false.

  4. c) Nuestro código verifica si $file es false (cuando no encuentra el registro) y redirige a index.php. Esto previene errores y mejora la experiencia de usuario.

  5. b) header() solo envía la cabecera HTTP, pero el script PHP continúa ejecutándose. exit() o die() detiene la ejecución inmediatamente, evitando que se procese código innecesario o que se muestre contenido después de la redirección.


🚀 Lo que viene después

¡Excelente! Ahora tenemos:
✅ Visualización individual de archivos
✅ Reproductor multimedia funcional
✅ Navegación completa entre archivos
✅ Validación de existencia de archivos

En la próxima lección vamos a:

  1. Implementar el sistema de traducción

  2. Usar ChatGPT para traducir contenido

  3. Guardar traducciones en la base de datos

  4. Mostrar múltiples idiomas disponibles

✨ ¡Increíble progreso! Tu aplicación ahora es completamente funcional para subir, procesar, listar y visualizar archivos. Solo falta la cereza del pastel: 


Comentarios

Entradas más populares de este blog

1-7. Transforma tu audio a texto

10. Haz que tu asistente hable

8. NUEVO - Solución si tu micrófono no está captando tu audio