20: Creando el Método para Actualizar la Traducción en la Base de Datos

 

20: Creando el Método para Actualizar la Traducción en la Base de Datos

¡Hola y bienvenido de nuevo!

En esta lección vamos a crear un método para actualizar la base de datos con la traducción del texto. Cuando el usuario seleccione un idioma y haga clic en "Translate", guardaremos la traducción en la base de datos para mostrarla posteriormente. ¡Vamos a ello!


📋 Contenido del Tutorial

  1. Entendiendo la actualización de datos

  2. Creando el método update

  3. La consulta UPDATE en SQL

  4. Vinculando parámetros para UPDATE

  5. Seguridad en las actualizaciones

  6. Código completo

  7. Explicación detallada

  8. Próximos pasos


💾 1. Entendiendo la actualización de datos

¿Qué es una actualización en SQL?

Una actualización (UPDATE) modifica datos existentes en una tabla de la base de datos. A diferencia de INSERT (que agrega nuevos registros), UPDATE cambia los valores de registros ya existentes.

Estructura de UPDATE

sql
UPDATE nombre_tabla 
SET columna1 = valor1, columna2 = valor2 
WHERE columna_condicion = valor_condicion;

Nuestro caso de uso

Cuando un usuario traduce el texto:

text
1. Texto original: "Hello world"
2. Idioma seleccionado: "Spanish"
3. Traducción: "Hola mundo"
4. Actualizar en la base de datos:
   - translated = "Hola mundo"
   - lang = "Spanish"
   - Donde ID = 1

Columnas a actualizar

ColumnaValorDescripción
translatedTexto traducidoContenido en el idioma destino
langIdioma destinoEj: "Spanish", "French"

🔧 2. Creando el método update

Estructura del método

php
public function update($fileID, $content, $lang) {
    // 1. Preparar la consulta UPDATE
    $stmt = $this->DB->prepare(
        "UPDATE `files` 
         SET `translated` = :content, `lang` = :lang 
         WHERE `ID` = :fileID"
    );
    
    // 2. Vincular los parámetros
    $stmt->bindParam(":content", $content, PDO::PARAM_STR);
    $stmt->bindParam(":lang", $lang, PDO::PARAM_STR);
    $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);
    
    // 3. Ejecutar la consulta
    $stmt->execute();
}

Parámetros del método

ParámetroTipoDescripciónEjemplo
$fileIDintID del archivo a actualizar1
$contentstringTexto traducido"Hola mundo"
$langstringIdioma de la traducción"Spanish"

📝 3. La consulta UPDATE en SQL

Consulta completa

sql
UPDATE `files` 
SET `translated` = :content, `lang` = :lang 
WHERE `ID` = :fileID

Desglose de la consulta

ParteSignificadoEjemplo
UPDATE filesTabla a actualizarfiles
SET translated = :contentColumna a modificartranslated = "Hola mundo"
SET lang = :langColumna a modificarlang = "Spanish"
WHERE ID = :fileIDCondición de búsquedaID = 1

¿Qué hace cada parte?

text
UPDATE files          ← Tabla: files
SET                   ← Comienza la asignación
  translated = :content  ← Columna: translated
  lang = :lang           ← Columna: lang
WHERE ID = :fileID    ← Solo el registro con este ID

Ejemplo de ejecución

sql
-- Antes de UPDATE
ID | content        | translated | lang
1  | "Hello world"  | NULL       | NULL

-- Ejecutar UPDATE
UPDATE files 
SET translated = "Hola mundo", lang = "Spanish" 
WHERE ID = 1

-- Después de UPDATE
ID | content        | translated  | lang
1  | "Hello world"  | "Hola mundo" | "Spanish"

🔗 4. Vinculando parámetros para UPDATE

¿Por qué vincular parámetros?

RazónExplicación
SeguridadPreviene inyección SQL
RendimientoLa consulta se compila una vez
MantenimientoCódigo más limpio

Código de vinculación

php
// Vincular el contenido (texto)
$stmt->bindParam(":content", $content, PDO::PARAM_STR);

// Vincular el idioma (texto)
$stmt->bindParam(":lang", $lang, PDO::PARAM_STR);

// Vincular el ID (entero)
$stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);

Tipos de parámetros PDO

ConstanteTipoUso
PDO::PARAM_STRStringTexto, nombres, descripciones
PDO::PARAM_INTIntegerIDs, contadores, números
PDO::PARAM_BOOLBooleanTrue/False
PDO::PARAM_NULLNULLValores nulos

🛡️ 5. Seguridad en las actualizaciones

Buenas prácticas

php
public function update($fileID, $content, $lang) {
    try {
        // 1. Validar que el ID existe
        if (!$this->fileExists($fileID)) {
            $this->error = "El archivo no existe";
            return false;
        }
        
        // 2. Validar que el contenido no esté vacío
        if (empty($content)) {
            $this->error = "El contenido no puede estar vacío";
            return false;
        }
        
        // 3. Validar que el idioma sea válido
        $allowedLanguages = ['English', 'Spanish', 'French', 'German'];
        if (!in_array($lang, $allowedLanguages)) {
            $this->error = "Idioma no válido";
            return false;
        }
        
        // 4. Ejecutar la actualización
        $stmt = $this->DB->prepare(
            "UPDATE `files` SET `translated` = :content, `lang` = :lang WHERE `ID` = :fileID"
        );
        $stmt->bindParam(":content", $content, PDO::PARAM_STR);
        $stmt->bindParam(":lang", $lang, PDO::PARAM_STR);
        $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);
        return $stmt->execute();
        
    } catch (PDOException $e) {
        $this->error = "Error al actualizar: " . $e->getMessage();
        return false;
    }
}

Verificaciones adicionales

VerificaciónCódigoPropósito
Existencia$this->fileExists($fileID)Verificar que el archivo existe
Contenido!empty($content)No guardar traducciones vacías
Idiomain_array($lang, $allowed)Solo idiomas permitidos
Excepcióntry-catchCapturar errores de base de datos

💻 6. Código completo

Archivo: backend/classes/Whisper.php (método update agregado)

php
<?php 
    class Whisper{
        public $error;
        public $dataType;
        public $file;
        public $lang;
        public $content;
        private $DB;

        public function __construct(){
            $db = new DB;
            $this->DB = $db->connect();
        }
        
        public function errors(){
            return $this->error;
        }

        public function getApiUrl(){
            if($this->dataType === "ASR"){
                return "https://api.openai.com/v1/audio/transcriptions";
            }else{
                return "https://api.openai.com/v1/chat/completions";
            }
        }

        public function getHeader(){
            if($this->dataType === "ASR"){
                return [
                    'Authorization: Bearer ' . API_TOKEN,
                    'Content-Type: multipart/form-data'
                ];
            }else{
                return [
                    'Authorization: Bearer ' . API_TOKEN,
                    'Content-Type: application/json'
                ];
            }
        }

        public function getData(){
            if($this->dataType === "ASR"){
                return [
                    'file' => $this->file,
                    'model' => 'whisper-1'
                ];
            }else{
                return json_encode([
                    'model' => 'gpt-3.5-turbo',
                    'messages' => [
                        [
                            'role' => 'system',
                            'content' => 'You will be provided with a text, and your task is to translate it into ' . $this->lang
                        ],
                        [
                            'role' => 'user',
                            'content' => $this->content
                        ]
                    ]
                ]);
            }
        }

        public function getFile(){
            if($this->dataType === 'ASR'){
                $this->file = curl_file_create($this->file);
            }
        }

        public function covert(){
            $apiUrl = $this->getApiUrl();
            $ch = curl_init($apiUrl);
            curl_setopt($ch, CURLOPT_POST, true);
            $this->getFile();
            curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getData());
            curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getHeader());
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($ch);
            curl_close($ch);

            if($response){
                return json_decode($response, true);
            }else{
                $this->error = "API REQUEST FAILED";
            }
        }

        public function upload($file){
            $fileTmp  = $file['tmp_name'];
            $filename = basename($file['name']);
            $fileSize = $file['size'];
            $errors   = $file['error'];
            $mime     = $file['type'];

            //get file extension
            $ext  = pathinfo($filename, PATHINFO_EXTENSION);
            $ext  = strtolower($ext);
            
            
            $parentDirectoy = dirname(dirname(dirname(__FILE__)));

            $allowedMedia = ['video/mp4','video/mpeg', 'audio/mpeg','audio/mpeg3','audio/wav'];

            if(in_array($mime, $allowedMedia)){
                if($fileSize <= 20000000){
                    $folder = 'files/';
                    $file   = $folder. md5(time() . mt_rand()) . '.'.$ext;
                    move_uploaded_file($fileTmp, $parentDirectoy . '/'.$file);
                    return $file;
                }else{
                    $this->error = "File is large!";
                }
            }else{
                $this->error = "invalid file format!";
            }
        }
        
        public function save($file, $content, $type){
            $stmt = $this->DB->prepare("INSERT INTO `files` (`fileUrl`, `content`, `type`) VALUES (:fileUrl, :content, :type)");
            $stmt->bindParam(":fileUrl", $file, PDO::PARAM_STR);
            $stmt->bindParam(":content", $content, PDO::PARAM_STR);
            $stmt->bindParam(":type", $type, PDO::PARAM_STR);
            $stmt->execute();
            return $this->DB->lastInsertId();
        }

        public function getRecentList(){
            $stmt = $this->DB->prepare("SELECT * FROM `files` ORDER BY `ID` DESC");
            $stmt->execute();
            $files = $stmt->fetchAll(PDO::FETCH_OBJ);

            foreach($files as $file){
                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">
                                '.(($file->type === 'audio') ? 
                                    '<img src="frontend/images/audio-img.png"/>' : 
                                    '<img src="frontend/images/video-img.png"/>' ) 
                                .'
                                </div>
                                <div class=" overflow-hidden w-60 font-normal h-auto font-bold p-2 ">
                                    <div>
                                        <p class="truncate">'.htmlspecialchars($file->content).'</p>
                                    </div>
                                </div>
                                    
                            </li>
                        </a>';
            }
        }

        public function getFileById($fileID){
            $stmt = $this->DB->prepare("SELECT * FROM `files` WHERE `ID` = :fileID");
            $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);
            $stmt->execute();
            return $stmt->fetch(PDO::FETCH_OBJ);
        }

        /**
         * Actualiza la traducción de un archivo en la base de datos
         * 
         * @param int $fileID ID del archivo a actualizar
         * @param string $content Texto traducido
         * @param string $lang Idioma de la traducción
         * @return bool True si se actualizó correctamente, false en caso de error
         */
        public function update($fileID, $content, $lang){
            try {
                // 1. Verificar que el archivo existe
                $file = $this->getFileById($fileID);
                if (!$file) {
                    $this->error = "El archivo no existe";
                    return false;
                }
                
                // 2. Verificar que el contenido no esté vacío
                if (empty($content)) {
                    $this->error = "El contenido no puede estar vacío";
                    return false;
                }
                
                // 3. Verificar que el idioma sea válido
                $allowedLanguages = ['English', 'Spanish', 'French', 'German'];
                if (!in_array($lang, $allowedLanguages)) {
                    $this->error = "Idioma no válido";
                    return false;
                }
                
                // 4. Preparar la consulta UPDATE
                $stmt = $this->DB->prepare(
                    "UPDATE `files` 
                     SET `translated` = :content, `lang` = :lang 
                     WHERE `ID` = :fileID"
                );
                
                // 5. Vincular los parámetros
                $stmt->bindParam(":content", $content, PDO::PARAM_STR);
                $stmt->bindParam(":lang", $lang, PDO::PARAM_STR);
                $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);
                
                // 6. Ejecutar la consulta
                $result = $stmt->execute();
                
                if ($result) {
                    return true;
                } else {
                    $this->error = "Error al ejecutar la actualización";
                    return false;
                }
                
            } catch (PDOException $e) {
                $this->error = "Error en la base de datos: " . $e->getMessage();
                return false;
            }
        }
    }
?>

Archivo: translate.php (nuevo archivo)

php
<?php
/**
 * Archivo para manejar las solicitudes de traducción
 * Recibe el contenido, idioma y ID del archivo, y guarda la traducción
 */

// Incluir la configuración
include 'backend/init.php';

// Configurar la respuesta como JSON
header('Content-Type: application/json');

// Verificar que sea una solicitud POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode([
        'success' => false,
        'error' => 'Método no permitido'
    ]);
    exit;
}

// Obtener los datos del formulario
$content = $_POST['content'] ?? '';
$lang = $_POST['lang'] ?? '';
$fileId = isset($_POST['file_id']) ? (int)$_POST['file_id'] : 0;

// Validar los datos
if (empty($content)) {
    echo json_encode([
        'success' => false,
        'error' => 'No hay contenido para traducir'
    ]);
    exit;
}

if (empty($lang)) {
    echo json_encode([
        'success' => false,
        'error' => 'No se seleccionó un idioma'
    ]);
    exit;
}

if ($fileId <= 0) {
    echo json_encode([
        'success' => false,
        'error' => 'ID de archivo inválido'
    ]);
    exit;
}

// Configurar Whisper para traducción
$whisperObj->dataType = 'TRANSLATE';
$whisperObj->content = $content;
$whisperObj->lang = $lang;

// Enviar a la API
$result = $whisperObj->convert();

// Verificar el resultado
if ($result && isset($result['choices'][0]['message']['content'])) {
    $translated = $result['choices'][0]['message']['content'];
    
    // Guardar en la base de datos
    $updated = $whisperObj->update($fileId, $translated, $lang);
    
    if ($updated) {
        echo json_encode([
            'success' => true,
            'translated' => $translated,
            'lang' => $lang,
            'message' => 'Traducción guardada correctamente'
        ]);
    } else {
        echo json_encode([
            'success' => false,
            'error' => $whisperObj->errors()
        ]);
    }
} else {
    echo json_encode([
        'success' => false,
        'error' => $whisperObj->errors() ?: 'Error al traducir el texto'
    ]);
}
?>

Archivo: view.php (actualizado con JavaScript para traducción)

php
<!-- JS CODE HERE -->
<script>
$(document).ready(function() {
    // Evento del botón de traducción
    $('#translateBtn').click(function(e) {
        e.preventDefault();
        
        // Mostrar loader
        $('#loader').removeClass('hidden');
        
        // Obtener datos
        var lang = $('#lang').val();
        var content = $('div#scroll h3:first').next('div').text().trim();
        var fileId = <?php echo $file ? $file->ID : 0; ?>;
        
        // Validar que haya contenido
        if (!content || content === 'No hay contenido para mostrar') {
            $('#loader').addClass('hidden');
            alert('No hay contenido para traducir');
            return;
        }
        
        // Validar que haya un archivo seleccionado
        if (fileId === 0) {
            $('#loader').addClass('hidden');
            alert('No hay archivo seleccionado');
            return;
        }
        
        // Enviar solicitud AJAX
        $.ajax({
            url: 'translate.php',
            method: 'POST',
            data: {
                content: content,
                lang: lang,
                file_id: fileId
            },
            dataType: 'json',
            success: function(response) {
                $('#loader').addClass('hidden');
                
                if (response.success) {
                    // Mostrar mensaje de éxito
                    alert('¡Traducción completada!');
                    
                    // Recargar la página para mostrar la traducción
                    location.reload();
                } else {
                    // Mostrar error
                    alert('Error: ' + (response.error || 'Error desconocido'));
                }
            },
            error: function(xhr, status, error) {
                $('#loader').addClass('hidden');
                console.error('Error AJAX:', error);
                alert('Error al conectar con el servidor');
            }
        });
    });
});
</script>

📖 7. Explicación detallada

El método update paso a paso

php
public function update($fileID, $content, $lang) {
    try {
        // 1. Verificar que el archivo existe
        $file = $this->getFileById($fileID);
        if (!$file) {
            $this->error = "El archivo no existe";
            return false;
        }
        
        // 2. Validar contenido
        if (empty($content)) {
            $this->error = "El contenido no puede estar vacío";
            return false;
        }
        
        // 3. Validar idioma
        $allowedLanguages = ['English', 'Spanish', 'French', 'German'];
        if (!in_array($lang, $allowedLanguages)) {
            $this->error = "Idioma no válido";
            return false;
        }
        
        // 4. Ejecutar UPDATE
        $stmt = $this->DB->prepare(
            "UPDATE `files` 
             SET `translated` = :content, `lang` = :lang 
             WHERE `ID` = :fileID"
        );
        $stmt->bindParam(":content", $content, PDO::PARAM_STR);
        $stmt->bindParam(":lang", $lang, PDO::PARAM_STR);
        $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);
        return $stmt->execute();
        
    } catch (PDOException $e) {
        $this->error = "Error en la base de datos: " . $e->getMessage();
        return false;
    }
}

¿Qué hace la consulta UPDATE?

Antes de UPDATE:

text
ID | fileUrl | content        | translated | lang
1  | files/  | "Hello world"  | NULL       | NULL

Después de UPDATE:

text
ID | fileUrl | content        | translated  | lang
1  | files/  | "Hello world"  | "Hola mundo" | "Spanish"

Validaciones implementadas

ValidaciónCódigoPropósito
Existencia$this->getFileById($fileID)Verificar que el ID existe
Contenido!empty($content)No guardar texto vacío
Idiomain_array($lang, $allowed)Solo idiomas permitidos

🧪 8. Probando la traducción

Paso 1: Subir un archivo

  1. Ve a http://localhost/whisper/

  2. Selecciona un archivo de audio o video

  3. Espera la transcripción

Paso 2: Traducir el texto

  1. En view.php, selecciona un idioma (ej: "Spanish")

  2. Haz clic en "Translate"

  3. Espera el procesamiento

Paso 3: Verificar la traducción

  1. La página se recarga automáticamente

  2. La traducción aparece en la sección "Translated Content"

  3. El idioma se muestra en el título

Paso 4: Verificar en la base de datos

sql
-- Verificar la traducción guardada
SELECT ID, content, translated, lang 
FROM files 
WHERE ID = [TU_ID];

📊 9. Resumen del flujo de traducción

Diagrama de secuencia

text
Usuario → view.php
    ↓
Selecciona idioma
    ↓
Clic en "Translate"
    ↓
JavaScript (AJAX) → translate.php
    ↓
translate.php → Whisper->convert()
    ↓
Whisper → API de OpenAI
    ↓
API devuelve traducción
    ↓
translate.php → Whisper->update()
    ↓
update → Base de datos
    ↓
Respuesta JSON → JavaScript
    ↓
Recargar página → Mostrar traducción

Datos guardados

ColumnaOrigenEjemplo
translatedAPI de OpenAI"Hola mundo"
langSelect del usuario"Spanish"

❓ Preguntas frecuentes

¿Qué hace UPDATE en SQL?

  • Modifica registros existentes en una tabla

  • Permite cambiar valores de columnas específicas

¿Por qué necesito WHERE en UPDATE?

  • Para especificar qué registros actualizar

  • Sin WHERE, se actualizarían TODOS los registros

¿Qué pasa si el ID no existe?

  • getFileById() retorna false

  • El método update() retorna false con un error

¿Puedo traducir varias veces?

  • Sí, cada traducción sobrescribe la anterior

  • El idioma y el texto se actualizan

¿Por qué usar PDO::PARAM_INT para el ID?

  • Especifica que el valor es un número entero

  • Mejora la seguridad y el rendimiento

¿Qué son las excepciones PDO?

  • Son errores que lanza PDO cuando algo falla

  • Se capturan con try-catch para manejarlos


🎯 Próximos pasos

Lo que hemos logrado

✅ Método update() implementado
✅ Validación de datos antes de actualizar
✅ Consulta UPDATE con parámetros vinculados
✅ Manejo de errores con try-catch
✅ Integración con traducción

Lo que viene en la próxima lección

En la siguiente lección vamos a:

  1. Completar el ciclo de traducción

  2. Mejorar la interfaz de usuario

  3. Agregar más idiomas de traducción

  4. Optimizar el rendimiento

Resumen del proyecto completo

text
📁 Proyecto Whisper
├── 📄 index.php          → Subir archivos
├── 📄 view.php           → Ver archivos y traducciones
├── 📄 translate.php      → Procesar traducciones
├── 📁 backend/
│   ├── 📄 init.php       → Configuración
│   └── 📁 classes/
│       ├── 📄 DB.php     → Conexión a BD
│       └── 📄 Whisper.php → Lógica principal
├── 📁 files/             → Archivos subidos
└── 📁 frontend/          → Estilos e imágenes

¡Excelente trabajo! Ahora tenemos un sistema completo de traducción que guarda los resultados en la base de datos. La aplicación está lista para ser usada y expandida.

Comentarios

Entradas más populares de este blog

Cómo usar Whisper para sacar el texto de un video

Tutorial 18: Creando la Página de Visualización de Archivos Recientes

Tutorial 11: ¿Qué es Whisper AI y Cómo Funciona?