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:
Recibe un ID de archivo
Consulta la base de datos
Retorna TODOS los datos de ese archivo específico
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
🎯 Puntos Clave para Recordar
$_GET['file'] → Obtiene parámetros de la URL
fetch() → Para UN registro, fetchAll() → Para MUCHOS registros
PDO::PARAM_INT → Especifica que el parámetro es entero
header('Location: ...') → Redirecciona al usuario
$file->translated !== NULL → Verifica si hay traducción (estricto)
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
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.
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.
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.
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.
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:
Implementar el sistema de traducción
Usar ChatGPT para traducir contenido
Guardar traducciones en la base de datos
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
Publicar un comentario