Como vimos cuando hicimos la web estática una de las páginas que creamos era una entrada de blog. Si quisiéramos añadir otra entrada necesitaríamos crear otra página similar y para listar todos los artículos deberíamos enlazarlos uno a uno, lo cuál no resulta práctico sobre todo cuando el blog creciera. Una alternativa es la gestión del blog mediante base de datos que te permite generar las entradas del blog de forma dinámica y crear listados de artículos mediante bucles en PHP.
Tabla de contenidos,
Mejorando el diseño del blog
El diseño inicial del blog era muy básico, había otras prioridades como la gestión del formulario de contacto, el CSS, el JavaSript y los plugins JQuery como Venom Button WhatsApp y jQuery Floating Social Share.
En lugar de una imagen a la derecha con el texto superpuesto como en la portada, he preferido que la imagen principal ocupe todo el frontal. Crear una caja central para todo el artículo del blog que se superponga en el centro de la foto en su parte inferior. Unas bandas laterales y la categoría con un fondo del color destacado de la web le da a la página un diseño que encaja con el resto de la web.
Hemos añadido al código html
<div class="row gx-5 gx-lg-10 caja "> <h4>Inteligencia Artificial</h4> </div> <div class="franja"></div>
Y en CSS hemos añadido las reglas siguientes:
.post-section-header .post-image { position: absolute; right: 0; top: 200px; width: 100%; height: 100%; z-index: -1; } .post-section-header .post-image img { width: 100%; height: 100%; object-position: center; object-fit: cover; } .franja{ height: 150px; background:#DB0EB7; } .caja{ height: 75px; width: 300px; background:#DB0EB7; margin-left: 737px; margin-top: 440px; padding-bottom: 85px; }
Y reglas similares para la versión móvil ajustando los valores de margin y padding.
Creación de una tabla en la base de datos para el blog
En nuestra base de datos aprendizajeprofundo añadimos una nueva tabla que llamamos post.
El campo author va a ser una clave externa, foreign key, por lo que sus valores han de ser únicos. Para poder establecer la relación es importante que tanto la clave primaria de la tabla usuarios como la clave externa de la tabla posts tengan el mismo tipo y cotejamiento.
Un usuario puede ser author de varios posts; pero los posts solamente puede tener un usuario como author. Es una relación 1:N entre usuarios y posts.
En phpMyAdmin las relaciones entre tablas se crean seleccionando la base de datos y la pestaña Diseñador.
De momento los artículos los cargaremos manualmente en la base de datos a través de phpMyAdmin, una mejora futura de la web será crear un editor para cargar la tabla post directamente con los nuevos artículos.
Plantilla de artículos del blog que los lea de la base de datos
Nos basamos en la página que teníamos con el artículo que ya hemos cargado en la base de datos. Al principio añadimos el código PHP para cargar los valores de la base de datos:
<?php session_start(); require_once 'conectar.php'; $sql = "SELECT * FROM posts WHERE url = '".$_GET["post_id"]."'"; $statement = $db->prepare($sql); $statement->execute(); $fila = $statement->fetch(); if ($fila['url'] == ""){ header("Location: index.php"); exit(); } $titulo = $fila['title']; $content = $fila['content']; $category = $fila['category']; $img = $fila['featured_img']; ?>
La llamada a un artículo será https://aprendizajeprofundo.es/post.php?post_id= junto con la url que tenemos almacenada en la base de datos y que será diferente para cada artículo. Por ejemplo para el primer artículo del blog:
https://aprendizajeprofundo.es/post.php?post_id=aproximacion-verdadera-inteligencia-artificial
¿Qué ocurre si se introduce una url que no está en la base de datos? Para evitar un error 404, hemos redirigido a la página principal de la web:
if ($fila['url'] == ""){
header("Location: index.php");
exit();
}
Como el título lo hemos guardado en la base de datos con parte del mismo entre <span></span> para darle el color destacado mediante css, hemos de eliminar las etiquetas para crear el título de la página del artículo:
<?php
$title = str_replace("</span>", "", str_replace("<span>", "", $titulo));
include("assets/templates/head.php");
?>
Con la función str_replace() de PHP aplicada dos veces, eliminamos <span> y </span>.
Para el menú de navegación incluimos su plantilla como hemos hecho en el resto de las páginas:
<nav class="navbar navbar-expand-lg">
<?php
include("assets/templates/menu.php");
?>
</nav>
El <h1> de la página con parte color destacado sería
<h1><?php echo $titulo ?></h1> Y el contenido de la página quedaría así: <div class="row gx-5 gx-lg-10 justify-content-center"> <div class="row gx-5 gx-lg-10 caja "> <h4><?php echo $category ?></h4> </div> <div class="post-content col-md-10 col-lg-10 col-xl-10"> <?php echo $content ?> <div class="post-image"> <img src=<?php echo "assets/img/blog/".$img ?> alt="Imagen principal del artículo"> <div class="franja"></div> </div> </div>
Y con el pie de página tengo la página de artículo del blog completa:
<footer class="footer">
<?php
include("assets/templates/footer.php");
?>
</footer>
Urls amigables modificando .htaccess
Una url del tipo post.php?post_id= no es lo más adecuado para el SEO, aunque peor sería si en vez la url que inclumos en la base de datos después del id pusiéramos un número. Para el SEO es mejor que las palabras clave estén más cerca del nombre de dominio. Lo que buscamos es una url como ésta:
https://aprendizajeprofundo.es/aproximacion-verdadera-inteligencia-artificial
La buena noticia es que contamos con el fichero .htaccess que permite configurar en un servidor Apache directivas de configuración. En nuestro caso conseguimos escribiendo en dicho fichero:
Options +FollowSymLinks
RewriteEngine on
RewriteRule ^([0-9a-zA-Z-]+)$ post.php?post_id=$1
RewriteRule ^([0-9a-zA-Z-]+).php $1.php
Es importante el orden de las dos regla RewriteRule, ya que la segunda sobreescribe la primera. Si el orden estuviera cambiado no podríamos ejecutar ficheros .php como contacto.php.
Página de artículos
Vamos a crear una página que lea la base de datos de posts y los presente con la foto, el título, el autor, la fecha, la categoría y un resumen. Cada artículo tendrá un botón que dará acceso al contenido completo. La gracia de esta página está en que se actualizará sola cada vez que añadamos un artículo a la base de datos.
Cómo queremos obtener el nombre del autor que está en la tabla usuarios el SELECT lo vamos a realizar uniendo la tabla posts con la de usuarios a través del campo usuario en usuarios y el campo author en posts. Los resultados los queremos en orden descendente para tener primero los artículos más recientes.
<?php
session_start();
require_once 'conectar.php';
$sql = "SELECT * FROM usuarios INNER JOIN posts ON usuarios.usuario = posts.author ORDER BY posts.id DESC";
$statement = $db->prepare($sql);
$statement->execute();
while ($fila = $statement->fetch()) {
$filas[] = array(
"author" => $fila['author'],
"date" => $fila['date'],
"url" => $fila['url'],
"title" => $fila['title'],
"excerpt" => $fila['excerpt'],
"featured_img" => $fila['featured_img'],
"category" => $fila['category'],
"nombre" => $fila['nombre']);
}
?>
Para presentar los resultados utilizamos un bucle for para recorrer el array $filas[] donde hemos almacenado el contenido de la base de datos.
<div class="row">
<?php for ($i = 0;
$i < sizeof($filas);
$i++) {
$d = "assets/img/blog/";
$fichero_foto = $d . $filas[$i]['featured_img'];
?>
<div class="col-12 col-lg-4 col-xl-4">
<div class="section-title mt-20">
<img class="featured" width="400px" src="<?php echo $fichero_foto; ?>"
alt="Foto del artículo">
<div class="mt-20"></div>
<h3><?php echo str_replace("</span>", "", str_replace("<span>", "", $filas[$i]['title'])); ?></h3>
<p>
<strong><?php echo $filas[$i]['nombre'] . " "; ?></strong><?php echo date("d/m/Y", strtotime($filas[$i]['date'])); ?>
</p>
<br>
<div class="caja-blog">
<p><?php echo $filas[$i]['category']; ?></p>
</div>
<div class="card">
<div class="card-body">
<p><?php echo $filas[$i]['excerpt']; ?></p>
</div>
</div>
<div class="mt-20">
<a href=<?php echo $filas[$i]['url']; ?>
class="main-btn btn-hover">Leer
más</a>
</div>
</div>
</div>
<?php } ?>
</div>
No termina de convencerme el diseño por lo que voy a hacer uno alternativo. Cambio el orden de los elementos, pensando también en lo más adecuado para la versión móvil. Pongo primero el título y le pongo el color destacado. Luego el autor del artículo y la fecha, la foto y para terminar la categoría con el resumen. Elimino el botón leer más y hago pulsables el título, la foto y el resumen. Esto es importante en la versión móvil, que había que bajar bastante por la pantalla para pulsar el botón y ponerlo más arriba no quedaba bien en el diseño. También pensando en la versión móvil pongo un borde que rodea todo lo relacionado con el mismo artículo.
El código sería:
<div class="col-12 col-lg-4 col-xl-4">
<div class="section-title mt-20 border">
<a href=<?php echo $filas[$i]['url']; ?>>
<h2><?php echo str_replace("</span>", "", str_replace("<span>", "", $filas[$i]['title'])); ?></h2>
</a>
<p>
<strong><?php echo $filas[$i]['nombre'] . " "; ?></strong><?php echo date("d/m/Y", strtotime($filas[$i]['date'])); ?>
<div class="mt-20">
<a href=<?php echo $filas[$i]['url']; ?>>
<img class="featured" width="390px" src="<?php echo $fichero_foto; ?>"
alt="Foto del artículo">
</a>
</div>
</p>
<br>
<div class="caja-blog">
<p><?php echo $filas[$i]['category']; ?></p>
</div>
<a href=<?php echo $filas[$i]['url']; ?>>
<div class="card">
<div class="card-body">
<p><?php echo $filas[$i]['excerpt']; ?></p>
</div>
</div>
</a>
</div>
</div>
Sección de blog de la portada
En la portada mostrábamos el primer artículo que tuvimos en el blog y además esta puesto directamente. Lo suyo es que se lea desde la base de datos y vaya cambiando cuando se publica un artículo nuevo. Vamos aprovechar para mejorar el formato y añadir un segundo artículo. Vamos a mostrar en la portada los dos últimos artículos que se han publicado en el blog.
Al principio de index.php realizamos una consulta a la base de datos. Usamos LIMIT 2 para que solamente carguemos los dos últimos registros. Si tuviéramos muchos artículos no tendría sentido cargar toda la base de datos para solamente usar dos artículos.
<?php session_start();
require_once 'conectar.php';
$sql = "SELECT * FROM usuarios INNER JOIN posts ON usuarios.usuario = posts.author ORDER BY posts.id DESC LIMIT 2";
$statement = $db->prepare($sql);
$statement->execute();
while ($fila = $statement->fetch()) {
$filas[] = array(
"author" => $fila['author'],
"date" => $fila['date'],
"url" => $fila['url'],
"title" => $fila['title'],
"excerpt" => $fila['excerpt'],
"featured_img" => $fila['featured_img'],
"category" => $fila['category'],
"nombre" => $fila['nombre']);
}
$d = "assets/img/blog/";
?>
La sección de blog quedaría así :
<!-- ========================= blog-section end ========================= --> <section id="blog" class="blog-section pt-20 pb-70"> <div class="container"> <div class="row justify-content-center"> <div class="col-lg-6 col-md-10"> <div class="section-title text-center mb-60"> <h2>Blog <span></span></h2> </div> </div> </div> <div class="row justify-content-center"> <div class="col-lg-6 col-md-6 col-sm-12"> <div class="single-blog"> <div class="image"> <div class="image"> <a href=<?php echo $filas[0]['url']; ?>><img src=<?php echo $d . $filas[0]['featured_img']; ?> alt=<?php echo str_replace("</span>", "", str_replace("<span>", "", $filas[0]['title'])); ?></h3></a> </div> <div class="content"> <h5> <a><?php echo str_replace("</span>", "", str_replace("<span>", "", $filas[0]['title'])); ?></h3></a> </h5> <a href=<?php echo $filas[0]['url']; ?>><p><?php echo $filas[0]['excerpt']; ?></p></a> <a href=<?php echo $filas[0]['url']; ?> class="main-btn btn-hover">Leer más</a></p> </div> </div> </div> </div> <div class="col-lg-6 col-md-6 col-sm-12"> <div class="single-blog"> <div class="image"> <a href=<?php echo $filas[1]['url']; ?>><img src=<?php echo $d . $filas[1]['featured_img']; ?> alt=<?php echo str_replace("</span>", "", str_replace("<span>", "", $filas[1]['title'])); ?></h3></a> </div> <div class="content"> <h5> <a><?php echo str_replace("</span>", "", str_replace("<span>", "", $filas[1]['title'])); ?></h3></a> </h5> <a href=<?php echo $filas[1]['url']; ?>><p><?php echo $filas[1]['excerpt']; ?></p></a> <a href=<?php echo $filas[1]['url']; ?> class="main-btn btn-hover">Leer más</a></p> </div> </div> </div> </div> </div> </section>
Además del botón leer más, se puede pulsar la foto y el resumen para acceder al artículo. Por accesibilidad en el alt de las imágenes se pone automáticamente el título del artículo.
Para darle formato uso el siguiente css:
/*===========================
05.BLOG css
===========================*/
.blog-section {
background: #F5F5F5;
}
.blog-section .single-blog {
position: relative;
margin-bottom: 30px;
}
.blog-section .single-blog .image img {
width: 100%;
}
.blog-section .single-blog .content {
position: relative;
width: calc(100% - 20px);
margin: -60px auto auto;
z-index: 2;
background: #fff;
padding: 30px;
}
@media only screen and (min-width: 768px) and (max-width: 991px) {
.blog-section .single-blog .content {
padding: 30px 20px;
}
.featured_img{
width: 300px;
}
}
.blog-section .single-blog .content h5 a {
font-weight: 900;
margin-bottom: 15px;
color: #01012C;
}
.blog-section .single-blog .content h5 a:hover {
color: #DB0EB7;
}
.blog-section .single-blog .content p {
font-size: 16px;
line-height: 26px;
margin-bottom: 18px;
}
.blog-section .single-blog .content a {
font-size: 18px;
color: white;
}
.blog-section .single-blog .content a:hover {
color: #DB0EB7;
}
En la web quedaría así:
En el móvil se vería así:
PageSpeed Insights de Aprendizaje Profundo
Cuando empecé el proyecto hice una análisis de rendimiento que obtuvo una puntuación alta. Tras añadir código PHP y acceder a la base de datos para el blog es previsible que el rendimiento haya empeorado, por lo que vamos a medirlo de nuevo.
En el móvil llegamos a tener 90 puntos y ahora hemos bajado a 82, hemos bajado 8 puntos; pero sigue siendo una buena puntuación:
En escritorio partíamos de 98 puntos, una cifra espectacular que prácticamente he conseguido mantener ya que la puntuación actual es 97:
También hemos ido realizando mejoras de usabilidad y accesibilidad que no se reflejan en esta herraminta.
Resumen final
Desde que hicimos la web estática, hemos ido realizando cambios en la web que resumo a continuación.
En el artículo Formulario PHP realicé las siguientes tareas:
- Cambiamos una página html a PHP
- Creamos templates para la cabecera, el menú y el pie de pagina
- Cambié la recogida de datos del formulario de JavaScript a PHP
- Envío de un email con los datos que yo recibo
En el artículo Gestión de usuarios en PHP hice lo siguiente:
- Creación de un formulario de registro en el que se carga una foto de perfil
- Creación de la base de datos para la gestión de usuarios
- Conexión a la base de datos
- Inserción de un registro en la base de datos
- El archivo de la foto de perfil se almacena en un directorio del hosting y el nombre en la base de datos
- Acceso de usuarios registrados
- Creación de sesión del usuario
- Cambios en el menú para que sea diferente si has iniciado sesión
- Cierre de sesión
- Borrar un registro de la base de datos
- Modificar Perfil y modificar el registro correspondiente en la base de datos
- Cambiar Contraseña y almacenamiento del nuevo hash
- Creación del Rol de administrador y creación de un menú distinto para él
- Creación de una página de usuarios donde se ve el contenido de la base de datos, solamente accesible para el Rol de administrador.
En este artículo hemos realizado las mejoras relativas al uso del blog que incluye:
- Creación de la tabla posts para guardar los artículos del blog con una relación N:1 con la tabla usuarios.
- Plantilla de artículos del blog, mejorando el diseño, para usar por todos los artículos. Se lee cada artículo de la base de datos y se compone en PHP.
- Urls amigables para los artículos del blog modificando .htaccess
- Página de artículos donde se recogen todos los registros de la tabla posts y se muestran en una página de blog generada en PHP
- Modificamos el diseño de la sección blog de la portada, y sustituimos el artículo fijo por los dos últimos artículos almacenados en la base de datos (los más recientes.
Otros artículos publicados en este blog relativos al proyecto son:
Tengo pensado mejoras futuras para el blog entre las que se incluyen:
- Permitir que el administrador desde la página donde se listan todos los usuarios pueda modificar el rol de éstos
- La creación del rol de editor y de un formulario solamente accesible por los editores para subir un nuevo artículo al blog
- Crear una nueva tabla en la base de datos para almacenar el contenido de las páginas que se mostrarían mediante una plantilla que la usaríamos para crear una página para cada servicio ofrecido en la web
- Creación de páginas para realizar demostraciones de Machine Learning y Deep Learning usando TensorFlow. js y PyScript, en el primer caso para programar en JavaScript y en el segundo para hacerlo en Python.
Espero que este artículo así como los relacionados te haya resultado interesante. Quedo a tu disposición para responderte tus dudas y ayudarte en tu propio trabajo. Déjame un comentario y te responderé lo antes posible. Hasta pronto.