Spanish (Español) translation by Xiomara Reynoso (you can also view the original English article)
En este tutorial, les enseñaré cómo construir una simple API REST con PHP y MySQL
REST se ha convertido en el estándar de facto cuando se trata de exponer datos a través de API y servicios web. De hecho, la mayoría de las aplicaciones web hoy en día acceden y exponen datos a través de API REST. Con la popularidad de los marcos frontales que pueden consumir la API REST sin esfuerzo, siempre será un más para ti si tu aplicación web expone las API REST.
En este artículo, vamos a construir una simple aplicación de demostración, que te permite obtener una lista de usuarios de la base de datos MySQL a través de un punto final REST.
Prepara el esqueleto
En esta sección, repasaremos brevemente la estructura del proyecto.
Veamos la estructura siguiente.
1 |
├── Controller |
2 |
│ └── Api |
3 |
│ ├── BaseController.php |
4 |
│ └── UserController.php |
5 |
├── inc |
6 |
│ ├── bootstrap.php |
7 |
│ └── config.php |
8 |
├── index.php |
9 |
└── Model |
10 |
├── Database.php |
11 |
└── UserModel.php |
Intentemos entender la estructura del proyecto.
- index.php: el punto de entrada de nuestra solicitud. Actuará como controlador frontal de nuestra aplicación.
- inc/configura.php: tiene la información de configuración de nuestra aplicación. Sobre todo, tendrás las credenciales de la base de datos.
- inc/bootstrap.php: usado para atrapar boots en nuestra aplicación incluyendo los archivos necesarios.
- Modelo/base de datos.php: la capa de acceso a la base de datos que se utilizará para interactuar con la base de datos subyacente MySQL.
-
Modelo/Modelo usuario.php: el archivo del modelo de
usuario
que implementa los métodos necesarios para interactuar con la tabla de usuarios de la base de datos MySQL. - Controlador/Api/Control de bases.php: un archivo de control de base que contiene métodos de utilidad comunes.
-
Controlador/Api/Controlador de Use.php: el archivo controlador del
usuario
que contiene el código de aplicación necesario para entretener las llamadas de la API REST.
Así que esa es la configuración básica que vamos a implementar en el resto del artículo.
Crea una base de datos y clases modelo
En esta sección, crearemos una base de datos y la tabla de usuarios. También crearemos clases de modelos necesarias que se usarán para traer a los usuarios de una base de datos.
Crea una base de datos y la tabla de usuarios
Crea la base de datos rest_api_demo
ejecutando la siguiente orden en su terminal MySQL. (Accede a esto con la orden mysql
de la línea de comando.)
1 |
$CREATE DATABASE rest_api_demo; |
También podrías usar una herramienta como phpMyAdmin si prefieres trabajar con tus bases de datos de esa manera.
Una vez creada la base de datos rest_api_demo
, ve y crea la tabla de usuarios
ejecutando las siguientes instrucciones.
1 |
$use rest_api_demo; |
2 |
$CREATE TABLE `users` ( |
3 |
`user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
4 |
`username` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', |
5 |
`user_email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', |
6 |
`user_status` int(11) NOT NULL DEFAULT '0', |
7 |
PRIMARY KEY (`user_id`) |
8 |
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; |
Eso debería crear la tabla de usuarios
en la base de datos rest_api_demo
. También querrás poblar esta tabla con unos pocos registros falsos para fines de prueba. ¡Incluye algunos discos y ya estás listo!
Crea clases de modelo
En esta sección, crearemos las clases de modelo necesarias.
Crea la base de datos del Modelo.el archivo de php con el siguiente contenido.
1 |
<?php
|
2 |
class Database |
3 |
{
|
4 |
protected $connection = null; |
5 |
|
6 |
public function __construct() |
7 |
{
|
8 |
try { |
9 |
$this->connection = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_DATABASE_NAME); |
10 |
|
11 |
if ( mysqli_connect_errno()) { |
12 |
throw new Exception("Could not connect to database."); |
13 |
}
|
14 |
} catch (Exception $e) { |
15 |
throw new Exception($e->getMessage()); |
16 |
}
|
17 |
}
|
18 |
|
19 |
public function select($query = "" , $params = []) |
20 |
{
|
21 |
try { |
22 |
$stmt = $this->executeStatement( $query , $params ); |
23 |
$result = $stmt->get_result()->fetch_all(MYSQLI_ASSOC); |
24 |
$stmt->close(); |
25 |
|
26 |
return $result; |
27 |
} catch(Exception $e) { |
28 |
throw New Exception( $e->getMessage() ); |
29 |
}
|
30 |
return false; |
31 |
}
|
32 |
|
33 |
private function executeStatement($query = "" , $params = []) |
34 |
{
|
35 |
try { |
36 |
$stmt = $this->connection->prepare( $query ); |
37 |
|
38 |
if($stmt === false) { |
39 |
throw New Exception("Unable to do prepared statement: " . $query); |
40 |
}
|
41 |
|
42 |
if( $params ) { |
43 |
$stmt->bind_param($params[0], $params[1]); |
44 |
}
|
45 |
|
46 |
$stmt->execute(); |
47 |
|
48 |
return $stmt; |
49 |
} catch(Exception $e) { |
50 |
throw New Exception( $e->getMessage() ); |
51 |
}
|
52 |
}
|
53 |
}
|
Esta es una clase de capa de acceso a la base de datos, que nos permite establecer una conexión a la base de datos MySQL. Además de la conexión establecida, contiene métodos genéricos como seleccionar
y ejecutarDeclaración
que nos permite seleccionar registros de una base de datos. No usaremos la clase de base de datos
directamente, crearemos clases de modelos correspondientes que amplíen la clase de base de datos
para acceder a la base de datos MySQL subyacente.
Después, vamos a crear el Modelo Model/UserModel.php con el siguiente contenido.
1 |
<?php
|
2 |
require_once PROJECT_ROOT_PATH . "/Model/Database.php"; |
3 |
|
4 |
class UserModel extends Database |
5 |
{
|
6 |
public function getUsers($limit) |
7 |
{
|
8 |
return $this->select("SELECT * FROM users ORDER BY user_id ASC LIMIT ?", ["i", $limit]); |
9 |
}
|
10 |
}
|
Es importante notar que la clase UsuarioModelo
amplía la clase de base de datos
.
Además, contiene el método ObtenUsuarios
, que nos permite seleccionar a los usuarios de la base de datos MySQL. Es obligatorio pasar el parámetro $límite
, lo que se asegura de que no seleccionará todos los registros a la vez.
Por supuesto, podría definir más métodos en la clase UsuarioModelo
según tus necesidades. Mantendremos las cosas simples en el contexto de este tutorial.
Así que ahora tenemos nuestras clases de base de datos y modelos establecidas. En la siguiente sección veremos cómo crear controles y los archivos que se emiten en nuestra aplicación demo.
Crea componentes de la capa de la aplicación
En esta sección, crearemos los archivos que quedan necesarios para que nuestra aplicación de demo funcione.
El directorio inc
Para empezar, crearemos los archivos de configuración necesarios.
Crea el archivo inc/config.php con el siguiente contenido.
1 |
<?php
|
2 |
define("DB_HOST", "localhost"); |
3 |
define("DB_USERNAME", "demo"); |
4 |
define("DB_PASSWORD", "demo"); |
5 |
define("DB_DATABASE_NAME", "rest_api_demo"); |
Asegúrate de actualizar todos los valores que estés usando en tu instalación.
Después, ve y crea el archivo inc/bootstrap.php con el siguiente contenido.
1 |
<?php
|
2 |
define("PROJECT_ROOT_PATH", __DIR__ . "/../"); |
3 |
|
4 |
// include main configuration file
|
5 |
require_once PROJECT_ROOT_PATH . "/inc/config.php"; |
6 |
|
7 |
// include the base controller file
|
8 |
require_once PROJECT_ROOT_PATH . "/Controller/Api/BaseController.php"; |
9 |
|
10 |
// include the use model file
|
11 |
require_once PROJECT_ROOT_PATH . "/Model/UserModel.php"; |
12 |
?>
|
Primero, hemos iniciado la constante PROJECT_ROOT_PATH
con la raíz del directorio de nuestra aplicación. De esta manera, podríamos usar la constante PROJECT_ROOT_PATH
para preparar caminos absolutos en nuestra aplicación. Después, incluimos el archivo config.php, que contiene la información de conexión de la base de datos. Finalmente, hemos incluido archivos de control y modelos.
Eso es para establecer los archivos comunes en nuestra aplicación.
El controlador del directorio
En esta sección, implantaremos controles que contengan la mayoría de nuestra lógica de la aplicación.
El archivo BaseController.php
Crea el archivo Controller/Api/BaseController.php con el siguiente contenido. La clase Base de control
contiene los métodos de utilidad que son utilizados por otros controladores.
1 |
<?php
|
2 |
class BaseController |
3 |
{
|
4 |
/**
|
5 |
* __call magic method.
|
6 |
*/
|
7 |
public function __call($name, $arguments) |
8 |
{
|
9 |
$this->sendOutput('', array('HTTP/1.1 404 Not Found')); |
10 |
}
|
11 |
|
12 |
/**
|
13 |
* Get URI elements.
|
14 |
*
|
15 |
* @return array
|
16 |
*/
|
17 |
protected function getUriSegments() |
18 |
{
|
19 |
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); |
20 |
$uri = explode( '/', $uri ); |
21 |
|
22 |
return $uri; |
23 |
}
|
24 |
|
25 |
/**
|
26 |
* Get querystring params.
|
27 |
*
|
28 |
* @return array
|
29 |
*/
|
30 |
protected function getQueryStringParams() |
31 |
{
|
32 |
return parse_str($_SERVER['QUERY_STRING'], $query); |
33 |
}
|
34 |
|
35 |
/**
|
36 |
* Send API output.
|
37 |
*
|
38 |
* @param mixed $data
|
39 |
* @param string $httpHeader
|
40 |
*/
|
41 |
protected function sendOutput($data, $httpHeaders=array()) |
42 |
{
|
43 |
header_remove('Set-Cookie'); |
44 |
|
45 |
if (is_array($httpHeaders) && count($httpHeaders)) { |
46 |
foreach ($httpHeaders as $httpHeader) { |
47 |
header($httpHeader); |
48 |
}
|
49 |
}
|
50 |
|
51 |
echo $data; |
52 |
exit; |
53 |
}
|
54 |
}
|
Repasemos rápidamente todos los métodos de clase Base de Control
.
El método __call
es un método mágico, y así se llama cuando intentas llamar un método que no existe. Estamos aprovechando esta oportunidad para lanzar el error no encontrado de HTTP/1.1 404
cuando alguien intenta llamar un método que no hemos implementado. Si esto te suena confuso, no te preocupes, tendrá más sentido cuando probemos nuestra aplicación en la siguiente sección.
Luego está el método getUriSegments
, que devuelve un conjunto de segmentos de URI. Es útil cuando intentamos validar el final REST llamado por el usuario. Después de eso, está el método getQueryStringParams
, que devuelve un conjunto de variables de la línea de requeté que se pasan junto con la petición entrante.
Finalmente, está el método sendOutput
, que se usa para enviar la respuesta API. Usaremos este método cuando queramos enviar la respuesta API al usuario.
El archivo UserController.php
Después, crea el archivo Controller/Api/UserController.php con el siguiente contenido.
1 |
<?php
|
2 |
class UserController extends BaseController |
3 |
{
|
4 |
/**
|
5 |
* "/user/list" Endpoint - Get list of users
|
6 |
*/
|
7 |
public function listAction() |
8 |
{
|
9 |
$strErrorDesc = ''; |
10 |
$requestMethod = $_SERVER["REQUEST_METHOD"]; |
11 |
$arrQueryStringParams = $this->getQueryStringParams(); |
12 |
|
13 |
if (strtoupper($requestMethod) == 'GET') { |
14 |
try { |
15 |
$userModel = new UserModel(); |
16 |
|
17 |
$intLimit = 10; |
18 |
if (isset($arrQueryStringParams['limit']) && $arrQueryStringParams['limit']) { |
19 |
$intLimit = $arrQueryStringParams['limit']; |
20 |
}
|
21 |
|
22 |
$arrUsers = $userModel->getUsers($intLimit); |
23 |
$responseData = json_encode($arrUsers); |
24 |
} catch (Error $e) { |
25 |
$strErrorDesc = $e->getMessage().'Something went wrong! Please contact support.'; |
26 |
$strErrorHeader = 'HTTP/1.1 500 Internal Server Error'; |
27 |
}
|
28 |
} else { |
29 |
$strErrorDesc = 'Method not supported'; |
30 |
$strErrorHeader = 'HTTP/1.1 422 Unprocessable Entity'; |
31 |
}
|
32 |
|
33 |
// send output
|
34 |
if (!$strErrorDesc) { |
35 |
$this->sendOutput( |
36 |
$responseData, |
37 |
array('Content-Type: application/json', 'HTTP/1.1 200 OK') |
38 |
);
|
39 |
} else { |
40 |
$this->sendOutput(json_encode(array('error' => $strErrorDesc)), |
41 |
array('Content-Type: application/json', $strErrorHeader) |
42 |
);
|
43 |
}
|
44 |
}
|
45 |
}
|
Es importante notar que la clase UserController
amplía la clase BaseController
. Idealmente, esta clase contendría los métodos de acción que están asociados con los puntos finales de REST que están definidos para la entidad usuaria. En nuestro caso, por ejemplo, el punto final /user/list REST
corresponde al método listAction
. De esta manera, también se pueden definir otros métodos para otros puntos finales de REST.
El método listAction
se utiliza para obtener una lista de usuarios de la base de datos MySQL. Contiene toda la lógica del final de /user/list REST
.
En el método listAction
, hemos iniciado un par de variables como $requestMethod
y $arrQueryStringParams
en primer lugar. Después, comprobamos que si el usuario ha llamado al punto usuario/lista
con el método OBTÉN
, de lo contrario no procesaremos más. Finalmente, creamos el objeto UserModel
y llamamos al método getUsers
para obtener una lista de usuarios de una base de datos. También hemos usado la función json_encode
para convertir un orden en un objeto JSON antes de que lo envíen al usuario.
Por último, hemos usado el método sendOutput
para enviar la respuesta JSON al usuario. Es importante notar que el valor del entero del tipo de contenido de respuesta está fijado en application/json
, ya que estamos enviando la respuesta JSON.
De manera similar, se podrían definir otros métodos también para otros puntos finales.
El archivo index.php
El archivo index.php es el punto de entrada de nuestra aplicación. Veamos cómo se ve.
1 |
<?php
|
2 |
require __DIR__ . "/inc/bootstrap.php"; |
3 |
|
4 |
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); |
5 |
$uri = explode( '/', $uri ); |
6 |
|
7 |
if ((isset($uri[2]) && $uri[2] != 'user') || !isset($uri[3])) { |
8 |
header("HTTP/1.1 404 Not Found"); |
9 |
exit(); |
10 |
}
|
11 |
|
12 |
require PROJECT_ROOT_PATH . "/Controller/Api/UserController.php"; |
13 |
|
14 |
$objFeedController = new UserController(); |
15 |
$strMethodName = $uri[3] . 'Action'; |
16 |
$objFeedController->{$strMethodName}(); |
17 |
?>
|
Primero, hemos usado funciones parse_url
y explotar
para iniciar segmentos de URI en la variable de orden $uri
. Después, validamos los segmentos de URI. Finalmente, hemos iniciado el controlador de usuario
y llamado al método de acción correspondiente.
Con eso hemos creado todos los archivos necesarios en nuestra aplicación demo REST. En la siguiente sección veremos cómo llamarlo desde la perspectiva del usuario final.
Cómo llamar a nuestra API REST
En esta sección veremos cómo llamar a nuestra aplicación de demostración. En nuestra aplicación, hemos construido un punto final REST para obtener una lista de usuarios.
Veamos cómo se ve el URL de nuestro punto final:
1 |
// https://localhost/index.php/{MODULE_NAME}/{METHOD_NAME}?limit={LIMIT_VALUE}
|
2 |
https://localhost/index.php/user/list?limit=20 |
Si pudieran recordar el archivo index.php, comprobamos que si la variable $uri[2]
está fijada en usuario
. Además, el valor de la variable $uri[3]
funcionaría como nombre del método. En el caso anterior, la variable $uri[3]
está fijada en lista
. Así, terminaría llamando el método listAction
de la clase UserController
.
La salida debería verse así:
1 |
[
|
2 |
{
|
3 |
"user_id":1, |
4 |
"username":"Bob", |
5 |
"user_email":"bob@gmail.com", |
6 |
"user_status":0 |
7 |
},
|
8 |
{
|
9 |
"user_id":2, |
10 |
"username":"John", |
11 |
"user_email":"john@gmail.com", |
12 |
"user_status":1 |
13 |
},
|
14 |
{
|
15 |
"user_id":3, |
16 |
"username":"Mark", |
17 |
"user_email":"mark@gmail.com", |
18 |
"user_status":1 |
19 |
},
|
20 |
{
|
21 |
"user_id":4, |
22 |
"username":"Ville", |
23 |
"user_email":"ville@gmail.com", |
24 |
"user_status":0 |
25 |
}
|
26 |
]
|
Como pueden ver, regresa una lista de usuarios como un objeto de JSON. Además, si hay algún error de aplicación, sería devuelto como un objeto de JSON también para fines de devanamiento.
Conclusión
Hoy discutimos cómo construir una aplicación de REST con PHP y MySQL. Para fines de demostración, creamos una aplicación de demostración que permite obtener una lista de usuarios de la base de datos MySQL a través de la API REST.