原文链接: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.在入口文件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。
<?php
/**
* FecShop file.
*
* [@link](/member/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的代码如下:
<?php
/**
* FecShop file.
*
* [@link](/member/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](/member/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](/member/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采用了这种实现方式。
本文由 fecommerce 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。
微信
有两个疑问:
new fecshop\services\Application($config['services']);
之后为什么要 unset($config['services']);
? 有点不明白。
这个 Yii::$service
之后会有代码提示吗?
@forecho [[#1楼](#comment1)](#comment1) 在config里面所有的配置,最后都是当做参数传递给 yii\web\Application($config);
$application = new yii\web\Application($config);
$application->run();
如果不执行:unset($config['services'])]
, yii\web\Application
就会set,但是它没有services属性,就会报错。
Yii::$app 就是 new yii\web\Application($config);
, 代码如下:
public function __construct($config = [])
{
Yii::$app = $this;
new fecshop\services\Application($config['services']);
的原理和上面类似。
public function __construct($config = [])
{
Yii::$service = $this;
$this->childService = $config;
}
@forecho #1楼
Yii::$service 就是 new fecshop\services\Application($config['services']);
的单例模式
Yii::$app 就是 new yii\web\Application($config);
的单例模式,
在console, Yii::$app 就是 new yii\console\Application($config);
的单例模式,
Yii::$service->cms 就可以访问配置的一个服务类了,原理就是用魔术方式_get实现的,也就是从配置中读取,然后用容器创建,保存在一个成员变量里面,下次访问的时候,从成员变量里面查看是否存在,如果存在则直接读取,不存在则创建,也就是单例模式