在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
$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
* @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.在入口文件index.php中,将对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。
/**
* 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);
}
}
在配置中添加配置:[
'services' => [
'cms' => [
'class' => 'fecshop\services\Cms',
# 子服务
'childService' => [
'article' => [
'class' => 'fecshop\services\cms\Article',
'storage' => 'mysqldb', # mysqldb or mongodb.
],
],
],
]
5.上面的配置,给cms添加了一个子服务article。
cms 对应的class的代码如下:
/**
* 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的代码如下:
/**
* 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类代码如下:
/**
* 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采用了这种实现方式。