This topic is locked

PHPRunner APP with RESTful API

4/23/2020 7:32:43 AM
PHPRunner Tips and Tricks
fhumanes author


In this example I am going to explain how, any application made in PHPRunner can have this interface so that other applications can consume data and business objects.
The example is made in PHPRunnner 10.2, but it could be done in version 9.8 or another, since the API is PHP and it is not generated by PHPRunner, although PHPRunner resources are used, such as the connection to the database.
Functional requirements of the application

  • The system must provide the list of second-hand vehicles, which are available for sale.
  • Vehicles will be coded by Make and Model.
  • The web application will have an interface to consult and maintain all this data.
  • The system will have a Restfull Api, with the following methods:

    . List of all available vehicles.

    . List of the Makes and Models of the catalog.

    . Registration of a new vehicle and if the Brand and / or Model are new, can of them in the catalogs.
    Data model


It is a very simple model because the objective of the example is to provide information from the Restfull API server.
It has been standardized to show that you can have, without complexity, the standardized model and have these APIs.
DEMO: https://fhumanes.com/car
Restfull API server description
To do this exercise I have relied on the information of Federico Guzmán, in his RESTful API article: How to make an API with PHP and Mysql ?.
It is very educational and very well explained, so I ask you to read it carefully to know the details of the exercise.
One of the most relevant aspects is the development testing tools, that is, Restfull API clients that we are going to use to check the functionality.

  • RESTClient, is a Firefox Add-on that works very well
  • Postman, desktop application that is in all operating systems and that supports both Restfull API as well as webservices and other data exchange protocols.


At the server code location level, I'm going to use the "restapi" directory, immediately below the application directory.


So the root directory for the access methods stays at: http: // localhost / car / restapi / v1 /.
The access methods remain:
1.- Consultation of all vehicles in the database.

GET http://localhost/car/restapi/v1/auto

2.- Consult the catalogs of the Makes and Models

GET http://localhost/car/restapi/v1/modelo

3.- Create a new vehicle. This option requires authentication

POSThttp://localhost/car/restapi/v1/auto

In the message header requires:

Authorization: 3d524a53c110e4c22463b10ed32cef9d

Content-Type: application/x-www-form-urlencoded
In Body (message body) requires passing parameters:
make=Citroen&model=Sara&year=2010&msrp=2563.15
Starting from the example of Federico Guzmán I have modified his code to have the persistence of the data in a Mysql database using the same and the same connection that we have in the PHPRunner application.
So the codes have been:
index.php. The main request management program.



<?php

/**

*

* @About: API Interface

* @File: index.php

* @Date: $Date:$ Nov-2015

* @Version: $Rev:$ 1.0

* @Developer: Federico Guzman (federicoguzman@gmail) and modified Fernando Humanes (frnandohumanes@gmail.com)

/* Los headers permiten acceso desde otro dominio (CORS) a nuestro REST API o desde un cliente remoto via HTTP

* Removiendo las lineas header() limitamos el acceso a nuestro RESTfull API a el mismo dominio

* Nótese los métodos permitidos en Access-Control-Allow-Methods. Esto nos permite limitar los métodos de consulta a nuestro RESTfull API

* Mas información: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

**/

header("Access-Control-Allow-Origin: *");

header('Access-Control-Allow-Credentials: true');

header('Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS');

header("Access-Control-Allow-Headers: X-Requested-With");

header('Content-Type: text/html; charset=utf-8');

header('P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"');

include_once '../include/Config.php';

require_once("../../include/dbcommon.php"); // DataBase PHPRunner

/* Puedes utilizar este file para conectar con base de datos incluido en este demo;

* si lo usas debes eliminar el include_once del file Config ya que le mismo está incluido en DBHandler

**/

//require_once '../include/DbHandler.php';

require '../libs/Slim/Slim.php';

\Slim\Slim::registerAutoloader();

$app = new \Slim\Slim();

/* Usando GET para consultar los autos */

$app->get('/auto', function() {



$response = array();

$autos = array();

$sql="SELECT brand.`Brand`,model.`Model`,car.`Year`,car.`Price` FROM car

join brand on (car.`brand_idbrand` = brand.`idbrand`)

join model on (car.`model_idmodel` = model.`idmodel`)

order by 1,2,3,4";

$rsSql=db_query($sql,$conn);

while ($data = db_fetch_array($rsSql)){

$autos[] = array('make'=>$data['Brand'], 'model'=>$data['Model'], 'year'=>$data['Year'], 'MSRP'=>number_format($data['Price'], 2, '.', ','));

}

$response["error"] = false;

$response["message"] = "Autos cargados: " . count($autos); //podemos usar count() para conocer el total de valores de un array

$response["autos"] = $autos;

echoResponse(200, $response);

});

/* Usando POST para crear un auto */

$app->post('/auto', 'authenticate', function() use ($app) {

// check for required params

verifyRequiredParams(array('make', 'model', 'year', 'msrp'));

$response = array();

//capturamos los parametros recibidos y los almacxenamos como un nuevo array

$param['make'] = $app->request->post('make');

$param['model'] = $app->request->post('model');

$param['year'] = $app->request->post('year');

$param['msrp'] = $app->request->post('msrp');



/* Podemos inicializar la conexion a la base de datos si queremos hacer uso de esta para procesar los parametros con DB */

require_once '../include/DBauto.php';

$db = new DbAuto();

/* Podemos crear un metodo que almacene el nuevo auto, por ejemplo: */

$auto = $db->createAuto($param);

if ( is_array($param) ) {

$response["error"] = false;

$response["message"] = "Auto creado satisfactoriamente!";

$response["auto"] = $param;

} else {

$response["error"] = true;

$response["message"] = "Error al crear auto. Por favor intenta nuevamente.";

}

echoResponse(201, $response);

});

/* Usando GET para consultar los modelos */

$app->get('/modelo', function() {



$response = array();

$modelos = array();

$sql="SELECT brand.`Brand`, model.`Model` FROM brand join model on (model.`brand_idbrand` = brand.`idbrand`) order by 1,2";

$rsSql=db_query($sql,$conn);

while ($data = db_fetch_array($rsSql)){

$modelos[] = array('make'=>$data['Brand'], 'model'=>$data['Model']);

}

$response["error"] = false;

$response["message"] = "Modelos cargados: " . count($modelos); //podemos usar count() para conocer el total de valores de un array

$response["modelos"] = $modelos;

echoResponse(200, $response);

});

/* corremos la aplicación */

$app->run();

/*********************** USEFULL FUNCTIONS **************************************/

/**

* Verificando los parametros requeridos en el metodo o endpoint

*/

function verifyRequiredParams($required_fields) {

$error = false;

$error_fields = "";

$request_params = array();

$request_params = $_REQUEST;

// Handling PUT request params

if ($_SERVER['REQUEST_METHOD'] == 'PUT') {

$app = \Slim\Slim::getInstance();

parse_str($app->request()->getBody(), $request_params);

}

foreach ($required_fields as $field) {

if (!isset($request_params[$field]) || strlen(trim($request_params[$field])) <= 0) {

$error = true;

$error_fields .= $field . ', ';

}

}



if ($error) {

// Required field(s) are missing or empty

// echo error json and stop the app

$response = array();

$app = \Slim\Slim::getInstance();

$response["error"] = true;

$response["message"] = 'Required field(s) ' . substr($error_fields, 0, -2) . ' is missing or empty';

echoResponse(400, $response);



$app->stop();

}

}



/**

* Validando parametro email si necesario; un Extra <img src='https://asprunner.com/forums/file.php?topicimage=1&fieldname=question&id=27323&image=4&table=forumtopics' class='bbc_emoticon' alt=';)' />

*/

function validateEmail($email) {

$app = \Slim\Slim::getInstance();

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {

$response["error"] = true;

$response["message"] = 'Email address is not valid';

echoResponse(400, $response);



$app->stop();

}

}



/**

* Mostrando la respuesta en formato json al cliente o navegador

* @param String $status_code Http response code

* @param Int $response Json response

*/

function echoResponse($status_code, $response) {

$app = \Slim\Slim::getInstance();

// Http response code

$app->status($status_code);



// setting response content type to json

$app->contentType('application/json');



echo json_encode($response);

}

/**

* Adding an intermediate layer of authentication for one or all methods, use as needed

* Revisa si la consulta contiene un Header "Authorization" para validar

*/

function authenticate(\Slim\Route $route) {

// Getting request headers

$headers = apache_request_headers();

$response = array();

$app = \Slim\Slim::getInstance();



// Verifying Authorization Header

if (isset($headers['Authorization'])) {

//$db = new DbHandler(); //utilizar para manejar autenticacion contra base de datos



// get the api key

$token = $headers['Authorization'];



// validating api key

if (!($token == API_KEY)) { //API_KEY declarada en Config.php



// api key is not present in users table

$response["error"] = true;

$response["message"] = "Acceso denegado. Token inválido";

echoResponse(401, $response);



$app->stop(); //Detenemos la ejecución del programa al no validar



} else {

//procede utilizar el recurso o metodo del llamado

}

} else {

// api key is missing in header

$response["error"] = true;

$response["message"] = "Falta token de autorización";

echoResponse(400, $response);



$app->stop();

}

}

?>


Config.php. The application control API key.



<?php

/**

* Database configuration



define('DB_USERNAME', '');

define('DB_PASSWORD', 'h');

define('DB_HOST', '');

define('DB_NAME', '');

*/

//referencia generado con MD5(uniqueid(<some_string>, true))

define('API_KEY','3d524a53c110e4c22463b10ed32cef9d');

/**

* API Response HTTP CODE

* Used as reference for API REST Response Header

*

*/

/*

200 OK

201 Created

304 Not Modified

400 Bad Request

401 Unauthorized

403 Forbidden

404 Not Found

422 Unprocessable Entity

500 Internal Server Error

*/

?>


DbAuto.php. The class that will facilitate the registration of new vehicles.



<?php

/**

*

* @About: ADD of a new car

* @File: DbAuto.php

* @Date: $Date:$ April 2020

* @Version: $Rev:$ 1.0

* @Developer: fernando humanes

**/

class DbAuto {



private $connDB;

// Field 'make', 'model', 'year', 'msrp'



function __construct() {

global $conn; // Connection data base of PHPRunner

$this->connDB = $conn;

}



public function createAuto($param)

{

$id_make = $this->buscaMake($param['make']);

$id_model = $this->buscaModel($id_make,$param['model']);

$year = $param['year'];

$msrp = $param['msrp'];

$sql="INSERT INTO car (brand_idbrand, model_idmodel, `Year`, Price) VALUES ($id_make,$id_model,$year,$msrp);";

$rsSql=db_query($sql,$this->connDB);

return $param;

}



private function buscaMake($make) {

$sql="SELECT idbrand, Brand FROM brand where upper(Brand) = upper('$make')";

$rsSql=db_query($sql,$this->connDB);

$row_cnt = $rsSql->num_rows;

$row= db_fetch_array($rsSql);

if ($row_cnt == 0) { // If it does not exist, a new catalog is registered

$sql="INSERT INTO brand (Brand) VALUES ('$make')";

$rsSql=db_query($sql,$this->connDB);

$id = $this->connDB->insert_id;

} else {

$id = $row['idbrand'];

}

return $id;

}



private function buscaModel($id_make,$model) {

$sql="SELECT idmodel, brand_idbrand, Model FROM model where brand_idbrand = $id_make and upper(Model) = upper('$model')";

$rsSql=db_query($sql,$this->connDB);

$row_cnt = $rsSql->num_rows;

$row= db_fetch_array($rsSql);



if ($row_cnt == 0) { // If it does not exist, a new catalog is registered

$sql="INSERT INTO model (brand_idbrand, Model) VALUES ($id_make,'$model')";

$rsSql=db_query($sql,$this->connDB);

$id = $this->connDB->insert_id;

} else {

$id = $row['idmodel'];

}

return $id;

}



}



?>


In my portal all the sources of the example and as always, for any questions or needs that arise from this example, contact me at [email="fernandohumanes@gmail.com"]In my portal all the sources of the example and as always, for any questions or needs that arise from this example, contact me at fernandohumanes@gmail.com[/email]