65.9K
CodeProject 正在变化。 阅读更多。
Home

如何将 Elasticsearch 与 ZF3 集成

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.60/5 (3投票s)

2017 年 7 月 20 日

CPOL

3分钟阅读

viewsIcon

13920

在数字世界中,我们正处于机器学习阶段。我们需要以闪电般的速度完成一切。数据的存储,以我们想要的自定义格式,以及它们的可用性、稳定性,都应该以指尖的轻松程度完成,并且基础设施成本低廉。

引言

在数字世界中,我们正处于机器学习阶段。我们需要以闪电般的速度完成一切。数据的存储,以我们想要的自定义格式,以及它们的可用性、稳定性,都应该以指尖的轻松程度完成,并且基础设施成本低廉。因此,我提议用一个开源解决方案来解决这个问题,无需任何成本。

我们将使用 APACHE 作为 Web 服务器,PHP 作为脚本语言,Elasticsearch 作为 NOSQL 非结构化数据库,以及 Zend Framework 3 (ZF3) 作为开发框架。所以我们正在使用 LAMP(完全开源且不涉及任何成本)。

背景

我一直在寻找安全、健壮、松耦合、敏捷、可配置、超快数据可用并且预算可控的解决方案,因此选择了 Ubuntu 操作系统、Apache Web 服务器、PHP Web 脚本编译器以及 Elasticsearch 作为健壮的非结构化数据库。

业务价值

  • 成本效益高
  • 降低开发和维护成本
  • 改进业务流程

使用代码

在开始之前,我假设开发环境已经搭建并运行。如果还没有,请参考下面的列表来设置环境。 

现在环境已经准备就绪,接下来我们将设置 ZF3 中的模块来从我们的非关系型数据库中获取记录/文档。在此之前,请先在 Elasticsearch 中插入数据(https://elastic.ac.cn/guide/en/elasticsearch/client/php-api/current/_indexing_documents.html)。

 

步骤 1:  

将 Elasticsearch 库添加到根目录的“vendor”文件夹中。您可以从 https://github.com/elastic/elasticsearch-php 获取 Elasticsearch 库。

 

步骤 2

打开“composer.json”文件,并在“require”数组中添加以下代码:

"require": {
    .....
    "elasticsearch/elasticsearch": "^5.0",
    .....
}

 

步骤 3

创建日志文件夹以跟踪应用程序级别的错误。因此,我们在根目录下创建“data/log”文件夹。并为“log”文件夹授予读、写和执行权限。

 

步骤 4

现在让我们创建 Elasticsearch 连接配置详细信息,该详细信息可在所有应用程序模块中访问。因此,打开“config/autoload”文件夹,如果不存在,则添加“local.php”文件。同一个文件将用于添加本地配置详细信息。在同一个文件中添加以下 Elasticsearch 连接配置,例如...

return[
…
'elasticsearch' => [
        'connections' => [
             'justbuylive' => [
                 'index'  => “es_local”,
                 'host'   => 'localhost',
                 'port'   => '9200',
                 'scheme' => 'http'
             ]
         ],
        'type'=>[
            'ES_CLIENT_MASTER'=>'client_master'
        ]
    ],
..
]

 

步骤 5

现在是时候创建“modules”及其路由和配置了。首先,我们用名称“Api”创建自定义模块。为此,我们的文件夹结构应如下所示:

[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

 

步骤 6

创建“module/Api/config/module.config.php” Api 模块配置文件和路由文件。包含以下代码...

<!--?php
/**
 * @link      http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

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',
        ],
    ],
];

 

步骤 7

现在是时候创建控制器了,它负责执行/处理请求。“module/Api/src/Controller/RetialerdataserviceController.php”。控制器包含以下代码行...

<!--?php
/**************************************************************************
 * Purpose   : Manging the retailer data request and response 
 * Created By: Jaiswar Vipin Kumar R.
 **************************************************************************/

namespace Api\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;

class RetialerdataserviceController extends AbstractActionController{
    /* Varaibe delclaration */
    private $_objRetailerDataServiceObject;

    /***************************************************************************
     * Purpose      : Default method get executed.
     * Inputs       : $pObjRetailerDataServiceManager :: Retailer Data servier 
     *                                                   Manager object refrence
     * Return       : None.
     * Created By   : Jaiswar Vipin Kumar R.
    ****************************************************************************/
    public function __construct($pObjRetailerDataServiceManager){
        /* Assign manager refrence */
        $this--->_objRetailerDataServiceObject = $pObjRetailerDataServiceManager;
    }

    /***************************************************************************
     * Purpose      : First Operation action need to be execute to process the 
     *                request.
     * Inputs       : None.
     * Return       : None.
     * Created By   : Jaiswar Vipin Kumar R.
    ****************************************************************************/
    public function indexAction(){
        /* Getting the consumer service request */
        $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.");
        }
        /* Setting data log */
        $this->setLog($strResponseDataArr);
        
        /* return the response is JSON Model Format */
        return  new JsonModel($strResponseDataArr);
    }

    /***************************************************************************
     * Purpose      : Setting log.
     * Inputs       : $pDataSet :: Data Set.
     * Return       : None.
     * Created By   : Jaiswar Vipin Kumar R.
    ****************************************************************************/
    public function setLog($pDataSet){
        /* Creating Today's log file name */
        $strFileName = date('Y-m-d');

        /* Setting the data log folder relative path */
        $strDataLogPath = 'data/log';
        
        /* Checking is data log exists */
        if (!file_exists($strDataLogPath) && !is_dir($strDataLogPath)) {
            /* if not exists then create with User - Group - Other RWX permission */
            mkdir($strDataLogPath, 0777, true);      
        }
        
        /* Setting the file name path */
        $strFileName = $strDataLogPath."/".$strFileName.".txt";
        
        /* Creating log data set */
        $strLogData = date('Y-m-d H:i:s')."\n----".json_encode($pDataSet)."\n";

        /* writing in the log file */
        file_put_contents($strFileName, $strLogData, FILE_APPEND | LOCK_EX);
        
        /* removed used variables */
        unset($strLogData, $strFileName, $strDataLogPath);
    }
}

 

步骤 8

现在创建控制器的工厂类,该类有助于将“Service Manager”集成到控制器中,处理业务逻辑,并通过 _invoking() 自动执行方法返回所需输出。导航到“module/Api/src/Controller/Factory/RetialerdataserviceControllerFactory.php”,工厂控制器包含以下代码行...

<?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);			
        // Instantiate the controller and inject dependencies
        return new RetialerdataserviceController($retialerdataserviceManager);
    }
}

?>

 

步骤 9

现在创建全局方法,以面向对象的方式简化操作。现在我们使用名称“module/Api/src/Controller/Plugin/ DefaultPlugin.php”来创建插件,这是一个全局资源,在 Api 模块中可用,包含以下代码行...

<?php
namespace Api\Controller\Plugin; 

use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\View\Model\JsonModel;
use Zend\Config\Factory;

/* 
 * 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.
 */

class DefaultPlugin extends AbstractPlugin{
    /*****************************************************************************************/
    /* Purpose  : Creating the result set as array response.
     * Inputs   : $pObjResultSet :: Result Set,
     *          : $pIntResultSetType :: 0: MYSQL, 1: Elasticsearch, 2: Postgrays
     * Returns  : Result set array.
     * CreatedBy: Jaiswar VIpin Kumar R.
    /*****************************************************************************************/
    public function getResultset($pObjResultSet = null, $pIntResultSetType = 0){
        /* Variable initialization */
        $strReturnArr           = array();
        $blnRecordExists        = false;
        $objResultSetContainer  = null;
        
        /* Based on the data source type checkig the result set */
        switch($pIntResultSetType){
            /* MYSQL */
            case 0:
                /* if result set not empty then do needful */
                if(!empty($pObjResultSet)){
                    /* Value overridding */
                   $blnRecordExists         = true;
                   $objResultSetContainer   = $pObjResultSet;
                }
                break;
            /* Elasticsearch */
            case 1:
                /* if result set not empty then do needful */
                if(isset($pObjResultSet['hits']['total']) && ($pObjResultSet['hits']['total'] > 0)){
                    /* Value overridding */
                   $blnRecordExists         = true; 
                   $objResultSetContainer   = $pObjResultSet['hits']['hits'];
                }
                break;
            /* Postgray */
            case 2:
                break;
        }
        
        /* if record exists then do needful */
        if($blnRecordExists){
            /* iterating the loop */
            foreach($objResultSetContainer as $objResultSetContainerKey => $objResultSetContainerValue){
                /* Based on the data source type checkig the result set */
                switch($pIntResultSetType){
                    /* MYSQL */
                    case 0:
                        /* Setting the value in array format */
                        $strReturnArr[] = (array)$objResultSetContainerValue;
                        break;
                    /* Elasticsearch */
                    case 1:
                        /* Setting the value in array format */
                        $strReturnArr[] = $objResultSetContainerValue['_source'];
                        break;
                    /* Postgray */
                    case 2:
                        break;
                }
            }
        }
        
        /* removed used variabled */
        unset($blnRecordExists, $objResultSetContainer);
        
        /* return the result set */
        return $strReturnArr;
    }
}

 

第 10 步

现在创建存储库类来处理请求,同一个存储库类将从服务管理器类调用。此类包含业务处理。现在创建用于处理“module/Api/src/Repository/RetailerdataserviceRepository.php”的存储库。类包含以下代码行...

<?php
/**************************************************************************
 * Purpose   : Manging the retailer DML / ORM operation 
 * Created By: Jaiswar Vipin Kumar R.
 **************************************************************************/

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();
    
    
    /***************************************************************************
     * Purpose      : Get the retailer details.
     * Inputs       : None.
     * Return       : Retailer Details.
     * Created By   : Jaiswar Vipin Kumar R.
    ****************************************************************************/
    public function getRetailerDetailsFromElastica($pStrFilterArr){
        /* Variable initialization */
        $strReturnArr   = array();
        
        try{
            /* Getting the local file refrence object */
            $objElasticConfiguration    = DefaultPlugin::getLocalCofigurationByKey('elasticsearch');
            
            /* if configuration is not set then do needful */
            if(empty($objElasticConfiguration)){
                /* return error message */
                return array("status" => 0, "message" => "Elasticsearch Configuration is not set");
                /* Stop the execution */
                exit;
            }
            
            /* Getting ES connection objects */
            $objESSettings = $objElasticConfiguration->connections->justbuylive;
            /* Creating ES hosts array */
            $strHostArray[] = $objESSettings->host.':'.$objESSettings->port;
            
            /* Creating Elasticssearch Objects */
            $esClientObj = ClientBuilder::create()->setHosts($strHostArray)->setRetries(2)->build();
            /* Get ES filter */
            $strSearchFilterArr    = $this->_getFilterCritriaElasticsearch($pStrFilterArr);
            
            /* Filter Array */
            $strFilterArr = [
                                'index'  => $objElasticConfiguration->connections->justbuylive->index,
                                'type'   => $objElasticConfiguration->type->ES_CLIENT_MASTER,
                                'body' => []
                            ];
            $strFilterArr['body']['query']['bool']['filter']    = $strSearchFilterArr;
            
            /* removed used variables */
            unset($objElasticConfiguration, $objESSettings, $strHostArray, $strSearchFilterArr);
            
            /* Fetching resule set and creating the return result set */
            $strReturnArr = DefaultPlugin::getResultset($esClientObj->search($strFilterArr), 1);
            
            /* removed used variables */
            unset($esClientObj);
            
        } catch (Exception $ex) {
             /* return error message */
            return array("status" => 0, "message" => $ex->getMessage());
            /* Stop the execution */
            exit;
        }
        
        /* return resultset */
        return $strReturnArr;
    }
}

 

第 11 步

现在创建服务管理器,为 API 模块提供服务作为工作单元。现在创建用于处理“module/Api/src/Service/RetailerdataserviceManager.php”的存储库。类包含以下代码行...

<?php
/**************************************************************************
 * Purpose   : Retailer Data Service Manager to process the configuration
 *             DQL and user request and return the appropriate response.
 * Created By: Jaiswar Vipin Kumar R.
 **************************************************************************/

namespace Api\Service;

use Api\Entity\ClientMaster;
use Exception;
use Zend\Config\Factory;

class RetailerdataserviceManager{
    /**
     * Doctrine entity manager.
     * @var Doctrine\ORM\EntityManager
     */
    /* varaible initialization */
    private $_entityManager;

    /***************************************************************************
     * Purpose      : Default method get executed in the manager
     * Inputs       : $pObjRetailerDataServiceEntityManager :: Retailer Data servier 
     *                Manager object refrence
     * Return       : None.
     * Created By   : Jaiswar Vipin Kumar R.
    ****************************************************************************/
    public function __construct($pObjRetailerDataServiceEntityManager) {
        $this->_entityManager = $pObjRetailerDataServiceEntityManager;
    }

    /***************************************************************************
     * Purpose      : Get the requested retailer details.
     * Inputs       : $pStrRequestArr :: Customer request array.
     * Return       : Retailer data set is any.
     * Created By   : Jaiswar Vipin Kumar R.
    ****************************************************************************/
    public function getRetailerDetails($pStrRequestArr){
       /* Return the Retailer details */
            return $this->_entityManager->getRepository(ClientMaster::class)->getRetailerDetailsFromElastica($pStrRequestArr);
    }
}

 

第 12 步

现在创建服务管理器工厂类,它实际上作为工作单元,并帮助服务管理器完成模块。现在创建用于处理“module/Api/src/Service/Factory/ RetialerdataserviceManagerFactory.php”的工厂。类包含以下代码行...

<?php
/***************************************************************************************************
 * Purpose   : This is the factory class for ConfigManager service. The purpose of the factory
 *             is to instantiate the service and pass it dependencies (inject dependencies)
 * Created By: Jaiswar Vipin Kumar R.
 ***************************************************************************************************/
namespace Api\Service\Factory;

use Interop\Container\ContainerInterface;
use Api\Service\RetailerdataserviceManager;

class RetialerdataserviceManagerFactory{
    /***************************************************************************************************
     * Purpose  : This method creates the ConfigManager service and returns its instance. 
     * Inputs   : $container :: Contianer Interface instant object,
     *          : $requestedName :: Request handller Name
     *          : $options  :: Configuration Variables
     * Returns  : Service configuration manager object
     ***************************************************************************************************/
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null){   
        /* Getting the ORM connection configuration value */
        $entityManager = $container->get('doctrine.entitymanager.orm_default');
        
        /* Return the retailer manager insetance */
        return new RetailerdataserviceManager($entityManager);
    }
}

 

注意:我没有添加实体类“ClientMaster”,您可以创建自己的 ORM 实体类并执行它。

编码愉快!!!

关注点

  1. 业务价值
  2. 运营价值
  3. 节省成本
  4. 可复用的
© . All rights reserved.