2023-02-12
TutorialesRecientemente, estuve revisando un sitio hecho en WordPress y empecé a pensar en todos los módulos que generalmente carga WordPress para servir una página, creo que a todos nos ha pasado que con el tiempo tu sitio empieza a hacerse más y más lento. Claro, WordPress es muy bueno si sabes lo suficiente de él como para tunearlo en performance y ese no es mi caso.
Bueno, entonces pensé en como usar JAM Stack y WordPress para generar un sitio estático y poderlo servir desde algún CDN. Si no lo sabían JAM viene JavaScript API Markdown y es un stack muy útil para generar sitios estáticos y escribir aplicaciones web usando precisamente Markdown y JavaScript y la parte de API entra cuando necesitas cargar contenido dinámico, por ejemplo los comentarios de un post.
Creo que hay varios plugins que hacen esto, pero decidí crea mi propio servicio para hacer esto. La idea final es generar un sitio de noticias con una página de inicio, cuatro secciones que serían cuatro categorías de noticias diferentes y por supuesto los posts de noticias.
La página de inicio tiene un layout particular y las cuatro secciones tiene exactamente la misma estructura al igual que los posts, todos tienen la misma estructura.
Bueno, los pasos para generar el sitio son los siguientes:
Ahora vamos a meterle un poco más de detalle a cada uno de los pasos empezando con el segundo porque escribir un post no es nada técnico. Ok, ¿cómo hago para que al salvar se mande un request a mi servicio? Pues un plugin de WordPress. Así que arme mi plugin. WordPress provee hooks así que definí los siguientes:
//Se ejecuta al crear o actualizar un post
add_action('post_updated', 'update_jam_post', 10, 3);
//Se ejecuta al eliminar un post
add_action('trashed_post', 'delete_jam_post');
//Para publicar post que fueron agendados
add_action('transition_post_status', 'publish_future_post', 10, 3);
//Para que el preview del post no sea el default de WP
add_filter('preview_post_link', 'preview_filter', 10, 3);
El primer parámetro nos dice que hook usamos, el segundo es el nombre de la función que tiene que ejecutar. Los dos últimos parámetros, la verdad, no tengo muy claro que hacen, pero son los valores que encontré en la mayoría de ejemplos de la documentación, si alguno sabe y me lo aclara !hermoso!.
Cada función hacía algo en específico, el update_jam_post recoge datos extras en información extra que mi servicio necesita para generar el sitio, cómo post relacionados, información de la imagen asociada al post.
function update_jam_post($post_ID, $post_after, $post_before) {
if( $post_after->post_status == 'publish'
|| ($post_before->post_status == 'publish'
&& $post_after->post_status == 'publish')) {
$body = get_post_information($post_after);
execute_request($body, 'POST', '/posts');
} elseif ($post_before->post_status == 'publish' &&
$post_after->post_status == 'draft') {
delete_jam_post($post_ID);
}
}
function get_post_information (WP_Post $post) {
$cats = $post->__get("post_category");
$categories = buiild_categories($cats);
$tags = wp_get_post_tags($post->ID);
$featuredImageId = get_post_thumbnail_id($post->ID);
$mediaDetails = build_media_details($featuredImageId);
$excerpt = get_the_excerpt($post->ID);
$body = '{ "id":'. $post->ID.',
"content": '.json_encode($post->post_content).',
"date": "'.$post->post_date.'",
"modified":"'.$post->post_modified.'",
"title":"'.$post->post_title.'",
"categories": '.json_encode($categories).',
"tags":'.json_encode($tags).',
"featuredMedia": '.json_encode($featuredImageId).',
"featureMediaInfo":'.json_encode($mediaDetails).',
"slug":"'.$post->post_name.'",
"excerpt": '.json_encode($excerpt).',
"relatedPosts":'.json_encode(fetch_related_posts($cats, $post->ID)).',
"interestingPosts":'.json_encode(fetch_interested_posts($post->ID)).'
}';
return $body;
}
Ok, entonces ya tenemos nuestro plugin codeado, ¿cómo hago para que funcione o para que WP lo use? Bueno, es súper fácil, solo tienes que colocar tu plugin dentro de la ruta wp-plugins/nombre-de-tu-plugin/nombre-de-tu-plugin.php. Listo, eso va a funcionar a menos que tengas un error en el código, pero bueh!.
Ya matamos el paso 2, vamos con el tercero. Aquí está el centro del asunto y podemos decir que hay tres pasos internos, el primero es atender el request que viene de nuestro plugin. El segundo es generar el sitio y finalmente el tercero es subirlo a un CDN. Vamos por partes, entonces, empezamos con el primero, ¿cómo atendemos el request? Cómo es un request HTTP hice un API en node con una capa de aplicación, digamos, que tiene unos controllers, luego tendríamos un par de servicios para generar data que finalmente la usaría el site generator que escojas.
Evidentemente primero tienes que crear tu proyecto en Node e inicializarlo con algunas dependencias, en mi caso use Express .
Los controllers son los siguientes:
Los servicios son estos otros:
Aquí los servicios se apoyan en otro par de componentes que hacen uso de otrass APIs para terminar de obtener la data de los posts, esto depende de tu caso particular, así que no voy a dar mucho detalle de eso.
Buenísimo. Ya tenemos los datos necesarios para construir el sitio y todo hermoso, pero, ¿cómo construimos ese sitio? Ok, aquí entra el site generator, yo utilice 11ty que es muy simple de usar y tienen un API en JavaScript. La gran mayoría de estos site generators funcionan igual, primero defines la maqueta de tu sitio utilizando algún template engine como nunjucks o incluso HTML, luego defines la data para generar las páginas del sitio que puede ser archivos JSON, JavaScrtip o incluso puedes usar archivos Markdown con el contenido de los posts, de hecho, este sitio y sus posts son archivos markdwon y es generado usando 11ty.
Para 11ty lo instalamos y configuramos:
npm install @11ty/eleventy --save-dev
Y para configurarlo, en la raíz de tu proyecto necesitas crear un archivo .eleventy.js asi:
module.exports = function (config) {
return {
dir: {
input: 'views', //ruta donde viven tus templates
output: 'public', //ruta donde 11ty va a generar tu sitio
data: '../data', //ruta donde 11ty va a buscar la data para generar el sitio
tempalteFomrats: ['njk'],//template engine que estás usando.
},
};
};
Para hacer notar acá, el valor de data en la configuración es el mismo que usan los servicios postService.js y homeSerice.js para escribir los archivos de datos porque de lo contrario no se genera el sitio.
Una vez que está todo esto configurado, tengo en mi ruta input los templates con la "maqueta" de mi sitio y mis datos en la ruta data solo queda correr 11ty para generar el sitio. Para esto tenemos dos formas, la primera es manual, desde una terminal te paras en la raíz de tu proyecto y corres:
npx @11ty/eleventy
No es la mejor opción porque tendrías que correr manualmente cada vez que se actualiza un post. La segunda es usar el API de 11ty. Así es cómo lo harás:
const Eleventy = require('@11ty/eleventy');
const generate = async () => {
const eleventy = new Eleventy();
eleventy.init();
await eleventy.write();
return true;
};
module.exports = {
generate,
};
Así puedo hacer que uno de los servicios postService.js o homeService.js genere el sitio.
Finalmente así fue que pude hacer este proceso de crear un sitio estático usando WordPress, Node y 11ty, espero les haya gustado. El repo de este proyecto es https://github.com/AnfferCastillo/wp-jam-site