Introduction
In the digital world, we are in Machine Leaning Phase. Where are need to everything on lighting speed. Data storing as we need, in our custom formant, and their availability, stability should be done on finger tips with low infrastructural cost. So I am proposing the solution open source solution with now cost.
We are going to us APACHE as web server, PHP an scripting language, Elasticsearch as NOSQL, non-structured database and Zend Framework 3 (ZF3) as development framework. So we are using LAMP (completely open source and no cost involve).
Background
I was looking for secure, robust, loose binded, agile, configurable, superfast data availability and the budged, hence chosen then Ubuntu OS, Apache web server, PHP web scripting complier and Elasticsearch as robust, non-structured database.
Business Value:
- Cost effective
- Reducing Development and Maintains cost
- Improving the business process
Using the code
Before starting I assume that development environment is up and running, however ready then please find the below list to setup the environment.
Now we are ready with environment, now lest setup the module in ZF3 for fetching the records / document from our non-relations database, before that please insert the data in Elasticsearch (https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/_indexing_documents.html).
Step 1:
Add the Elasticsearch library in the root “vendor” folder. You can get the Elasticsearch library from https://github.com/elastic/elasticsearch-php
Step2:
Open the “composer.json” file and add the below code in size “require” array
"require": {
.....
"elasticsearch/elasticsearch": "^5.0",
.....
}
Step 3:
Creat log folder for to tracking application level error. So in the root directory we are creating the “data/log” folder. And giving the read, write and execute permission to “log” folder.
Steps 4:
Not let’s create the Elasticsearch connection configuration details, which is accessible across the all application module. So open the “config/autoload” folder and add the “local.php” file if not exists. Same file are got used to adding the local configuration details. Add below Elasticsearch connection configuration in same file such as…
return[
…
'elasticsearch' => [
'connections' => [
'justbuylive' => [
'index' => "es_local",
'host' => 'localhost',
'port' => '9200',
'scheme' => 'http'
]
],
'type'=>[
'ES_CLIENT_MASTER'=>'client_master'
]
],
..
]
Step 5:
Now it’s time to creating “modules” with routing and configuration so first thing first. Now creating our custom module with name “Api”. For same our folder structure should be look like
[project name]
--config
----autoload
------local.php
----module.config.php
--data
----log
--module
----Api
------config
--------module.config.php
------src
--------Controller
----------Factory
------------RetialerdataserviceControllerFactory.php
----------Plugin
------------ DefaultPlugin.php
------------Factory
------------RetialerdataserviceController.php
--------Entity
--------Listener
--------Repository
------------RetailerdataserviceRepository.php
--------Service
----------Factory
------------RetialerdataserviceManagerFactory.php
----------RetailerdataserviceManager.php
--------Module.php
--vendor
----elasticsearch
------elasticsearch
Step 6:
Creating “module/Api/config/module.config.php” Api module configuration and routing file. Contains below code…
<!--?php
namespace Api;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
return [
'doctrine' =----> [
'driver' => [
__NAMESPACE__ . '_driver' => [
'class' => AnnotationDriver::class,
'cache' => 'array',
'paths' => [__DIR__ . '/../src/Entity']
],
'orm_default' => [
'drivers' => [
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
],
],
'orm_accesslog' => [
'drivers' => [
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
],
],
]
],
'router' => [
'routes' => [
'retialerdataservice' => [
'type' => Literal::class,
'options' => [
'route' => '/getretailer',
'defaults' => [
'__NAMESPACE__' => 'Api\Controller',
'controller' => Controller\RetialerdataserviceController::class,
'action' => 'index',
],
],
],
'/' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'__NAMESPACE__' => 'Api\Controller',
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\RetialerdataserviceController::class => Controller\Factory\RetialerdataserviceControllerFactory::class,
],
],
'controller_plugins' => [
'invokables' => [
'DefaultPlugin' => 'Api\Controller\Plugin\DefaultPlugin',
]
],
'service_manager' => [
'factories' => [
Service\RetialerdataserviceManager::class => Service\Factory\RetialerdataserviceManagerFactory::class,
'ElasticSearch' => Service\Factory\ElasticClientFactory::class,
]
],
'view_manager' => [
'strategies' => [
'ViewJsonStrategy',
],
],
];
Step 7:
Now it’s time to create the controller, which is responsible to perform / process the requested. “module/Api/src/Controller/RetialerdataserviceController.php”. controller contains below line of code…
<!--?php
namespace Api\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;
class RetialerdataserviceController extends AbstractActionController{
private $_objRetailerDataServiceObject;
public function __construct($pObjRetailerDataServiceManager){
$this--->_objRetailerDataServiceObject = $pObjRetailerDataServiceManager;
}
public function indexAction(){
$strRequestContentArr = json_decode($this->getRequest()->getContent(), true);
$strResponseDataArr = array("status" => 0, "message" => "Invalid Requests.");
if(!empty($strRequestContentArr)){
$strResponseDataArr = $this->_objRetailerDataServiceObject->getRetailerDetails($strRequestContentArr);
}else{
$strResponseDataArr = array("status" => 0, "message" => "Invalid Requests.");
}
$this->setLog($strResponseDataArr);
return new JsonModel($strResponseDataArr);
}
public function setLog($pDataSet){
$strFileName = date('Y-m-d');
$strDataLogPath = 'data/log';
if (!file_exists($strDataLogPath) && !is_dir($strDataLogPath)) {
mkdir($strDataLogPath, 0777, true);
}
$strFileName = $strDataLogPath."/".$strFileName.".txt";
$strLogData = date('Y-m-d H:i:s')."\n----".json_encode($pDataSet)."\n";
file_put_contents($strFileName, $strLogData, FILE_APPEND | LOCK_EX);
unset($strLogData, $strFileName, $strDataLogPath);
}
}
Step 8:
Now create the factory class of the controller which helps to make “Service Manager” avabible in the controller and process the business logic and return the desired output by help of _invoking() auto execute method. Navigating to “module/Api/src/Controller/Factory/RetialerdataserviceControllerFactory.php” factory controller contains below line of code…
<?php
namespace Api\Controller\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Api\Controller\RetialerdataserviceController;
use Api\Service\RetialerdataserviceManager;
class RetialerdataserviceControllerFactory implements FactoryInterface{
public function __invoke(ContainerInterface $container,$requestedName,array $options = null){
$retialerdataserviceManager = $container->get(RetialerdataserviceManager::class);
return new RetialerdataserviceController($retialerdataserviceManager);
}
}
?>
Step 9:
Now creating the global method for making operation life easy using oops. Now creating the plugging for same with name “module/Api/src/Controller/Plugin/ DefaultPlugin.php” global resource, available across the Api module, contains below line of code…
<?php
namespace Api\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\View\Model\JsonModel;
use Zend\Config\Factory;
class DefaultPlugin extends AbstractPlugin{
public function getResultset($pObjResultSet = null, $pIntResultSetType = 0){
$strReturnArr = array();
$blnRecordExists = false;
$objResultSetContainer = null;
switch($pIntResultSetType){
case 0:
if(!empty($pObjResultSet)){
$blnRecordExists = true;
$objResultSetContainer = $pObjResultSet;
}
break;
case 1:
if(isset($pObjResultSet['hits']['total']) && ($pObjResultSet['hits']['total'] > 0)){
$blnRecordExists = true;
$objResultSetContainer = $pObjResultSet['hits']['hits'];
}
break;
case 2:
break;
}
if($blnRecordExists){
foreach($objResultSetContainer as $objResultSetContainerKey => $objResultSetContainerValue){
switch($pIntResultSetType){
case 0:
$strReturnArr[] = (array)$objResultSetContainerValue;
break;
case 1:
$strReturnArr[] = $objResultSetContainerValue['_source'];
break;
case 2:
break;
}
}
}
unset($blnRecordExists, $objResultSetContainer);
return $strReturnArr;
}
}
Step 10:
Now creating the repository class for processing the request, same repository class get invoke from service manager class. This class contains the business processing. Now creating the repository for processing the “module/Api/src/Repository/RetailerdataserviceRepository.php”. Class contains below line of code…
<?php
namespace Api\Repository;
use Api\Entity\ClientMaster;
use Api\Controller\Plugin\DefaultPlugin;
use Doctrine\ORM\EntityRepository;
use Elasticsearch\ClientBuilder;
use Elasticsearch\Transport;
use Zend\View\Model\JsonModel;
class RetailerdataserviceRepository extends EntityRepository{
private $_strEntityArr = array();
public function getRetailerDetailsFromElastica($pStrFilterArr){
$strReturnArr = array();
try{
$objElasticConfiguration = DefaultPlugin::getLocalCofigurationByKey('elasticsearch');
if(empty($objElasticConfiguration)){
return array("status" => 0, "message" => "Elasticsearch Configuration is not set");
exit;
}
$objESSettings = $objElasticConfiguration->connections->justbuylive;
$strHostArray[] = $objESSettings->host.':'.$objESSettings->port;
$esClientObj = ClientBuilder::create()->setHosts($strHostArray)->setRetries(2)->build();
$strSearchFilterArr = $this->_getFilterCritriaElasticsearch($pStrFilterArr);
$strFilterArr = [
'index' => $objElasticConfiguration->connections->justbuylive->index,
'type' => $objElasticConfiguration->type->ES_CLIENT_MASTER,
'body' => []
];
$strFilterArr['body']['query']['bool']['filter'] = $strSearchFilterArr;
unset($objElasticConfiguration, $objESSettings, $strHostArray, $strSearchFilterArr);
$strReturnArr = DefaultPlugin::getResultset($esClientObj->search($strFilterArr), 1);
unset($esClientObj);
} catch (Exception $ex) {
return array("status" => 0, "message" => $ex->getMessage());
exit;
}
return $strReturnArr;
}
}
Step 11:
Now creating service manager for giving the service to the API module as worker Now creating the repository for processing the “module/Api/src/Service/RetailerdataserviceManager.php”. Class contains below line of code…
<?php
namespace Api\Service;
use Api\Entity\ClientMaster;
use Exception;
use Zend\Config\Factory;
class RetailerdataserviceManager{
private $_entityManager;
public function __construct($pObjRetailerDataServiceEntityManager) {
$this->_entityManager = $pObjRetailerDataServiceEntityManager;
}
public function getRetailerDetails($pStrRequestArr){
return $this->_entityManager->getRepository(ClientMaster::class)->getRetailerDetailsFromElastica($pStrRequestArr);
}
}
Step 12:
Now creating service manager factory class which actually work an worker and helps to service manger to complete the module. Now creating the factory for processing the “module/Api/src/Service/Factory/ RetialerdataserviceManagerFactory.php”. Class contains below line of code…
<?php
namespace Api\Service\Factory;
use Interop\Container\ContainerInterface;
use Api\Service\RetailerdataserviceManager;
class RetialerdataserviceManagerFactory{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
$entityManager = $container->get('doctrine.entitymanager.orm_default');
return new RetailerdataserviceManager($entityManager);
}
}
Note: I havent' added the entity class "ClientMaster", so you can create your own ORM entity class and excust the same.
Happy Coding !!!
Points of Interest
- Business Value
- Operational Value
- Cost Saving
- Reusable