Cómo hacer que Wordpress sea más emocionante con la API de Wordpress, ACF y Express.js

He estado trabajando con Wordpress desde su proliferación como sistema de gestión de contenido. Apenas me emociono cuando los clientes o compañeros de trabajo ya lo mencionan. "Encontré la luz" en marcos más sólidos y aprendí mucho más sobre las diferentes partes de las aplicaciones web personalizadas.

Entonces, en un esfuerzo por rejuvenecer mi pasión por Wordpress, comencé a buscar diferentes formas de implementar el marco. Una de esas formas es separar el front-end del back-end y evitar algunos de los puntos débiles de usar las etiquetas de plantilla de Wordpress y el sistema de temas. Vamos a ver.

Aplicaciones monolíticas frente a aplicaciones distribuidas

Wordpress es un marco monolítico, lo que significa que las diferentes partes del marco (base de datos, almacenamiento de archivos, estructura de presentación y archivos de activos, archivos de lógica empresarial) están todos empaquetados. Esta es una gran parte de la razón por la que Wordpress es tan fácil de poner en marcha. Instale MAMP, copie los últimos archivos de Wordpress, cree una base de datos y cambie el wp-config.phparchivo. Bueno para ir.

Vamos a ir en contra de la convención monolítica y dividir este sitio de Wordpress en dos partes diferentes: front-end y back-end, presentación y administración.

Usaremos Wordpress para la administración de datos de nuestra aplicación y aprovecharemos un complemento para ayudar con la creación y administración de atributos (campos) para nuestro tipo de publicación personalizada. Por el lado de la presentación, vamos a renunciar a un tema por completo y consumiremos puntos finales de API de una aplicación Express.js.

Ejemplo

En este ejemplo, vamos a crear una lista de productos simple. La idea es que ya tenga un sitio web impulsado por Wordpress y le gustaría administrar una lista de productos a la venta a través de la misma interfaz. Pero quieres crear un sitio web completamente diferente para la tienda.

API de Wordpress

Desde la versión 4.7, Wordpress expone automáticamente sus publicaciones publicadas (y otros datos) a través de su API REST, presentada en formato JSON. Si ha desarrollado un sitio web con Wordpress 4.7+, simplemente agréguelo /wp-jsona la URL raíz y maravíllese con la pared de texto que se devuelve.

Con esta API integrada automáticamente en la instalación de Wordpress, gran parte del trabajo de una aplicación distribuida ya está hecho por nosotros. La creación de API puede ser un obstáculo al comenzar con esta nueva forma de pensar sobre las aplicaciones. Wordpress ha creado una API básica fantástica para consumir nuestros datos de la forma que prefiramos.

En este punto, solo estaría saturando Internet escribiendo un tutorial sobre cómo instalar Wordpress localmente. Entonces, en cambio, voy a señalarle una fuente confiable sobre el tema.

No importa qué ruta tome para poner en funcionamiento una instancia de Wordpress, debería poder acceder a ella a través de //localhosto alguna otra URL. Una vez que tengamos una URL, hagamos una prueba rápida para asegurarnos de que recibamos datos. Prefiero una herramienta como Postman, pero lo haremos simple y visitaremos la siguiente URL en nuestro navegador (cambiando la URL en consecuencia, por supuesto).

//localhost/mysite/wp-json

Esto debería devolver una lista de todos los puntos finales disponibles para la API REST de su instalación de Wordpress.

Pero de verdad, cartero ...

Cartero

Postman es el único entorno de desarrollo de API completo, para desarrolladores de API, utilizado por más de 5 millones de desarrolladores ... www.getpostman.com

Tipos de publicaciones personalizadas

Dado que Wordpress nos limita a dos tipos de datos (Publicaciones y Páginas), vamos a necesitar crear un tipo de publicación personalizado para nuestros Productos. Esto creará una clara separación de las publicaciones del producto y cualquier otra publicación que tengamos.

Hay varias formas diferentes de crear tipos de publicaciones personalizadas. Aquí, vamos a crear un complemento de Wordpress de un solo archivo para registrar el tipo de publicación de Productos.

function create_product_cpt() { $labels = array( 'name' => __( 'Products', 'Post Type General Name', 'products' ), 'singular_name' => __( 'Product', 'Post Type Singular Name', 'products' ), 'menu_name' => __( 'Products', 'products' ), 'name_admin_bar' => __( 'Product', 'products' ), 'archives' => __( 'Product Archives', 'products' ), 'attributes' => __( 'Product Attributes', 'products' ), 'parent_item_colon' => __( 'Parent Product:', 'products' ), 'all_items' => __( 'All Products', 'products' ), 'add_new_item' => __( 'Add New Product', 'products' ), 'add_new' => __( 'Add New', 'products' ), 'new_item' => __( 'New Product', 'products' ), 'edit_item' => __( 'Edit Product', 'products' ), 'update_item' => __( 'Update Product', 'products' ), 'view_item' => __( 'View Product', 'products' ), 'view_items' => __( 'View Products', 'products' ), 'search_items' => __( 'Search Product', 'products' ), 'not_found' => __( 'Not found', 'products' ), 'not_found_in_trash' => __( 'Not found in Trash', 'products' ), 'featured_image' => __( 'Featured Image', 'products' ), 'set_featured_image' => __( 'Set featured image', 'products' ), 'remove_featured_image' => __( 'Remove featured image', 'products' ), 'use_featured_image' => __( 'Use as featured image', 'products' ), 'insert_into_item' => __( 'Insert into Product', 'products' ), 'uploaded_to_this_item' => __( 'Uploaded to this Product', 'products' ), 'items_list' => __( 'Products list', 'products' ), 'items_list_navigation' => __( 'Products list navigation', 'products' ), 'filter_items_list' => __( 'Filter Products list', 'products' ), );
 $args = array( 'label' => __( 'Product', 'products' ), 'description' => __( '', 'products' ), 'labels' => $labels, 'menu_icon' => 'dashicons-products', 'supports' => array('title', 'editor', 'excerpt', 'thumbnail'), 'taxonomies' => array('products'), 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 5, 'show_in_admin_bar' => true, 'show_in_nav_menus' => true, 'can_export' => true, 'has_archive' => true, 'hierarchical' => false, 'exclude_from_search' => false, 'show_in_rest' => true, 'rest_base' => 'products', 'publicly_queryable' => true, 'capability_type' => 'post', );
 register_post_type( "product", $args );}%>

While long-winded, this is pretty standard code for creating a custom post type in Wordpress. Two things to note in our $args array:

  • 'show_in_rest' => true makes the custom post type accessible via the REST API
  • 'rest_base' => 'products' sets the name we use to access Products via the REST API endpoints

Once you have your custom post type showing in the Wordpress admin, let’s make sure we can get a response via the API (you’ll need to create a product so it doesn’t return empty).

//localhost/mysite/wp-json/wp/v2/products

And here’s what we get…

Original text


Sweet!

Advanced Custom Fields

I try to limit my usage of plugins as much as possible, but I’ll make an exception for Advanced Custom Fields (ACF). ACF takes all the work out of creating and managing custom fields for post types. Head to your Plugins page, search for Advanced Custom Fields then click “Install” & “Activate”. All done.

It would also be redundant for me to walk you through creating a Field Group using Advanced Custom Fields, so I’ll let their documentation walk you through it if you don’t know how.

Let’s create a Field Group called “Product Meta” and add fields for “Normal Price”, “Discount Price” and “Inventory Quantity” and position them in the sidebar area.

Good.

Now comes the tricky part. The fields we just created through ACF aren’t exposed via the REST API by default. We will have to leverage add_filter and rest_prepare_{$post_type} to add the custom field values to the JSON response. Here, I’ve simply added this bit of code to the bottom of our custom post type plugin file for the sake of brevity.

function my_rest_prepare_post($data, $post, $request) { $_data = $data->data; $fields = get_fields($post->ID);
 foreach ($fields as $key => $value){ $_data[$key] = get_field($key, $post->ID); }
 $data->data = $_data; return $data;}
add_filter("rest_prepare_product", 'my_rest_prepare_post', 10, 3);

Thanks to Cody Sand for the tidbit above.

Express.js

Our Express.js app will provide us a framework for consuming the Wordpress API and presenting products in our store website. Since we are simply consuming an API, we could use any framework of our choosing. Vue.js. Angular. Backbone. React. Rails. Django. Middleman. Jekyll. The front-end world is your oyster.

I’ll assume you already have Node.js installed. If you don’t, it’s dead simple. Let’s start a new Express.js app.

npm install -g express-generator nodemonexpress --css=sass --view=jade --git mystorecd mystorenpm install --save request request-promise && npm install

Here, we are using the Express Generator package to generate a skeleton for our Express app. We’ll also be using SASS for stylesheets and Jade Template Engine. Choose whatever you’re comfortable with. Nodemon will restart our app automatically for us when a file changes, and the Request library will help us make HTTP requests to the Wordpress API. Let’s serve up our Express app:

nodemon

Now, when we pull up //localhost:3000 we should see our Express app running.

Alright, now let’s pull in our products.

var express = require('express');var router = express.Router();const rp = require('request-promise');
/* GET index page. */router.get('/', function(req, res, next) { rp({uri: '//127.0.0.1:8888/test/wp-json/wp/v2/products', json: true}) .then((response) => { console.log(response); res.render('index', {products: response}); }) .catch((err) => { console.log(err); });});
module.exports = router;

In our index.js route file, let’s include the Request-Promise library then make a call to the products endpoint within our root route (/).

If the request is successful, then we render our index view. If there’s an error with the request, we simply log it. Now, the view…

extends layout
block content h1 MyStore ul each product in products li product.title.rendered product.price

Using Jade, we will simply list the products out. Ok, let’s check out our store site.

? There’s your prize. I’ll leave it up to you to continue down the Express road and figure out how to get product listing and index pages working.

Beyond

This is a fairly simple example of how distributed apps work using Wordpress. We could have continued to separate the app into even more parts by integrating a CDN for media storage or moving the database to a separate server. We also didn’t cover authentication for the Wordpress API which is something you would absolutely need in production.

From here, you could implement Stripe or another payment processor and have a fully functional store site. I hope this has inspired some of you to leverage Wordpress in different ways and continue using one of the most ubiquitous CMS solutions out there. Happy coding!