Diferencia entre revisiones de «Usuario:ManuelRomero/PHP/BasesDatos»

De WikiEducator
Saltar a: navegación, buscar
(Realizar consultas con PDO)
Línea 1: Línea 1:
 
{{TEP}}
 
{{TEP}}
 
 
 
 
<!--__NOEDITSECTION__-->
 
<!--__NOEDITSECTION__-->
 
__NOTOC__
 
__NOTOC__
Línea 13: Línea 10:
 
[[Imagen:DWES_TituloTema3.gif|center]]
 
[[Imagen:DWES_TituloTema3.gif|center]]
 
|}
 
|}
 
+
{{#widget:Slides}}
 +
<div class="slides layout-regular template-default">
 
<div class="slide">
 
<div class="slide">
=BASES DE DATOS=
+
===BASES DE DATOS: Introducción===
===Introducción===
+
 
*Idea de independizar la base de datos y el lenguaje de programación: concepto de driver, conector y extensión (mysql, mysqli, PDO).
 
*Idea de independizar la base de datos y el lenguaje de programación: concepto de driver, conector y extensión (mysql, mysqli, PDO).
 
  http://php.net/manual/es/mysqli.overview.php
 
  http://php.net/manual/es/mysqli.overview.php
 
*Para trabajar con bases de datos debemos
 
*Para trabajar con bases de datos debemos
 
**Conectarnos (Para ello necesitamos un software específico del gestor de bases de datos con el que vayamos a trabajar).
 
**Conectarnos (Para ello necesitamos un software específico del gestor de bases de datos con el que vayamos a trabajar).
**Seleccionar (La base de datos con la que vamos a trabajr).
+
**Seleccionar (La base de datos con la que vamos a trabajar).
*Normalmente también
+
</div>
 +
 
 +
<div class="slide">
 +
;Trabajar con Bases de datos
 
**Actuar con la base de datos (Consultas, inserciones, modificaciones y/o borrados)
 
**Actuar con la base de datos (Consultas, inserciones, modificaciones y/o borrados)
 
**Procesar información
 
**Procesar información
 
**Cerrar la base de datos
 
**Cerrar la base de datos
 
*Para realizar estas acciones disponemos de diversas funciones específicas dentro de PHP,
 
*Para realizar estas acciones disponemos de diversas funciones específicas dentro de PHP,
 +
</div>
 +
 +
<div class="slide">
 +
;Bases de datos y PHP
 
*PHP tiene un API especifico para trabajar directamente con mysql '''''mysqli''''', el cual incorpora el driver y conector necesario para trabajar con ella de forma nativa
 
*PHP tiene un API especifico para trabajar directamente con mysql '''''mysqli''''', el cual incorpora el driver y conector necesario para trabajar con ella de forma nativa
 
*Que el driver sea nativo es que está implementado utlizando un  framework de extensiones dde php.
 
*Que el driver sea nativo es que está implementado utlizando un  framework de extensiones dde php.
Línea 33: Línea 37:
 
#'''''mysqli''''' usar una extensión nativa con su SGBD en concreto mysql que viene con el propio lenguaje
 
#'''''mysqli''''' usar una extensión nativa con su SGBD en concreto mysql que viene con el propio lenguaje
 
#'''''PDO''''' usar una extensión genéricoa que permite conectarse con cualquier gestor de BD, sin necesidad de cambiar nada de código.
 
#'''''PDO''''' usar una extensión genéricoa que permite conectarse con cualquier gestor de BD, sin necesidad de cambiar nada de código.
===Introduccion Usao de extensiones orientadas a objetos===
+
</div>
 +
 
 +
<div class="slide">
 +
===Introducción al Uso de extensiones orientadas a objetos===
 
*Para trabajar con las extensiones, las usaremos siempre orientadas a objetos, aunque tengan  
 
*Para trabajar con las extensiones, las usaremos siempre orientadas a objetos, aunque tengan  
 
la correspondiente funcionalidad en el lenguaje estructurado.
 
la correspondiente funcionalidad en el lenguaje estructurado.
Línea 43: Línea 50:
 
     $miObjeto = new Clase();
 
     $miObjeto = new Clase();
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 +
;Introducción
 
*Para acceder a los diferentes métodos del objeto instaciado
 
*Para acceder a los diferentes métodos del objeto instaciado
 
<source lang=php>
 
<source lang=php>
Línea 59: Línea 69:
 
*El fichero de configuración  
 
*El fichero de configuración  
 
  /etc/mysql/my.cnf
 
  /etc/mysql/my.cnf
 +
</div>
 +
<div class="slide">
 +
;Mysql
 
*En él podemos ver el puerto (3306 por defecto), el usuario y otros parámetros del servicio
 
*En él podemos ver el puerto (3306 por defecto), el usuario y otros parámetros del servicio
 
*Podemos accecder a la documentación que php da sobre mysqli (mysql improve), o sea interfaz mysql mejorada http://php.net/manual/es/class.mysqli.php
 
*Podemos accecder a la documentación que php da sobre mysqli (mysql improve), o sea interfaz mysql mejorada http://php.net/manual/es/class.mysqli.php
Línea 72: Línea 85:
 
#sal de sql <!-- quit-->
 
#sal de sql <!-- quit-->
 
}}
 
}}
 +
</div>
 +
<div class="slide">
 +
;Bases de datos
 
[[archivo:baseDatos.pdf]]
 
[[archivo:baseDatos.pdf]]
 
{{Actividad|
 
{{Actividad|
Línea 86: Línea 102:
 
*Y más información en la página oficial
 
*Y más información en la página oficial
 
  http://www.phpmyadmin.net/home_page/index.php
 
  http://www.phpmyadmin.net/home_page/index.php
 +
</div>
 +
<div class="slide">
 +
 
{{Actividad|
 
{{Actividad|
 
Probamos a conectarno por phpmyadmin a nuestro servidor local
 
Probamos a conectarno por phpmyadmin a nuestro servidor local
Línea 99: Línea 118:
 
     sudo apt-get install  mysql-workbench
 
     sudo apt-get install  mysql-workbench
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 +
 
{{Actividad|
 
{{Actividad|
 
Instalamos y probamos las diferentes opciones con esta herramienta
 
Instalamos y probamos las diferentes opciones con esta herramienta
Línea 104: Línea 126:
 
*Probamos las opciones '''''forward engineer''''' y '''''reverser engineer'''''
 
*Probamos las opciones '''''forward engineer''''' y '''''reverser engineer'''''
 
}}
 
}}
 +
</div>
 +
<div class="slide">
 +
 
===Herramientas de administración===
 
===Herramientas de administración===
 
*Con '''''phpmyadmin''''', podemos hacer casi todo de administrar y manejar las bases de datos
 
*Con '''''phpmyadmin''''', podemos hacer casi todo de administrar y manejar las bases de datos
 
 
*No obstante mysql proporciona una serie de herraminetas que perminten administrar por línea de comandos .
 
*No obstante mysql proporciona una serie de herraminetas que perminten administrar por línea de comandos .
 
*En muchas ocasiones este tipo de operaciones resultan muy interesantes.
 
*En muchas ocasiones este tipo de operaciones resultan muy interesantes.
Línea 112: Línea 136:
 
#myhsqladmin
 
#myhsqladmin
 
#mysqlshow
 
#mysqlshow
:mysql
+
</div>
 +
<div class="slide">
 +
 
 +
;mysql
 
*Esta heramienta ya la hemos comentado, permite conectarse a un servidor MySQL para ejecutar sentencias SQL
 
*Esta heramienta ya la hemos comentado, permite conectarse a un servidor MySQL para ejecutar sentencias SQL
 
*Opciomes básicas ya conocidas  
 
*Opciomes básicas ya conocidas  
Línea 120: Línea 147:
 
# help Para conocer los comandos que se pueden usar.
 
# help Para conocer los comandos que se pueden usar.
 
       http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/sql-syntax.html
 
       http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/sql-syntax.html
:mysqladmin
+
</div>
 +
 
 +
<div class="slide">
 +
;mysqladmin
 
*Es un cliente específico para la administración
 
*Es un cliente específico para la administración
 
*Entre otras acciones podemos realizar
 
*Entre otras acciones podemos realizar
Línea 130: Línea 160:
 
#ver la version del servidor  
 
#ver la version del servidor  
 
       http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/mysqladmin.html
 
       http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/mysqladmin.html
 +
</div>
 +
<div class="slide">
 
{{Actividad|
 
{{Actividad|
 
Prueba a hacer cada una de las acciones especificadas anteriormente
 
Prueba a hacer cada una de las acciones especificadas anteriormente
Línea 139: Línea 171:
 
-->
 
-->
 
}}
 
}}
:mysqlshow
+
;mysqlshow
 
  http://ftp.nchu.edu.tw/MySQL//doc/refman/5.0/es/mysqlshow.html
 
  http://ftp.nchu.edu.tw/MySQL//doc/refman/5.0/es/mysqlshow.html
 
*muestra informaicón sobre la base de datos
 
*muestra informaicón sobre la base de datos
 
  mysqlshow -u root -proot  
 
  mysqlshow -u root -proot  
 
*Nos mostraría las tablas de ese ususario
 
*Nos mostraría las tablas de ese ususario
 +
</div>
  
 +
<div class="slide">
 
===Mysql y su extensión mysqli para php===
 
===Mysql y su extensión mysqli para php===
 
: CONECTARNOS A LA BASE DE DATOS
 
: CONECTARNOS A LA BASE DE DATOS
Línea 153: Línea 187:
 
  $miConexion = new mysqli(....);
 
  $miConexion = new mysqli(....);
 
</source>
 
</source>
 
+
</div>
 +
<div class="slide">
 +
;Extensión Mysqli
 
*El constructor de la clase puede recibir hasta 5 parámetros, de los cuales 4 se suelen usar con bastante frecuencia
 
*El constructor de la clase puede recibir hasta 5 parámetros, de los cuales 4 se suelen usar con bastante frecuencia
 
#$host nombre o ip del equipo (null o localhost, identificaría el equipo actual).i
 
#$host nombre o ip del equipo (null o localhost, identificaría el equipo actual).i
Línea 161: Línea 197:
 
#'''''$puerto'''''
 
#'''''$puerto'''''
 
#'''''$shocket'''''  
 
#'''''$shocket'''''  
 +
</div>
 +
<div class="slide">
 
  <source lang=php>
 
  <source lang=php>
 
  $host="localhost"
 
  $host="localhost"
Línea 173: Línea 211:
 
     echo "Conectado a la base de datos";
 
     echo "Conectado a la base de datos";
 
</source>
 
</source>
 
+
</div>
 +
<div class="slide">
 +
;mysqli(...)
 
*Esta función retorna el recurso de la conexión
 
*Esta función retorna el recurso de la conexión
 
*Para gestionar los errores deberíamos usar el atributo '''''connect_error''''' de la clase '''''mysqli'''''
 
*Para gestionar los errores deberíamos usar el atributo '''''connect_error''''' de la clase '''''mysqli'''''
Línea 182: Línea 222:
 
   echo "Error conectando con la base de datos: ".$miConexion->connect_error;
 
   echo "Error conectando con la base de datos: ".$miConexion->connect_error;
 
</source>
 
</source>
 +
</div>
 +
 +
<div class="slide">
 
*Para ver información sobre la conexión se puede usar los atributos *server_info o host_info
 
*Para ver información sobre la conexión se puede usar los atributos *server_info o host_info
 
{{Resumen|
 
{{Resumen|
Línea 192: Línea 235:
 
</source>
 
</source>
 
}}
 
}}
 +
</div>
 +
<div class="slide">
 
*Hay muchas opciones de mysqli que se pueden configurar en el fichero php.ini
 
*Hay muchas opciones de mysqli que se pueden configurar en el fichero php.ini
 
*Aquí tenemos alguna de ellas
 
*Aquí tenemos alguna de ellas
Línea 206: Línea 251:
 
:mysqli.default_pw
 
:mysqli.default_pw
 
;Contraseña predeterminada a usar cuando se conecta al servidor de base de datos.
 
;Contraseña predeterminada a usar cuando se conecta al servidor de base de datos.
 +
</div>
 +
<div class="slide">
  
 
*La lista completa la podemos ver en el siguiente link
 
*La lista completa la podemos ver en el siguiente link
Línea 212: Línea 259:
 
*configura dicho fichero, para poder conectar a la base de datos sin aportar parámetros al constructor
 
*configura dicho fichero, para poder conectar a la base de datos sin aportar parámetros al constructor
 
*Luego déjalo como estaba :)}}
 
*Luego déjalo como estaba :)}}
 
 
 
</div>
 
</div>
 
<div class="slide">
 
<div class="slide">
Línea 226: Línea 271:
 
$miConexion->close();
 
$miConexion->close();
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 
===DML===
 
===DML===
 
*En SQL sabemos que tenmos tres tipos de lenguajes DDL, DML, DCL
 
*En SQL sabemos que tenmos tres tipos de lenguajes DDL, DML, DCL
Línea 234: Línea 281:
 
#Las que pueden retornan  una colección de filas (SELECT), generalmente conocidas como cursor.
 
#Las que pueden retornan  una colección de filas (SELECT), generalmente conocidas como cursor.
 
*En mysqli podemos enviar cualquiera de estas claúsulas con el método '''''query'''''
 
*En mysqli podemos enviar cualquiera de estas claúsulas con el método '''''query'''''
 +
</div>
 +
<div class="slide">
 +
 
===INSERT, UPDATE Y DELETE===
 
===INSERT, UPDATE Y DELETE===
 
*Este tipo de sentencias retornan un booleano que indica si se ha realizado o no la acción
 
*Este tipo de sentencias retornan un booleano que indica si se ha realizado o no la acción
Línea 245: Línea 295:
 
</source>
 
</source>
 
}}
 
}}
 +
</div>
 +
<div class="slide">
 +
 
<source lang=php>
 
<source lang=php>
 
$sentenciaDelete="DELETE FROM tabla WHERE condicion";
 
$sentenciaDelete="DELETE FROM tabla WHERE condicion";
Línea 270: Línea 323:
  
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
  
 
*Observemos su uso en el ejemplo
 
*Observemos su uso en el ejemplo
Línea 292: Línea 347:
 
}
 
}
 
</source>
 
</source>
 +
 
</div>
 
</div>
  
Línea 302: Línea 358:
 
*En el primero caso el método nos retorna un cursor que será de la calse mysqli_result
 
*En el primero caso el método nos retorna un cursor que será de la calse mysqli_result
 
*En el segundo caso nos retornará un booleano y para leer los datos deberemos usar o '''''store_result''''' o '''''use_result''''' según veamos a continuación.
 
*En el segundo caso nos retornará un booleano y para leer los datos deberemos usar o '''''store_result''''' o '''''use_result''''' según veamos a continuación.
 +
</div>
 +
<div class="slide">
 
===Método query===
 
===Método query===
 
*Una vez que tenemos los datos almacenados debemos saber acceder.
 
*Una vez que tenemos los datos almacenados debemos saber acceder.
Línea 308: Línea 366:
 
:Va obteniendo cada registro como un array
 
:Va obteniendo cada registro como un array
 
:Este array podemos usar tanto de forma indexada, como asociativa (con el nombre del campo)
 
:Este array podemos usar tanto de forma indexada, como asociativa (con el nombre del campo)
 +
</div>
 +
<div class="slide">
 +
 
;fetch_assoc()
 
;fetch_assoc()
 
:En este caso el array que retorna es asociativo
 
:En este caso el array que retorna es asociativo
Línea 315: Línea 376:
 
:En este caso en lugar de retornar un array, retorna un objeto, donde cada campo son los diferentes atributos de ese objeto
 
:En este caso en lugar de retornar un array, retorna un objeto, donde cada campo son los diferentes atributos de ese objeto
 
*En todos los casos cada vez que leemos un elemento de mysqli_result, lo que por comparativa sería un cursor, vamos avanzando al siguiente. Cuando hayamos leído todos retornaría null
 
*En todos los casos cada vez que leemos un elemento de mysqli_result, lo que por comparativa sería un cursor, vamos avanzando al siguiente. Cuando hayamos leído todos retornaría null
 +
</div>
 +
<div class="slide">
 +
 
{{Actividad|
 
{{Actividad|
 
Obtén todos los registros de la tabla familia  y visualizalos en una tabla usando los tres modos de lectura de datos vistos anteriormente}}
 
Obtén todos los registros de la tabla familia  y visualizalos en una tabla usando los tres modos de lectura de datos vistos anteriormente}}
Línea 325: Línea 389:
 
*Tenemos una lista completa
 
*Tenemos una lista completa
 
  http://es.php.net/manual/es/class.mysqli-result.php
 
  http://es.php.net/manual/es/class.mysqli-result.php
 +
</div>
 +
<div class="slide">
 +
 
{{Resumen|
 
{{Resumen|
 
<source lang=php>
 
<source lang=php>
Línea 343: Línea 410:
 
</source>
 
</source>
 
}}
 
}}
 
+
</div>
 
+
<div class="slide">
 
{{Actividad|
 
{{Actividad|
 
Haciendo una consulta del tipo select * from producto whre pvp < 200, realiza un código que visualizce en una tabla los resultados }}
 
Haciendo una consulta del tipo select * from producto whre pvp < 200, realiza un código que visualizce en una tabla los resultados }}
 +
</div>
 +
<div class="slide">
 +
 
===Transacciones===
 
===Transacciones===
 
<br/>
 
<br/>
Línea 352: Línea 422:
 
Una transacción es un conjunto de acciones u operaciones que se realizan contra una base de datos de modo que o bien se realizan correctamente todas o no se realiza ninguna}}
 
Una transacción es un conjunto de acciones u operaciones que se realizan contra una base de datos de modo que o bien se realizan correctamente todas o no se realiza ninguna}}
 
*Supongamos que hacemos una transferencia bancaria; cuando menos implica descontar dinera de una cuenta e ingresaro en otra
 
*Supongamos que hacemos una transferencia bancaria; cuando menos implica descontar dinera de una cuenta e ingresaro en otra
 +
</div>
 +
<div class="slide">
 +
;Transacciones
 
*Ahora supongamos que nada mas descontar el dinero de una cuenta se cae la luz y se apaga el servidor
 
*Ahora supongamos que nada mas descontar el dinero de una cuenta se cae la luz y se apaga el servidor
 
*Esto creará una inconsistencia en la base de datos
 
*Esto creará una inconsistencia en la base de datos
Línea 360: Línea 433:
 
  .....
 
  .....
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 +
 
*Si se ha desactivado el autocomit, para terminar una transacción debemos usar los métodos '''''commit''''' o '''''rollback'''''
 
*Si se ha desactivado el autocomit, para terminar una transacción debemos usar los métodos '''''commit''''' o '''''rollback'''''
 
<source lang=php>
 
<source lang=php>
Línea 376: Línea 452:
 
   }
 
   }
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 +
 
{{Actividad|
 
{{Actividad|
 
Ejercicio de transacciones
 
Ejercicio de transacciones
 
}}
 
}}
 +
</div>
 +
<div class="slide">
  
 
===Injecciones SQL===
 
===Injecciones SQL===
Línea 384: Línea 465:
 
*Existe mucha documentación al respecto, en general podemos afirmar que un buen conocimiento de SQL proporcina herramientas tanto para poder establecer este tipo de ataques, como para podernos prevenir de ellos.
 
*Existe mucha documentación al respecto, en general podemos afirmar que un buen conocimiento de SQL proporcina herramientas tanto para poder establecer este tipo de ataques, como para podernos prevenir de ellos.
 
*Aportamos referenicas de web que nos pueden interesar consultar
 
*Aportamos referenicas de web que nos pueden interesar consultar
 +
</div>
 +
<div class="slide">
 +
 
{{Recursos de la Web|
 
{{Recursos de la Web|
 
#http://php.net/manual/es/security.database.sql-injection.php
 
#http://php.net/manual/es/security.database.sql-injection.php
Línea 393: Línea 477:
 
#http://blog.netrunners.es/tabla-de-trucos-para-inyeccion-de-sql/
 
#http://blog.netrunners.es/tabla-de-trucos-para-inyeccion-de-sql/
 
}}
 
}}
 +
</div>
 +
<div class="slide">
 +
 
*A continuación y usando el ejemplo anterior de '''''acceso''''' vamos a probar a realizar un sencillo  ataques sql.
 
*A continuación y usando el ejemplo anterior de '''''acceso''''' vamos a probar a realizar un sencillo  ataques sql.
 
#Entrar en la plataforma sin tener acceso
 
#Entrar en la plataforma sin tener acceso
Línea 401: Línea 488:
 
*Como no sabemos el usuario ni contraseña probamos a ver si se puede hacer una inserción no controlada
 
*Como no sabemos el usuario ni contraseña probamos a ver si se puede hacer una inserción no controlada
 
*Como programadores esperamos que en el código haya algo del estilo, como es nuestro caso
 
*Como programadores esperamos que en el código haya algo del estilo, como es nuestro caso
 +
</div>
 +
<div class="slide">
 +
 
<source lang=php>
 
<source lang=php>
 
  $nombre=$_POST['usuario'];
 
  $nombre=$_POST['usuario'];
Línea 407: Línea 497:
 
  $consulta="select * from usuarios where nombre = \"$nombre\" and pass = \"$pass\" ";
 
  $consulta="select * from usuarios where nombre = \"$nombre\" and pass = \"$pass\" ";
 
  $resultado = $conexion->query($consulta);
 
  $resultado = $conexion->query($consulta);
</php>
+
</source>
 
*Si todo fuera normal y nombre fuera por ejemplo "maría" la consulta que se envía al servidor sería  
 
*Si todo fuera normal y nombre fuera por ejemplo "maría" la consulta que se envía al servidor sería  
 
<source lang=php>
 
<source lang=php>
Línea 415: Línea 505:
 
*Pero si añadimos más cosas obtendremos segura una respuesta, por ejemplo si en el codigo $nombre="maria or \"1\"= \"1\" "
 
*Pero si añadimos más cosas obtendremos segura una respuesta, por ejemplo si en el codigo $nombre="maria or \"1\"= \"1\" "
 
*Entonces la consulta quedaría  
 
*Entonces la consulta quedaría  
 +
</div>
 +
<div class="slide">
 +
 
<source lang=php>
 
<source lang=php>
 
       select * from usuarios where nombre = "maria" or "1"="1"
 
       select * from usuarios where nombre = "maria" or "1"="1"
Línea 424: Línea 517:
 
[[Archivo:accesoInjectado.png]]
 
[[Archivo:accesoInjectado.png]]
  
 +
</div>
 +
<div class="slide">
 
===Consultas preparadas===
 
===Consultas preparadas===
 
*Una consulta preparada consiste en establecer una consulta como si fuera una variable y ejecutarla posteriormente tantas veces como sea necesario.
 
*Una consulta preparada consiste en establecer una consulta como si fuera una variable y ejecutarla posteriormente tantas veces como sea necesario.
 
*Estas consultas se almacenan en el servidor  y están listas para ser ejecutadas cuando sea necesario. El servidor solo tiene que analizarlas una vez
 
*Estas consultas se almacenan en el servidor  y están listas para ser ejecutadas cuando sea necesario. El servidor solo tiene que analizarlas una vez
 
*Para trabajar con consultas preparadas, debemos usar  la clase '''''mysqli_stmt''''', e inicializarla con  el método '''''stmt_init '''''
 
*Para trabajar con consultas preparadas, debemos usar  la clase '''''mysqli_stmt''''', e inicializarla con  el método '''''stmt_init '''''
 +
</div>
 +
<div class="slide">
 +
 
<source lang=php>
 
<source lang=php>
 
   $conexion = new mysqli('localhost', 'dwes', 'abc123.', 'dwes');
 
   $conexion = new mysqli('localhost', 'dwes', 'abc123.', 'dwes');
Línea 433: Línea 531:
 
   $consulta = $conexion->stmt_init();
 
   $consulta = $conexion->stmt_init();
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 +
 
Los pasos para trabajar con consultas preparadas son:
 
Los pasos para trabajar con consultas preparadas son:
 
#Preparar la consulta en el servidor MySQL utilizando el método '''''prepare'''''.
 
#Preparar la consulta en el servidor MySQL utilizando el método '''''prepare'''''.
Línea 444: Línea 545:
 
$conexion->close();
 
$conexion->close();
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
  
 
===Parametrizar las consultas preparadas===
 
===Parametrizar las consultas preparadas===
Línea 453: Línea 556:
 
*Consiste en indicar en la consulta preparada en lugar de los valores, signos de interrogación '''''?'''''
 
*Consiste en indicar en la consulta preparada en lugar de los valores, signos de interrogación '''''?'''''
 
*En el caso anterior
 
*En el caso anterior
 +
</div>
 +
<div class="slide">
 +
 
<source lang=php>
 
<source lang=php>
 
$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES (?,?);
 
$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES (?,?);
</php>
+
</source>
 
*Ahora habría que asigar los valores. Para ello usamos el método '''''bind_param''''
 
*Ahora habría que asigar los valores. Para ello usamos el método '''''bind_param''''
 
<bind_param(tipoDatos, >
 
<bind_param(tipoDatos, >
 +
</div>
 +
<div class="slide">
 +
 
*Este método recibe dos tipos de parámetros
 
*Este método recibe dos tipos de parámetros
 +
 
#El primero es una cadena de caracteres, donde cada carácter especifica el tipo de valor que va a recibir cada uno de los valores esperados en la consulta.
 
#El primero es una cadena de caracteres, donde cada carácter especifica el tipo de valor que va a recibir cada uno de los valores esperados en la consulta.
 
**La codificación sería :
 
**La codificación sería :
Línea 468: Línea 578:
 
#El segundo grupo sería cada uno de los valores.SIEMPRE hay que especificar variables
 
#El segundo grupo sería cada uno de los valores.SIEMPRE hay que especificar variables
 
*En el ejemplo que estamos siguiendo
 
*En el ejemplo que estamos siguiendo
 +
</div>
 +
<div class="slide">
 +
 
<source lang=php>
 
<source lang=php>
 
$consulta = $conexion->stmt_init();
 
$consulta = $conexion->stmt_init();
Línea 475: Línea 588:
 
$consulta->bind_param('ss', $cod_producto, $nombre_producto);
 
$consulta->bind_param('ss', $cod_producto, $nombre_producto);
 
</source>
 
</source>
 +
</div>
 +
<div class="slide">
 +
 
*Insistimos en que siempre hay que especificar variables, de modo que  
 
*Insistimos en que siempre hay que especificar variables, de modo que  
 
<source lang=php>
 
<source lang=php>
Línea 490: Línea 606:
 
</source>
 
</source>
 
}}
 
}}
 +
</div>
 +
<div class="slide">
  
 
===Consultas preparadas que retornan valores===
 
===Consultas preparadas que retornan valores===
Línea 511: Línea 629:
 
*Modifica el ejercicio anterior usando consultas parametrizadas
 
*Modifica el ejercicio anterior usando consultas parametrizadas
 
}}
 
}}
 +
</div>
 +
<div class="slide">
  
 
{{Actividad|
 
{{Actividad|
Línea 523: Línea 643:
 
*No es necesario tener en cuenta las tareas de inserción (no existían unidades anteriormente) y borrado (si el número final de unidades es cero).
 
*No es necesario tener en cuenta las tareas de inserción (no existían unidades anteriormente) y borrado (si el número final de unidades es cero).
 
}}
 
}}
 +
</div>
 +
<div class="slide">
 
<!--
 
<!--
 +
 
===Definición de funiones=
 
===Definición de funiones=
 
  function  ‘’’nombre_de_funcion’’’ (’’’’lista_de_parametros’’’’){
 
  function  ‘’’nombre_de_funcion’’’ (’’’’lista_de_parametros’’’’){
Línea 543: Línea 666:
 
</div>
 
</div>
 
-->
 
-->
 
+
<!--
 
==Bases de datos con PDO==
 
==Bases de datos con PDO==
 
===Qué es PDO===
 
===Qué es PDO===
Línea 858: Línea 981:
  
  
 
+
-->
 
|}
 
|}

Revisión de 11:27 3 nov 2015

Road Works.svg Trabajo en proceso, espera cambios frecuentes. Tu ayuda y retroalimentación son bienvenidos.
Ver página de charlas.
Road Works.svg


DWES TituloTema3.gif

BASES DE DATOS: Introducción

  • Idea de independizar la base de datos y el lenguaje de programación: concepto de driver, conector y extensión (mysql, mysqli, PDO).
http://php.net/manual/es/mysqli.overview.php
  • Para trabajar con bases de datos debemos
    • Conectarnos (Para ello necesitamos un software específico del gestor de bases de datos con el que vayamos a trabajar).
    • Seleccionar (La base de datos con la que vamos a trabajar).
Trabajar con Bases de datos
    • Actuar con la base de datos (Consultas, inserciones, modificaciones y/o borrados)
    • Procesar información
    • Cerrar la base de datos
  • Para realizar estas acciones disponemos de diversas funciones específicas dentro de PHP,
Bases de datos y PHP
  • PHP tiene un API especifico para trabajar directamente con mysql mysqli, el cual incorpora el driver y conector necesario para trabajar con ella de forma nativa
  • Que el driver sea nativo es que está implementado utlizando un framework de extensiones dde php.
  • También vamos a disponer de la extensión PDO, la cual se independiza del gestor concreto de bases datos que vayamos a utilizar.
  • Por lo tanto en este tema vamos a ver dos extensiones:
  1. mysqli usar una extensión nativa con su SGBD en concreto mysql que viene con el propio lenguaje
  2. PDO usar una extensión genéricoa que permite conectarse con cualquier gestor de BD, sin necesidad de cambiar nada de código.

Introducción al Uso de extensiones orientadas a objetos

  • Para trabajar con las extensiones, las usaremos siempre orientadas a objetos, aunque tengan

la correspondiente funcionalidad en el lenguaje estructurado.

  • Para recordar muy brevemene posemos usar el siguiente enlace
http://www.desarrolloweb.com/articulos/1540.php
  • Posteriormente veremos un tema de php orientado a objetos
  • Recordamos que para crear una nueva instancia de una clase usamos el operador new
    $miObjeto = new Clase();
Introducción
  • Para acceder a los diferentes métodos del objeto instaciado
 $miObjeto->metodo($parametros);
  • Con estos detalles nos sobra de momento para este tema.


Mysql y php
  • Instalar mysql lo hicimos en el primer tema
apt-get install mysql-server mysql-client
  • Para parar/arrancar el servicio
service mysql start/stop/restart/status
  • El fichero de configuración
/etc/mysql/my.cnf
Mysql
  • En él podemos ver el puerto (3306 por defecto), el usuario y otros parámetros del servicio
  • Podemos accecder a la documentación que php da sobre mysqli (mysql improve), o sea interfaz mysql mejorada http://php.net/manual/es/class.mysqli.php
  • Debmos saber conectarnos por línea de comandos


Icon activity.jpg

Actividad

  1. Conectar a mysql
  2. Mira Las bases de datos que tienes
  3. usa una base de datos concreta
  4. mira las tablas que tiene esa bases de datos
  5. mira la estructura de la tabla
  6. Haz una consulta de los valores que tiene su primera columna
  7. ejecuta el comando help
  8. sal de sql




Bases de datos

Archivo:BaseDatos.pdf


Icon activity.jpg

Actividad

Usa mysql para cargar por líneas de comandos el siguiente fichero arriba especificado




  • En la siguiente página puedes hacer un repaso de mysql
http://dev.mysql.com/doc/refman/5.7/en/index.html
  • Un manual en castellano
http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/
  • Debemos conocer también la herramienta phpmyadmin
sudo apt-get install phpmyadmin
  • Aquí tienes una pequeña guía es de uso sencillo e intuitivo
http://www.ite.educacion.es/formacion/materiales/85/cd/linux/m5/instalacin_y_configuracin_de_phpmyadmin.html
  • Y más información en la página oficial
http://www.phpmyadmin.net/home_page/index.php



Icon activity.jpg

Actividad

Probamos a conectarno por phpmyadmin a nuestro servidor local




Archivo:Datos.sql


Icon activity.jpg

Actividad

Usando phpmyadmin, carga los datos para poblar la base de datos



  • Otra herramienta importante que permite realizar diseños es workbrench
http://dev.mysql.com/doc/workbench/en/index.html
    sudo add-apt-repository ppa:olivier-berten/misc 
    sudo apt-get update 
    sudo apt-get install  mysql-workbench



Icon activity.jpg

Actividad

Instalamos y probamos las diferentes opciones con esta herramienta Probamos a hacer un diseño en modelo realcional y a partir del modelo creamos las tablas.

  • Probamos las opciones forward engineer y reverser engineer




Herramientas de administración

  • Con phpmyadmin, podemos hacer casi todo de administrar y manejar las bases de datos
  • No obstante mysql proporciona una serie de herraminetas que perminten administrar por línea de comandos .
  • En muchas ocasiones este tipo de operaciones resultan muy interesantes.
  1. mysql
  2. myhsqladmin
  3. mysqlshow
mysql
  • Esta heramienta ya la hemos comentado, permite conectarse a un servidor MySQL para ejecutar sentencias SQL
  • Opciomes básicas ya conocidas
  1. connect -h,-u, -p
  2. use database
  3. exit o quit
  4. help Para conocer los comandos que se pueden usar.
     http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/sql-syntax.html
mysqladmin
  • Es un cliente específico para la administración
  • Entre otras acciones podemos realizar
  1. crear y eliminar bases de datos.
  2. mostrar la configuración y el estado del servidor.
  3. cambiar contraseñas.
  4. detener un servidor.
  5. ver la version del servidor
     http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/es/mysqladmin.html


Icon activity.jpg

Actividad

Prueba a hacer cada una de las acciones especificadas anteriormente




mysqlshow
http://ftp.nchu.edu.tw/MySQL//doc/refman/5.0/es/mysqlshow.html
  • muestra informaicón sobre la base de datos
mysqlshow -u root -proot 
  • Nos mostraría las tablas de ese ususario

Mysql y su extensión mysqli para php

CONECTARNOS A LA BASE DE DATOS
  • A continuación iremos viendo como implementar las acciones básicas en el lenguaje
Conectarse
  • Para conectarse a una base de datos , creamos una instacia de la clase mysqli de la forma
 $miConexion = new mysqli(....);
Extensión Mysqli
  • El constructor de la clase puede recibir hasta 5 parámetros, de los cuales 4 se suelen usar con bastante frecuencia
  1. $host nombre o ip del equipo (null o localhost, identificaría el equipo actual).i
  2. $usario es el usuario de la base de datos
  3. $pass
  4. $nombreBD
  5. $puerto
  6. $shocket
 $host="localhost"
 $usuario="manolo";
 $pass="romero";
 $nombreBD="alumnos";
 
 $miConexion = new mysqli ($host,$usuario,$pass,$nombreBD);
 if ($miConexion==null)
     echo"Error conectando a la base de datos";
 else
     echo "Conectado a la base de datos";
mysqli(...)
  • Esta función retorna el recurso de la conexión
  • Para gestionar los errores deberíamos usar el atributo connect_error de la clase mysqli
  • Este atributo aporta información sobre el error o contiene null si no se ha producido ninguno.
  • En el código anterior
 if ($miConexion->connect_error)
   echo "Error conectando con la base de datos: ".$miConexion->connect_error;
  • Para ver información sobre la conexión se puede usar los atributos *server_info o host_info


Icon summary.gif

Resumen

$conesion= new mysqli($host,$user,$pass,$bd);
mysqli->conect_error
mysqli->conect_errno
mysqli->server_info
mysqli->host_info


  • Hay muchas opciones de mysqli que se pueden configurar en el fichero php.ini
  • Aquí tenemos alguna de ellas
mysqli.allow_persistent
Permite crear conexiones persistentes.
mysqli.default_port
Número de puerto TCP predeterminado a utilizar cuando se conecta al Servidor de base de datos.
mysqli.reconnect
Indica si se debe volver a conectar automáticamente en caso de que se pierda la conexión.
mysqli.default_host.
Host predeterminado a usar cuando se conecta al servidor de base de datos.
mysqli.default_user.
Nombre de usuario predeterminado a usar cuando se conecta al servidor de base de datos.
mysqli.default_pw
Contraseña predeterminada a usar cuando se conecta al servidor de base de datos.
  • La lista completa la podemos ver en el siguiente link
http://php.net/manual/es/mysqli.configuration.php


Icon activity.jpg

Actividad

  • configura dicho fichero, para poder conectar a la base de datos sin aportar parámetros al constructor
  • Luego déjalo como estaba :)



Cambiar la base de datos

  • Si hemos seleccionado una base de datos, o no hemos seleccionado ninguna y queremos cambiar a otra
$miConexion->select_db("nombre_base_datos");
  • Cuando ya no vamos a usar un recurso, conviente y repito CONVIENE, liberarlo.
$miConexion->close();

DML

  • En SQL sabemos que tenmos tres tipos de lenguajes DDL, DML, DCL
  • Ahora toca DML, Leguane de maninputación de datos
  • Podemos clasificar en dos tipos de clúsulas:
  1. las que no devuelven registros de datos (INSERT, DELETE, UPDATE)
    1. Generalmente retornan un entero que es el número de filas aceptadas o un booleano que indica si se realizó no la operación
  2. Las que pueden retornan una colección de filas (SELECT), generalmente conocidas como cursor.
  • En mysqli podemos enviar cualquiera de estas claúsulas con el método query

INSERT, UPDATE Y DELETE

  • Este tipo de sentencias retornan un booleano que indica si se ha realizado o no la acción
  • El número de filas afectadas lo podemos ver en el atributo affected_rows


Icon summary.gif

Resumen

$reusultado=miConexion->query($consulta)
if($resultado){
   echo"Se han afectado $miConexion->affected_rows filas en esta acción");
}


$sentenciaDelete="DELETE FROM tabla WHERE condicion";
$sentenciaInsert="INSERT INTO tabla (opcional lista de campos) VALUES (valores en posiciones respectivas a los campos);
$sentenciaUpdate="UPDATE tabla WHERE condicion";
 
 
$miConexion->query($sentenciasDelete);
$resultado =$miConexion->query($sentenciaDELETE);
if ($resultado){
    echo "Se han borrado $miConexion->affected_rows filas ";
}
 
$miConexion->query($sentenciasUpdate);
$resultado = $miConexion->query($sentenciaUPDATE);
if ($resultado){
    echo "Se han actualizado $miConexion->affected_rows filas ";
}
 
$miConexion->query($sentenciasInsert);
$resultado = $miConexion->query($sentenciaINSERT);
if ($resultado){
    echo "Se han insertado $miConexion->affected_rows filas ";
}
  • Observemos su uso en el ejemplo
 
  //Establecemos la conexión
  $miConexion = new mysqli('localhost', 'manolo', '12345.', 'baseDatosPrueba');
 
  //Capturamos un posible error
  $error = $miConexion->connect_errno;
 
  //En caso de error informamos de ello
  if ($error == null) {
    $resultado = $miConexion->query('DELETE FROM stock WHERE unidades=0');
 
    if ($resultado) {
       print "<p>Se han borrado $miConexion->affected_rows registros.</p>";
    }
  }
  $miConexion->close();
}

Clausula SELECT con query

  • Tenemos dos maneras de realizar consultas con mysqli
  1. query
  2. real_query
  • En el primero caso el método nos retorna un cursor que será de la calse mysqli_result
  • En el segundo caso nos retornará un booleano y para leer los datos deberemos usar o store_result o use_result según veamos a continuación.

Método query

  • Una vez que tenemos los datos almacenados debemos saber acceder.
  • Tenemos 4 formas de poder acceder a los datos según usemos un método u otro
fetch_array()
Va obteniendo cada registro como un array
Este array podemos usar tanto de forma indexada, como asociativa (con el nombre del campo)
fetch_assoc()
En este caso el array que retorna es asociativo
fetch_row()
En este caso el array que retorna es indexado
fetch_object()
En este caso en lugar de retornar un array, retorna un objeto, donde cada campo son los diferentes atributos de ese objeto
  • En todos los casos cada vez que leemos un elemento de mysqli_result, lo que por comparativa sería un cursor, vamos avanzando al siguiente. Cuando hayamos leído todos retornaría null



Icon activity.jpg

Actividad

Obtén todos los registros de la tabla familia y visualizalos en una tabla usando los tres modos de lectura de datos vistos anteriormente



  • Para liberar un recurso del tipo mysqli_result, usamos el método free();
  • La clase mysqli_result, además de los métodos vistos tiene un par de atributos interesantes
int $field_count;
Nos dice cuantas columnos tiene el query actual
int $num_rows;
Nos dice cuantas filas hemos obtenido con la consulta
  • Tenemos una lista completa
http://es.php.net/manual/es/class.mysqli-result.php



Icon summary.gif

Resumen

$conexion= new mysqli($host,$user,$pass,$bd);
if ($conexion->connect_errno==null){
   $resultado = $conexion->query($consulta);
   $numFilas = $resultado->num_rows;
   $numCampos = $resultado->fields_count;
   echo "La consulta ha retornado $numFilas filas con  $numCampos columnas";
   $fila = $resultado->fetch_array();
   while ( $fila){
          echo"El valor del primer campo es $fila[0]";
          $fila = $resultado->fetch_array();
   }
   $resultado->free();
   $conexion->close();
}



Icon activity.jpg

Actividad

Haciendo una consulta del tipo select * from producto whre pvp < 200, realiza un código que visualizce en una tabla los resultados



Transacciones



Icon define.gif

Definición

Una transacción es un conjunto de acciones u operaciones que se realizan contra una base de datos de modo que o bien se realizan correctamente todas o no se realiza ninguna


  • Supongamos que hacemos una transferencia bancaria; cuando menos implica descontar dinera de una cuenta e ingresaro en otra
Transacciones
  • Ahora supongamos que nada mas descontar el dinero de una cuenta se cae la luz y se apaga el servidor
  • Esto creará una inconsistencia en la base de datos
  • Por defecto en mysqli cada acción con la base de datos es una transacción en sí misma, pero esto se puede modificar
 $conexion = new mysqli(..);
 $conexion->autocomit(false);
 .....
  • Si se ha desactivado el autocomit, para terminar una transacción debemos usar los métodos commit o rollback
 $conexion = new mysqli(..);
 $conexion->autocomit(false);
 .....
 
 if (CondicionOK){
    //Terminamos la transacción confirmando todas las acciones sobre la base de datos desde que se inició la     
       transaccion
    $conexion->commit();
 }else{
    //Terminamos la transacción desaciendo  todas las acciones sobre la base de datos desde que se inició la     
       transaccion, y dejando la base de datos igual que estaba al principio
  $conexion->rollback();
  }



Icon activity.jpg

Actividad

Ejercicio de transacciones




Injecciones SQL

  • Es un problema de seguridad importante, que hay que conocer y evitar
  • Existe mucha documentación al respecto, en general podemos afirmar que un buen conocimiento de SQL proporcina herramientas tanto para poder establecer este tipo de ataques, como para podernos prevenir de ellos.
  • Aportamos referenicas de web que nos pueden interesar consultar
  • A continuación y usando el ejemplo anterior de acceso vamos a probar a realizar un sencillo ataques sql.
  1. Entrar en la plataforma sin tener acceso

Entrar en la plataforma sin tener acceso

  • Entramos en una página y vemos el siguiente acceso

MiAcceso.png

  • Como no sabemos el usuario ni contraseña probamos a ver si se puede hacer una inserción no controlada
  • Como programadores esperamos que en el código haya algo del estilo, como es nuestro caso
 $nombre=$_POST['usuario'];
 $pass=$_POST['pass'];
 
 $consulta="select * from usuarios where nombre = \"$nombre\" and pass = \"$pass\" ";
 $resultado = $conexion->query($consulta);
  • Si todo fuera normal y nombre fuera por ejemplo "maría" la consulta que se envía al servidor sería
      select * from usuarios where nombre = "maria"
  • Esta consulta si existe el usuario maría nos retornará una tupla, si no no devolverá ninguna.
  • Pero si añadimos más cosas obtendremos segura una respuesta, por ejemplo si en el codigo $nombre="maria or \"1\"= \"1\" "
  • Entonces la consulta quedaría
      select * from usuarios where nombre = "maria" or "1"="1"
  • Que nos devolverá todas las filas
  • Así que si introducimos estos datos

MiAccesoInjeccion.png

  • Entramos al sistema sin conocer usuario y contraseña

AccesoInjectado.png

Consultas preparadas

  • Una consulta preparada consiste en establecer una consulta como si fuera una variable y ejecutarla posteriormente tantas veces como sea necesario.
  • Estas consultas se almacenan en el servidor y están listas para ser ejecutadas cuando sea necesario. El servidor solo tiene que analizarlas una vez
  • Para trabajar con consultas preparadas, debemos usar la clase mysqli_stmt, e inicializarla con el método stmt_init
   $conexion = new mysqli('localhost', 'dwes', 'abc123.', 'dwes');
   //Preparo el objeto $consulta para crear consultas preparadas en él
   $consulta = $conexion->stmt_init();

Los pasos para trabajar con consultas preparadas son:

  1. Preparar la consulta en el servidor MySQL utilizando el método prepare.
  2. Ejecutar la consulta, tantas veces como sea necesario, con el método execute.
  3. Una vez que ya no se necesita más, se debe ejecutar el método close.
$consulta = $conexion->stmt_init();
$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES ("TABLET", "Tablet PC")');
$consulta->execute();
$consulta->close();
$conexion->close();

Parametrizar las consultas preparadas

  • El uso real de las consultas preparadas es que los valores que pasas se asignen antes de ejectuar la consulta.
  • La idea es preapara la consulta sin indicar los valores.
  • Asignar los valores y ejectuar la consulta cuantas veces sea necesario.
  • Veamos el proceso
Parametrizar la consulta
  • Consiste en indicar en la consulta preparada en lugar de los valores, signos de interrogación ?
  • En el caso anterior
$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES (?,?);
  • Ahora habría que asigar los valores. Para ello usamos el método bind_param'

<bind_param(tipoDatos, >

  • Este método recibe dos tipos de parámetros
  1. El primero es una cadena de caracteres, donde cada carácter especifica el tipo de valor que va a recibir cada uno de los valores esperados en la consulta.
    • La codificación sería :
  1. s: cadena de caracteres
  2. i: número entero
  3. d: número float
  4. b: valor binario (BLOB)
  • En nuestro caso como va a recibir en los dos parámetros cada uno una cadena de caracteres sería "ss"
  1. El segundo grupo sería cada uno de los valores.SIEMPRE hay que especificar variables
  • En el ejemplo que estamos siguiendo
$consulta = $conexion->stmt_init();
$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES (?, ?)');
$cod_producto = "TABLET";
$nombre_producto = "Tablet PC";
$consulta->bind_param('ss', $cod_producto, $nombre_producto);
  • Insistimos en que siempre hay que especificar variables, de modo que
$consulta->bind_param('ss', 'TABLET', 'Tablet PC');  // Genera un error


Icon summary.gif

Resumen

$conexion = new mysqli(...);
$consulta = $conexion->stmt_init();
$consulta->prepare(...sentencia ... con ???)
$consulta->bind_param('s-i-b-d(tipo_de_valores)',valores_en_variables_respectivos_a_????');
$consulta->execute();
$consulta->close();
$conexion->close();


Consultas preparadas que retornan valores

  • En caso de que la consulta preparada retorne valores se recogen con el método bind_result'
  • Este método recibirá variables en los que se almacenarán los valores
  • Para recorre el conjunto de valores, usamos el método fectch(), como se ve en el ejemplo
$consulta = $conexion->stmt_init();
$consulta->prepare('SELECT producto, unidades FROM stock WHERE unidades<2');
$consulta->execute();
$consulta->bind_result($producto, $unidades);
while($consulta->fetch()) {
	print "<p>Producto $producto: $unidades unidades.</p>";
}
$consulta->close();
$conexion->close();
  • Aquí hay un enlace para una información completa sobre consultas preparadas
http://php.net/manual/es/class.mysqli-stmt.php


Icon activity.jpg

Actividad

  • Modifica el ejercicio anterior usando consultas parametrizadas






Icon activity.jpg

Actividad

  • Vamos a trabajar con la base de datos de la tienda
  • Lo primero usando la herramienta workbench generamos el modelo de tablas de la base de datos dwes y la analizamos
  • Crea una página web en la que se muestre el stock existente de un determinado producto en cada una de las tiendas.
  • Para seleccionar el producto concreto utiliza un cuadro de selección dentro de un formulario en esa misma página.
  • Puedes usar como base los siguientes ficheros css y plantilla adjuntos.
  • Añade la opción de modificar el número de unidades del producto en cada una de las tiendas.
  • Utiliza una consulta preparada para la actualización de registros en la tabla stock.
  • No es necesario tener en cuenta las tareas de inserción (no existían unidades anteriormente) y borrado (si el número final de unidades es cero).