Fecshop 服务原理
Fecshop 服务原理
fecshop服务是一个公用性的底层,为各个应用系统提供底层服务
1.原则约定
各个“应用系统”,譬如appfront,appadmin,apphtml5,是一个独立的文件结构 ,他们都有独立的模块,里面有controller(控制层) block(数据中间逻辑处理层) theme(模板view层) ,但是没有model层,在原则上约定,各个“应用系统”不能直接访问model, 只能访问Fecshop Service(服务),来进行数据的获取和处理工作,然后由service层 访问model层进行数据的获取处理等工作。
2.服务层的功能粒度:
首先,对于model层的函数粒度,对应的是数据的操作,譬如更改某一行数据,添加一行数据等,这个地球人都知道。
对于service层的函数粒度,一般是我们语言描述需求的最小粒度,譬如:把一个产品加入购物车, 删除购物车的某个产品,调出某个分类下的产品,登录用户,计算产品的最终价格,等等,对于上面的这些最小的 语言描述粒度,会在服务层实现,然后直接访问该服务中的方法即可。
3.实现原理
3.1 实例化过程:
当我们执行 Yii::$service->cart
,就会访问fecshop cart service 服务
当执行Yii::$service->cart->coupon
就会访问 cart的子服务coupon。
下面是实例化原理:
在index.php入口文件中可以看到如下代码:
new fecshop\services\Application($config['services']);
unset($config['services']);
查看 fecshop\services\Application.php的代码如下:
<?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);
}
}
service配置,譬如:@fecshop\config\services\Cart.php
<?php
/**
* FecShop file.
* @link http://www.fecshop.com/
* @copyright Copyright (c) 2016 FecShop Software LLC
* @license http://www.fecshop.com/license/
*/
return [
'cart' => [
'class' => 'fecshop\services\Cart',
# 子服务
'childService' => [
'quote' => [
'class' => 'fecshop\services\cart\Quote',
],
'quoteItem' => [
'class' => 'fecshop\services\cart\QuoteItem',
],
'info' => [
'class' => 'fecshop\services\cart\Info',
/**
* 单个sku加入购物车的最大个数。
*/
'maxCountAddToCart' => 100,
# 上架状态产品加入购物车时,
# 如果addToCartCheckSkuQty设置为true,则需要检查产品qty是否>购买qty,
# 如果设置为false,则不需要,也就是说产品库存qty小于购买qty,也是可以加入购物车的。
'addToCartCheckSkuQty' => true,
],
'coupon' => [
'class' => 'fecshop\services\cart\Coupon',
],
],
],
];
Yii::$service->cart 就是cart服务
Yii::$service 对应的是 fecshop\services\Application
当执行Yii::$service->cart时,找不到cart变量就会执行 __get()魔术方法,进而执行
getChildService(),将上面cart配置的class对应的文件fecshop\services\Cart
,
进行实例化,也就是说 Yii::$service->cart 对应的是 fecshop\services\Cart 实例化的对象,
如果下次使用 Yii::$service->cart ,不会再实例化对象,FecShop Service是单例模式。
Fecshop子服务:Yii::$service->cart->coupon,通过上面的配置,会实例化
fecshop\services\cart\Coupon
,子服务的原理和服务类似,都是单例模式。
3.2 关于service类
上面讲解了 Yii::$service->cart,如何找到 fecshop\services\Cart的步骤,下面详细讲述 service类。
所有的服务类,譬如上面说的cart服务,都必须继承
@fecshop\services\Service
。
里面的方法都必须以action开头,和controller中类似,
譬如执行 Yii::$service->cart->addProductToCart($item)
,对应的是
fecshop\services\Cart中的 protected function actionAddProductToCart($item)
方法。
原理为:当访问 addProductToCart时,由于找不到该函数,就会执行
@fecshop\services\Service->__call()
魔术方法,然后由魔术方法,
将 addProductToCart
改成 actionAddProductToCart
,然后去查找函数,就会找到
,这样做的好处是,可以在__call()
函数中记录每一个service的方法调用开始时间
和结束时间,这样就可以更好的调试出来哪一个service方法耗费的时间长,
这个是为了更好地统计各个services的状况,譬如:排查耗费时间最长的services,
使用最频繁的services等,
当然会耗费一定的时间,
在线上可以关掉log记录时间的功能,也可以间断性的手动开启,进行线上调试。
关于services log的开启:@fecshop\config\services\Helper
中看到如下配置:
return [
'helper' => [
'class' => 'fecshop\services\Helper',
# 子服务
'childService' => [
'ar' => [
'class' => 'fecshop\services\helper\AR',
],
'log' => [
'class' => 'fecshop\services\helper\Log',
'log_config' => [
# service log config
'services' => [
# if enable is false , all services will be close
'enable' => false, # 这里可以开启services log功能。
通过配置helper log服务的enable设置为true,可以开启services的日志功能
当然helper log服务还有其他的一些设置,具体请参看详细代码。
当然,您可以把服务中的类函数定义成public
,函数名不以action
开头,
这种方式定义的函数,开启services log,不会被记录,因为直接找到函数名,不会
访问魔术方法__call()
。
更多建议: