martes, 5 de diciembre de 2017

API Rest con PHP y MySQL para el manejo de imágenes

Las cargas de imágenes son una cosa que siempre se torna bastante complicada y manejar esto a través de una API Rest es aún más retador. En este artículo vamos a ver cómo insertar imágenes y demás datos en una tabla de MySQL a través de servicios web de tipo REST que permitirán ejecutar las diferentes operaciones sobre la base de datos. Como consideraciones iniciales, es importante mencionar que debamos revisar antes el siguiente artículo:
http://blog.rolandopalermo.com/2017/12/slim-composer-php-rest-api-install.html.

También tendremos que configurar nuestro servidor Apache para poder tener URL más amigables. Para esto modificar los archivos:

C:\xampp\apache\conf\extra\httpd-vhosts.conf
C:\Windows\System32\drivers\etc\hosts

Los cuales deberán quedarnos con el siguiente contenido:

Archivo httpd-vhosts.conf
NameVirtualHost *:80

<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/rest-api/public"
    ServerName rest-api
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/rest-api-demo/public"
    ServerName rest-api-demo
</VirtualHost>
Archivo hosts
127.0.0.1 rest-api-demo

Recuerden que a lo largo de todo el artículo haré referencia a la ruta C:/xampp que es donde tengo instalado a mi servidor Xampp.

Como primera parte, tendremos una base de datos con una tabla para gestionar usuarios. El script es el siguiente:
CREATE DATABASE IF NOT EXISTS rest_api_base;
USE rest_api_base;

--
-- Definition of table `user`
--

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id_user` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `avatar` blob NOT NULL,
  PRIMARY KEY (`id_user`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Ahora la clase PHP para conectarnos a la base de datos:
<?php

class DB {

    private $db_host = 'localhost';
    private $db_user = 'root';
    private $db_pass = '123456';
    private $db_name = 'rest_api_base';

    public function connect() {
        $mysql_connect_str = "mysql:host=$this->db_host;dbname=$this->db_name";
        $dbConnection = new PDO($mysql_connect_str, $this->db_user, $this->db_pass);
        $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $dbConnection;
    }

}
Y la clase que mapea a la tabla users de la base de datos.
<?php

class User {

    public $idUser;
    public $name;
    public $avatar;

    function __construct($idUser, $name, $avatar) {
        $this->idUser = $idUser;
        $this->name = $name;
        $this->avatar = $avatar;
    }

}
También las clase DAO donde se implementarán las operaciones sobre la base de datos.
<?php

require '../src/model/user.php';

class UserDAO {

    public function findAll() {
        $sql = "SELECT * FROM user;";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->query($sql);
                $usuarios = $stmt->fetchAll();
                $arr_usuarios = array();  //array to parse jason from
                $db = null;
                foreach ($usuarios as $row) {
                    $usr = new User($row[0], $row[1], base64_encode($row[2]));
                    $arr_usuarios[] = $usr;
                }
                echo '{"error": false, "data": ' . json_encode($arr_usuarios) . '}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }

    public function findOne($idUser) {
        $sql = "SELECT * FROM user WHERE id_user=?;";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->prepare($sql);
                $array = array($idUser);
                $stmt->execute($array);
                $usuarios = $stmt->fetchAll();
                $arr_usuarios = array();  //array to parse jason from
                foreach ($usuarios as $row) {
                    $usr = new User($row[0], $row[1], base64_encode($row[2]));
                    $arr_usuarios[] = $usr;
                }
                echo '{"error": false, "data": ' . json_encode($arr_usuarios) . '}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }

    public function put($user) {
        $sql = "INSERT INTO user(name, avatar) VALUES (?,?)";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->prepare($sql);
                $array = array($user->name, base64_decode($user->avatar));
                $stmt->execute($array);
                $id_producto_insertado = $db->lastInsertId();
                $db = null;
                echo '{"error": false, "data": ' . $id_producto_insertado . '}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }
    
    public function update($user) {
        $sql = "UPDATE user SET name=?, avatar=? WHERE id_user=?";
        try {
            $db = new DB();
            $db = $db->connect();
            if (is_null($db)) {
                echo '{"error": true, "message": "text": "No se pudo conectar a la base de datos."}';
            } else {
                $stmt = $db->prepare($sql);
                $array = array($user->name, base64_decode($user->avatar),$user->idUser);
                $stmt->execute($array);
//                $id_producto_insertado = $db->lastInsertId();
                $db = null;
                echo '{"error": false, "data": "Usuario actualizado"}';
            }
        } catch (PDOException $ex) {
            echo '{"error": true, "message": "' . $ex->getMessage() . '"}}';
        }
    }

}
Ahora, como mencioné al inicio del artículo, utilizaremos el Framework Slim cuya instalación la expliqué en el post del cual hice referencia al inicio de este artículo. En esta clase se definirán las rutas y los métodos a través de los cuales se accederá a las distintas opciones de nuestra API.
<?php

use PsrHttpMessageServerRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;

//$app_productos = new SlimApp;

require '../src/dao/user_dao.php';

$app_dapp->get('/api/users/findall', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $dao->findAll();
});

$app_dapp->get('/api/users/find/{idUser}', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $dao->findOne($request->getAttribute('idUser'));
});

$app_dapp->post('/api/users/put', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $user = new User(null, $request->getParam('name'), $request->getParam('avatar'));
    $dao->put($user);
});

$app_dapp->post('/api/users/update', function(Request $request, Response $response) {
    $dao = new UserDAO();
    $user = new User($request->getParam('idUser'), $request->getParam('name'), $request->getParam('avatar'));
    $dao->update($user);
});
Si todo ha marchado bien, podremos acceder desde cualquier cliente y visualizar la información que se solicite. Por ejemplo, lanzado una petición a través de POSTMAN a la opción de listar los usuarios, tendremos lo siguiente:



En este caso, las imágenes fueron tratas en su codificación de Base64. Ahora, si queremos más de cerca nuestra API en acción, podemos utilizar POSTMAN para insertar una imagen:



También podemos escribir un sencillo cliente en PHP. El código sería el siguiente:
<?php

$url = "http://rest-api-demo/api/users/findall";
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
$curl_response = curl_exec($client);
//echo $curl_response;
$obj_response = json_decode($curl_response);
$array_data = $obj_response->data;
//header("Content-type: image/gif");
foreach($array_data as $item) {
 echo $item->name . '<br>';
 echo '<img width="150" src="data:image/gif;base64,' . $item->avatar . '" />';
 echo '<br>';
}
Y deberíamos tener algo similar a esto:



El proyecto completo lo pueden encontrar en Github y lo pueden descargar del siguiente enlace:
https://github.com/rolandopalermo/rest-api-demo

Espero que este artículo les sea de utilidad y no olviden seguirme a través de Facebook.

lunes, 4 de diciembre de 2017

String split with Transact-SQL

In spite of The STRING_SPLIT function is now available under compatibility level 130, knowing how to implement this function is something that is definitely worthwhile.
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1
       
        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)
        
    END 
    RETURN 
END
GO
How to use:
DECLARE
    @_txt_tmp  NVARCHAR(MAX),
    @full_string NVARCHAR(MAX) = "Hello World From Transact-SQL"
DECLARE
    @cursor_for_spliting CURSOR
    SET @cursor_for_spliting = CURSOR FOR SELECT splitdata FROM [dbo].[fnSplitString](@full_string,'')
    OPEN @cursor_for_spliting FETCH NEXT FROM @cursor_for_spliting INTO @_txt_tmp
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Do whatever with @_txt_tmp
    FETCH NEXT FROM @cursor_for_spliting INTO @_txt_tmp
END;
CLOSE @cursor_for_spliting ;
DEALLOCATE @cursor_for_spliting;

sábado, 2 de diciembre de 2017

Creando una REST Api para PHP con el Framework Slim 3

Slim es un framework para crear microservicios REST utilizando PHP. En este post se va a mostrar la manera cómo instalar Slim utilizando una herramienta para la gestión de dependencias en PHP llamada Composer. Resumiento, para poder seguir este artículo, necesitaremos lo siguiente:
Una vez tengamos instaladas todas estas herramientas, procederemos a descargar Slim vía Composer. Para esto, primero debemos crear nuestra carpeta de trabajo llamada rest-api dentro del directo C:\xampp\htdocs (La ruta dependerá de dónde hayan instalado Xampp pero se recomiendo no utilizar nombres muy largos), situarnos en la carpeta utilizando un CDM y descargar la versión 3 del Framework Slim.
C:\xampp\htdocs\rest-api>cd C:\xampp\htdocs\rest-api
C:\xampp\htdocs\rest-api>composer require slim/slim "^3.0"
Ahora utilizaremos SublimeText para abrir la carpeta, en donde podremos visualizar todos los archivos de dependencias que se nos ha creado. Dentro de la carpeta rest-api crearemos dos subcarpetas: public y src. La siguiente imagen ilustra lo descrito anteriormente:



Ahora crearemos un archivo en la carpeta public llamada index.php cuyo contenido será el siguiente:
<?php
use PsrHttpMessageServerRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;

require '../vendor/autoload.php';

$app = new SlimApp;
$app->get('/hello/{name}', function (Request $request, Response $response) {
    $name = $request->getAttribute('name');
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();
El código anterior es un ejemplo sencillo de un servicio Rest que recibe como parámetro un nombre y retorna un saludo. Y ahora, para poder verificar el funcionamiento de nuestra api podemos ingresar a la siguiente URL: http://localhost/rest-api/public/index.php/hello/rolando. En este punto quiero hacer algunas anotaciones, en primer lugar la ruta depende de la carpeta que hayan creado, en mi caso dicha carpeta se llama rest-api. Es importante también no olvidar que debemos escribir la ruta completa incluído el nombre del archivo index.php sino obtendremos de respuesta un error 404.

Espero que este post les sea de ayuda para iniciarse en el mundo de los Microservicios con PHP. No se olviden seguirme a través de mis redes sociales de Facebook haciendo clic aquí.