Lecciones de PHP - Lección 3.5 - Trabajo con la base de datos MySQL. Operador JOIN. Carga de archivos al servidor.
Antes de comenzar a escribir esta lección, pensé mucho en cómo enseñar mejor las consultas con operadores JOIN. La cuestión es que el operador JOIN se utiliza para hacer selecciones desde varias tablas al mismo tiempo. Y como se necesita otra tabla, vamos a crearla. Propongo crear una tabla para los archivos que cargaremos a través del formulario ya en esta lección. Será una lección sobre dos temas diferentes: trabajo con base de datos y trabajo con formularios.
Empecemos agregando un campo para subir archivos. Para que el formulario pueda subir archivos, debemos añadir un tipo de formulario en sus parámetros:
$content .= '<form action="' . $_SERVER['PHP_SELF'] . '" method="post" enctype="multipart/form-data">';
De esta forma, mediante el parámetro enctype
, le indicamos al navegador que este formulario será utilizado para cargar archivos. Ahora que el formulario está preparado, agreguemos el campo para cargar el archivo:
public function display_admin() { $content = ''; $content .= '<form action="' . $_SERVER['PHP_SELF'] . '" method="post" enctype="multipart/form-data">'; $content .= '<label for="title">Nombre:</label><br />'; $content .= '<input name="title" id="title" type="text" maxlength="150" />'; $content .= '<div class="clear"></div>'; $content .= '<label for="bodytext">Mensaje:</label><br />'; $content .= '<textarea name="bodytext" id="bodytext"></textarea>'; $content .= '<input type="file" name="filename">'; $content .= '<div class="clear"></div>'; $content .= '<input type="submit" value="Agregar mensaje" />'; $content .= '</form>'; $content .= '<p><a href="/index.php">Volver al inicio</a></p>'; return $content; }
Con el input de tipo file
cargaremos nuestro archivo. Si guardas los archivos y actualizas la página de agregar mensaje, deberías ver el campo para subir el archivo.
Ahora veamos cómo se transfiere el archivo en la solicitud POST.
public function write($p) { print_r($p); print_r($_FILES); $sql = 'INSERT INTO Messages (title, bodytext, created) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ')'; return mysql_query($sql); }
Ahora subimos algún archivo y guardamos el mensaje. Todos los archivos que se suban a través del formulario estarán disponibles en la variable superglobal $_FILES
. Yo subí un archivo y obtuve los siguientes arrays:
Array ( [title] => asfasdf [bodytext] => asfasdf ) Array ( [filename] => Array ( [name] => ip.txt [type] => text/plain [tmp_name] => Y:\tmp\phpAA.tmp [error] => 0 [size] => 13 ) )
Ahora que tenemos el array de datos y archivos, creemos una tabla para guardar los archivos subidos. Empezamos por crear la carpeta files
en la raíz del sitio, y si estás en un sistema tipo Linux o en un hosting, dale permisos 777 a esa carpeta para que el script pueda guardar archivos en ella.
Modificamos la consulta para crear las tablas en el método buildDB()
:
$sql = "CREATE TABLE Messages ( mid int NOT NULL AUTO_INCREMENT, PRIMARY KEY(mid), title varchar(15), bodytext text, created int(11), file int(11) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; CREATE TABLE Files ( fid int NOT NULL AUTO_INCREMENT, PRIMARY KEY(fid), filename varchar(255), filepath varchar(255), filemime varchar(255), filesize int(10), timestamp int(10) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;";
Además de agregar una nueva tabla, también agregamos el campo fid
, que será la clave externa hacia la tabla de archivos. Para la nueva tabla Files
tendremos los siguientes campos:
- fid – clave primaria, número del archivo.
- filename – nombre del archivo.
- filepath – ruta al archivo desde la raíz del sitio.
- filemime – tipo de archivo, para saber la extensión y uso (por ejemplo
text/plain
para texto,image/jpg
oimage/png
para imágenes). - filesize – tamaño del archivo en bytes.
- timestamp – fecha y hora de carga del archivo.
Ahora escribamos el código para guardar el archivo y hacer el INSERT en la base de datos. Puedes crear los campos necesarios mediante phpMyAdmin.
if($_FILES["filename"]["size"] > 1024*3*1024) { echo ("El archivo supera los tres megabytes"); exit; } if(is_uploaded_file($_FILES["filename"]["tmp_name"])) { move_uploaded_file($_FILES["filename"]["tmp_name"], "/files/".$_FILES["filename"]["name"]); } else { echo("Error al subir el archivo"); }
De este modo, guardamos el archivo en la carpeta files
. Luego de subir exitosamente el archivo, hacemos el INSERT en la tabla Files
:
$sql = 'INSERT INTO Files (filename, filepath, filemime, filesize, timestamp) VALUES ("'. $_FILES['filename']['name'] . '", "files/' . $_FILES['filename']['name'] . '", "'. $_FILES['filename']['type'] .'", '. $_FILES['filename']['size'] .', '. time() . ')'; mysql_query($sql);
Ahora que insertamos el archivo en la base de datos, también insertamos el mensaje. Cambiamos la consulta agregando el campo fid
:
$sql = 'INSERT INTO Messages (title, bodytext, created, fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID())';
Nota la función LAST_INSERT_ID()
, propia de MySQL, que devuelve el último ID insertado en cualquier tabla. En este caso, se usará el ID del archivo recién insertado en Files
.
Ahora los datos se insertan correctamente. Solo falta mostrarlos usando JOIN
. Modificamos la consulta para que también devuelva el nombre del archivo:
public function display_public() { $content = ''; $sql = 'SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid ORDER BY mid DESC'; $result = mysql_query($sql) or die(mysql_error()); ...
Utilizamos el operador JOIN
con LEFT
. Más adelante exploraremos los tipos de JOIN, pero por ahora ten en cuenta que LEFT JOIN muestra todos los registros, incluso aquellos sin fid
.
Si imprimimos el array de resultados:
while($row = mysql_fetch_array($result)) { print_r($row);
Veremos que se muestran datos tanto de Messages
como de Files
. Si no hay archivo relacionado, los campos estarán vacíos. Por eso, hay que comprobar si están vacíos:
if(!empty($row['filename'])) { $content .= '<p>Adjunto: <a target="_blank" href="/'. $row['filepath'] .'">'. $row['filename'] .'</a></p>'; }
Ahora el mensaje se mostrará con un adjunto en forma de enlace al archivo cargado. En la siguiente lección exploraremos más a fondo el operador JOIN, que permite combinar datos de múltiples tablas. Desde ahora en adelante, incluiré también un volcado actual de la base de datos realizado desde phpMyAdmin.