Diferencia entre revisiones de «PHP UD3 OP MODIFICAR»

De MediaWiki
Ir a la navegación Ir a la búsqueda
 
Línea 1123: Línea 1123:
  
 
nin construír a consulta con <code>sprintf()</code>, porque os valores se envían mediante unha consulta preparada.
 
nin construír a consulta con <code>sprintf()</code>, porque os valores se envían mediante unha consulta preparada.
 +
 +
<br />
 +
 +
=== Exercicios propostos ===
 +
 +
* De todos os exercicios fai dúas versións, unha empregando a orde SQL UPDATE e outra chamando a un procedemento almacenado de Mysql.
 +
 +
 +
<br />
 +
==== Modificación de Autores ====
 +
 +
* Ao carón de cada autor, amosa enlace para cargar o formulario de modificación de autores.
 +
: O campo id non debe aparecer visualmente no formulario.
 +
: Emprega o mesmo arquivo de campos_?????.php onde se gardan os datos no formulario.
 +
 +
 +
 +
 +
 +
 +
<br />
 +
 +
==== Modificación de Libros ====
 +
 +
* Ao carón de cada libro, amosa enlace para cargar o formulario de modificación de libros.
 +
: O campo id non debe aparecer visualmente no formulario.
 +
: Emprega o mesmo arquivo de campos_?????.php onde se gardan os datos no formulario.
 +
 +
 +
 +
 +
<br />
 +
==== Modificación de Exemplares ====
 +
 +
* Ao carón de cada exemplar, amosa enlace para cargar o formulario de modificación de exemplares.
 +
: O campo id non debe aparecer visualmente no formulario.
 +
: Emprega o mesmo arquivo de campos_?????.php onde se gardan os datos no formulario.
 +
 +
 +
 +
 +
 +
 +
  
 
<br />
 
<br />

Revisión actual del 09:43 6 oct 2026

Introdución

Nesta unidade veremos como realizar operacións de modificación sobre unha base de datos MySQL/MariaDB desde PHP 8.5.

O mecanismo principal empregado nos apuntes será PDO (PHP Data Objects). Ao final inclúese tamén unha explicación equivalente empregando mysqli como ampliación.

  • Requirimentos previos:
  • Ter creada a base de datos indicada nunha sección anterior.
  • Ter gardados os parámetros de conexión nun arquivo separado, fóra da ruta /var/www/html.
  • Ter instalada e habilitada a extensión pdo_mysql.
  • Para os exemplos que traballan con cadeas UTF-8 empregaremos tamén a extensión mbstring.
  • Os exemplos están pensados para PHP 8.5.
  • Partiremos do código realizado anteriormente nas seccións de alta e baixa.
  • NOTA IMPORTANTE: cando empregamos datos procedentes dun formulario debemos validalos no servidor e utilizar consultas preparadas para incorporalos ás ordes SQL.
A protección fronte a ataques CSRF (Cross-Site Request Forgery) incorporarase cando se estuden as sesións. Unha petición POST por si soa non evita un ataque CSRF.



  • CONSIDERACIÓNS:
  • Crear un usuario/password con permisos de acceso a dita base de datos (sempre dádelle os mínimos permisos posibles, neste caso, serán os de SELECT/UPDATE/DELETE/INSERT/EXECUTE de toda a base de datos)
  • Crear nun arquivo separado do sitio web todos os datos necesarios para conectarse á base de datos e onde van estar definidas as constantes coas ordes SQL, tal como vimos nun punto anterior desta sección.
  • Lembrar poñer as ordes para amosar os erros que se poidan dar. Nunha aplicación final esas ordes hai que quitalas.
  • En Linux, lembrar dar permiso 644 (rw,r,r) aos arquivos php e cambiar o propietario a www-data:www-data. Nos cartafoles permisos 755 cando rematedes.
  • Ao principio ides faiar en moitas cousas. Lembrade ter posto o display_errors como vimos nas seccións anteriores. Tamén axuda moito facer un echo da sentenza a enviar á base de datos antes de enviala para ver que estea todo ben (espazos en branco, comillas,...).
  • Lembrar que tamén podedes activar o debugger como está indicado nesta sección da wiki.




Organización dos exemplos

Nos exemplos completos empregaremos o mesmo esquema que nas seccións anteriores.

No arquivo /var/www/confBD/bd.php, situado fóra da ruta pública /var/www/html, gardaremos os datos da conexión e as consultas que se vaian empregar:

<?php

define('SERVIDOR', 'localhost');
define('USUARIO', 'user_php');
define('PASSWORD', 'user_php');
define('BD', 'php');

define(
    'NOME_DA_CONSULTA',
    'SELECT col1, col2
     FROM taboa
     ORDER BY col1'
);

Nas páxinas PHP que accedan á base de datos empregaremos como base o seguinte esquema:

<?php

try {

    require '/var/www/confBD/bd.php';

    $dsn = sprintf(
        'mysql:host=%s;dbname=%s;charset=utf8mb4',
        SERVIDOR,
        BD
    );

    $conex = new PDO(
        $dsn,
        USUARIO,
        PASSWORD,
        [
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]
    );

    // Operacións coa base de datos...

} catch (PDOException $e) {

    error_log($e->getMessage());

    die('Erro relacionado coa base de datos.');

} catch (Throwable $e) {

    error_log($e->getMessage());

    die('Produciuse un erro na aplicación.');
}


Modificación de rexistros: UPDATE

  • A modificación de datos realízase mediante a orde SQL UPDATE.

Por exemplo:

UPDATE estados
SET descripcion = 'NOVO VALOR'
WHERE id = 10;
  • A orde UPDATE indicará:
  • a táboa que queremos modificar;
  • as columnas e os novos valores mediante SET;
  • as filas que se deben modificar mediante WHERE.
  • IMPORTANTE: debemos ter especial coidado coa cláusula WHERE.

A seguinte orde:

UPDATE estados
SET descripcion = 'NOVO VALOR';

modificaría todas as filas da táboa.

  • Normalmente non modificaremos a clave primaria. O habitual é empregar a clave primaria para identificar a fila e modificar un ou varios dos seus atributos.
  • Se unha clave que se modifica está referenciada mediante unha clave foránea, o comportamento dependerá da acción definida na relación, por exemplo ON UPDATE CASCADE ou ON UPDATE RESTRICT.


UPDATE empregando PDO

Sempre que a orde conteña valores variables empregaremos unha consulta preparada.

Por exemplo:

$stmt = $conex->prepare(
    'UPDATE estados
     SET descripcion = :descripcion
     WHERE id = :id_estado'
);

$stmt->execute([
    ':descripcion' => $descripcion,
    ':id_estado' => $id
]);

Non construiremos a orde SQL mediante concatenación nin empregando sprintf() para introducir os datos.


Número de filas afectadas

Despois dun UPDATE podemos empregar:

$numRexistros = $stmt->rowCount();

para coñecer o número de filas afectadas.

No caso dunha modificación realizada a partir dunha clave primaria normalmente obteremos:

  • 1: unha fila foi modificada.
  • 0: ningunha fila foi modificada.

IMPORTANTE: en MySQL un resultado de 0 non significa necesariamente que o identificador non exista.

Por exemplo, se a fila xa contén exactamente o mesmo valor que intentamos gardar, a orde pode executarse correctamente pero non producir ningún cambio.

Polo tanto, non debemos interpretar automaticamente:

$stmt->rowCount() === 0

como un erro.


Caso práctico: modificación dun estado

Imos continuar o mantemento da táboa ESTADOS realizado nas seccións anteriores.

O proceso será:

  1. Desde consultar_estados.php seleccionamos a opción Editar.
  2. Enviamos o identificador do estado a editar_estado.php.
  3. editar_estado.php consulta os datos actuais da fila.
  4. Amosamos un formulario cos valores actuais.
  5. O formulario envía os novos datos mediante POST a procesar_editar_estado.php.
  6. Validamos novamente todos os datos no servidor.
  7. Executamos un UPDATE mediante unha consulta preparada.
  8. Rediriximos novamente a consultar_estados.php.

Neste caso é correcto empregar unha petición GET para abrir a páxina de edición:

editar_estado.php?id=5

porque esa petición unicamente consulta e amosa os datos.

A modificación real realizarase posteriormente mediante unha petición POST.


Arquivo /var/www/confBD/bd.php

Engadimos as seguintes consultas:

define(
    'CONSULTAR_ESTADO_POR_ID',
    'SELECT id, descripcion
     FROM estados
     WHERE id = :id_estado'
);

define(
    'ACTUALIZAR_ESTADO',
    'UPDATE estados
     SET descripcion = :descripcion
     WHERE id = :id_estado'
);

Fixarse en que tanto o identificador como a nova descrición se envían mediante parámetros.


Modificación de consultar_estados.php

Engadimos a opción Editar na columna de accións.

Podemos manter a consulta e a estrutura realizadas nas seccións anteriores:

<?php

try {

    require '/var/www/confBD/bd.php';

    $dsn = sprintf(
        'mysql:host=%s;dbname=%s;charset=utf8mb4',
        SERVIDOR,
        BD
    );

    $conex = new PDO(
        $dsn,
        USUARIO,
        PASSWORD,
        [
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]
    );

    $stmt = $conex->query(CONSULTAR_ESTADOS);
    $estados = $stmt->fetchAll();

} catch (PDOException $e) {

    error_log($e->getMessage());

    die('Erro relacionado coa base de datos.');

} catch (Throwable $e) {

    error_log($e->getMessage());

    die('Produciuse un erro na aplicación.');
}
?>
<!DOCTYPE html>
<html lang="gl">
<head>
    <meta charset="UTF-8">
    <title>Xestión de estados</title>
</head>
<body>

    <h1>Xestión de estados</h1>

    <form action="alta_estado.php" method="get">
        <button type="submit">Novo estado</button>
    </form>

    <table>
        <thead>
            <tr>
                <th>Id</th>
                <th>Descrición</th>
                <th>Accións</th>
            </tr>
        </thead>

        <tbody>

        <?php foreach ($estados as $estado): ?>

            <tr>

                <td>
                    <?= htmlspecialchars(
                        (string) $estado['id'],
                        ENT_QUOTES | ENT_SUBSTITUTE,
                        'UTF-8'
                    ) ?>
                </td>

                <td>
                    <?= htmlspecialchars(
                        $estado['descripcion'],
                        ENT_QUOTES | ENT_SUBSTITUTE,
                        'UTF-8'
                    ) ?>
                </td>

                <td>

                    <a href="editar_estado.php?id=<?= urlencode(
                        (string) $estado['id']
                    ) ?>">
                        Editar
                    </a>

                    <form
                        action="procesar_borrar_estado.php"
                        method="post"
                        style="display:inline"
                        onsubmit="return confirm(
                            'Estás seguro de que queres borrar este estado?'
                        );"
                    >

                        <input
                            type="hidden"
                            name="id"
                            value="<?= htmlspecialchars(
                                (string) $estado['id'],
                                ENT_QUOTES | ENT_SUBSTITUTE,
                                'UTF-8'
                            ) ?>"
                        >

                        <button
                            type="submit"
                            name="btnBorrarEstado"
                            value="BORRAR"
                        >
                            Borrar
                        </button>

                    </form>

                </td>

            </tr>

        <?php endforeach; ?>

        </tbody>
    </table>

    <?php if (isset($_GET['msg'])): ?>

        <p>
            <?= htmlspecialchars(
                $_GET['msg'],
                ENT_QUOTES | ENT_SUBSTITUTE,
                'UTF-8'
            ) ?>
        </p>

    <?php endif; ?>

    <?php if (isset($_GET['error'])): ?>

        <p>
            <?= htmlspecialchars(
                $_GET['error'],
                ENT_QUOTES | ENT_SUBSTITUTE,
                'UTF-8'
            ) ?>
        </p>

    <?php endif; ?>

</body>
</html>

Aquí empregamos urlencode() para construír o valor do parámetro da URL.

Iso non substitúe a validación no servidor: editar_estado.php debe comprobar igualmente que o identificador recibido é válido.


Modificación de campos_estado.php

O arquivo campos_estado.php xa se utilizaba na operación de alta.

Modificámolo para que poida utilizarse tanto na alta como na edición:

<label for="descripcion">Descrición:</label>

<input
    type="text"
    id="descripcion"
    name="descripcion"
    minlength="4"
    maxlength="100"
    required
    value="<?= htmlspecialchars(
        $descripcion ?? '',
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8'
    ) ?>"
>

Se a variable $descripcion non existe, como sucederá inicialmente na páxina de alta, empregamos unha cadea baleira:

$descripcion ?? ''

Se estamos editando unha fila, $descripcion conterá o valor recuperado da base de datos.

IMPORTANTE: o dato gárdase na base de datos na súa forma normal. Aplicamos htmlspecialchars() neste momento porque estamos introducindo o valor dentro dun documento HTML.

Non debemos gardar previamente na base de datos o resultado de htmlspecialchars() ou htmlentities().


Arquivo editar_estado.php

Esta páxina recibe o identificador mediante GET, valida o seu formato, recupera os datos e amosa o formulario.

<?php

try {

    require '/var/www/confBD/bd.php';

    $dsn = sprintf(
        'mysql:host=%s;dbname=%s;charset=utf8mb4',
        SERVIDOR,
        BD
    );

    $conex = new PDO(
        $dsn,
        USUARIO,
        PASSWORD,
        [
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]
    );

    /*
     * Validamos o identificador recibido na URL.
     */
    $id = filter_input(
        INPUT_GET,
        'id',
        FILTER_VALIDATE_INT,
        [
            'options' => [
                'min_range' => 1
            ]
        ]
    );

    if ($id === false || $id === null) {

        header(
            'Location: consultar_estados.php?error=' .
            urlencode('O identificador non é válido.')
        );
        exit;
    }

    /*
     * Recuperamos os datos actuais do estado.
     */
    $stmt = $conex->prepare(
        CONSULTAR_ESTADO_POR_ID
    );

    $stmt->execute([
        ':id_estado' => $id
    ]);

    $estado = $stmt->fetch();

    if ($estado === false) {

        header(
            'Location: consultar_estados.php?error=' .
            urlencode('Non se atopou o estado indicado.')
        );
        exit;
    }

    /*
     * Estes valores aínda non se codifican para HTML.
     * O escapado farase no momento de xerar a saída.
     */
    $id = (int) $estado['id'];
    $descripcion = $estado['descripcion'];

} catch (PDOException $e) {

    error_log($e->getMessage());

    die('Erro relacionado coa base de datos.');

} catch (Throwable $e) {

    error_log($e->getMessage());

    die('Produciuse un erro na aplicación.');
}
?>
<!DOCTYPE html>
<html lang="gl">
<head>
    <meta charset="UTF-8">
    <title>Modificar estado</title>
</head>
<body>

    <h1>Modificar estado</h1>

    <form
        method="post"
        action="procesar_editar_estado.php"
    >

        <?php require 'campos_estado.php'; ?>

        <input
            type="hidden"
            name="id_orixinal"
            value="<?= htmlspecialchars(
                (string) $id,
                ENT_QUOTES | ENT_SUBSTITUTE,
                'UTF-8'
            ) ?>"
        >

        <button
            type="submit"
            name="btnModificarEstado"
            value="MODIFICAR"
        >
            Modificar
        </button>

    </form>

</body>
</html>


Por que volvemos enviar o identificador?

A páxina editar_estado.php recibe o identificador mediante:

editar_estado.php?id=5

pero cando enviamos o formulario a:

procesar_editar_estado.php

necesitamos indicar que fila debe modificarse.

Por iso incluímos:

<input
    type="hidden"
    name="id_orixinal"
    value="5"
>

IMPORTANTE: que un campo sexa hidden non significa que sexa seguro.

O usuario pode modificar manualmente o valor antes de enviar a petición. Por iso procesar_editar_estado.php debe validalo novamente.

Nunha aplicación con control de usuarios tamén deberemos comprobar que o usuario autenticado ten autorización para modificar a fila indicada.


Arquivo procesar_editar_estado.php

Esta páxina recibe os datos mediante POST, valida os valores e realiza o UPDATE.

<?php

try {

    require '/var/www/confBD/bd.php';

    $dsn = sprintf(
        'mysql:host=%s;dbname=%s;charset=utf8mb4',
        SERVIDOR,
        BD
    );

    $conex = new PDO(
        $dsn,
        USUARIO,
        PASSWORD,
        [
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]
    );

    /*
     * Comprobamos que se chegou desde
     * a operación prevista.
     */
    if (
        !isset($_POST['btnModificarEstado']) ||
        $_POST['btnModificarEstado'] !== 'MODIFICAR'
    ) {

        header(
            'Location: consultar_estados.php?error=' .
            urlencode('O identificador non é válido.')
        );
        exit;
        /* Outra forma de enviar o erro
        throw new RuntimeException(
            'Operación non válida.'
        );
        */
    }

    /*
     * Validamos o identificador.
     */
    $id = filter_input(
        INPUT_POST,
        'id_orixinal',
        FILTER_VALIDATE_INT,
        [
            'options' => [
                'min_range' => 1
            ]
        ]
    );

    if ($id === false || $id === null) {

        header(
            'Location: consultar_estados.php?error=' .
            urlencode('O identificador non é válido.')
        );
        exit;
    }

    /*
     * Recuperamos e normalizamos a descrición.
     */
    $descripcion = trim(
        $_POST['descripcion'] ?? ''
    );

    $lonxitude = mb_strlen(
        $descripcion,
        'UTF-8'
    );

    if (
        $lonxitude < 4 ||
        $lonxitude > 100
    ) {

        header(
            'Location: consultar_estados.php?error=' .
            urlencode(
                'A descrición debe ter entre 4 e 100 caracteres.'
            )
        );
        exit;
    }

    /*
     * Normalización definida pola aplicación.
     */
    $descripcion = mb_strtoupper(
        $descripcion,
        'UTF-8'
    );

    /*
     * Executamos o UPDATE mediante
     * unha consulta preparada.
     */
    $stmt = $conex->prepare(
        ACTUALIZAR_ESTADO
    );

    $stmt->execute([
        ':descripcion' => $descripcion,
        ':id_estado' => $id
    ]);

    $numRexistros = $stmt->rowCount();

    /*
     * En MySQL rowCount() pode devolver 0 se
     * o valor novo é igual ao que xa estaba gardado.
     */
    if ($numRexistros === 1) {

        $mensaxe =
            'Estado actualizado correctamente.';

    } else {

        $mensaxe =
            'A operación realizouse, pero non foi necesario ' .
            'modificar ningún dato.';
    }

    header(
        'Location: consultar_estados.php?msg=' .
        urlencode($mensaxe)
    );
    exit;

} catch (PDOException $e) {

    error_log($e->getMessage());

    header(
        'Location: consultar_estados.php?error=' .
        urlencode('Erro relacionado coa base de datos.')
    );
    exit;

} catch (Throwable $e) {

    error_log($e->getMessage());

    header(
        'Location: consultar_estados.php?error=' .
        urlencode('Produciuse un erro na aplicación.')
    );
    exit;
}


Validación da descrición

Empregamos:

$descripcion = trim(
    $_POST['descripcion'] ?? ''
);

para eliminar espazos ao principio e ao final.

Despois comprobamos a lonxitude con:

$lonxitude = mb_strlen(
    $descripcion,
    'UTF-8'
);

Empregamos mb_strlen() porque estamos traballando con texto UTF-8 e queremos contar correctamente caracteres como:

á
é
í
ó
ú
ñ

A validación HTML realizada no formulario mediante:

minlength="4"
maxlength="100"
required

mellora a experiencia do usuario, pero non substitúe a validación no servidor.


Normalización a maiúsculas

Se a regra da aplicación indica que as descricións deben gardarse en maiúsculas, empregaremos:

$descripcion = mb_strtoupper(
    $descripcion,
    'UTF-8'
);

Por exemplo:

área pública

convértese en:

ÁREA PÚBLICA

Esta operación é unha normalización dos datos. Non é unha medida para evitar SQL Injection.

A protección fronte a SQL Injection obtense empregando correctamente a consulta preparada.


Comportamento de rowCount() nun UPDATE

Consideremos que a táboa contén:

id = 5
descripcion = ACTIVO

Se executamos:

UPDATE estados
SET descripcion = 'INACTIVO'
WHERE id = 5;

produciuse unha modificación e rowCount() normalmente devolverá:

1

Pero se executamos:

UPDATE estados
SET descripcion = 'ACTIVO'
WHERE id = 5;

cando o valor xa era ACTIVO, MySQL pode indicar:

0

filas modificadas.

Por iso un resultado de 0 non permite distinguir por si só entre:

  • unha fila inexistente;
  • unha fila existente cuxo valor xa era igual ao novo valor.

Se a aplicación necesita distinguir ambos os casos, podemos comprobar previamente a existencia da fila mediante unha consulta adicional ou deseñar a lóxica da aplicación para facelo.


Evitando valores duplicados

Se a descrición dun estado debe ser única, a regra debería estar garantida tamén na base de datos.

Por exemplo:

ALTER TABLE estados
ADD CONSTRAINT uq_estados_descripcion
UNIQUE (descripcion);

Deste xeito a base de datos impediría que unha modificación deixase dous estados coa mesma descrición.

Se se viola unha restrición UNIQUE, MySQL pode devolver un erro de clave duplicada.

PDO lanzará unha PDOException, que debe tratarse sen mostrar ao usuario directamente o contido de:

$e->getMessage()

O detalle técnico debe rexistrarse:

error_log($e->getMessage());

e ao usuario mostraráselle unha mensaxe axeitada.


Transaccións

  • Se unha operación lóxica require varias operacións (alta/baixa/modificación) na base de datos e todas deben completarse ou cancelarse xuntas, debemos empregar unha transacción.
Isto será explicado posteriormente nun punto da wiki.



Modificación mediante un procedemento almacenado

Tamén podemos realizar a modificación mediante un procedemento almacenado.

Por exemplo:

DELIMITER $$

CREATE PROCEDURE ACTUALIZAR_ESTADO_PROC(
    IN p_id INT,
    IN p_descripcion VARCHAR(100)
)
BEGIN

    UPDATE estados
    SET descripcion = p_descripcion
    WHERE id = p_id;

    SELECT ROW_COUNT() AS rexistros_modificados;

END$$

DELIMITER ;

No arquivo bd.php definimos:

define(
    'PROCEDEMENTO_ACTUALIZAR_ESTADO',
    'CALL ACTUALIZAR_ESTADO_PROC(
        :id_estado,
        :descripcion
    )'
);

E desde PHP:

$stmt = $conex->prepare(
    PROCEDEMENTO_ACTUALIZAR_ESTADO
);

$stmt->execute([
    ':id_estado' => $id,
    ':descripcion' => $descripcion
]);

$fila = $stmt->fetch();

$stmt->closeCursor();

$numRexistros =
    (int) $fila['rexistros_modificados'];

Neste caso:

  • PDO non necesita multi_query().
  • Recuperamos o conxunto de resultados devolto polo procedemento.
  • Chamamos a closeCursor() para liberar posibles resultados pendentes antes de executar outra orde na mesma conexión.


mysqli como ampliación

PDO será o mecanismo principal dos apuntes, pero podemos realizar a mesma operación empregando mysqli.


Conexión con mysqli

<?php

try {

    require '/var/www/confBD/bd.php';

    mysqli_report(
        MYSQLI_REPORT_ERROR |
        MYSQLI_REPORT_STRICT
    );

    $conex = new mysqli(
        SERVIDOR,
        USUARIO,
        PASSWORD,
        BD
    );

    $conex->set_charset('utf8mb4');

    // Operacións coa base de datos...

} catch (mysqli_sql_exception $e) {

    error_log($e->getMessage());

    die('Erro relacionado coa base de datos.');

} catch (Throwable $e) {

    error_log($e->getMessage());

    die('Produciuse un erro na aplicación.');
}


Consultas preparadas con mysqli

No arquivo bd.php poderiamos definir:

define(
    'CONSULTAR_ESTADO_POR_ID_MYSQLI',
    'SELECT id, descripcion
     FROM estados
     WHERE id = ?'
);

define(
    'ACTUALIZAR_ESTADO_MYSQLI',
    'UPDATE estados
     SET descripcion = ?
     WHERE id = ?'
);

Para consultar unha fila:

$stmt = $conex->prepare(
    CONSULTAR_ESTADO_POR_ID_MYSQLI
);

$stmt->bind_param(
    'i',
    $id
);

$stmt->execute();

$resultado = $stmt->get_result();

$estado = $resultado->fetch_assoc();

Para modificar:

$stmt = $conex->prepare(
    ACTUALIZAR_ESTADO_MYSQLI
);

$stmt->bind_param(
    'si',
    $descripcion,
    $id
);

$stmt->execute();

$numRexistros = $stmt->affected_rows;

Neste caso:

  • s indica unha cadea.
  • i indica un enteiro.

Non necesitamos empregar:

$conex->real_escape_string(...)

nin construír a consulta con sprintf(), porque os valores se envían mediante unha consulta preparada.


Exercicios propostos

  • De todos os exercicios fai dúas versións, unha empregando a orde SQL UPDATE e outra chamando a un procedemento almacenado de Mysql.



Modificación de Autores

  • Ao carón de cada autor, amosa enlace para cargar o formulario de modificación de autores.
O campo id non debe aparecer visualmente no formulario.
Emprega o mesmo arquivo de campos_?????.php onde se gardan os datos no formulario.





Modificación de Libros

  • Ao carón de cada libro, amosa enlace para cargar o formulario de modificación de libros.
O campo id non debe aparecer visualmente no formulario.
Emprega o mesmo arquivo de campos_?????.php onde se gardan os datos no formulario.




Modificación de Exemplares

  • Ao carón de cada exemplar, amosa enlace para cargar o formulario de modificación de exemplares.
O campo id non debe aparecer visualmente no formulario.
Emprega o mesmo arquivo de campos_?????.php onde se gardan os datos no formulario.






Resumo

  • En PHP 8.5 empregaremos PDO como mecanismo principal para realizar modificacións.
  • As consultas gardaranse en /var/www/confBD/bd.php, fóra de /var/www/html.
  • Os valores variables dun UPDATE enviaranse mediante consultas preparadas.
  • Non construiremos a orde SQL concatenando ou interpolando datos recibidos do usuario.
  • Empregar GET para abrir o formulario de edición é correcto porque esa petición non modifica datos.
  • A modificación realizarase mediante POST.
  • Un formulario POST non substitúe a protección CSRF.
  • Os identificadores recibidos deben validarse no servidor.
  • Un campo hidden pode ser modificado polo usuario.
  • A validación dun identificador non substitúe as comprobacións de autorización.
  • Os datos recuperados da base de datos non se codifican antes de gardalos nunha variable.
  • Empregaremos htmlspecialchars() cando introduzamos os datos nun documento HTML.
  • Para texto UTF-8 empregaremos mb_strlen() e, cando sexa necesario normalizar a maiúsculas, mb_strtoupper().
  • PDOStatement::rowCount() permite coñecer cantas filas foron modificadas por un UPDATE.
  • En MySQL, rowCount() === 0 non implica necesariamente que a fila non exista: pode significar que o novo valor era igual ao xa gardado.
  • As regras de unicidade e integridade deben estar garantidas tamén pola base de datos.
  • Os detalles técnicos das excepcións deben gardarse no log e non mostrarse directamente aos usuarios.
  • mysqli explicarase como alternativa adicional, tamén empregando consultas preparadas.


-- Ángel D. Fernández González -- (2026).