Diferencia entre revisiones de «PHP Operacións BD Mysql»
| Línea 853: | Línea 853: | ||
* Cando utilicemos datos de tipo cadea, debemos lembrar poñelos entre comillas simples ('dato') na orde SQL. | * Cando utilicemos datos de tipo cadea, debemos lembrar poñelos entre comillas simples ('dato') na orde SQL. | ||
| + | |||
| + | <u>Nota:</u> Lembra que no paso de datos temos que ter en conta os posibles 'ataques' que poidan intentar facerse. Repasar o dado [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Paso_de_datos_con_formularios#Seguridade_no_uso_dos_formularios na wiki anteriormente]. | ||
| + | |||
| + | |||
| + | * Cando facemos unha consulta ou operación contra base de datos (alta, baixa e modificación) imos ter que 'enviar' datos. | ||
| + | : A forma de facelo é utilizando a consulta SQL cun parámetro e substituíndo dito parámetro polo valor que queiramos enviar. | ||
| + | |||
| + | * Así: SELECT col1, col2 FROM TABOA_1 WHERE col2=''''%d'''' | ||
| + | |||
| + | : No exemplo, col2 é de tipo cadea (por exemplo, varchar ou char). | ||
| + | ::<syntaxhighlight lang="java" line enclose="div" highlight="" > | ||
| + | define ('CONSULTA_CON_PARAM_CADEA',"SELECT col1, col2 FROM TABOA_1 WHERE col2='%s'"); | ||
| + | |||
| + | $query = sprintf(CONSULTA_CON_PARAM_CADEA,'valor_cadea_a_enviar'); | ||
| + | </syntaxhighlight> | ||
| + | : Fixarse como o parámetro col2 vai entrecomillado e o formato do parámetro é '%s'. | ||
| + | |||
| + | |||
| + | : Agora facemos o mesmo código que no exemplo anterior. | ||
| + | ::<syntaxhighlight lang="java" line enclose="div" highlight="" > | ||
| + | ...... | ||
| + | $result=$conex->query(CONSULTA_CON_PARAM_CADEA); | ||
| + | ...... | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | |||
| + | ==== Caso práctico: Enviando parámetros de tipo cadea ==== | ||
* Imos a aplicalo ao noso exemplo e imos crear unha páxina no que buscaremos libros en base a un nome do autor. | * Imos a aplicalo ao noso exemplo e imos crear unha páxina no que buscaremos libros en base a un nome do autor. | ||
Revisión del 14:45 16 ene 2017
Sumario
- 1 Introdución
- 2 Conexión
- 3 Creando as táboas en Mysql
- 4 Cambiando de base de datos activa
- 5 Consulta
- 5.1 Caso Práctico
- 5.2 Consulta enviando parámetros de tipo numérico
- 5.3 Consulta enviando parámetros de tipo cadea
- 5.4 Consulta chamando a procedementos almacenados
- 5.5 Consulta chamando a procedementos almacenados con parámetros de entrada
- 5.6 Consulta chamando a procedementos almacenados con parámetros de saída
- 5.7 Consulta chamando a funcións
- 5.8 Paxinación de resultados
- 6 Alta
- 7 Baixa
- 8 Modificación
- 9 Transaccións
- 10 Permisos mínimos
Introdución
- Como comentamos no punto anterior, para conectarnos a un xestor de base de datos específico imos ter que ter instalados os drivers correspondentes.
- Un driver é o software que nos permite comunicarnos cun xestor de base de datos, pero normalmente non imos facer dita comunicación utilizando o driver directamente se non que o usaremos a través dunha API, que son un conxunto de clases, funcións,... que nos permiten facer uso do driver e conectar a un xestor de base de datos.
- Existen dous tipos de API´s:
- Procedimentais: Facemos uso de funcións para realizar as operacións contra a base de datos.
- Orientadas a obxectos: Instanciamos clases e chamamos a métodos dos obxectos creados.
Normalmente faremos uso desta segunda opción.
- En PHP, ditas API´s se 'activan' facendo uso de Extensións.
- Indicar que unha API non é o mesmo que unha extensión en PHP, xa que algunhas extensións non proporcionan ningunha API ao usuario (por exemplo a extensión do driver PDO MySQL, non proporciona ningunha API ó programador PHP).
- Existen tres APIs principais para conectar a un servidor de bases de datos MySQL:
- Extensión MySQL de PHP
- Extensión mysqli de PHP
- Obxectos de Datos de PHP (PDO)
- Para conectarnos imos utilizar as funcións mysqli (mysql improved) que foron introducidas a partires de Mysql 4.1
- A extensión mysqli ven incluída nas versiones PHP 5 e posteriores.
- Algunha das melloras de dita extensión con respecto á extensión mysql:
- Interfaz orientada a obxectos
- Soporte para declaracións preparadas.
- Soporte para Transaccións
- Dita extensión tamén ten unha versión procedural, de tal forma que imos poder chamar ás funcións de dúas maneiras diferentes:
Exemplo de función cun estilo procedimental que pecha unha conexión a unha base de datos ($link é un identificador cando se fai a conexión contra a base de datos):
1 mysqli_close($link);
Exemplo de función cun estilo obxectual que pecha unha conexión a unha base de datos:
1 mysqli::close();
- Aquí tedes un cadro comparativo obtido de php.net onde se ve o comentado anteriormente:
Nota: Máis información neste enlace.
Conexión
- Partimos da base de que xa temos instalado:
- O xestor de bases de datos Mysql
- Unha base de datos creada. Neste manual vai ter de nome PHP. Facer que o 'default collation' sexa utf8_spanish2_ci.
- Un usuario / password que teña todos os permisos sobre a base de datos creada. Neste manual o usuario/password creado son user_php/user_php.
- Todo o anterior xa está indicado no punto anterior da wiki.
- Utilizando procedementos: mysqli_connect
- Utilizando obxectos: mysql::_construct
- Sintaxe (se indica a sintaxe do construtor e polo tanto será equivalente a facer un 'new' da clase mysqli):
1 mysqli::__construct ([ string $host = ini_get("mysqli.default_host") [, string $username = ini_get("mysqli.default_user") [, string $passwd = ini_get("mysqli.default_pw") [, string $dbname = "" [, int $port = ini_get("mysqli.default_port") [, string $socket = ini_get("mysqli.default_socket") ]]]]]] )
- Todos eles son optativos, pero normalmente indicaremos o nome de host, login, password e base de datos.
- Os parámetros son:
- Host a conectar (cadea): Pode ser o nome ou a dirección IP. Normalmente imos poñer 'localhost' (se o xestor de bases de datos está instalado no mesmo equipo que o servidor web).
- Nome do usuario con acceso á base de datos (cadea): Nome do usuario que ten acceso á base de datos.
- Password (cadea): Password do usuario.
- Dbname (cadea): Nome da base de datos sobre a que imos traballar.
- Porto (numérico): Porto no que está escoitando o xestor de base de datos. No caso de mysql ó o 3306.
- Socket (cadea): Socket ou tubería para conectar có xestor de bases de datos. En Linux é un tipo especial de arquivo que serve para 'transmitir' información, neste caso, cara o xestor de bases de datos.
- Aplicado ao noso caso, teremos a seguinte orde:
1 $conex = new mysqli('localhost','user_php','user_php','PHP');
- En caso de erro, $conex é un obxecto que represente a conexión á base de datos.
- En caso de ter unha versión PHP anterior á PHP 5.2.9 e 5.3.0 teremos que chamar á función mysqli_connect_error() desta forma:
1 $conex = new mysqli('localhost','user_php','user_php','PHP'); 2 if (mysqli_connect_error()){ 3 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 4 }
- En caso de ter unha versión PHP posterior á PHP 5.2.9 e 5.3.0:
1 $conex = new mysqli('localhost','user_php','user_php','PHP'); 2 if ($conex->connect_error) { 3 die('Erro de Conexión (' . $conex->connect_errno . ') ' . $conex->connect_error); 4 }
- Como vemos amosamos por un lado a mensaxe de erro que devolve o xestor de bases de datos e o número de erro asociado a esa mensaxe.
- Podemos consultar os número de erro e mensaxes asociadas neste enlace.
- Por exemplo, imos modificar o password do usuario por un que non exista e veremos o resultado:
1 $conex = new mysqli('localhost','user_php','password_non_existe','PHP'); 2 if (mysqli_connect_error()){ 3 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 4 }
- Dará como resultado:
Gardando os datos de conexión nun lugar seguro
- Como é obvio os datos de base de datos, usuario e password son moi importantes e non debemos permitir que alguén poida descubrilos.
- O que faremos será o seguinte:
- Definiremos en forma de constantes ditos datos nun arquivo de include.
- Gardaremos dito arquivo fora do cartafol de acceso do noso sitio web.
- O primeiro punto, definir en forma de constante e nun arquivo de include os datos da conexión:
Arquivo: bd.inc.php:
1 <?php 2 3 define ('SERVIDOR','localhost'); 4 define ('USUARIO','user_php'); 5 define ('PASSWORD','user_php'); 6 define ('BD','PHP');
- Faremos un require deste arquivo en todas as páxinas que necesiten unha conexión á base de datos. Desta forma, se necesitamos cambiar algo soamente o cambiaríamos nun arquivo.
- O segundo punto consiste en descubrir cal é a ruta física á que apunta o noso servidor web e colocar o arquivo de include 'fora' desa ruta.
- Temos neste punto da Wiki información sobre como funciona Apache e como saber a onde apunta o servidor web no seu arquivo de configuración.
- Temos varias opcións:
- Facendo uso da constante __DIR__.
- Facendo uso do array global $_SERVER e dentro deste a entrada $_SERVER['DOCUMENT_ROOT'].
- Se facemos un echo destas variables dentro dunha páxina do noso sitio web, obteremos unha ruta, coma por exemplo:
1 /var/www/html/
- Nota: Se o facemos coa constante __DIR__ e a páxina que está a facer o echo se atopa dentro dun cartafol, tamén aparecerá o cartafol (por exemplo /var/www/html/cartafol/).
- Isto quere dicir que o servidor web (e polo tanto calquera usuario de Internet) vai poder acceder as páxinas que se atopan no cartafol /var/www/html e calquera dentro del.
- Unha medida de seguridade é colocar o arquivo de include 'fora' desta ruta. Por exemplo, podemos crear un arquivo no cartafol /var/www/confBD
- A dito cartafol ten que poder acceder o usuario que utiliza o Apache para acceder aos cartafoles a nivel do S.O. (no caso de Apache soe ser www-data, podedes miralo no punto da wiki indicado anteriormente).
- Creando o cartafol onde se vai gardar o arquivo de configuración
Cambiamos os permisos: sudo chmod -R 550 /var/www/confBD e cambiamos o propietario: sudo chown -R www-data:www-data /var/www/confBD. Nota: O usuario e grupo pode variar dependendo do S.O. En Linux podedes sabelo mirando este enlace da wiki. En Windows o podedes saber neste enlace.
- Agora, calquera páxina que faga uso destes datos terá que facer o seguinte require:
1 <?php 2 3 //error_reporting(0); // Inabilita os erros de Apache se temos activada a directiva display_erros no php.ini 4 5 6 require ('/var/www/confBD/bd.inc.php'); 7 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD);
O xogo de caracteres
- Debemos ter en conta que unha páxina web está 'aberta' a todo o mundo que se conecte a ela.
- Cada persoa en diferentes países terá diferente xogos de caracteres no que cada carácter está relacionado cun código numérico.
- Se gardamos como dato dentro da base de datos unha cadea cun 'ñ' entre os seus datos, dito carácter estará asociado a un código nun xogo de caracteres. Se o usuario que vai cargar a páxina o carga cun xogo de caracteres diferente non vai poder visualizalo.
- Cando creamos aplicacións en PHP con acceso a base de datos teremos que ter en conta os seguintes xogos de caracteres:
- Xogo de caracteres da base de datos (no noso caso Mysql): É o que se coñece como Charset/Collation.
- Se vos fixades cando creamos a base de datos en Mysql, escollemos o charset (primeira columna) e o collation (segunda columna).
- Como indicamos antes, cando creamos a base de datos o charset/collation indicado foi utf8/utf8_spanish2_ci.
- O charset é como mysql garda internamente os caracteres e os relaciona cun código numérico e o collation indica como comparar e ordenar o texto. Podedes consultar esta blog para obter máis información.
- Xogo de caracteres da páxina HTML.
- Na páxina web indicamos coa etiqueta <meta> o xogo de caracteres para que o navegador web poida visualizalos correctamente.
- No caso que nos ocupa teremos que poñer: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- Máis información en w3schools.
- No código PHP ao conectar coa base de datos.
- Cando conectemos coa base de datos teremos que indicarlle que imos utilizar o xogo de caracteres utf8 con esta liña:
1 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 2 $conex->set_charset('utf8');
- E FUNDAMENTAL QUE NOS TRES SITIOS ESTEA POSTO O MESMO XOGO DE CARACTERES.
Creando as táboas en Mysql
- Unha vez temos a base de datos teremos que facer o Modelo EE/R, pasalo ao Modelo Relacional e facer un Proceso de Normalización ata polo menos a forma normal de Boyce-Codd.
- Imos implementar unha base de datos para xestionar a venda de libros de segunda mano.
- O modelo EE/R é o seguinte:
Nota:
- A táboa ESTADOS podería eliminarse e crear un campo estado de tipo enum en Mysql se ditos 'estados' van ser sempre os mesmos e non van modificarse...
- O campo 'imaxe' da táboa EXEMPLARES está nesta táboa e non na táboa LIBROS, xa que a idea é amosar ao comprador unha imaxe do exemplar para amosar o seu estado.
- O modelo relacional implementado nunha base de datos Mysql é o seguinte:
CREATE TABLE `PHP`.`LIBROS` ( `id_libro` INT NOT NULL AUTO_INCREMENT, `titulo` VARCHAR(300) NOT NULL, PRIMARY KEY (`id_libro`)); CREATE TABLE `PHP`.`ESTADOS` ( `id_estado` INT NOT NULL AUTO_INCREMENT, `descripcion` VARCHAR(100) NOT NULL, PRIMARY KEY (`id_estado`)); CREATE TABLE `PHP`.`EXEMPLARES` ( `libro_id` INT NOT NULL, `id_exemplar` INT NOT NULL, `imaxe` VARCHAR(100) NULL, `prezo` DECIMAL(6,2) NOT NULL, `estado_id` INT NULL, INDEX `fk_EXEMPLARES_LIBROS_idx` (`libro_id` ASC), INDEX `fk_EXEMPLARES_ESTADOS_idx` (`estado_id` ASC), PRIMARY KEY ( `libro_id`,`id_exemplar`), CONSTRAINT `fk_EXEMPLARES_LIBROS` FOREIGN KEY (`libro_id`) REFERENCES `PHP`.`LIBROS` (`id_libro`) ON DELETE NO ACTION ON UPDATE CASCADE, CONSTRAINT `fk_EXEMPLARES_ESTADOS` FOREIGN KEY (`estado_id`) REFERENCES `PHP`.`ESTADOS` (`id_estado`) ON DELETE NO ACTION ON UPDATE CASCADE); CREATE TABLE `PHP`.`AUTORES` ( `id_autor` INT NOT NULL AUTO_INCREMENT, `apelido` VARCHAR(200) NOT NULL, `nome` VARCHAR(50) NOT NULL, PRIMARY KEY (`id_autor`)); CREATE TABLE `PHP`.`LIBROS_AUTORES` ( `libro_id` INT NOT NULL, `autor_id` INT NOT NULL, PRIMARY KEY (`libro_id`, `autor_id`), INDEX `fk_LIBROS_AUTORES_AUTORES_idx` (`autor_id` ASC), CONSTRAINT `fk_LIBROS_AUTORES_LIBROS` FOREIGN KEY (`libro_id`) REFERENCES `PHP`.`LIBROS` (`id_libro`) ON DELETE NO ACTION ON UPDATE CASCADE, CONSTRAINT `fk_LIBROS_AUTORES_AUTORES` FOREIGN KEY (`autor_id`) REFERENCES `PHP`.`AUTORES` (`id_autor`) ON DELETE NO ACTION ON UPDATE CASCADE);
- Para ter algún dato inicial imos a executar estas ordes:
INSERT INTO `ESTADOS` VALUES (1,'NOVO'),(2,'COMO NOVO'),(3,'DETERIODADO'),(4,'CON ANOTACIÓNS'); INSERT INTO `PHP`.`AUTORES` (`apelido`, `nome`) VALUES ('Drumont', 'Eduardo'); INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('El caso Dreyfus, expresión del antisemitismo, temas, Gallica, BNF'); INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('Les amis'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('1', '1', 'dreyfus.jpg', '20,52', '2'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('1', '2', 'dreyfus.jpg', '15,34', '3'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('2', '1', 'amis.jpg', '35,35', '1'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('2', '2', 'amis.jpg', '20,25', '4'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('2', '3', 'amis.jpg', '25,34', '3'); INSERT INTO `LIBROS_AUTORES` VALUES (1,1),(2,1);
- Agora faremos o seguinte:
- Crearemos dentro do noso sitio web (normalmente /var/www/html/) un cartafol de nome IMAXES
- Copiamos as imaxes que veñen a continuación a dito cartafol.
- Cambiaremos o propietario coa orde chown -R www-data:www-data IMAXES/
Nome: amis.jpg
Nome: dreyfus.jpg
Regras de nomeado de táboas e columnas
- Aínda que non existen 'regras' únicas sobre como nomear as táboas e campos nos imos seguir as seguintes:
- Nomes de táboas en PLURAL e con MAIÚSCULAS. Desta forma distinguiremos facilmente o nome da táboa e os campos nunha consulta SQL.
- Nomes de campos en minúscula e singular.
- Se o campo é a clave primaria terá de nome: id_nometáboa_en_singular
- Se o campo é unha clave foránea terá de nome: nometáboa_en_singular_id
- Se o nome de campo está formado por varias palabras, estarán separadas por un guión baixo ( _ )
- Non usar til nin caracteres 'raros' como espazos en branco, interrogacións,...
- As claves foráneas irán ao final da lista de columnas da táboa.
Cambiando de base de datos activa
- Cando creamos a cadea de conexión, un dos parámetros que enviamos foi a base de datos na que iamos traballar.
- Podemos non enviar ese parámetro ou querer cambiar de base de datos dentro dunha páxina php.
- Se queremos escoller outra diferente teremos que facer uso da función mysqli::select_db.
- Por exemplo:
1 $conex = new mysqli('localhost','user_php','user_php','PHP'); 2 $conex->set_charset('utf8'); 3 4 if (!$conex->select_db('OutraBD')){ 5 die('Erro ao cambiar de base de datos'); 6 }
Consulta
- Para conectarnos primeiro teremos que ter a consulta SQL.
- Indicar que o SQL (Structure Query Language) é a linguaxe utilizada para obter datos dunha base de datos relacional.
- Consulta SQL: SELECT col1, col2 FROM TABOA_1
- Funcións para realizar consultas a base de datos e funcións relacionadas:
- mysqli::query: Realiza a consulta
- mysqli_result: En caso de realizar unha consulta de tipo 'select' este obxecto representa o conxunto de resultados obtidos.
- int $mysqli_result->num_rows: Devolve o número de filas devoltas en caso de realizar unha consulta.
- array mysqli_result::fetch_assoc(void): Devolve nun array asociativo unha das filas do resultado da consulta e 'pasa' á fila seguinte. Cando remate e xa non teña máis filas devolve NULL.
- O proceso para realizar unha consulta é o seguinte:
- Abrimos a conexión á base de datos.
- A idea é chamar ao procedemento query enviando como parámetro a consulta.
1 $result=$conex->query('SELECT col1, col2 FROM TABOA_1');
- $result vai ter o conxunto de filas que devolva o resultado. Podemos velo como unha táboa formada polas filas do resultado e polas columnas indicadas no select.
- Se a consulta está mal feita (por exemplo, seleccionamos unha táboa que non existe) devolverá false.
- Polo tanto, o seguinte será comprobar se temos datos
1 if ($result && ($result->num_rows>0)){ 2 // Veñen datos 3 4 } 5 else { 6 echo 'Non hai datos ou a consulta está mal feita'; 7 }
- Agora necesitaremos percorrer dende o primeiro rexistro ata o último amosando o resultado.
- Isto o faremos cun bucle, chamando á función mysqli_result::fetch_assoc(void).
- Debemos entender que, inicialmente, despois de facer a consulta, temos un punteiro que está 'apuntando' xusto antes do primeiro rexistro de resultados. O que fai a función anterior é mover ese punteiro ao seguinte rexistro (a primeira vez apuntará ao primeiro do conxunto de resultados) e devolve nun array a fila á que está 'apuntando'.
- Se volvemos a chamar, volve a moverse ao seguinte rexistro e así ata ao final no que devolve false.
1 if ($result && ($result->num_rows>0)){ 2 // Veñen datos 3 while($row = $result->fetch_assoc()) { 4 printf("%s-%s",$row["col1"],$row["col2"]); 5 } 6 } 7 else { 8 echo 'Non hai datos ou a consulta está mal feita'; 9 }
- Ao rematar de percorrer o resultado, o liberaremos de memoria.
1 if ($result && ($result->num_rows>0)){ 2 // Veñen datos 3 while($row = $result->fetch_assoc()) { 4 printf("%s-%s",$row["col1"],$row["col2"]); 5 } 6 $result->free(); // Liberamos da memoria os recursos 7 } 8 else { 9 echo 'Non hai datos ou a consulta está mal feita'; 10 }
- Pechamos a conexión.
1 $conex->close(); // Pechamos a conexión
Caso Práctico
- Imos crear unha consulta que nos devolva todos os libros da táboa LIBROS:
- Para non ter as consultas no medio do código PHP das diferentes páxinas, imos definila no arquivo bd.inc.php creado no punto anterior.
define ('CONSULTAR_LIBROS','select id_libro,titulo from LIBROS order by titulo');
- Normas nos nomes das constantes:
- Comenzarán cun verbo que vai poder ser: CONSULTAR, INSERIR, MODIFICAR e ELIMINAR
- Seguirá cun guión baixo.
- En caso de consultar os datos dunha única táboa se indicará o nome da táboa. En caso de consultar datos de varias táboas se indicará con varias palabras separadas con guión baixo que indiquen o obxectivo da consulta.
- Aplicado ao exemplo que estamos a seguir...
Arquivo: Consultar_Libros.php
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 ?> 13 14 15 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 16 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 17 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 18 <head> 19 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 20 <title>Operacións BD</title> 21 </head> 22 23 <body> 24 <?php 25 26 $result=$conex->query(CONSULTAR_LIBROS); 27 if ($result->num_rows>0){ 28 echo "<table border='1'>"; 29 while($row = $result->fetch_assoc()) { 30 echo '<tr>'; 31 printf("<td>%s</td>",$row["titulo"]); 32 echo '</tr>'; 33 } 34 echo '</table>'; 35 $result->free(); // Liberamos da memoria os recursos 36 } 37 else { 38 echo "Non hai datos que amosar!!!!"; 39 } 40 $conex->close(); // Pechamos a conexión 41 42 ?> 43 </body> 44 </html>
- Resultado:
- Fixarse como neste exemplo, aínda que a consulta devolve o id_libro, como non o necesitamos non o amosamos ao usuario, xa que dito dato é un código interno que utilizamos como clave primaria.
- O método fetch_assoc() move un punteiro interno á seguinte fila de resultados.
- Se queremos modificar dito punteiro e situarnos en calquera fila do resultado, debemos facer uso da función mysqli_data_seek·
Consulta enviando parámetros de tipo numérico
Nota: Lembra que no paso de datos temos que ter en conta os posibles 'ataques' que poidan intentar facerse. Repasar o dado na wiki anteriormente.
- Cando facemos unha consulta ou operación contra base de datos (alta, baixa e modificación) imos ter que 'enviar' datos.
- A forma de facelo é utilizando a consulta SQL cun parámetro e substituíndo dito parámetro polo valor que queiramos enviar.
- Así: SELECT col1, col2 FROM TABOA_1 WHERE col1=%d
- No exemplo, col1 é de tipo numérico.
1 define ('CONSULTA_CON_PARAM_NUMERICO','SELECT col1, col2 FROM TABOA_1 WHERE col1=%d'); 2 3 $query = sprintf(CONSULTA_CON_PARAM_NUMERICO,valor_numérico_a_enviar);
- Agora facemos o mesmo código que no exemplo anterior.
1 ...... 2 $result=$conex->query(CONSULTA_CON_PARAM_NUMERICO); 3 ......
Caso práctico: Enviando parámetros de tipo numérico
- Seguindo con exemplo, imos facer que cando o usuario seleccione un libro dentro dunha combobox, aparezan os exemplares do mesmo.
- Para iso teremos que:
- Crear a sentencia SQL correspondente enviando un parámetro que vai ser o código do libro seleccionado.
- Obter dito dato. Isto o faremos utilizando un formulario cun método POST.
- Modificar o código da páxina anterior (Consultar_Libros.php) e facer que os libros aparezan nunha combobox (dropdownlist ou <select> en html).
- Facer que cando o usuario escolla un libro 'active' o formulario e polo tanto provoque o 'submit' do mesmo amosando os exemplares do libro seleccionado.
- Manter seleccionado o libro na combo.
Nota: Ao principio ides ter problema de tipo 'non amosa ningún dato'. O primeiro que tedes que descartar é que a consulta SQL estea ben e devolva datos, polo que vos aconsello que fagades un 'echo' de dita consulta e a executedes no MysqlWorkBench para ver que realmente devolve algún resultado.
- Crearemos unha nova páxina de nome Consultar_Exemplares_Por_Libro.php.
- Modificamos o código para que aparezan os libros dentro da etiqueta <select> de html:
- Neste caso, na combo imos gardar o código do libro xa que cando escollamos un, necesitamos obter dito código para facer outra consulta buscando os exemplares dese libro...
1 <body> 2 <?php 3 4 $result=$conex->query(CONSULTAR_LIBROS); 5 if ($result && $result->num_rows>0){ 6 echo "<select name='lstLibros'>"; 7 echo "<option value='-1'></option>"; 8 while($row = $result->fetch_assoc()) { 9 printf("<option value='%d'>%s</option>",$row["id_libro"],$row["titulo"]); 10 } 11 echo '</select>'; 12 $result->free(); // Liberamos da memoria os recursos 13 } 14 else { 15 echo "Non hai datos que amosar!!!!"; 16 } 17 $conex->close(); // Pechamos a conexión 18 19 ?> 20 </body>
- Liña 7: Engadimos unha entrada en branco un valor -1 no seu value, para 'obrigar' a escoller un valor e que por defecto non apareza cun libro seleccionado.
- Dependerá da aplicación, podedes ter que implementar variantes como ter unha entrada que indique 'TODAS' ou facer que por defecto estea seleccionada algunha das entradas...
- Resultado:
- Arquivo bd.inc.php
- Definimos nunha constate a consulta que vai obter os exemplares do libro seleccionado:
define ('CONSULTAR_EXEMPLARES_POR_LIBRO','select libro_id,id_exemplar,imaxe,prezo,estado_id from EXEMPLARES where libro_id=%d order by estado_id');
- Como vemos imos enviar o código por medio dun parámetro de tipo numérico (usaremos a función sprintf).
- Definimos nunha constante onde se atopan as imaxes relacionados cos libros:
define ('CARTAFOL_IMAXES_WEB','http://www.meusitio.es/IMAXES/');
- Teredes que cambiar a url pola vosa.
- Para facer que cando se seleccione un elemento do lista se 'active' o formulario, existen múltiples formas, unha delas pode ser:
- echo "<select name='lstLibros' onchange='this.form.submit()'>";
- O código que vai amosar os exemplares do libro seleccionado será o seguinte:
1 <?php 2 // CONSULTAMOS OS EXEMPLARES DUN LIBRO SELECCIONADO 3 if (!empty($_POST['lstLibros']) && filter_var($_POST['lstLibros'],FILTER_VALIDATE_INT) && $_POST['lstLibros']!=-1){ 4 5 $query=sprintf(CONSULTAR_EXEMPLARES_POR_LIBRO,$_POST['lstLibros']); 6 $result=$conex->query($query); 7 if ($result && ($result->num_rows>0)){ 8 echo "<table border='1'>"; 9 echo "<th>Num. Exemplar</th><th>Imaxe</th><th>Prezo</th><th>Estado</th>"; 10 while($row = $result->fetch_assoc()) { 11 echo '<tr>'; 12 printf("<td>%d</td><td><img alt='%s' src='%s' width='42' height='42' /></td><td>%s</td><td>%d</td>",$row["id_exemplar"],$row["imaxe"],CARTAFOL_IMAXES_WEB . $row["imaxe"],$row["prezo"],$row["estado_id"]); 13 echo '</tr>'; 14 } 15 echo '</table>'; 16 $result->free(); // Liberamos da memoria os recursos 17 } 18 else { 19 echo "Non hai datos que amosar!!!!"; 20 } 21 22 } 23 $conex->close(); // Pechamos a conexión 24 25 ?>
- Liña 3: Comprobamos que veñan datos no formulario (que se escollera algún exemplar da lista) e que sexa de tipo numérico. Tamén comprobamos se seleccionamos o elemento baleiro (valor -1 na combo).
- Liña 5: Definimos a consulta a realizar contra a base de datos, utilizando a constante definida e substituíndo o parámetro polo código do libro seleccionado.
- Liñas 7-17: Construímos a táboa para que amose os datos. Fixarse que na imaxe temos que definir o atributo 'alt' para que cumpra o estándar XHTML, polo que enviamos dúas veces o nome do arquivo. No dato que vai no atributo 'src' enviamos o nome do arquivo xunto coa URL onde se atopan as imaxes.
- Liña 23: Pechamos a conexión. Fixarse que esta liña debe aparecer unha vez. Se non teremos que volver a abrir a conexión á base de datos.
- Facemos que se manteña seleccionado o libro:
1 <?php 2 $result=$conex->query(CONSULTAR_LIBROS); 3 if ($result->num_rows>0){ 4 echo "<select name='lstLibros' onchange='this.form.submit()'>"; 5 echo "<option value='-1'></option>"; 6 while($row = $result->fetch_assoc()) { 7 if(isset($_POST['lstLibros']) && $row['id_libro']==$_POST['lstLibros']){ 8 $cadea="selected='selected'"; 9 } 10 else{ 11 $cadea=''; 12 } 13 printf("<option value='%d' %s>%s</option>",$row["id_libro"],$cadea,$row["titulo"]); 14 } 15 echo '</select>'; 16 $result->free(); // Liberamos da memoria os recursos 17 } 18 else { 19 echo "Non hai datos que amosar!!!!"; 20 } 21 22 ?>
- O código completo sería o seguinte:
Arquivo: bd.inc.php:
1 <?php 2 3 define ('SERVIDOR','localhost'); 4 define ('USUARIO','user_php'); 5 define ('PASSWORD','user_php'); 6 define ('BD','PHP'); 7 8 define ('CARTAFOL_IMAXES_WEB','http://www.meusitio.es/IMAXES/'); 9 10 define ('CONSULTAR_LIBROS','select id_libro,titulo from LIBROS order by titulo'); 11 define ('CONSULTAR_EXEMPLARES_POR_LIBRO','select libro_id,id_exemplar,imaxe,prezo,estado_id from EXEMPLARES where libro_id=%d order by estado_id');
Arquivo: Consultar_Exemplares_Por_Libro.php:
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 ?> 13 14 15 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 16 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 17 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 18 <head> 19 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 20 <title>Operacións BD</title> 21 </head> 22 23 <body> 24 <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method='post'> 25 26 <?php 27 $result=$conex->query(CONSULTAR_LIBROS); 28 if ($result->num_rows>0){ 29 echo "<select name='lstLibros' onchange='this.form.submit()'>"; 30 echo "<option value='-1'></option>"; 31 while($row = $result->fetch_assoc()) { 32 if(isset($_POST['lstLibros']) && $row['id_libro']==$_POST['lstLibros']){ 33 $cadea="selected='selected'"; 34 } 35 else{ 36 $cadea=''; 37 } 38 printf("<option value='%d' %s>%s</option>",$row["id_libro"],$cadea,$row["titulo"]); 39 } 40 echo '</select>'; 41 $result->free(); // Liberamos da memoria os recursos 42 } 43 else { 44 echo "Non hai datos que amosar!!!!"; 45 } 46 47 ?> 48 </form> 49 50 51 <?php 52 // CONSULTAMOS OS EXEMPLARES DUN LIBRO SELECCIONADO 53 if (!empty($_POST['lstLibros']) && filter_var($_POST['lstLibros'],FILTER_VALIDATE_INT) && $_POST['lstLibros']!=-1){ 54 55 $query=sprintf(CONSULTAR_EXEMPLARES_POR_LIBRO,$_POST['lstLibros']); 56 $result=$conex->query($query); 57 if ($result && ($result->num_rows>0)){ 58 echo "<table border='1'>"; 59 echo "<th>Num. Exemplar</th><th>Imaxe</th><th>Prezo</th><th>Estado</th>"; 60 while($row = $result->fetch_assoc()) { 61 echo '<tr>'; 62 printf("<td>%d</td><td><img alt='%s' src='%s' width='42' height='42' /></td><td>%s</td><td>%d</td>",$row["id_exemplar"],$row["imaxe"],CARTAFOL_IMAXES_WEB . $row["imaxe"],$row["prezo"],$row["estado_id"]); 63 echo '</tr>'; 64 } 65 echo '</table>'; 66 $result->free(); // Liberamos da memoria os recursos 67 } 68 else { 69 echo "Non hai datos que amosar!!!!"; 70 } 71 72 } 73 $conex->close(); // Pechamos a conexión 74 75 ?> 76 77 78 </body> 79 </html>
- Imos realizar unha mellora nos datos que se visualizan.
- Como vemos, aparece o código do estado do libro. O que queremos amosar é a descrición, polo que teremos que facer un Join coa táboa ESTADOS.
Arquivo bd.inc.php:
1 define ('CONSULTAR_EXEMPLARES_POR_LIBRO','select libro_id,id_exemplar,imaxe,prezo,estado_id,ESTADOS.descripcion as desc_estado' 2 . ' from EXEMPLARES INNER JOIN ESTADOS ON (ESTADOS.id_estado=EXEMPLARES.estado_id)' 3 . ' where libro_id=%d order by desc_estado');
Arquivo: Consultar_Exemplares_Por_Libro.php:
1 .................. 2 3 <?php 4 // CONSULTAMOS OS EXEMPLARES DUN LIBRO SELECCIONADO 5 if (!empty($_POST['lstLibros']) && filter_var($_POST['lstLibros'],FILTER_VALIDATE_INT) && $_POST['lstLibros']!=-1){ 6 7 $query=sprintf(CONSULTAR_EXEMPLARES_POR_LIBRO,$_POST['lstLibros']); 8 $result=$conex->query($query); 9 if ($result && ($result->num_rows>0)){ 10 echo "<table border='1'>"; 11 echo "<th>Num. Exemplar</th><th>Imaxe</th><th>Prezo</th><th>Estado</th>"; 12 while($row = $result->fetch_assoc()) { 13 echo '<tr>'; 14 printf("<td>%d</td><td><img alt='%s' src='%s' width='42' height='42' /></td><td>%s</td><td>%s</td>",$row["id_exemplar"],$row["imaxe"],CARTAFOL_IMAXES_WEB . $row["imaxe"],$row["prezo"],$row["desc_estado"]); 15 echo '</tr>'; 16 } 17 echo '</table>'; 18 $result->free(); // Liberamos da memoria os recursos 19 } 20 else { 21 echo "Non hai datos que amosar!!!!"; 22 } 23 24 } 25 $conex->close(); // Pechamos a conexión 26 27 ?> 28 29 ..................
- Liña 14: Modificamos o tipo de parámetro que pasa de ser numérico (%d) a cadea (%s) e amosamos a descrición do estado (as dúas columnas do select non deben ter o mesmo nome, descrición do libro e descrición do estado).
EXERCICIO PROPOSTO
- Crea unha páxina de nome Consultar_Exemplares_Por_Estado.php no que aparezan en forma de radiobutton os estados da táboa ESTADOS.
- Ao seleccionar un estado, amosaranse en forma de lista ordenada os exemplares (libro_id,num_exemplar,prezo) ordenados por libro_id (quen queira pode facer un join coa táboa LIBROS e amosar o título en vez do libro_id e ordenar os resultados polo título).
- O radiobutton debe manterse seleccionado.
Consulta enviando parámetros de tipo cadea
- Cando utilicemos datos de tipo cadea, debemos lembrar poñelos entre comillas simples ('dato') na orde SQL.
Nota: Lembra que no paso de datos temos que ter en conta os posibles 'ataques' que poidan intentar facerse. Repasar o dado na wiki anteriormente.
- Cando facemos unha consulta ou operación contra base de datos (alta, baixa e modificación) imos ter que 'enviar' datos.
- A forma de facelo é utilizando a consulta SQL cun parámetro e substituíndo dito parámetro polo valor que queiramos enviar.
- Así: SELECT col1, col2 FROM TABOA_1 WHERE col2='%d'
- No exemplo, col2 é de tipo cadea (por exemplo, varchar ou char).
1 define ('CONSULTA_CON_PARAM_CADEA',"SELECT col1, col2 FROM TABOA_1 WHERE col2='%s'"); 2 3 $query = sprintf(CONSULTA_CON_PARAM_CADEA,'valor_cadea_a_enviar');
- Fixarse como o parámetro col2 vai entrecomillado e o formato do parámetro é '%s'.
- Agora facemos o mesmo código que no exemplo anterior.
1 ...... 2 $result=$conex->query(CONSULTA_CON_PARAM_CADEA); 3 ......
Caso práctico: Enviando parámetros de tipo cadea
- Imos a aplicalo ao noso exemplo e imos crear unha páxina no que buscaremos libros en base a un nome do autor.
- A busca farase utilizando a orde SQL like, que busca patróns de cadea. Este operador non distingue maiúsculas de minúsculas.
- Se queremos distinguir maiúsculas de minúsculas utilizaremos o operador like binary.
- O operador like o imos aplicar ao nome completo do autor, polo que teremos que concatenar o nome e apelido. Usaremos a función concat_ws ou concat.
- O usuario introducirá nunha caixa de texto o nome (ou parte del) do autor.
- Nota: Lembra que no paso de datos temos que ter en conta os posibles 'ataques' que poidan intentar facerse. Repasar o dado na wiki anteriormente.
- Mysql incorpora tamén unha función para codificar os caracteres (como espazos en branco, saltos de liña,...Función string mysqli::real_escape_string ( string $escapestr ). Devolve a cadea 'codificada'.
- Por exemplo:
1 $ciudad = "'s Hertogenbosch"; 2 3 /* esta consulta fallará debido a que no escapa $ciudad */ 4 if (!$mysqli->query("INSERT into miCiudad (Name) VALUES ('$ciudad')")) { 5 printf("Error: %s\n", $mysqli->sqlstate); 6 } 7 8 $ciudad = $mysqli->real_escape_string($ciudad); 9 10 /* esta consulta con $ciudad escapada funcionará */ 11 if ($mysqli->query("INSERT into miCiudad (Name) VALUES ('$ciudad')")) { 12 printf("%d fila insertada.\n", $mysqli->affected_rows); 13 }
- Exemplo obtido de php.net
- Pode suceder que un mesmo autor teña varios libros. Como imos a amosar os datos dentro dun grid no que na primeira columna vai ser o nome do autor e a segunda o nome do libro, vai repetirse o nome do autor como se amosa na seguinte imaxe:
- Para evitalo, teremos que gardar en cada iteración unha vez 'impresa' a liña do autor, o nome do autor e comparalo cando carguemos a seguinte fila de datos. Se o nome do autor da nova liña de datos é igual ao anterior, entón non o debemos de amosar.
- O código sería algo parecido a isto:
1 $autor=''; // A utilizamos para que non se repita o autor na táboa cando hai varios libros do mesmo autor 2 $last_autor=''; // A utilizamos para que non se repita o autor na táboa cando hai varios libros do mesmo autor 3 while($row = $result->fetch_assoc()) { // PROCESAMOS AS FILAS DO RESULTADO DA CONSULTA 4 if ($last_autor==$row["nome_autor"]){ // Comprobamos se o novo autor é igual ao anterior procesado 5 $autor=''; // Se é igual entón quere dicir que estamos a ler unha fila co mesmo autor que a anterior. Polo tanto non 'imprimimos o autor' 6 } 7 else { 8 $autor=$row["nome_autor"]; // En caso contrario quere dicir que o autor actual é diferente ao anterior e polo tanto debemos sacalo por pantalla. 9 } 10 11 printf(AMOSAMOS OS DATOS DO LIBRO E DA VARIABLE $autor); 12 13 $last_autor=$row["nome_autor"]; // Gardamos o nome do autor para comparar có autor que veña na seguinte fila de datos. 14 }
- Imos facer que cada libro sexa un enlace para abrir unha páxina nova de nome Detalle_Libro.php. No resultado da busca soamente imos amosar o nome do libro, de tal forma que cando o usuario preme sobre o enlace, apareza unha nova xanela cos datos completos dese libro, incluíndo os exemplares).
- Para facelo imos facer uso de javascript e da función window.open.
- Crearei unha función en javascript que espere recibir un parámetro, que sexa a url a abrir e chamarei a window.open cunha serie de modificacións, como o tamaño da xanela, que non poida modificar a url,...
- O código será o seguinte:
1 <head> 2 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 3 <title>Operacións BD</title> 4 <script type="text/javascript"> 5 function abrirVentana($url){ 6 window.open($url,'_blank','channelmode=0,status=0;directories=0,location=0,menubar=0,titlebar=0,toolbar=0,width=600,height=400'); 7 } 8 </script> 9 10 </head>
- Para chamar a esta función usaremos a etiqueta <a> e o evento onclick, coma o seguinte exemplo:
1 <a href='#' onclick="javascript:abrirVentana('Detalle_Libro.php?libro=1');">Título Libro</a>
- Hai que ter moito coidado cando estamos a traballar con PHP-Javascript polo tema das comillas xa que teremos que 'xogar' coas simples e dobres para que se pechen correctamente.
- SOLUCIÓN
Arquivo: bd.inc.php
Nota: Definimos a consulta a realizar á base de datos (lembrar que é mellor facer dita consulta directamente no Mysql e despois copiala ao código PHP pasando os parámetros correspondentes)
<?php define ('SERVIDOR','localhost'); define ('USUARIO','user_php'); define ('PASSWORD','user_php'); define ('BD','PHP'); define ('CARTAFOL_IMAXES_WEB','http://www.meusitio.es/IMAXES/'); define ('CONSULTAR_LIBROS','select id_libro,titulo from LIBROS order by titulo'); define ('CONSULTAR_EXEMPLARES_POR_LIBRO','select libro_id,id_exemplar,imaxe,prezo,estado_id,ESTADOS.descripcion as desc_estado' . ' from EXEMPLARES INNER JOIN LIBROS ON (LIBROS.id_libro=EXEMPLARES.libro_id)' . ' INNER JOIN ESTADOS ON (ESTADOS.id_estado=EXEMPLARES.estado_id)' . ' where id_libro=%d order by titulo,desc_estado'); define ('CONSULTAR_LIBROS_POR_AUTOR',"select CONCAT(apelido,',',nome) as nome_autor,id_libro,titulo from LIBROS " . ' INNER JOIN LIBROS_AUTORES ON (LIBROS_AUTORES.libro_id=LIBROS.id_libro) ' . ' INNER JOIN AUTORES ON (LIBROS_AUTORES.autor_id=AUTORES.id_autor)' . " where CONCAT(AUTORES.nome,' ',AUTORES.apelido) like '%%%s%%' " . 'order by nome_autor,titulo');
- Nota: Fixarse como o parámetro que recibe 'like' é de tipo cadea e polo tanto vai entre comillas.
Arquivo Buscar_Libros_Por_Autor.php
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 $erro=""; // Variable que a usaremos para amosar os posibles erros que poidan darse 13 ?> 14 15 16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 17 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 19 <head> 20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 21 <title>Operacións BD</title> 22 <script type="text/javascript"> 23 function abrirVentana($url){ 24 window.open($url,'_blank','channelmode=0,status=0;directories=0,location=0,menubar=0,titlebar=0,toolbar=0,width=600,height=400'); 25 } 26 </script> 27 28 </head> 29 30 <body> 31 <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method='post'> 32 Introduce o nome do autor a buscar: 33 <input type='text' size='50' maxlength="100" name="txtAutor" value='<?php echo (isset($_POST['txtAutor']) ? $_POST["txtAutor"]:""); ?>'/> 34 <input type="submit" value="BUSCAR" /> 35 <input type='button' onclick="javascript:this.form['txtAutor'].value='';" value="LIMPAR" /> 36 37 38 <?php 39 if (!empty($_POST['txtAutor'])){ // Premimos o botón de busca 40 $textoBuscar = filter_var($_POST['txtAutor'],FILTER_SANITIZE_STRING); 41 if (strlen($textoBuscar)>100){ 42 $erro="<h2>A cadea a buscar non pode ser maior de 100 caracteres</h2>"; 43 } 44 else { 45 $query=sprintf(CONSULTAR_LIBROS_POR_AUTOR,$textoBuscar); 46 $result=$conex->query($query); 47 if ($result->num_rows>0){ 48 $autor=''; // A utilizamos para que non se repita o autor na táboa cando hai varios libros do mesmo autor 49 $last_autor=''; // A utilizamos para que non se repita o autor na táboa cando hai varios libros do mesmo autor 50 echo "<table border='1'>"; 51 echo "<th>Autor</th><th>Libro</th>"; 52 while($row = $result->fetch_assoc()) { 53 if ($last_autor==$row["nome_autor"]){ 54 $autor=$row["nome_autor"]; 55 } 56 else { 57 $autor=$row["nome_autor"]; 58 } 59 echo '<tr>'; 60 $onclick=sprintf("javascript:abrirVentana(\"Detalle_Libro.php?libro=%d\");",$row["id_libro"]); 61 printf("<td>%s</td><td><a href='#' onclick='%s'>%s</a></td>",$autor,$onclick,$row['titulo']); 62 echo '</tr>'; 63 $last_autor=$row["nome_autor"]; 64 } 65 $result->free(); 66 echo "</table>"; 67 } 68 else { 69 echo "<h2><b>Non hai resultados</b></h2>"; 70 } 71 72 73 } 74 } 75 76 ?> 77 </form> 78 79 80 <?php 81 if ($erro!='') { 82 printf("<h3>%s</h3>",$erro); 83 } 84 $conex->close(); // Pechamos a conexión 85 86 ?> 87 88 89 </body> 90 </html>
- Fixarse que nesta páxina non necesitamos conectar ao principio, xa que soamente se carga a caixa de texto para que o usuario introduza o nome do autor a buscar.
- Polo tanto a conexión e o peche da base de datos se podería poñer dentro do if da liña 39.
- Fixarse que o botón de limpar non é de tipo 'reset' xa que ao cargar o que veña en $_POST o botón tipo reset volvería a amosar dito valor.
- Resultado:
Consulta chamando a procedementos almacenados
- Un procedemento almacenado ven a ser o equivalente a unha función en PHP, coa diferenza que as ordes que conforman o procedemento van permitir realizar accións (inserción, borrado, modificación e selección) sobre a base de datos.
- Como comentamos anteriormente nesta wiki, unha das mellores formas de evitar ataques de tipo 'inxección de CSS' é utilizar procedementos almacenados.
- Tamén é unha forma de aumentar a seguridade de acceso á base de datos Mysql, xa que teremos que dar permiso de execución sobre o procedemento almacenado e o usuario non vai poder acceder ás táboas da base de datos nin examinar a estrutura das mesmas.
- Desta forma podemos cambiar a estrutura da táboa (como por exemplo cambiar o nome dunha columna) e simplemente cambiando a sentenza SQL que está dentro do procedemento almacenado e poñendo un alias có nome antigo, a aplicación no cliente segue funcionando.
- Outra vantaxe é que as sentencias SQL non están definidas nas páxinas PHP e polo tanto se realizamos un cambio o faremos no Mysql sendo 'transparente' cara ao usuario.
- Máis información neste enlace.
- O primeiro que temos que facer é crear o procedemento almacenado.
- A orde SQL é CREATE PROCEDURE.
- Vexamos un exemplo. Imos copiar o código da páxina Consultar_Libros.php e imos crear unha nova de nome Consultar_Libros_Proc co mesmo código, facendo as modificacións que se indican a continuación para que faga uso da chamada a un procedemento almacenado.
- Creamos o procedemento almacenado:
- Cando definimos un procedemento almacenado debemos facer uso da palabra DELIMITER para indicarlle ao Mysql onde acaba a definición do procedemento almacenado. Máis información neste enlace.
Creamos un procedemento almacenado dentro do MysqlWorkBench
1 DELIMITER $$ 2 3 CREATE PROCEDURE `CONSULTAR_LIBROS` () 4 BEGIN 5 6 select id_libro,titulo 7 from LIBROS 8 order by titulo; 9 10 11 END 12 $$
- Definimos nunha constante o nome do procedemento almacenado dentro do arquivo bd.inc.php.
- Arquivo: bd.inc.php
1 define('PROCEDEMENTO_CONSULTAR_LIBROS','CONSULTAR_LIBROS()'); // Se levara parámetros faríamos o mesmo que nos casos anteriores coas sentencias select...
- Debemos cambiar a chamada a 'query' pola chamada mysqli::real_query ou mysqli::multi_query
- Nos imos facer uso da chamada a multi_query xa que tamén a imos poder utilizar cando dentro dun mesmo procedemento almacenado teñamos varios select´s e queiramos recuperar o contido de todos eles (cada un será un conxunto de resultados).
- Arquivo: Consultar_Libros_Proc.php
1 <body> 2 <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method='post'> 3 <?php 4 $proc = sprintf("CALL %s",PROCEDEMENTO_CONSULTAR_LIBROS); 5 if ($conex->multi_query($proc)){ 6 $result = $conex->store_result(); 7 if ($result->num_rows>0){ 8 echo "<select name='lstLibros' onchange='this.form.submit()'>"; 9 echo "<option value='-1'></option>"; 10 while($row = $result->fetch_assoc()) { 11 if(isset($_POST['lstLibros']) && $row['id_libro']==$_POST['lstLibros']){ 12 $cadea="selected='selected'"; 13 } 14 else{ 15 $cadea=''; 16 } 17 printf("<option value='%d' %s>%s</option>",$row["id_libro"],$cadea,$row["titulo"]); 18 } 19 echo '</select>'; 20 $result->free(); // Liberamos da memoria os recursos 21 } 22 else { 23 echo "Non hai datos que amosar..."; 24 } 25 26 } 27 else { 28 echo "Houbo un erro na chamado ao procedemento:" . $conex->error; // A mensaxe do erro ($conex->error) non a teremos que amosar coa aplicación funcionando 29 } 30 ?> 31 </form>
- Liña 4: Creamos a cadea que vai chamar ao procedemento almacenado: CALL CONSULTAR_LIBROS()
- Liña 5: Modificamos a liña e agora chamamos ao procedemento mysqli::multi_query(nome_proc). Dita chamada devolve false en caso de erro.
- Liña 6: Gardamos o resultado (o primeiro select do procedemento almacenado) no obxecto $result e xa operamos igual que no caso anterior cando usamos as sentencias 'select' directamente.
Consulta chamando a procedementos almacenados con parámetros de entrada
- Podemos chamar a un procedemento almacenado enviando datos ao mesmo en forma de parámetros.
- Indicar que en case todos os xestores os parámetros poden ser de 3 tipos: de entrada, de saída e de entrada/saída.
- Os parámetros de saída son variables que van poder ser modificadas no interior do procedemento e cuxo valor vai poder ser lido dentro do código da páxina php.
- Os parámetros de entrada son variables que van poder ser utilizadas dentro do procedemento (chegan en forma de parámetros) pero as modificacións que se fagan no seu valor non chegan á paxina PHP.
- Os parámetros de entrada/entrada son unha combinación dos dous anteriores.
- Máis información neste enlace.
- Seguindo có noso exemplo...imos crear unha nova páxina de nome Buscar_Libros_Por_Autor_Proc que faga uso de procedementos almacenados en vez de sentencias SQL directamente no código.
Solución:
- Procedemento almacenado CONSULTAR_LIBROS_POR_AUTOR en Mysql
1 DELIMITER $$ 2 3 CREATE PROCEDURE `CONSULTAR_LIBROS_POR_AUTOR`(IN _autor_buscar varchar(100)) 4 BEGIN 5 6 select CONCAT(apelido,',',nome) as nome_autor,id_libro,titulo from LIBROS 7 INNER JOIN LIBROS_AUTORES ON (LIBROS_AUTORES.libro_id=LIBROS.id_libro) 8 INNER JOIN AUTORES ON (LIBROS_AUTORES.autor_id=AUTORES.id_autor) 9 where CONCAT(AUTORES.nome,' ',AUTORES.apelido) like CONCAT('%', _autor_buscar , '%') 10 order by nome_autor,titulo; 11 12 END
- Nota: Para comprobar se funciona facede primeiro unha chamada dende o propio Mysql coa orde: call CONSULTAR_LIBROS_POR_AUTOR('dru');
- Fixarse como o parámetro é de tipo IN (valor por defecto, podería non poñerse nada).
- Definimos a constante en PHP no que está definido o nome do procedemento e o parámetro que vai levar.
- Arquivo: bd.inc.php
1 define('PROCEDEMENTO_CONSULTAR_LIBROS_POR_AUTOR','CONSULTAR_LIBROS_POR_AUTOR("%s")');
- Nota: Fixarse como o parámetro ao ser de tipo 'cadea' debe ir entre comillas.
- Arquivo Buscar_Libros_Por_Autor_Proc
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 $erro=""; // Variable que a usaremos para amosar os posibles erros que poidan darse 13 ?> 14 15 16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 17 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 19 <head> 20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 21 <title>Operacións BD</title> 22 <script type="text/javascript"> 23 function abrirVentana($url){ 24 window.open($url,'_blank','channelmode=0,status=0;directories=0,location=0,menubar=0,titlebar=0,toolbar=0,width=600,height=400'); 25 } 26 </script> 27 28 </head> 29 30 <body> 31 <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method='post'> 32 Introduce o nome do autor a buscar: 33 <input type='text' size='50' maxlength="100" name="txtAutor" value='<?php echo (isset($_POST['txtAutor']) ? $_POST["txtAutor"]:""); ?>'/> 34 <input type="submit" value="BUSCAR" /> 35 <input type='button' onclick="javascript:this.form['txtAutor'].value='';" value="LIMPAR" /> 36 37 38 <?php 39 if (!empty($_POST['txtAutor'])){ // Premimos o botón de busca 40 $textoBuscar = filter_var($_POST['txtAutor'],FILTER_SANITIZE_STRING); 41 if (strlen($textoBuscar)>100){ 42 $erro="<h2>A cadea a buscar non pode ser maior de 100 caracteres</h2>"; 43 } 44 else { 45 $proc = sprintf(PROCEDEMENTO_CONSULTAR_LIBROS_POR_AUTOR,$textoBuscar); // Cambiamos o texto a buscar polo parámetro definido na constante 46 $proc = sprintf("CALL %s",$proc); 47 if ($conex->multi_query($proc)){ 48 $result = $conex->store_result(); 49 if ($result->num_rows>0){ 50 $autor=''; // A utilizamos para que non se repita o autor na táboa cando hai varios libros do mesmo autor 51 $last_autor=''; // A utilizamos para que non se repita o autor na táboa cando hai varios libros do mesmo autor 52 echo "<table border='1'>"; 53 echo "<th>Autor</th><th>Libro</th>"; 54 while($row = $result->fetch_assoc()) { 55 if ($last_autor==$row["nome_autor"]){ 56 $autor=''; 57 } 58 else { 59 $autor=$row["nome_autor"]; 60 } 61 echo '<tr>'; 62 $onclick=sprintf("javascript:abrirVentana(\"Detalle_Libro.php?libro=%d\");",$row["id_libro"]); 63 printf("<td>%s</td><td><a href='#' onclick='%s'>%s</a></td>",$autor,$onclick,$row['titulo']); 64 echo '</tr>'; 65 $last_autor=$row["nome_autor"]; 66 } 67 $result->free(); 68 echo "</table>"; 69 } 70 else { 71 echo "<h2><b>Non hai resultados</b></h2>"; 72 } 73 } 74 else{ 75 echo "Houbo un erro na chamado ao procedemento:" . $conex->error; // A mensaxe do erro non a teremos que amosar coa aplicación funcionando 76 } 77 78 } 79 } 80 81 ?> 82 </form> 83 84 85 <?php 86 if ($erro!='') { 87 printf("<h3>%s</h3>",$erro); 88 } 89 $conex->close(); // Pechamos a conexión 90 91 ?> 92 93 94 </body> 95 </html>
- Liñas 45 e 46: Creamos a cadea que vai chamar ao procedemento almacenado de Mysql: CALL CONSULTAR_LIBROS_POR_AUTOR('dru');
Consulta chamando a procedementos almacenados con parámetros de saída
- Agora imos aplicar o concepto de parámetros de saída, que como indicamos no punto anterior, son parámetros que van poder ser modificados o seu valor dentro do procedemento almacenado e recoller o seu valor modificado dende a parte de PHP.
- Segundo o noso exemplo, imos crear un procedemento almacenado que devolva nun parámetro de saída o número de libros que temos na nosa tenda.
- Procedemento almacenado CONSULTAR_NUM_LIBROS en Mysql
1 DELIMITER $$ 2 3 CREATE DEFINER=`root`@`localhost` PROCEDURE `CONSULTAR_NUM_LIBROS`(OUT _num_libros int) 4 BEGIN 5 SELECT COUNT(*) 6 INTO _num_libros 7 FROM LIBROS; 8 9 END 10 $$
- Como se pode ver leva un parámetro de saída (OUT). Indicar que podemos poñer calquera combinación de parámetros na definición dun procedemento almacenado.
- Arquivo bd.inc.php:
- Definimos a chamada ao procedemento cunha variable no seu interior que vai ser a que garde o valor modificado de dentro do procedemento.
1 define('PROCEDEMENTO_CONSULTAR_NUM_LIBROS','CONSULTAR_NUM_LIBROS(@numero_libros)');
- Arquivo Consultar_Num_Libros.php
- Esta páxina está a modo de exemplo. Loxicamente isto podería ser utilizado dentro dunha páxina para obter o número de libros sen necesidade de crear unha páxina específica.
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 13 ?> 14 15 16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 17 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 19 <head> 20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 21 <title>Operacións BD</title> 22 23 </head> 24 25 <body> 26 27 <?php 28 $proc= sprintf("CALL %s",PROCEDEMENTO_CONSULTAR_NUM_LIBROS); 29 if ($conex->multi_query($proc)){ 30 $result = $conex->store_result(); 31 if (!($resultado = $conex->query("SELECT @numero_libros as _p_out"))) { 32 echo "Fallou a obtención: (" . $conex->errno . ") " . $conex->error; 33 } 34 else { 35 $fila = $resultado->fetch_assoc(); 36 printf("<h1>NUMERO DE LIBROS A VENDA:%d",$fila['_p_out']); 37 } 38 } 39 else { 40 echo "Fallou a chamada ao proc.: (" . $conex->errno . ") " . $conex->error; 41 } 42 43 44 $conex->close(); // Pechamos a conexión 45 46 ?> 47 48 49 </body> 50 </html>
Consulta chamando a funcións
- Dende Mysql pódense definir funcións, que son procedementos almacenados que devolven un valor dun tipo de datos.
- Vexamos un exemplo:
1 DELIMITER $$ 2 3 CREATE FUNCTION `EXEMPLO_FUNCION`() RETURNS int 4 BEGIN 5 declare variable int; 6 7 SELECT col1 8 INTO variable 9 FROM TABLA_1; 10 11 return variable; 12 13 14 END
- Como vemos, esta función vai devolver un número de tipo integer.
- Para chamar á función dende Mysql teremos que 'engadila' dentro dunha cláusula SELECT.
1 define('FUNCION_EXEMPLO_MYSQL','select EXEMPLO_FUNCION() as valor_devolto');
- Como vemos, a tratamos coma dunha cláusula SELECT calquera, dándolle un nome (alias) ao resultado da execución da función.
- Agora soamente queda obter o resultado como fixemos no exemplo de consulta, anteriormente visto.
1 <?php 2 3 define('FUNCION_EXEMPLO_MYSQL','select EXEMPLO_FUNCION() as valor_devolto'); 4 5 $conex = new mysqli('SERVIDOR','USUARIO','PASSWORD','BD'); 6 $conex->set_charset('utf8'); 7 8 if ($conex->connect_error){ 9 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 10 } 11 12 ?> 13 14 15 <?php 16 $result=$conex->query(FUNCION_EXEMPLO_MYSQL); 17 if ($result || ($result->num_rows>0)){ 18 $row = $result->fetch_assoc(); 19 printf("<h1>Dato devolto:%d</h1>",$row['valor_devolto']); 20 $result->free(); 21 } 22 else { 23 echo "Fallou a chamada á función: (" . $conex->errno . ") " . $conex->error; 24 } 25 26 27 $conex->close(); // Pechamos a conexión 28 29 ?>
- Nota: Fixarse como na liña 20 a referencia que utilizamos é o alias posto no DEFINE.
- Imos facer un exemplo aplicado ao noso proxecto.
- Imos facer que o número de libros da nosa tenda sexa obtido utilizando unha función e non o procedemento almacenado con parámetro de saída da sección anterior.
- Creamos a función en Mysql
1 DELIMITER $$ 2 3 CREATE FUNCTION `CONSULTAR_NUM_LIBROS`() RETURNS int 4 BEGIN 5 declare num_libros int; 6 7 SELECT COUNT(*) 8 INTO num_libros 9 FROM LIBROS; 10 11 return num_libros; 12 13 14 END
- Aqruivo bd.inc.php
- Definimos a constante que vai chamar á función.
1 define('FUNCION_CONSULTAR_NUM_LIBROS','select CONSULTAR_NUM_LIBROS() as num_libros');
- Fixarse no alias da chamada á función.
- Creamos unha páxina Consultar_Num_Libros_Con_FuncionMYSQL.php
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 //error_reporting(0); 5 6 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 7 $conex->set_charset('utf8'); 8 9 if ($conex->connect_error){ 10 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 11 } 12 13 ?> 14 15 16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 17 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 19 <head> 20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 21 <title>Operacións BD</title> 22 23 </head> 24 25 <body> 26 27 <?php 28 $result=$conex->query(FUNCION_CONSULTAR_NUM_LIBROS); 29 if ($result && ($result->num_rows>0)){ 30 $row = $result->fetch_assoc(); 31 printf("<h1>Número de libros:%d</h1>",$row['num_libros']); 32 $result->free(); 33 } 34 else { 35 echo "Fallou a chamada ao proc.: (" . $conex->errno . ") " . $conex->error; 36 } 37 38 39 $conex->close(); // Pechamos a conexión 40 41 ?> 42 43 44 </body> 45 </html>
Paxinación de resultados
- En moitas ocasións imos necesitar paxinar os resultados dunha consulta.
- Isto é necesario cando o resultado pode devolver unha cantidade moi grande de rexistros que obrigaría ao usuario a facer demasiado scroll.
- A idea é moi simple. Utilizando a cláusula LIMIT da orde SQL Select.
- Dita cláusula pode levar dous parámetros separados por coma: LIMIT numero_fila,tamaño e vai ao final da orde SELECT.
- Por exemplo:
1 SELECT id_libro,titulo 2 FROM LIBROS 3 ORDER by titulo 4 LIMIT 0,2
- Este exemplo amosaría os dous primeiros rexistros empezando polo primeiro do conxunto de resultados (rexistros 0 e 1)
- Se modifico a orde e poño:
1 SELECT id_libro,titulo 2 FROM LIBROS 3 ORDER by titulo 4 LIMIT 2,2
- Devolverá os rexistros 2 e 3 do conxunto de resultados. Estamos paxinando os resultados de dous en dous !!!!
- Polo tanto para paxinar os datos dunha consulta terei que:
- Establecer o número de rexistros por páxina. Isto será o que leve a cláusula LIMIT como segundo dato.
- Gardar a páxina actual e mandar dito dato entre as diferentes peticións da páxina PHP (normalmente enlaces có número de páxina).
- Para saber o número de páxinas terei que obter o número de rexistros en total, xa que o número de páxinas será igual ao número de rexistros dividido (parte enteira) entre o número de rexistros por páxina.
- Aplicado ao noso exemplo, imos engadir máis datos para poder facer unha paxinación.
- Táboa AUTORES
INSERT INTO `PHP`.`AUTORES` (`apelido`, `nome`) VALUES ('ABBOTT', 'RACHEL'); INSERT INTO `PHP`.`AUTORES` (`apelido`, `nome`) VALUES ('HJORTH', 'MICHAEL'); INSERT INTO `PHP`.`AUTORES` (`apelido`, `nome`) VALUES ('LAURENS', 'STEPHANIE'); INSERT INTO `PHP`.`AUTORES` (`apelido`, `nome`) VALUES ('ESTRINGANA', 'MORUENA'); INSERT INTO `PHP`.`AUTORES` (`apelido`, `nome`) VALUES ('NIXON', 'RITA');
- Táboa LIBROS
INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('COMO UNA EXTRAÑA'); INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('CRÍMENES DUPLICADOS'); INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('A SALVO CON SU CAPTOR'); INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('MI ERROR FUE NO SER YO MISMA. PARTE II'); INSERT INTO `PHP`.`LIBROS` (`titulo`) VALUES ('EL AMOR NO ES UN INVENTO DE LOS POETAS');
- Táboa LIBROS_AUTORES
- Teredes que ter en conta os identificadores autonuméricos xerados nos insert´s anteriores.
INSERT INTO `PHP`.`LIBROS_AUTORES` (`libro_id`, `autor_id`) VALUES ('3', '2'); INSERT INTO `PHP`.`LIBROS_AUTORES` (`libro_id`, `autor_id`) VALUES ('4', '3'); INSERT INTO `PHP`.`LIBROS_AUTORES` (`libro_id`, `autor_id`) VALUES ('5', '4'); INSERT INTO `PHP`.`LIBROS_AUTORES` (`libro_id`, `autor_id`) VALUES ('6', '5'); INSERT INTO `PHP`.`LIBROS_AUTORES` (`libro_id`, `autor_id`) VALUES ('7', '6');
- Táboa EXEMPLARES
- Teredes que ter en conta os identificadores autonuméricos xerados nos insert´s da táboa libros.
INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `prezo`, `estado_id`) VALUES ('3', '1', '9,99', '1'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `prezo`, `estado_id`) VALUES ('3', '2', '9,99', '1'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('4', '1', '', '29,99', '1'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `prezo`, `estado_id`) VALUES ('5', '1', '23,35', '1'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('6', '1', '', '34,76', '1'); INSERT INTO `PHP`.`EXEMPLARES` (`libro_id`, `id_exemplar`, `imaxe`, `prezo`, `estado_id`) VALUES ('7', '1', '', '22,54', '1');
- Creamos o procedemento almacenado en Mysql para que devolva os libros con paxinación.
- Poderíamos modificar o que xa tiñamos, comprobando se enviamos valores aos parámetros ou non (valor null) e facendo un select ou outro en función dos valores...
Procedemento almacenado CONSULTAR_LIBROS_PAXINACION en Mysql
1 DELIMITER $$ 2 3 CREATE DEFINER=`root`@`localhost` PROCEDURE `CONSULTAR_LIBROS_PAXINACION`(IN _num_rexistro int,IN _tam_paxina INT) 4 BEGIN 5 SELECT id_libro,titulo 6 FROM LIBROS 7 ORDER BY titulo 8 LIMIT _num_rexistro,_tam_paxina; 9 10 END
- Creamos a constante que vai facer uso deste procedemento.
Arquivo: bd.inc.php
define('PROCEDEMENTO_CONSULTAR_LIBROS_PAXINACION','CONSULTAR_LIBROS_PAXINACION(%d,%d)');
- Como vemos imos enviar dous parámetros.
- Partimos do código da páxina Consultar_Libros.php e creamos unha nova páxina de nome Consultar_Libros_Con_Paxinas.php
- Arquivo: Consultar_Libros_Con_Paxinas.php
1 <?php 2 3 require ('/var/www/confBD/bd.inc.php'); 4 define('CONST_NUM_LIBROS_PAXINA',2); 5 //error_reporting(0); 6 7 $conex = new mysqli(SERVIDOR,USUARIO,PASSWORD,BD); 8 $conex->set_charset('utf8'); 9 10 if ($conex->connect_error){ 11 die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); 12 } 13 ?> 14 15 16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 17 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> 19 <head> 20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 21 <title>Operacións BD</title> 22 </head> 23 24 <body> 25 26 <?php 27 // OBTEMOS O NUMERO DE LIBROS 28 $numLibros=0; 29 $proc= sprintf("CALL %s",PROCEDEMENTO_CONSULTAR_NUM_LIBROS); 30 if ($conex->multi_query($proc)){ 31 $result = $conex->store_result(); 32 if (!($resultado = $conex->query("SELECT @numero_libros as _p_out"))) { 33 die ("Fallou a obtención: (" . $conex->errno . ") " . $conex->error); 34 } 35 else { 36 $fila = $resultado->fetch_assoc(); 37 $numLibros = $fila['_p_out']; 38 } 39 } 40 else { 41 die("Fallou a chamada ao proc.: (" . $conex->errno . ") " . $conex->error); 42 } 43 ?> 44 <?php 45 // AMOSAMOS AS PAXINAS EN BASE AO NUMERO TOTAL E O NUMERO POR PAXINA 46 $total_paxinas = ceil($numLibros/CONST_NUM_LIBROS_PAXINA); 47 48 // Establecemos cal é a páxina actual. Se o usuario elixiu algunha virá en forma de URL $_GET 49 if(!empty($_GET['paxina'])){ 50 $paxina_actual=$_GET['paxina']; 51 } 52 else{ 53 $paxina_actual=1; // A primeira vez o usuario non pulsou ningunha páxina e polo tanto a páxina inicial é a 1 54 } 55 56 // 'Imprimimos as páxinas... 57 echo "<div>"; 58 for($cont=1;$cont<=$total_paxinas;$cont++){ 59 if ($cont==$paxina_actual){ // A páxina actual non leva link 60 echo $cont . ' '; 61 } 62 else{ 63 printf("<a href='%s?paxina=%d'>%d</a> ",$_SERVER['PHP_SELF'],$cont,$cont); 64 } 65 } 66 echo "</div>"; 67 ?> 68 <?php 69 $rexistro_actual=($paxina_actual-1)*CONST_NUM_LIBROS_PAXINA; // Restamos 1 á páxina actual xa que a páxina 1 empeza no rexistro 0 70 $proc=sprintf(PROCEDEMENTO_CONSULTAR_LIBROS_PAXINACION,$rexistro_actual,CONST_NUM_LIBROS_PAXINA); 71 $proc = sprintf("CALL %s",$proc); 72 if ($conex->multi_query($proc)){ 73 $result = $conex->store_result(); 74 if ($result->num_rows>0){ 75 echo "<table border='1'>"; 76 while($row = $result->fetch_assoc()) { 77 echo '<tr>'; 78 printf("<td>%s</td>",$row["titulo"]); 79 echo '</tr>'; 80 } 81 echo '</table>'; 82 $result->free(); // Liberamos da memoria os recursos 83 } 84 else { 85 echo "Non hai datos que amosar!!!!"; 86 } 87 } 88 89 $conex->close(); // Pechamos a conexión 90 91 92 ?> 93 </body> 94 </html>
- Liña 4: Indicamos o número de libros a amosar en cada páxina.
- Liñas 27-42: Obtemos o número de libros que temos na base de datos. Estamos a aproveitar o código dun exercicio anterior no que amosábamos como traballar cun parámetro de saída, pero neste caso sería máis sinxelo crear un novo procedemento ou unha función, que devolvera o número de libros.
- Liñas 45-66: Amosamos as páxinas en base ao número de rexistros totais e o número de libros a amosar por páxina.
- Liñas 69-70: Pasamos ao procedemento almacenado a partires de que número de libro e a cantidade de libros que vai devolver o select no procedemento almacenado.
Alta
Baixa
Modificación
Transaccións
Permisos mínimos
-- Ángel D. Fernández González -- (2016).
