Nueva pregunta

Pregunta:

Fecha: 17-04-2017 08:37:58 (En Español)

Crear columnas dinámicamente en MySQL con PHP[Resuelta]

Saludos nuevamente, el problema que tengo ahora es el siguiente, tengo este formulario:

<form action="guardarc.php" method="POST">
    <div class="form-group">
        <label for="idusuario">Ingresar cliente:</label>
        <input type="text" REQUIRED class="form-control" name="nombre" id="idusuario" placeholder="Ingresar Nombre">
    </div>
    <div class="form-group">
        <label class="col-sm-3 control-label">Ingrese el producto:</label>
        <div class="col-sm-9">
            <div class="input-group">
                <input type="text" name="field_producto[]" class="form-control"  placeholder="Ingrese el producto">
                <span class="input-group-btn" style="width:0px;"></span>
                <input type="text" name="field_cantidad[]" class="form-control" placeholder="Ingrese la cantidad">
                <div class="input-group-btn">
                    <button type="button" class="btn btn-danger">+</button>
                </div>
            </div>
        </div>
    </div>
    <button type="submit" class="btn btn-primary">Registrar</button>
    <button type="button" class="btn btn-default">Cancelar</button>
</form>

con el cual genero campos dinamicamente y los envio al servidor, logro guardarlos con el siguiente codigo:
<?php
if (!empty($_POST["field_producto"]) && is_array($_POST["field_producto"])) {
    foreach ($_POST["field_producto"] as $como) {
        $sql2 = "ALTER TABLE contactos ADD  $como varchar(30) ";
        $stmt = $con->prepare($sql2);
        $stmt->execute();
    }
}
?>

la idea es crear nuevas columnas en la tabla especificada, las primeras dos que agrego lo hace bien pero al hacer una nueva inserción borra una de las que estaba y ahí me quedé, gracias.
Etiquetas: Base de Datos - HTML - Javascript - MySQL - PHP - PHP Avanzado - PHP PDO - Pregunta Votos: 0 - Respuestas: 15 - Vistas: 28 Compartir en: Google Facebook Twitter LinkedIn Link
 

Respuestas:

  • Fecha: 18-04-2017 04:51:06 Hola HQM, no he analizado mucho tu código, pero a simple vista tu problema podría producirse por no validar la existencia de la nueva columna que quieres crear, si ingresas 2 input con el mismo contenido tu script fallará por "Error Code: 1060. Duplicate column name 'XXXXXXX'".

    Por último algunos detalles:
    * no es necesario usar prepare, ya que no utilizas ningún parámetro con PDOStatement::bindParam
    * el SQL para agregar una columna esta ok, pero mejor sería: "ALTER TABLE contactos ADD COLUMN $como VARCHAR(30);"
    * remplaza el if (!empty($_POST["field_producto"])... por if (isset($_POST["field_producto"])...

    Saludos y buen código!
      Votos: 2 - Link respuesta
     
  • Fecha: 18-04-2017 18:38:48 Fernando creo que usar la funcion empty() para validar $_POST["field_producto"] es mas efectiva pues si usamos isset() va retornar true aun el no inserte valor alguno en ese field   Votos: 1 - Link respuesta
     
  • Fecha: 19-04-2017 05:52:34 Hola Ambiorix, es valido lo que dices, pero te cuento cual fue mi razonamiento:

    Como "field_producto" es el "name" de los input dinámicos puede haber situaciones en las que el input no exista en el form y preguntar por empty terminará arrojando "Notice: Undefined index: field_producto in..."

    Habiendo dicho esto, algo más correcto (sumando tu observación) sería validar las dos situaciones, pero siempre colocando primero el isset antes que el empty, ya que si isset no devuelve true no se evalua el empty (ya que se utiliza una evaluación de cortocircuito).

    Lo dicho en código sería así:
    if (isset($_POST["field_producto"]) && !empty($_POST["field_producto"]) && is_array($_POST["field_producto"])) {
    //... código ....
    }
    


    Ahora bien, algo que no se menciono respecto a este código es la forma en que se confecciona la query, es un claro error que permitirá un ataque de inyección sql.

    $sql2 = "ALTER TABLE contactos ADD  $como varchar(30) ";


    Hacer esto sin sanitizar la variable $como es como abrir una consola de MySQL a cualquier programador (y no digo hacker porque no tiene que saber mucho como para poder explotar esta situación), por lo que por lo menos deberás hacer algo como esto:

    //aquí utilizo el if ternario de php (aclaro esto para el que no pueda estar familiarizado con el concepto)
    $productosNoSanitizados = (isset($_POST["field_producto"]) && !empty($_POST["field_producto"]) && is_array($_POST["field_producto"])) ? $_POST["field_producto"] : array();
    
    //tu función para sanitizar los valores
    function sanitizarNombreColumna($valor) {
        // aquí limpias el contenido de $valor
        //si bien las columnas de MySQL soportan espacios en sus nombres, luego sera un dolor de cabeza referenciar dichas columnas (vas a tener que estas poniendo las millas inclinadas siempre `columna con espacio`, por lo que te recomiendo remplazar los espacios por underscore _ )
        //quitas los acentos, nuflos, etc
        return $valor;
    }
    
    //aquí gracias a array_map paso cada valor del array $productosNoSanitizados por la función sanitizarNombreColumna()
    $productosSanitizados = array_map('sanitizarNombreColumna', $productosNoSanitizados);
    


    Todo siempre es mejorable, muchas gracias Ambiorix por tu aporte.

    Saludos!
      Votos: 3 - Link respuesta
     
  • Fecha: 19-04-2017 07:39:19 Muchas gracias de nuevo Fernando y Ambiorix.   Votos: 1 - Link respuesta
     
  • Fecha: 22-04-2017 07:50:03 Ahora lo que necesito es guardar el contenido del segundo campo field_cantidad como el valor de la columna recien creada tengo el siguiente código:
    if (isset($_POST["field_producto"]) && is_array($_POST["field_producto"]) && isset($_POST["field_cantidad"]) && is_array($_POST["field_cantidad"])) {
        $var1 = $_POST["field_producto"];
        $var2 = $_POST["field_cantidad"];
        $arr = array($var1 => $var2);
        foreach ($arr as $campo => $valor) {
        $sql2 = "ALTER TABLE usuarios ADD COLUMN $campo varchar(30) ";
        $sql2 = "INSERT INTO usuarios ($campo) VALUES('" . $valor . "')";
        $stmt->execute();
        }
        }
    

    sin embargo me tira illegal offset type en la linea $arr = array($var1 => $var2);
      Votos: 0 - Link respuesta
     
  • Fecha: 22-04-2017 11:18:23 Basandome en tu codigo $_POST["field_producto"] es un arreglo y $_POST["field_cantidad"] es otro arreglo entonces, se preoduce el illegal offset type porque estas intentando usar un arreglo como tu index key ,no puedes usar un arreglo como el key de otro arreglo

      Votos: 0 - Link respuesta
     
  • Fecha: 22-04-2017 13:53:28 Entiendo, ahora como hago para asignarle el valor de $_POST["field_cantidad"] a la nueva columna que creo con $_POST["field_producto"]?   Votos: 0 - Link respuesta
     
  • Fecha: 22-04-2017 14:44:23 $_POST["field_cantidad"] es un arreglo has lo siguiente print_r( $_POST["field_cantidad"]); y verifica cual es el key que contiene el valor que deseas agregar   Votos: 0 - Link respuesta
     
  • Fecha: 22-04-2017 18:47:16 Como recorrer ese arreglo si tengo como hacerlo seria con este codigo:
    if ( isset($_POST["field_cantidad"]) && is_array($_POST["field_cantidad"]) ) {
      foreach ($_POST["field_cantidad"] as $kal) {
      $sql2 = "INSERT INTO  usuarios  $kal  ";
                
                $stmt->execute();  
    	}

    pero lo que no se como entrarle es propiamente a la query que hace el insert, cómo especifico el nombre de la columna creada en el paso anterior?
      Votos: 0 - Link respuesta
     
  • Fecha: 23-04-2017 03:41:51 Hola HQM, aquí te presento un ejemplo que es la base para la solución:

    Ejemplo conceptual:
    $fieldProducto = array('chocolate', 'caramelo', 'arroz');
    $fieldCantidad = array('5', '20', '1');
    
    for ($i = 0; $i < count($fieldProducto); $i++) {
        echo "ALTER TABLE usuarios ADD COLUMN `$fieldProducto[$i]` varchar(30);";
        echo '<br />';
        echo "INSERT INTO usuarios (`$fieldProducto[$i]`) VALUES ('$fieldCantidad[$i]');";
        echo '<br /><br />';
    }
    

    Utilizo un bucle for, ya que parto de la base que ambos array tiene la misma cantidad de elementos.

    Ahora bien, la solución a tu caso debería ser la siguiente (o algo similiar):
    $productosNoSanitizados = (isset($_POST["field_producto"]) && !empty($_POST["field_producto"]) && is_array($_POST["field_producto"])) ? $_POST["field_producto"] : array();
    $cantidadesNoSantizadas = (isset($_POST["field_cantidad"]) && !empty($_POST["field_cantidad"]) && is_array($_POST["field_cantidad"])) ? $_POST["field_cantidad"] : array();
    
    function sanitizarNombreColumna($valor) {
        // ... aquí acondicionas el $valor a un formato de "columna MySQL" ...
        return $valor;
    }
    
    $productosSanitizados = array_map('sanitizarNombreColumna', $productosNoSanitizados);
    $cantidadesSantizadas = array_map('intval', $cantidadesNoSantizadas); //aquí utilizo la función nativa de php intval() para convertir todas las cantidades a integer
    
    $countProductos = count($productosSanitizados);
    $countCantidades = count($cantidadesSantizadas);
    
    if ($countProductos > 0 && $countProductos === $countCantidades) {
        for ($i = 0; $i < count($productosSanitizados); $i++) {
            $stmt->query("ALTER TABLE usuarios ADD COLUMN `$productosSanitizados[$i]` varchar(30);");
            $stmt->query("INSERT INTO usuarios (`$productosSanitizados[$i]`) VALUES ('$cantidadesSantizadas[$i]');");
        }
    }
    

    Observa que:
    * siempre sanitizo los datos, no puedes confiar en el ingreso del usuario
    * utilizo intval() para sanitizar las cantidades
    * comparo la cantidad de elementos presentes en cada array, deben estar aparejados, $countProductos === $countCantidades
    * utilizo las comillas inclinadas ` para delimitar el nombre de la columna (ojo, no son estas ', ´, ")
    * ejecuto las consultas SQL por medio de $stmt->query(...), ya que no es necesario hacer un prepare() y luego el execute()

    Saludos y buen código!
      Votos: 1 - Link respuesta
     
  • Fecha: 23-04-2017 13:52:09 Me tira dos errores: undefined variable stmt y call to a member function query() on a non object   Votos: 0 - Link respuesta
     
  • Fecha: 24-04-2017 03:42:58 Hola HQM, remplaza la variable $stmt por tu variable $con, ya que al no usar prepare() no se inicializa un PDOStatement, y además el método query() viene dado por el objeto PDO, o sea, el que deberías tener instanciado en tu varialbe $con.

    Saludos y buen código!
      Votos: 2 - Link respuesta
     
  • Fecha: 24-04-2017 17:31:33 Me agrega la nueva columna hasta ahí bien, sin embargo para lo otro tiene un comportamiento extraño, agrega un nuevo registro en lugar de agregar ese valor el registro que estoy insertando al mismo tiempo, los demás valores que ingreso son los campos que ya están definidos en la tabla, el código que uso es este:
    $sql = "INSERT INTO contactos (nombre,apellido1,apellido2,telefonoOficina,Celular,correo,proyecto,observaciones) VALUES (:nombre,:apellido1,:apellido2,:telefono1,:telefono2,:correo,:proy,:observac)";
    $stmt = $con->prepare($sql);
    $stmt->bindParam(':nombre', $nombre, PDO::PARAM_STR); 
    $stmt->bindParam(':apellido1', $apellido1, PDO::PARAM_STR);
    $stmt->bindParam(':apellido2', $apellido2, PDO::PARAM_STR); 
    $stmt->bindParam(':telefono1', $telefono1, PDO::PARAM_STR);
    $stmt->bindParam(':telefono2', $telefono2, PDO::PARAM_STR);
    $stmt->bindParam(':correo', $correo, PDO::PARAM_STR);
    $stmt->bindParam(':proy', $proy, PDO::PARAM_STR);
    $stmt->bindParam(':observac', $observac, PDO::PARAM_STR);
    $stmt->execute();
    

    supongo que lo correcto sería incluir esta linea:
    $con->query("INSERT INTO usuarios (`$productosNoSanitizados[$i]`) VALUES ('$cantidadesNoSantizadas[$i]')");
    

    dentro de $sql pero no tengo idea de como hacerlo ya que estaría fuera del ciclo.
      Votos: 0 - Link respuesta
     
  • Fecha: 26-04-2017 03:48:46 Hola HQM, observa que tienes que utilizar los array sanitizados:

    if ($countProductos > 0 && $countProductos === $countCantidades) {
        for ($i = 0; $i < count($productosSanitizados); $i++) {
            $stmt->query("ALTER TABLE usuarios ADD COLUMN `$productosSanitizados[$i]` varchar(30);");
            $stmt->query("INSERT INTO usuarios (`$productosSanitizados[$i]`) VALUES ('$cantidadesSantizadas[$i]');");
        }
    }
    

    en el mismo bucle donde creas la columna también insertas el registro.

    Saludos
      Votos: 1 - Link respuesta
     
  • Fecha: 26-04-2017 12:42:24 Excelente, muchas gracias por los aportes   Votos: 0 - Link respuesta
     
Para participar activamente de la comunidad primero debes autenticarte, ingresa al sistema.Iniciar Sesión
 
frjcbbae garagebible.com