viernes, 15 de agosto de 2014

Implementing Web Service REST with SLIM microframework

A Quick Guide to implement a REST Web Service with database connection using SLIM MicroFramework

1.- SLIM INSTALLATION
Install composer in your project:
curl -s https://getcomposer.org/installer | php
Create a composer.json file in your project root:
{
    "require": {
        "slim/slim": "2.*"
    }
}
Install via composer:
php composer.phar install
Add this line to your application’s index.php file:
<?php
require 'vendor/autoload.php';

2.- Testing installation (Create an index.php to test) 
$app = new \Slim\Slim(array(
    'debug' => true
        ));

$app->get('/', function () {
    echo 'Hola Mundo'; 
});

$app->run();



3.- Optional Managing of VIRTUAL HOST  
<VirtualHost local.televisa:8081>
    ServerName local.pruebas
    DocumentRoot /Users/Tomas/projects/empresa/php
    SetEnv APPLICATION_ENV "development"
    <Directory /Users/Tomas/projects/empresa/php>
        DirectoryIndex index.php
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

4.-Dir Structure
4a) config.xml: Logger configuration file
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://logging.apache.org/log4php/">
    <appender name="myAppender" class="LoggerAppenderFile">
        <layout class="LoggerLayoutPattern">
            <param name="conversionPattern" value="%date [%logger] %message%newline" />
        </layout>
        <param name="file" value="/Users/Tomas/projects/empresa/php/WSSubscriber/logs/WSSS.log" />
    </appender>
    <root>
        <level value="DEBUG" />
        <appender_ref ref="myAppender" />
    </root>
</configuration>
4b) veo-config.ini: Database configuration and other conf parameters for the application.
[database]
host = localhost
username = dummyuser
password = dummypass
dbname = databasename
type = mysql
charset = 'utf8'

4c) GenericDAO:
<?php
include_once __DIR__.'/../init.php';
include_once __HELPER_PATH__.'/VeoConfiguration.php';

class GenericDAO {

    private $logger = NULL;
    
    protected $pdo_obj = NULL;     // Stores the open connection PDO object
    private $connection_string = NULL; // Used to build the database connection
    private $db_type = NULL;   // Stores the database type
    private $db_host = NULL;
    private $db_user = NULL;
    private $db_pass = NULL;
    private $db_name = NULL;
    private $db_charset = NULL;
    private $is_active = false;               // Checks to see if the connection is active

    protected $pdo_obj_rr = NULL;     // Stores the open connection PDO object
    private $connection_string_rr = NULL; // Used to build the database connection
    private $db_type_rr = NULL;   // Stores the database type
    private $db_host_rr = NULL;
    private $db_user_rr = NULL;
    private $db_pass_rr = NULL;
    private $db_name_rr = NULL;
    private $db_charset_rr = NULL;
    private $is_active_rr = false;               // Checks to see if the connection is active

    public function __construct() {
        $config = VeoConfiguration::getInstance();
        $this->logger = Logger::getLogger("GenericDAO");
        $this->db_host = $config->get('database', 'host');
        $this->db_user = $config->get('database', 'username');
        $this->db_pass = $config->get('database', 'password');
        $this->db_name = $config->get('database', 'dbname');
        $this->db_type = $config->get('database', 'mysql');
        $this->db_charset = $config->get('database', 'charset');
        $this->connection_string = "mysql:host=" . $this->db_host . ";dbname=" . $this->db_name;

        $this->db_host_rr = $config->get('databaserr', 'host');
        $this->db_user_rr = $config->get('databaserr', 'username');
        $this->db_pass_rr = $config->get('databaserr', 'password');
        $this->db_name_rr = $config->get('databaserr', 'dbname');
        $this->db_type_rr = $config->get('databaserr', 'mysql');
        $this->db_charset_rr = $config->get('databaserr', 'charset');
        $this->connection_string_rr = "mysql:host=" . $this->db_host_rr . ";dbname=" . $this->db_name_rr;

        return $this;
    }

    /*
     * only one connection allowed
     */
    protected function connect() {
        if (!$this->is_active) {
            try {
                $attrs = array(
                    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . $this->db_charset
                );
                $this->pdo_obj = new PDO($this->connection_string, $this->db_user, $this->db_pass, $attrs);
                $this->pdo_obj->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
                $this->is_active = true;
            } catch (PDOException $e) {
                $this->logger->error("ERROR:" . $e->getMessage());
                $this->logger->error("ERROR:" . $e->getTraceAsString());
                throw $e;
            }
        }
        return $this->is_active;
    }

    protected function connect_rr(){        
        if (!$this->is_active_rr) {
            try {
                $attrs = array(
                    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . $this->db_charset_rr
                );
                $this->pdo_obj_rr = new PDO($this->connection_string_rr, $this->db_user_rr, $this->db_pass_rr, $attrs);
                $this->pdo_obj_rr->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
                $this->is_active_rr = true;
            } catch (PDOException $e) {
                $this->logger->error("ERROR:" . $e->getMessage());
                $this->logger->error("ERROR:" . $e->getTraceAsString());
                throw $e;
            }
        }
        return $this->is_active_rr;
    }

    protected function disconnect() {
        $isDisconnect = false;
        if ($this->is_active) {
            unset($this->pdo_obj);
            $this->is_active = false;
            $isDisconnect = true;
        }
        return $isDisconnect;
    }

     protected function disconnect_rr() {
        $isDisconnect = false;
        if ($this->is_active_rr) {
            unset($this->pdo_obj_rr);
            $this->is_active_rr = false;
            $isDisconnect = true;
        }
        return $isDisconnect;
    }

    /*
     * SELECT (any)
     * Required: $sqlSelect
     */
    public function select($sqlSelect) {
        $this->connect_rr();        
        $exception = NULL;
        $arrayResult = NULL;
        try {
            $sql = $this->pdo_obj_rr->prepare($sqlSelect);
            $sql->execute();
            $arrayResult = $sql->fetchAll(PDO::FETCH_ASSOC);
        } catch (PDOException $e) {
            $exception = $e;
            $this->logger->error("ERROR:" . $e->getMessage());
            $this->logger->error("ERROR:" . $e->getTraceAsString());
        } 
        $this->disconnect_rr();
        if($exception){
            throw $exception;
        }            
        return $arrayResult;
    }

    /*
     * INSERT
     * Required: $strInsert sql insert
     */
    public function insert($strInsert, $getLastId = FALSE) {
        $exception = NULL;
        $totalRows = -1;
        $this->connect();
        try {
            $ins = $this->pdo_obj->prepare($strInsert);
            $ins->execute();
            if ($getLastId) {
                $totalRows = $this->pdo_obj->lastInsertId();
            } else {
                $totalRows = $ins->rowCount();
            }
        } catch (PDOException $e) {
            $this->logger->error("ERROR:" . $e->getMessage());
            $this->logger->error("ERROR:" . $e->getTraceAsString());
            $exception = $e;
        } 
        $this->disconnect();
        if($exception){
            throw $e; 
        }
        return $totalRows;
    }

    /*
     * DELETE
     * Required: Stament to sql delete
     */

    public function delete($strDelete) {
        $exception = NULL;
        $totalRows = -1;
        $this->connect();
        try {
            $del = $this->pdo_obj->prepare($strDelete);
            $del->execute();
            $totalRows = $del->rowCount();
        } catch (PDOException $e) {
            $this->logger->error("ERROR:" . $e->getMessage());
            $this->logger->error("ERROR:" . $e->getTraceAsString());
            $exception = $e; 
        }
        $this->disconnect();
        if($exception){
            throw $e; 
        }
        return $totalRows;
    }

    /*
     * UPDATE
     * Required: $strUpdate
     */
    public function update($strUpdate) {
        $exception = NULL;
        $totalRows = -1;
        $this->connect();
        try {
            $upd = $this->pdo_obj->prepare($strUpdate);
            $upd->execute();
            $totalRows = $upd->rowCount();            
        } catch (Exception $e) {
            $this->logger->error("ERROR:" . $e->getMessage());
            $this->logger->error("ERROR:" . $e->getTraceAsString());
            $exception = $e; 
        }
        $this->disconnect();
        
        if($exception){
            throw $e;
        }
        return $totalRows;
    }
}

?>

4d) VeoServicesDAO DAO APP specific functions.
<?php

include_once __DIR__ . '/../../init.php';
include_once __DATA_PATH__ . '/GenericDAO.php';

/**
 * Description of VeoServicesDAO
 *
 */
class VeoServicesDAO extends GenericDAO {

    private $logger;

    public function __construct() {
        parent::__construct();
        $this->logger = Logger::getLogger('VeoServicesDAO');
    }
 /**
     * Looks for entitlement using the contract number and the mso_id.
     * @param string $contract The contract code. 
     * @param string $mso_id The mso_id
     * @return array The subscriber information.
     */
    public function getEntitlementByContractAndCityId($contract, $city=null) {
        $sqlSelect = 'SELECT * FROM entitlement WHERE contract = "' . $contract . '"';
        if (!empty ($city))
                $sqlSelect.= ' AND city_id = "' .$city.'"';
        $this->logger->debug('getEntitlementByContractAndCity: ' . $sqlSelect);
        $result = $this->select($sqlSelect);
        if (count($result) === 1 )
            return $result[0];
        else if(count($result) > 1)
            return 1;
        else 
            return NULL;
    }
}

?>

4e) VeoConfiguration A singleton pattern Class implementation to get config file.
<?php

include_once __DIR__ . '/../init.php';

/**
 * This class implements the singleton pattern to return configuration values 
 * without opening and closing the file every time needed.
 * 
 * @author Abraham Soto
 */
class VeoConfiguration {
    
    //The reference to the file
    private $file_ini;
    
    //The ¡nstance of the class that will be returned when needed.
    private static $instance = NULL;
    
    /** Constructor of the class.*/
    private function __construct() {
        $this->file_ini = parse_ini_file(__CONFIG_FILE_INI__, true);
    }
    
    /** Singleton method, returns an instance of this class. */
    public static function getInstance(){
        if(self::$instance == NULL){
            self::$instance = new VeoConfiguration();
        }
        return self::$instance;
    }
    
    /**
     * This method returns the value set in the config file.
     * @param type $section optional, the section to which the config property belongs to.
     * @param type $name The name of the property.
     * 
     * @return string returns an string with the value obtained from the config file. if the value is not in the file returns NULL.
     */
    public function get($section, $name){
        return isset($this->file_ini[$section][$name]) ? 
            $this->file_ini[$section][$name] : '';
    }
    
 }
?>

4f) WSSS.log: Log file with write permissions.  

4g) Init.php Constants definition and Logger initially instantiation.
<?php
//Root path of the application.
define('__ROOT_PATH__', __DIR__);

//Path to the Config folder.
define('__CONFIG_PATH__', __ROOT_PATH__ . '/configs');

//Path to the veo-config.ini file.
define('__CONFIG_FILE_INI__', __CONFIG_PATH__ . '/veo-config.ini');
        
//Path to the helper class
define('__HELPER_PATH__', __ROOT_PATH__ . '/helpers');

//Path to the log files
define('__LOGGER_FILE_APP__', __ROOT_PATH__ . '/logs/veo_app_errors.log');

//Path to the library class
define('__LIBRARY_PATH__', __ROOT_PATH__ . '/vendor');

//Path to the dao class
define('__DATA_PATH__', __ROOT_PATH__ . '/data');
        
//Include logger and initialize it
include_once __LIBRARY_PATH__.'/log4php/Logger.php';
Logger::configure(__CONFIG_PATH__.'/config.xml');

?>

4h) index.php The main file with methods and logic implementation.
<?php

/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

require 'vendor/autoload.php';
include_once 'init.php';
include_once __DATA_PATH__ . '/dao/VeoServicesDAO.php';

$dao = new VeoServicesDAO();
$logger = Logger::getLogger("index.php");


$app = new \Slim\Slim(array(
    'debug' => false
        ));



$app->get('/', function () {
    echo 'Utilice <b>/consulta</b> para acceder al Web Service para consultar estatus de un suscriptor tomando como parametros POST contrato y opcionalmente una ciudad-<br>  Recibe POST:   <br> <b>{"contract":"value", "city":"value"}</b> <br> Regresa <br> <b>{"status":"value"}</b>'; 
});



/**
 * Consult.
 * Input:       It will take the POST containing a contract and optional parameter city.
 * Output:      Return the status for the subscriber contract. 
 * Test Cases:
 *  Success:            HTTP 200 OK
 *  Failure:            HTTP 500 Error
 *  Errors:             HTTP 400 Exception
 *      Exceptions:
 *          Missin Data Fields       HTTP 417 Exception
 * 
 * @todo finish the functions on this page
 */
$app->post('/consulta', function () use ($app, $logger,$dao) {
    $app->response()->header('Content-Type', 'application/json; charset=utf-8');
    $response = array();
    try {
        if($app->request()->getBody() === ''){
            throw new Exception('Missing Data Fields', 417);
        }
        $request = json_decode($app->request()->getBody());
        
        if(empty($request->contract)){
             throw new Exception('Missing Data Field contract', 417);
        }
            
        $city = !empty($request->city)?$request->city:NULL;
        $contract = $request->contract;
        
        $entitlement = $dao->getEntitlementByContractAndCityId($contract,$city);
        
        if (!$entitlement){
            throw new Exception('No data found', 418);
        }
        else if ($entitlement==1){
            throw new Exception('More than one register found', 419);
        }
        else{
            $response['status'] = $entitlement['account_status'];
        }
    } catch (Exception $e) {
        $response['error_code'] = $e->getCode();
        $response['error_message'] = $e->getMessage();
    }
    echo json_encode($response, JSON_UNESCAPED_UNICODE);
});
$app->run();
?>





No hay comentarios:

Publicar un comentario