動態

詳情 返回 返回

php中閉包函數在實際項目的一次妙用 - 動態 詳情

php中閉包函數(Closure) 的基本語法如下:

使用 function 關鍵字定義, 但不寫函數名:

$closure = function ($name) {
    return "Hello, $name!";
};

echo $closure("World"); // 輸出: Hello, World!

之前只是知道有這麼個東西, 但是在實際項目中一直沒有用過.

實際場景

這次在項目中需要出一個如下頁面的接口

需要寫一個接口主要功能就是統計前台傳遞時間段的列表以及彙總數據.

image.png

單純是返回帶分頁的列表或者這彙總數據都ok, 但是如果要在一個接口方法中兩者返回就有點兒麻煩了.
因為 在thinkphp中 Query查詢構造對象的方法(如 where、with、sum、count、paginate 等) 是鏈式調用的, 但是每次執行查詢方法( 如 sum、count、paginate、select) 都會立即執行SQL並銷燬當前查詢條件
這就意味着要在同一個方法中返回list,sales,count這三個相同查詢條件的數據, 同樣的查詢構造對象需要構造3次, 否則前面執行過之後就會銷燬查詢條件, 導致後面再執行統計數據的查詢條件就不一樣了.
這個時候在函數中藉助閉包就能方便解決這個問題, 不需要再像之前那樣同樣的查詢構造方法寫多次了:

//1. 抽離公共查詢條件(用閉包封裝查詢構造方法,避免重複代碼)
$buildQuery = function () use ($selfBuySnList, $startDay, $endDay) {
    $query = ScoreOrderModel::whereIn('score_status', [3, 4])      
        ->whereIn('selfbuy_sn', $selfBuySnList);
    if ($startDay && $endDay) {
        $startTime = strtotime($startDay . ' 00:00:00');
        $endTime = strtotime($endDay . ' 23:59:59');
        $query->where('create_time', '>=', $startTime)
            ->where('create_time', '<=', $endTime);
    }
    return $query;
};

這樣後面無論是list,sales,count需要基於同樣的查詢條件查詢的時候就可以通過調用閉包構造同樣查詢條件的構造器, 相互之間也不影響

//列表數據
$list =  $buildQuery()
        ->with(['detail', 'device'])//還可以繼續構造自己特有的查詢條件
        ->paginate($params['page_size'] ?? 10)
//彙總統計
$salesMoney = $buildQuery()->sum('pay_money');//統計彙總金額
$sales_count = $listQuery->count();//統計訂單數

附錄: 完整代碼


public function detailStatistics()
    {
        $params = request()->param();
        $startDay = $params['start_day'] ?? '';
        $endDay = $params['end_day'] ?? '';
        $operatorInfo = $this->auth->getUser();
        $operatorId = $operatorInfo['id'];
        $selfBuySn = $params['selfbuy_sn'] ?? '';
        $selfBuySnList = $selfBuySn ? [$selfBuySn] : DeviceModel::getOperatorSelfBuySnList($operatorId);

        //1. 抽離公共查詢條件(用閉包封裝,避免重複代碼)
        $buildQuery = function () use ($selfBuySnList, $startDay, $endDay) {
            $query = ScoreOrderModel::whereIn('score_status', [3, 4])              
                ->whereIn('selfbuy_sn', $selfBuySnList);
            if ($startDay && $endDay) {
                $startTime = strtotime($startDay . ' 00:00:00');
                $endTime = strtotime($endDay . ' 23:59:59');
                $query->where('create_time', '>=', $startTime)
                    ->where('create_time', '<=', $endTime);
            }
            return $query;
        };

        // 2. 複製查詢對象,用副本執行分頁(原$query保留條件)            
        $list = $buildQuery()
            ->with(['detail', 'device'])
            ->paginate($params['page_size'] ?? 10)
            ->each(function ($item) use ($operatorInfo) {
                $scoreStatus = $item['score_status'];
                $item['score_status_text'] = ScoreOrderModel::$statusMap[$scoreStatus];
                $item['discount_money'] = 0;//訂單優惠金額
                $item['income_money'] = Operator::getIncomeMoney($operatorInfo, $item['pay_money']);
            });
        $salesMoney = $buildQuery()->sum('pay_money');
        $sales_count = $listQuery->count();

        return frontReturn(1, 'ok', [
            'sales_count' => $sales_count,
            'sales_money' => $salesMoney,
            'income_money' => Operator::getIncomeMoney($operatorInfo, $salesMoney),
            'list' => $list,
        ]);
    }
user avatar nihaojob 頭像 koogua 頭像 feixiangdemojing 頭像 renxingdebenma 頭像 beckyyyy 頭像 cbuc 頭像
點贊 6 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.