yii2 给Yii 添加一个变量,Yii::$service,并像组件component那样可以添加单例配置

在yii2中,组件是可以通过配置的方式添加到Yii::$app中的。

现在我们想添加一个Yii静态变量,$service,下面都称呼这个变量为服务

可以通过Yii::$service访问,然后添加服务,以及服务的子服务,
譬如我添加了一个cms服务,以及cms服务的子服务article。

那么我想访问cms服务,可以通过Yii::$service->cms访问,

如果我想访问cms服务的子服务,那么,我可以通过Yii::$service->cms->article访问。

上面的使用访问和yii2的组件很类似,是单例模式。下面说具体实现方式:

1.创建一个Yii.php添加$service参数:  @vendor/fancyecommerce/fecshop/yii/Yii.php

<?php
$dir = __DIR__ . '/../../../yiisoft/yii2';
require($dir.'/BaseYii.php');

/**
 * Yii is a helper class serving common framework functionalities.
 *
 * It extends from [[\yii\BaseYii]] which provides the actual implementation.
 * By writing your own Yii class, you can customize some functionalities of [[\yii\BaseYii]].
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Yii extends \yii\BaseYii
{
  public static $service;
  
}

spl_autoload_register(['Yii', 'autoload'], true, true);
Yii::$classMap = require($dir.'/classes.php');
Yii::$container = new yii\di\Container();

 

2.在入口文件,将对yii的Yii.php去掉,换成:

require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/yii/Yii.php');

也就是加载上面,我创建的文件。

然后修改index.php底部代码:

new fecshop\services\Application($config['services']);
unset($config['services']);
//var_dump($config);
$application = new yii\web\Application($config);
$application->run();

也就是添加了代码:

new fecshop\services\Application($config['services']);
unset($config['services']);

这个代码的作用是,给$service变量赋值,Yii::$service = fecshop\services\Application($config[‘services’]);

3.下面我们实现这个Application。

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Application
{
  public $childService;
  public $_childService;
  
  
  public function __construct($config = [])
    {
        Yii::$service 		= $this;
        $this->childService = $config;
    }
  /**
   * 得到services 里面配置的子服务childService的实例
   */
  public function getChildService($childServiceName){
    if(!$this->_childService[$childServiceName]){
      $childService = $this->childService;
      if(isset($childService[$childServiceName])){
        $service = $childService[$childServiceName];
        $this->_childService[$childServiceName] = Yii::createObject($service);
      }else{
        throw new InvalidConfigException('Child Service ['.$childServiceName.'] is not find in '.get_called_class().', you must config it! ');
      }
    }
    return $this->_childService[$childServiceName];
  }
  
  /**
   * 
   */
  public function __get($attr){
    return $this->getChildService($attr);
    
  }
  
}

4. 在配置中添加配置:

[

'services' => [
    'cms' => [
        'class' => 'fecshop\services\Cms',
        
        # 子服务
        'childService' => [
            'article' => [
                'class'             => 'fecshop\services\cms\Article',
                'storage' => 'mysqldb', # mysqldb or mongodb.
            ],
        ],
    ],

]

5.上面的配置,给cms添加了一个子服务article。

cms 对应的class的代码如下:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services;
use Yii;
use yii\base\InvalidValueException;
use yii\base\InvalidConfigException;
use fec\helpers\CSession;
use fec\helpers\CUrl;
/**
 * Breadcrumbs services
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Cms extends Service
{
  /**
   * cms storage db, you can set value: mysqldb,mongodb.
   */
  public $storage = 'mysqldb';
  
}

artile的代码如下:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services\cms;
use Yii;
use yii\base\InvalidValueException;
use yii\base\InvalidConfigException;
use fec\helpers\CSession;
use fec\helpers\CUrl;
use fecshop\services\Service;
use fecshop\services\cms\article\ArticleMysqldb;
use fecshop\services\cms\article\ArticleMongodb;
/**
 * Breadcrumbs services
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Article extends Service
{
  public $storage = 'mongodb';
  protected $_article;
  
  
  public function init(){
    if($this->storage == 'mongodb'){
      $this->_article = new ArticleMongodb;
    }else if($this->storage == 'mysqldb'){
      $this->_article = new ArticleMysqldb;
    }
  }
  /**
   * Get Url by article's url key.
   */
  public function getUrlByPath($urlPath){
    //return Yii::$service->url->getHttpBaseUrl().'/'.$urlKey;
    return Yii::$service->url->getUrlByPath($urlPath);
  }
  /**
   * get artile's primary key.
   */
  public function getPrimaryKey(){
    return $this->_article->getPrimaryKey();
  }
  /**
   * get artile model by primary key.
   */
  public function getByPrimaryKey($primaryKey){
    return $this->_article->getByPrimaryKey($primaryKey);
  }
  
  
  
  /**
   * @property $filter|Array
   * get artile collection by $filter
   * example filter:
   * [
   * 		'numPerPage' 	=> 20,  	
   * 		'pageNum'		=> 1,
   * 		'orderBy'	=> ['_id' => SORT_DESC, 'sku' => SORT_ASC ],
   * 		'where'			=> [
   * 			'price' => [
   * 				'?gt' => 1,
   * 				'?lt' => 10,
   * 			],
   * 			'sku' => 'uk10001',
   * 		],
   * 	'asArray' => true,
   * ]
   */
  public function coll($filter=''){
    return $this->_article->coll($filter);
  }
  
  /**
   * @property $one|Array , save one data .
   * @property $originUrlKey|String , article origin url key.
   * save $data to cms model,then,add url rewrite info to system service urlrewrite.                 
   */
  public function save($one,$originUrlKey){
    return $this->_article->save($one,$originUrlKey);
  }
  
  public function remove($ids){
    return $this->_article->remove($ids);
  }
  
}

他们继承的service类代码如下:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services;
use Yii;
use yii\base\Object;
use yii\base\InvalidConfigException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Service extends  Object
{
  public $childService;
  public $_childService;
  
  /**
   * 得到services 里面配置的子服务childService的实例
   */
  public function getChildService($childServiceName){
    if(!$this->_childService[$childServiceName]){
      $childService = $this->childService;
      if(isset($childService[$childServiceName])){
        $service = $childService[$childServiceName];
        $this->_childService[$childServiceName] = Yii::createObject($service);
      }else{
        throw new InvalidConfigException('Child Service ['.$childServiceName.'] is not find in '.get_called_class().', you must config it! ');
      }
    }
    return $this->_childService[$childServiceName];
  }
  
  /**
   * 
   */
  public function __get($attr){
    return $this->getChildService($attr);
    
  }
  
}

然后,我就可以使用了,按照上面的方法

Yii::$service->cms->$storage     # 获取存储方式,这个变量可以在service配置中注入。

Yii::$service->cms->article->remove($ids)  # cms服务对应子服务artile的删除功能。

 

由于这种方式是单例模式,因此,可以用这种方式,在controller和model之间,做一个中间服务层,方便日后的扩展和使用,譬如我如果把mysql换成了mongodb,只需要把这个服务的public方法 重新实现以下就可以了,目前fecshop采用了这种实现方式。