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.

Comentarios