Saltar al contenido

Categoría: Desarrollo y Programación

Agregar rutas personalizadas en el API REST de WooCommerce

WooCommerce es una excelente plataforma para montar tu propia tienda en línea. Aunque mucha gente piensa que WordPress sigue siendo solo una plataforma para blogs, desde hace años se ha cambiado el funcionamiento interno para soportar todo tipo de contenido. WooCommerce es un ejemplo de ellos, pues este plugin permite soportar todo el contenido requerido en una tienda en línea como: productos, órdenes, pagos, descuentos, entre otros. Además de incluir una API REST para realizar acciones adicionales sobre él.

API REST

El plugin incluye una API REST para las operaciones mas básicas (se debe activar previamente), permitiendo comunicación hacia o desde aplicaciones externas, extendiendo aún mas las posibilidades de procesos que se pueden implementar en la tienda. Sin embargo, si manejas flujos de compras personalizados o deseas realizar acciones mas allá de las disponibles en el API por defecto, puedes agregar las tuyas tal como se explicará a continuación.

Desarrollo del plugin

El plugin se divide en 2 archivos, el principal para definir el plugin como tal y permitirle indicar a WordPress en que momento se va a cargar el segundo archivo, que contiene los métodos que responden a las rutas personalizadas del API.

Cargador de la clases

Para modificar WordPress, siempre es recomendable hacerlo a través de plugins, por eso el primer paso es definir un plugin como cualquier otro y dentro del constructor de la clase del plugin, indicar a WooCommerce que luego de cargar el código necesario para implementar el API (a través del hook woocommerce_api_loaded) se deben agregar la(s) clase(s) que implementan las rutas personalizadas (a través del hook woocommerce_api_classes), este código lo pueden ver a continuación:

/**
 * Custom API REST path  class
 *
 * @package MyPlugin
 * @author Skatox
 */
class WC_API_Custom extends WC_API_Resource
{

    const PATH = '/custom';

    /**
     * Function to define each of the custom path
     */
    public function register_routes($routes)
    {
        //GET Request
        $routes[self::PATH . '/orders'] = array(
            array(array($this, 'listShippingOrders'), WC_API_Server::READABLE),
        );

        //POST Request
        $routes[self::PATH . '/orders/(?P<order_id>\d+)/fulfillments.json'] = array(
            array(array($this, 'createItem'), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA),
        );

        //PUT Request
        $routes[self::PATH . '/variants/(?P<id>\d+).json'] = array(
            array(array($this, 'updateItem'), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA),
        );

        return $routes;
    }

    public function listShippingOrders($fields = null, $filter = array(), $status = null, $page = 1)
    {
        $wcApiOrders = new WC_API_Orders($this->server);
        $orders = $wcApiOrders->get_orders($fields, $filter, $status, $page);

        //Removes orders without shipping methods
        foreach ($orders['orders'] as $key => $order)
        {
            if (empty($order['shipping_methods'])) {
                unset($orders['orders'][$key]);
                continue;
            }
        }

        return $order;
    }

    public function createItem($order_id, $data)
    {
        //Do insert proccess and then return the response array
    }

    public function updateItem($id, $data)
    {
        //Do update proccess and then return the response array

    }
}

Leyendo observarán que es un plugin de WordPress donde la clase WD_API_LOADER registra los hooks mencionados anteriormente, para inyectar en el momento que Woocommerce carga sus clases del API, las clases que instancia los procesos personalizados a ser implementados en el API.

Al final es recomendable instanciar la clase en un objeto, de este modo es posible acceder a los métodos de forma global desde plugines externos, como por ejemplo para detener la carga de las clases o implementar modificaciones permitiendo dar mayor flexibilidad al plugin.

Clase con los métodos de la API REST

El siguiente paso corresponde a crear la clase que responde a las peticiones HTTP del API. En este ejemplo, se define una constante con el PATH personalizado y en el constructor se definen las rutas personalizadas y el método que se debe llamar que responderá a la petición realizada. Si observas, el parámetro recibido es un array donde la llave representa la ruta relativa del API y como valor, otro arreglo con el nombre de la función a procesar la petición y las banderas del tipo de petición. Estas últimas banderas permiten definir el tipo de petición a recibir, por ejemplo
WC_API_Server::READABLE indica que lee datos desde la petición tal como es una petición GET,
WC_API_Server::ACCEPT_DATA permite decir que lea el cuerpo de la petición y permite leer información para peticiones POST y PUT, entre otros.

/**
 * Custom API REST path  class
 *
 * @package MyPlugin
 * @author Skatox
 */
class WC_API_Custom extends WC_API_Resource
{

    const PATH = '/custom';

    /**
     * Function to define each of the custom path
     */
    public function register_routes($routes)
    {
        //GET Request
        $routes[self::PATH . '/orders'] = array(
            array(array($this, 'listShippingOrders'), WC_API_Server::READABLE),
        );

        //POST Request
        $routes[self::PATH . '/orders/(?P<order_id>\d+)/fulfillments.json'] = array(
            array(array($this, 'createItem'), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA),
        );

        //PUT Request
        $routes[self::PATH . '/variants/(?P<id>\d+).json'] = array(
            array(array($this, 'updateItem'), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA),
        );

        return $routes;
    }

    public function listShippingOrders($fields = null, $filter = array(), $status = null, $page = 1)
    {
        $wcApiOrders = new WC_API_Orders($this->server);
        $orders = $wcApiOrders->get_orders($fields, $filter, $status, $page);

        //Removes orders without shipping methods
        foreach ($orders['orders'] as $key => $oreder)
        {
            if (empty($oreder['shipping_methods'])) {
                unset($oreders['orders'][$key]);
                continue;
            }
        }

        return $order;
    }

    public function createItem($order_id, $data)
    {
        //Do insert proccess and then return the response array
    }

    public function updateItem($id, $data)
    {
        //Do update proccess and then return the response array

    }
}

Una vez que proceses la petición, si necesitas devolver un objeto JSON, simplemente debes retornar un array con la información deseada y automáticamente Woocommerce se encargará de transformarlo en un objeto JSON e imprimirlo en la salida.

Recomendaciones

Como podrás observar, no es difícil. En caso que no sepas como desarrollar debido a la poca documentación. Te recomiendo leer el código fuente de las clases del API de Woocommerce, son fáciles de entender, creadas por el mismo equipo del plugin y verás como están creados los métodos existentes. Una de las mejores clases para guiarte, es la de productos que se encuentra ubicada en la ruta wp-plugins/woocommerce/includes/api/class-wc-api-products.php, pues es muy completa y posee todas las operaciones REST soportadas.

Recuerda crear un plugin para esto, evita colocarlo en el functions.php pues será difícil de mantener y dificulta migrar de tema (porque el API dependerá de ésta). Finalmente, recuerda respetar el estándar REST, he visto gente que implementa todo en POST y agrega incompatibilidades a aplicaciones.

Ya depende de tu imaginación, descubrir como extender las funcionalidades de WooCommerce a través de un API REST personalizado.

2 comentarios

Mis artículos en la semana de WordPress de Oja.la

En el blog de Oja.la, están celebrando la semana de WordPress y publicarán una serie de mis artículos (con un poco de mas mejoras y mas contenido) sobre este maravilloso CMS. Así que les invito leer el blog de Oja.la y aumentar sus conocimientos de WordPress.

A continuación les dejo enlazado el primer artículo publicado sobre como aumentar la seguridad de WordPress en 2 pasos.

Deja un comentario

Cloud 9 un entorno de desarrollo en la nube

Hace unas semanas debido a una fuerte gripe, me tocó trabajar en casa para un trabajo que debía hacer una oficina donde tenía mi computadora con todo el entorno de desarrollo configurado. Sin embargo en la organización tenían configurado el servicio Cloud9 para poder trabajar remotamente.

Cloud9 es un completo entorno de desarrollo y ejecución en la nube, permitiéndote desde tu navegador web crear y ejecutar software web. El mismo posee un editor muy parecido a Sublime Text (de hecho muchos atajos del teclado son iguales), acceso a la terminal de un servidor y la posibilidad de configurar dependencias necesarias para ejecutar el programa: servidor web, framework, base de datos, etc.

Entorno de desarrollo de Cloud9
Entorno de desarrollo de Cloud9, todo se está ejecutando desde el navegador

El espacio de trabajo

El uso de Cloud9 es muy sencillo, primero debes configurar un espacio de trabajo (Workspace), actualmente (al menos en la versión gratuita) te ofrece entornos como LAMP, Django, NodeJS, puro HTML, Rails, entre otros. Luego de seleccionar el entorno especificas asignas un repositorio de control de versiones (creo que es impensable hoy en día hacer un proyecto sin eso), para obtenerlas fuentes, haces las configuraciones requeridas por tu aplicación y listo. Luego se encarga de correr los servicios necesarios y puedes acceder a tu aplicación web desde una URL.

Plantilla de espacios de trabajo disponibles en Cloud9
Plantilla de espacios de trabajo disponibles en Cloud9

¿Por qué usarlo?

La principal ventaja de usar Cloud9 es poder escribir el código de un programa, guardar, cambiarte de equipo o de localidad y seguir escribiendo código el mismo código sin problemas. Aunque puedes sacrificar privacidad (en la licencia no se ve nada extraño) es muy útil en casos donde trabajas en distintos lugares y no puedes cargar tu portátil (en mi caso me muevo entre casa, universidad y oficinas de clientes pero por la inseguridad no puedo cargar mi portátil en todos lados). Otro caso, como en el que descubrí la herramienta, en organizaciones pueden usarlo como respaldo en caso que un empleado necesite trabajar desde su casa u otro lugar de forma urgente.

Algo interesante, es que en pocos segundos puedes tener un entorno de ejecución funcional. Con unos clics puedes tener un servidor web hecho Django y empezar a trabajar en él (o estudiarlo), luego con otros clics tienes un entorno en Ruby. Lo cual es mas fácil que estar instalando dependencias y configurar servicios en tu sistema operativo.

El editor está realizado en Javascript y tecnología detrás de ella es un servidor NodeJS que crea contenedores en Docker, me parece interesante esta arquitectura. Algo que me parece «fácil» de replicar en otros servidores y poder contar con una solución similar.

Si deseas contar con IDE y entorno de ejecución que puedas usar en cualquier computadora sin estar descargando cosas, te recomiendo utilizar Cloud9. Lo único es que como toda nube, dependes de un tercero y a veces puedes desconfiar de la seguridad del mismo o de la calidad de protección de los datos. Aunque, me parece un buen entorno para aprender tecnologías o hacer experimentos rápidamente.

Sitio oficial de Cloud9

1 comentario

Cómo crear pruebas unitarias y funcionales con Yii 1.x usando Composer

Si tienes configurado tu proyecto hecho en Yii 1.x para cargar las clases mediante composer y deseas realizar pruebas automatizadas, notarás que existen ciertas incompatibilidades entre Yii 1.x y el PHPUnit del repositorio de composer. A continuación te explicaré como instalarlo.

Instalación de las dependencias

Lo primero es instalar todos los paquetes necesarios para realizar las pruebas, todas se colocarán en la sección require-dev debido a que solo serán utilizadas durante el desarrollo y no en el servidor de producción para evitar bugs/exploits, ocupar espacio, rendimiento, etc. El paquete requerido por Yii es PHPUnit, pero al momento de desarrollo del framework, PHPUnit incluía varias funcionalidades que actualmente están disponibles en otros paquetes, por esta razón también debemos agregar php-invoker, phpunit-story y phpunit-runner. Ahora, si deseas utilizar fixtures (datos de prueba) o vas a realizar pruebas con base de datos debes agregar dbunit como dependencia, si vas a realizar pruebas funcionales agrega phpunit-selenium y finalmente, si utilizas un generador de pruebas (Netbeans incluye uno) agrega phpunit-skeleton-generator para con un clic generar un archivo de con los métodos de una clase para escribir pruebas.

Entonces la sección de require-dev en tu composer.lock debe quedar así:

"require-dev": {
	"phpunit/phpunit": "4.7.*",
	"phpunit/php-invoker": "*",
	"phpunit/phpunit-story": "*",
	"phpunit/dbunit": ">=1.2",
	"phpunit/phpunit-selenium": ">=1.2",
	"phpunit/phpunit-skeleton-generator": "*",
	"hot/phpunit-runner": "dev-master"
}

Luego ejecuta composer install y tendrás las dependencias en tu proyecto.

Configuración en Netbeans

Primero se debe configurar el proyecto para indicar cuales son las carpetas con los archivos y la configuración de phpunit a utilizar, para ello, haz clic derecho en el proyecto. En la parte izquierda entra a la sección de Testing, luego en la ventana de diálogo selecciona PhpUnit y en la selección de la carpeta, escoge /protected/tests/ para indicar que esa es la carpeta de pruebas del proyecto. Finalmente, en la parte inferior se debe seleccionar PHPUnit en la sección de Testing Providers

Selección de PHPunit en Netbeans
Selección de PHPunit en Netbeans

A continuación, en el panel izquierdo haz clic en PHPUnit, y en el panel derecho:

  • Selecciona la opción Use Bootstrap y selecciona la ruta /protected/tests/bootstrap.php
  • Activa la opción de Use Bootstrap for creating new tests
  • Selecciona la opción Use XML Configuration y selecciona la ruta /protected/tests/phpunit.xml
  • Use Custom PHPUnit Script y coloca la ruta /protected/external/phpunit/phpunit/phpunit

De esta forma le indicamos a Netbeans el uso de la configuración de arranque incluída en Yii y que utilice el script de PHPUnit que instalamos con composer.

Selección del PHPUnit de composer

Ahora necesitamos indicarle a Netbeans que utilice la plataforma de PHPUnit instalada con composer, de lo contrario puede utilizar la que viene incluida en el IDE o la del sistema operativo opertivo, las cuales son incompatibles con las clases incluidas en composer. Para cambiarlo, haz clic al menú Tools y luego Options, entra a la sección de PHP, selecciona la pestaña de Framework & Tools, luego en el panel izquierdo selecciona PHPUnit, y selecciona/escribe las siguientes rutas:

  • En la ruta de PHPUnit Scripts, coloca la ruta de phpunit de composer: /protected/external/phpunit/phpunit/phpunit
  • En la ruta de Skeleton Generator Script, /protected/external/phpunit/phpunit-skeleton-generator/phpunit-skelgen
Selección de scripts de PHPunit de Composer en Netbeans
Selección de scripts de PHPunit de Composer en Netbeans

Ejecución

¡Listo! Ya puedes ejecutar las pruebas desde la clase principal con F6 o desde la clase de pruebas con Alt + F6. Ademas, al hacer clic derecho en una clase, puedes entrar al menú de Tests y luego Create Tests para crear automáticamente los archivos pruebas, estos ya vienen con los métodos que debes escribir y métodos para ejecutar métodos antes de las pruebas.

Ahora no tienes excusa para probar tu software de forma automática utilizando Yii.

Deja un comentario