18. Creating a page to display recent generated files
18. Creación de una página para mostrar archivos generados recientemente
¡Hola y bienvenido de nuevo!
En esta lección vamos a crear una página de visualización completa donde los usuarios podrán ver sus archivos procesados, reproducirlos, leer el texto transcrito y acceder a todos sus archivos anteriores. ¡Es como crear el "historial" de nuestra aplicación!
🎯 ¿Qué vamos a crear?
Vamos a crear view.php que incluye:
Reproductor multimedia (audio/video)
Texto transcrito por la IA
Lista de archivos recientes (historial)
Interfaz para traducciones (próxima lección)
🔍 Estructura de la página view.php
text
┌─────────────────────────────────────────────────────────────┐
│ VIEW.PHP │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌────────────────────────────────────────┐ │
│ │ REPRODUCTOR │ │ TEXTO TRANSCRITO │ │
│ │ Audio/Video │ │ • "Hola, esto es una prueba..." │ │
│ │ │ │ │ │
│ ├──────────────┤ ├────────────────────────────────────────┤ │
│ │ LISTA DE │ │ │ │
│ │ ARCHIVOS │ │ TRADUCCIÓN: │ │
│ │ RECIENTES │ │ ┌─────────────────┐ ┌─────────────┐ │ │
│ │ • Entrevista│ │ │ Select Language │ │ Translate │ │ │
│ │ • Podcast │ │ └─────────────────┘ └─────────────┘ │ │
│ │ • Clase │ │ │ │
│ └──────────────┘ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
🛠️ Creando la página view.php
1. Estructura básica (copiada del HTML template):
php
<?php
include 'backend/init.php'; // Incluye conexión DB y clase Whisper
?>
<!DOCTYPE html>
<html>
<!-- ... head section igual que index.php ... -->
<body>
<div class="Wrapper">
<!-- Contenedor principal -->
<div class="inner-wrapper h-screen flex justify-center p-20">
<div class="h-full border-2 rounded-xl border-gray-700">
<!-- Encabezado con botón "Back" -->
<div class="flex w-full justify-between p-6">
<div>
<h2 class="text-2xl font-semibold">My Whisper</h2>
<span><a class="font-semibold text-lime-600" href="#">Meralesson</a></span>
</div>
<div>
<a href="index.php" class="border rounded px-4 py-2 cursor-pointer hover:bg-gray-100">
Back
</a>
</div>
</div>
<!-- Cuerpo de la página -->
<div class="px-10 overflow-hidden">
<div class="flex">
<!-- COLUMNA IZQUIERDA: Reproductor + Lista reciente -->
<div class="w-1/3">
<!-- Aquí va el reproductor -->
<!-- Aquí va la lista de archivos recientes -->
</div>
<!-- COLUMNA DERECHA: Texto + Traducción -->
<div class="flex-1">
<!-- Aquí va el texto transcrito -->
<!-- Aquí va el formulario de traducción -->
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
📝 Método getRecentList() en la clase Whisper
php
/**
* Obtiene y muestra la lista de archivos procesados recientemente
*
* @return void Muestra HTML directamente
*/
public function getRecentList()
{
// 1. Preparamos consulta SQL para obtener archivos
$stmt = $this->DB->prepare("SELECT * FROM `files` ORDER BY `ID` DESC");
// 2. Ejecutamos la consulta
$stmt->execute();
// 3. Obtenemos resultados como objetos
$files = $stmt->fetchAll(PDO::FETCH_OBJ);
// 4. Recorremos y mostramos cada archivo
foreach($files as $file){
// Creamos el enlace con el ID del archivo
echo '<a href="view.php?file='.$file->ID.'">
<li class="rounded flex my-5 cursor-pointer hover:bg-gray-100 items-center">
<div class="w-14">';
// 5. Mostramos icono según tipo (audio o video)
// Operador ternario: condición ? si_true : si_false
echo ($file->type === 'audio') ?
'<img src="frontend/images/audio-img.png"/>' :
'<img src="frontend/images/video-img.png"/>';
echo '</div>
<div class="overflow-hidden w-60 font-normal h-auto font-bold p-2">
<div>
<p class="truncate">'.$file->content.'</p>
</div>
</div>
</li>
</a>';
}
}
🔍 Explicando conceptos importantes
1. PDO::FETCH_OBJ vs PDO::FETCH_ASSOC
php
// FETCH_OBJ → Acceso con -> (objeto)
$files = $stmt->fetchAll(PDO::FETCH_OBJ);
echo $file->ID; // Acceso como objeto
echo $file->content;
// FETCH_ASSOC → Acceso con [] (array)
$files = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo $file['ID']; // Acceso como array
echo $file['content'];
Analogía:
FETCH_OBJ = Como recibir una caja con etiquetas (propiedades)
FETCH_ASSOC = Como recibir una caja con instrucciones (claves)
2. Operador ternario (? :)
php
// Forma larga (if-else):
if($file->type === 'audio'){
echo '<img src="audio-img.png"/>';
} else {
echo '<img src="video-img.png"/>';
}
// Forma corta (ternario):
echo ($file->type === 'audio') ?
'<img src="audio-img.png"/>' :
'<img src="video-img.png"/>';
Estructura: condición ? valor_si_true : valor_si_false
3. ORDER BY ID DESC
sql
-- DESC (Descendente): Más reciente primero
SELECT * FROM files ORDER BY ID DESC
-- Resultado: ID 10, 9, 8, 7... (nuevo → viejo)
-- ASC (Ascendente): Más antiguo primero
SELECT * FROM files ORDER BY ID ASC
-- Resultado: ID 1, 2, 3, 4... (viejo → nuevo)
🎨 Diagrama del flujo de datos
text
┌─────────────────────────────────────────────────────────────┐
│ FLUJO DE DATOS: BD → PHP → HTML │
├─────────────────────────────────────────────────────────────┤
│ BASE DE DATOS: │
│ ┌─────────┬──────────────┬──────────────────────┬────────┐ │
│ │ ID: 25 │ fileUrl │ content │ type │ │
│ ├─────────┼──────────────┼──────────────────────┼────────┤ │
│ │ 26 │ files/b.mp3 │ "Segunda prueba" │ audio │ │
│ │ 25 │ files/a.mp3 │ "Primera prueba" │ audio │ │
│ └─────────┴──────────────┴──────────────────────┴────────┘ │
│ ↓ ORDER BY ID DESC │
│ ↓ (26 aparece primero) │
│ ↓ │
│ PHP: getRecentList() │
│ • SELECT * FROM files ORDER BY ID DESC │ │
│ • fetchAll(PDO::FETCH_OBJ) │ │
│ • foreach($files as $file) │ │
│ ↓ │
│ HTML GENERADO: │ │
│ <a href="view.php?file=26"> │ │
│ <li> │ │
│ <img src="audio-img.png"/> ← Ternario evalúa 'audio' │ │
│ <p>Segunda prueba</p> ← Truncado si es muy largo │ │
│ </li> │ │
│ </a> │ │
│ <a href="view.php?file=25"> │ │
│ ... (similar para ID 25) │ │
│ </a> │ │
└─────────────────────────────────────────────────────────────┘
🔧 Cómo integrar en view.php
php
<!-- En view.php, en la sección de lista reciente: -->
<div style="height: 400px; overflow:scroll;">
<div class="flex">
<ul class="w-full h-full">
<!-- RECENT GENERATED FILES -->
<?php $whisperObj->getRecentList(); ?>
</ul>
</div>
</div>
Explicación del código:
<?php ... ?> → Tags PHP embebidos en HTML
$whisperObj → Instancia de la clase Whisper (creada en init.php)
->getRecentList() → Llama al método que muestra la lista
El método echo directamente el HTML
💡 Mejoras y consideraciones
1. Truncar texto largo (truncate)
css
/* En Tailwind, truncate corta texto con puntos suspensivos */
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
Resultado: "Este es un texto muy largo que se cortará..." en lugar de desbordar.
2. Paginación (para muchos archivos)
php
// Mejora futura: LIMIT para paginación
$stmt = $this->DB->prepare("
SELECT * FROM `files`
ORDER BY `ID` DESC
LIMIT 0, 10 -- 10 primeros resultados
");
3. Imágenes responsivas
html
<!-- Mejor práctica: tamaño fijo y alt descriptivo -->
<img src="frontend/images/audio-img.png"
class="w-10 h-10"
alt="Icono de audio">
🚀 Proceso completo de funcionamiento
Paso a paso:
Usuario sube archivo en index.php
Se procesa con OpenAI y guarda en BD (lección anterior)
Redirecciona a view.php?file=ID
view.php carga y muestra:
Archivo actual (próxima lección)
Lista de TODOS los archivos (getRecentList())
Usuario hace click en cualquier archivo de la lista
Recarga view.php con nuevo ID en URL
Ciclo continúa...
🔍 Explicando problemas comunes
Error: "Column 'content' not found"
sql
-- PROBLEMA: La tabla tenía 'name' en lugar de 'content'
ALTER TABLE files CHANGE name content TEXT;
-- SOLUCIÓN: Cambiar nombre de columna o ajustar consulta
Solución paso a paso:
Abrir phpMyAdmin o MySQL Workbench
Ir a la tabla files
Verificar nombres de columnas
Cambiar si es necesario:
fileUrl → VARCHAR(255)
content → TEXT
type → ENUM('audio', 'video') o VARCHAR(20)
🎨 Diseño visual de la lista
text
┌─────────────────────────────────────────────────────────────┐
│ LISTA DE ARCHIVOS │
├─────────────────────────────────────────────────────────────┤
│ ┌─────┐ ┌─────────────────────────────────────────────┐ │
│ │ 🔊 │ │ Entrevista con el cliente sobre el nuevo... │ │
│ └─────┘ └─────────────────────────────────────────────┘ │
│ (click → view.php?file=26) │
│ │
│ ┌─────┐ ┌─────────────────────────────────────────────┐ │
│ │ 🎥 │ │ Tutorial completo de PHP para principia... │ │
│ └─────┘ └─────────────────────────────────────────────┘ │
│ (click → view.php?file=25) │
│ │
│ ┌─────┐ ┌─────────────────────────────────────────────┐ │
│ │ 🔊 │ │ Podcast sobre inteligencia artificial y... │ │
│ └─────┘ └─────────────────────────────────────────────┘ │
│ (click → view.php?file=24) │
└─────────────────────────────────────────────────────────────┘
Características:
Icono diferente para audio (🔊) y video (🎥)
Texto truncado con puntos suspensivos
Efecto hover (cambio de fondo al pasar mouse)
Enlace clickeable a view.php con ID
📊 Tabla de métodos y responsabilidades
🎯 Puntos Clave para Recordar
ORDER BY ID DESC → Orden descendente (más reciente primero)
PDO::FETCH_OBJ → Convierte resultados a objetos accesibles con ->
Operador ternario → condición ? true : false (if-else en una línea)
echo en métodos → Puede generar HTML directamente (no siempre buena práctica)
<p class="truncate"> → Corta texto largo con ...
📋 Cuestionario de Repaso
Pregunta 1:
¿Qué hace ORDER BY ID DESC en la consulta SQL?
a) Ordena alfabéticamente por contenido
b) Ordena por ID de mayor a menor (más reciente primero)
c) Ordena por fecha de modificación
d) Ordena aleatoriamente
Pregunta 2:
¿Cuál es la diferencia entre PDO::FETCH_OBJ y PDO::FETCH_ASSOC?
a) Uno es más rápido que el otro
b) Uno retorna objetos ($file->ID) y otro arrays ($file['ID'])
c) Uno funciona con MySQL y otro con PostgreSQL
d) No hay diferencia
Pregunta 3:
¿Qué hace el operador ternario ($file->type === 'audio') ? 'A' : 'B'?
a) Asigna 'A' si es audio, 'B' si no
b) Retorna 'A' si es audio, 'B' si es video
c) Compara tres valores diferentes
d) Es un operador matemático
Pregunta 4:
¿Por qué usamos class="truncate" en el párrafo del contenido?
a) Para hacer el texto en negrita
b) Para cortar texto largo con puntos suspensivos (...)
c) Para cambiar el color del texto
d) Para hacer el texto más pequeño
Pregunta 5:
¿Qué problema soluciona include 'backend/init.php' al inicio de view.php?
a) Incluye la conexión a BD y la instancia de Whisper
b) Carga los estilos CSS de la página
c) Inicia la sesión del usuario
d) Configura las variables de entorno
📝 Respuestas del Cuestionario
b) DESC significa "descendente", por lo que ordena del ID más alto (más reciente) al más bajo (más antiguo). ASC sería ascendente (antiguo → nuevo).
b) FETCH_OBJ retorna objetos donde accedes con -> (ej: $file->ID). FETCH_ASSOC retorna arrays asociativos donde accedes con [] (ej: $file['ID']).
b) El operador ternario evalúa la condición antes del ?. Si es verdadera, retorna el valor después del ?. Si es falsa, retorna el valor después del :.
b) La clase truncate de Tailwind CSS aplica text-overflow: ellipsis que corta texto largo y añade ... al final para evitar desbordamientos visuales.
a) init.php incluye DB.php y Whisper.php, crea la conexión a la base de datos y la instancia $whisperObj que necesitamos en toda la página.
🚀 Lo que viene después
¡Perfecto! Ahora tenemos:
✅ Página de visualización (view.php)
✅ Lista de archivos recientes
✅ Navegación entre archivos
En la próxima lección vamos a:
Cargar y mostrar el archivo específico según el ID en la URL
Implementar el reproductor de audio/video
Mostrar el texto transcrito correspondiente
Preparar la funcionalidad de traducción
Comentarios
Publicar un comentario