fecshop服務(wù)是一個(gè)公用性的底層,為各個(gè)應(yīng)用系統(tǒng)提供底層服務(wù)
各個(gè)“應(yīng)用系統(tǒng)”,譬如appfront,appadmin,apphtml5,是一個(gè)獨(dú)立的文件結(jié)構(gòu) ,他們都有獨(dú)立的模塊,里面有controller(控制層) block(數(shù)據(jù)中間邏輯處理層) theme(模板view層) ,但是沒有model層,在原則上約定,各個(gè)“應(yīng)用系統(tǒng)”不能直接訪問model, 只能訪問Fecshop Service(服務(wù)),來(lái)進(jìn)行數(shù)據(jù)的獲取和處理工作,然后由service層 訪問model層進(jìn)行數(shù)據(jù)的獲取處理等工作。
首先,對(duì)于model層的函數(shù)粒度,對(duì)應(yīng)的是數(shù)據(jù)的操作,譬如更改某一行數(shù)據(jù),添加一行數(shù)據(jù)等,這個(gè)地球人都知道。
對(duì)于service層的函數(shù)粒度,一般是我們語(yǔ)言描述需求的最小粒度,譬如:把一個(gè)產(chǎn)品加入購(gòu)物車, 刪除購(gòu)物車的某個(gè)產(chǎn)品,調(diào)出某個(gè)分類下的產(chǎn)品,登錄用戶,計(jì)算產(chǎn)品的最終價(jià)格,等等,對(duì)于上面的這些最小的 語(yǔ)言描述粒度,會(huì)在服務(wù)層實(shí)現(xiàn),然后直接訪問該服務(wù)中的方法即可。
3.1 實(shí)例化過(guò)程:
當(dāng)我們執(zhí)行 Yii::$service->cart
,就會(huì)訪問fecshop cart service 服務(wù)
當(dāng)執(zhí)行Yii::$service->cart->coupon
就會(huì)訪問 cart的子服務(wù)coupon。
下面是實(shí)例化原理:
在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 里面配置的子服務(wù)childService的實(shí)例
*/
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',
# 子服務(wù)
'childService' => [
'quote' => [
'class' => 'fecshop\services\cart\Quote',
],
'quoteItem' => [
'class' => 'fecshop\services\cart\QuoteItem',
],
'info' => [
'class' => 'fecshop\services\cart\Info',
/**
* 單個(gè)sku加入購(gòu)物車的最大個(gè)數(shù)。
*/
'maxCountAddToCart' => 100,
# 上架狀態(tài)產(chǎn)品加入購(gòu)物車時(shí),
# 如果addToCartCheckSkuQty設(shè)置為true,則需要檢查產(chǎn)品qty是否>購(gòu)買qty,
# 如果設(shè)置為false,則不需要,也就是說(shuō)產(chǎn)品庫(kù)存qty小于購(gòu)買qty,也是可以加入購(gòu)物車的。
'addToCartCheckSkuQty' => true,
],
'coupon' => [
'class' => 'fecshop\services\cart\Coupon',
],
],
],
];
Yii::$service->cart 就是cart服務(wù)
Yii::$service 對(duì)應(yīng)的是 fecshop\services\Application
當(dāng)執(zhí)行Yii::$service->cart時(shí),找不到cart變量就會(huì)執(zhí)行 __get()魔術(shù)方法,進(jìn)而執(zhí)行
getChildService(),將上面cart配置的class對(duì)應(yīng)的文件fecshop\services\Cart
,
進(jìn)行實(shí)例化,也就是說(shuō) Yii::$service->cart 對(duì)應(yīng)的是 fecshop\services\Cart 實(shí)例化的對(duì)象,
如果下次使用 Yii::$service->cart ,不會(huì)再實(shí)例化對(duì)象,F(xiàn)ecShop Service是單例模式。
Fecshop子服務(wù):Yii::$service->cart->coupon,通過(guò)上面的配置,會(huì)實(shí)例化
fecshop\services\cart\Coupon
,子服務(wù)的原理和服務(wù)類似,都是單例模式。
3.2 關(guān)于service類
上面講解了 Yii::$service->cart,如何找到 fecshop\services\Cart的步驟,下面詳細(xì)講述 service類。
所有的服務(wù)類,譬如上面說(shuō)的cart服務(wù),都必須繼承
@fecshop\services\Service
。
里面的方法都必須以action開頭,和controller中類似,
譬如執(zhí)行 Yii::$service->cart->addProductToCart($item)
,對(duì)應(yīng)的是
fecshop\services\Cart中的 protected function actionAddProductToCart($item)
方法。
原理為:當(dāng)訪問 addProductToCart時(shí),由于找不到該函數(shù),就會(huì)執(zhí)行
@fecshop\services\Service->__call()
魔術(shù)方法,然后由魔術(shù)方法,
將 addProductToCart
改成 actionAddProductToCart
,然后去查找函數(shù),就會(huì)找到
,這樣做的好處是,可以在__call()
函數(shù)中記錄每一個(gè)service的方法調(diào)用開始時(shí)間
和結(jié)束時(shí)間,這樣就可以更好的調(diào)試出來(lái)哪一個(gè)service方法耗費(fèi)的時(shí)間長(zhǎng),
這個(gè)是為了更好地統(tǒng)計(jì)各個(gè)services的狀況,譬如:排查耗費(fèi)時(shí)間最長(zhǎng)的services,
使用最頻繁的services等,
當(dāng)然會(huì)耗費(fèi)一定的時(shí)間,
在線上可以關(guān)掉log記錄時(shí)間的功能,也可以間斷性的手動(dòng)開啟,進(jìn)行線上調(diào)試。
關(guān)于services log的開啟:@fecshop\config\services\Helper
中看到如下配置:
return [
'helper' => [
'class' => 'fecshop\services\Helper',
# 子服務(wù)
'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功能。
通過(guò)配置helper log服務(wù)的enable設(shè)置為true,可以開啟services的日志功能
當(dāng)然helper log服務(wù)還有其他的一些設(shè)置,具體請(qǐng)參看詳細(xì)代碼。
當(dāng)然,您可以把服務(wù)中的類函數(shù)定義成public
,函數(shù)名不以action
開頭,
這種方式定義的函數(shù),開啟services log,不會(huì)被記錄,因?yàn)橹苯诱业胶瘮?shù)名,不會(huì)
訪問魔術(shù)方法__call()
。
更多建議: