方法容器-流程控制模式 的控制器編寫模式

anyuzhe發表於2017-04-29

mvc模式裡的 c 也就是控制器,在每次編寫程式碼的過程中,其中有一大部分高度複用的
如果把這部分程式碼解耦出來,並且保證其原子性,成為可以高度複用的方法,可以帶來極大的好處
可以減少重複的程式碼,在減少程式碼的同時也會減少bug程式碼的產生,並且容易修改
這是Repository模式的作用

那控制器裡的程式碼 有多少可以封裝到respository類裡呢?
我覺得是90%以上(除了呼叫程式碼)
所以我在這裡想提出一種模式
也就是標題 方法容器-流程控制模式

簡單來說就是
有一個方法容器,可以依次呼叫你定義的方法(也就是respository裡的方法),最後返回引數

  1. 它的好處是,像laravel呼叫控制器的方法一樣 自動傳入引數,你只需要在方法中申明需要的引數
    方法容器去呼叫的時候,會自動傳入引數,這些引數就是方法容器類裡的引數這個屬性裡的,或者是laravel容器裡定義的物件(這個就和控制器裡的一樣) 變數是前者,而申明的是類就是後者 抄襲了laravel裡的程式碼
  2. 可以對單個方法做快取
  3. 強迫自己寫原子性的方法,提高程式碼的複用
  4. 讓控制器裡的程式碼非常直觀

而在寫控制器方法的時候,其實寫的是呼叫流程,我們一起來看一下到底是咋回事

這是git倉庫(看名字就知道 依賴於laravel)
laravel-function-flow

首先用composer載入包

composer require anyuzhe/laravel-function-flow

在配置檔案config/app.php的服務容器陣列中加入

\Anyuzhe\LaravelFunctionFlow\FunctionFlowServiceProvider::class,

在門面陣列中加入

'Flow' => \Anyuzhe\LaravelFunctionFlow\FlowFacade::class,

執行命令 生成配置檔案

php artisan funcFlow:publish

會在config資料夾中生成function-flow.php檔案

可在裡面配置倉庫類 類似如下

return array(
//'Base'=>BaseController::class 類似這樣的key value定義類 名稱和位置 無任何限制
    'Default' => '',//這是預設方法 也就是不寫類名時候 預設去找的類
    'GoodsRecord' => \App\Repositories\GoodsRepository::class,
    'Picture' => \App\Repositories\PicturesRepository::class,
    'Store'  => \App\Repositories\StoresRepository::class,
    'Goods'  => \App\Repositories\GoodsRepository::class,
    'GoodsRecord'  => \App\Repositories\GoodsRecordsRepository::class,
    'Response'  => \App\ZL\ResponseLayout::class,
    'Qrcode'   => \App\Repositories\QrcodesRepository::class,
    'User'   => \App\Repositories\UsersRepository::class,
);

以下是控制器中的使用示例

public function bindRecord(Request $request)
{
    return \Flow::setLastFunc(['Response/flowReturn'])->setParam($request->all())->flow([
            ['Store/getIdByNo'],
            ['User/bindRecord'],
            ['User/checkMemberExist'],
    ])['response'];
}

首先除了flow方法。別的方法呼叫都是返回物件本身 所以可以鏈式呼叫 以上是facade模式的呼叫
再看一下這個詳細的例子

public function beforeStore($id, $data)
{
    $data['cover_url'] = 'http://weiyicrm.oss-cn-shanghai.aliyuncs.com/ico-fuzhaung.png';
    $data['input_goods_no'] = isset($data['goods_no'])?$data['goods_no']:null;
    $data['id'] = $id;
    //setParam方法是設定執行中的引數 可以是陣列 或者是兩個引數鍵值的形式
    return \Flow::setParam($data)->flow([   //flow為主執行方法
            ['Store/getOneId'],
            //  陣列第二個引數是array型 可以設定函式的額外引數  第三個引數int型 是一個快取的時間值可以設定是否快取 單位為分鐘
            ['Qrcode/generateGoodsNoAndCreateQrcodeByNum',['created_pic'=>false]],
            ['Picture/getIds'],
            ['Picture/getModelByIds'],
            ['Goods/getGoodsNo'],
            ['Goods/findByNo'],
            ['Goods/updateByStock'],
            ['Goods/updateBySell'],
            ['Goods/createOne'],
            ['Qrcode/bingQrcodeGoodsNo'],// 
            ['Qrcode/bingQrcodeLabelNo'],// 
            ['GoodsRecord/updateOne'],
            ['Response/flowReturn'],
    ])['response'];
}

此例子中並沒有使用setLastFunc方法(用來設定最後執行方法的)
需要注意的是快取是用了類名+方法名+引數轉成字串的值作為快取的鍵名。
可以適用於一些場景 可以配合前面函式輸出快取的依據引數 配合使用應該還不錯

最後看一下 幾個方法的例項 瞭解下方法的編寫
注意方法中的引數都是通過方法容器自動傳入的(變數名與引數名相同) 如果方法容器的引數陣列中不存在並且沒有預設值 會傳入null
flow方法會把方法容器的parameters成員返回 也就是所有的引數

這一用來返回最後的reponse陣列的方法。 判斷在別的方法中是否輸出了錯誤碼和錯誤資訊
public function flowReturn($res,$errcode,$errmsg,$msg)
{
    if(!$errcode){
            $data['status'] = 1;
            $data['data'] = $res;
            $data['msg'] = $msg?$msg:'Success';
    }else{
            $data['status'] = $errcode;
            $data['data'] = null;
            $data['msg'] = $errmsg?$errmsg:'Error';
    }
    return ['response'=>$data];
}
public function getOneId()
{
    $user = Auth::user();
    if($user) {
            $stores = $user->stores;
            if ($stores->count() == 1) {
                    //最後輸出的陣列 會合併入方法容器的引數陣列中 之後的方法中 可以申明引數 只要變數名與引數名相同 就可以自動傳入
                    return ['store_id'=>$stores->first()->id];
            }
    }
    //這裡返回的skip是可以跳過之後的一些方法  直接執行想要的方法 可以作為報錯後的處理   這裡出錯 所以返回了錯誤碼和錯誤資訊
    return ['skip'=>'flowReturn','store_id'=>null,'errcode'=>ErrorCode::$canNotFindStoreError['code'],'errmsg'=>ErrorCode::$canNotFindStoreError['msg']];
}
public function getIdByNo($store_no)
{
    if($store_no){
            $store = $this->findBy('identifying',$store_no);
            if($store){
                    return ['store_id'=>$store->id];
            }
    }
    //這裡返回的next為false 會讓方法容器不執行之後的方法。直接執行lastFunc(如果定義了的話)ps:可以把最後的返回引數處理函式定義為lastFunc
    return ['next'=>false,'store_id'=>null,'errcode'=>ErrorCode::$canNotFindStoreError['code'],'errmsg'=>ErrorCode::$canNotFindStoreError['msg']];
}

有興趣的小夥伴可以體驗一下,也可以來github上提bug和問題,我會立刻改正

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章