You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套foreach代码重构咨询:拆分函数与参数传递疑问

拆分发票价格计算PHP代码的子函数方案

我编写了一段用于计算发票产品价格的PHP代码,包含双层foreach循环。当产品数量为负值时代表该产品是退货,需从总价中扣减对应金额。目前这段代码因大量计算逻辑集中在循环内,显得冗余杂乱,希望将其拆分为多个子函数,但不清楚子函数应该传递哪些参数,原代码如下:

$dataset                = 'supplier_invoices';
$SupplierInvoice        = new Dbm_Supplier($dataset);
$totalValoare               = 0;
$costAdaos                  = 0;
$totalTva_neex              = 0;
$totalVal_cu_tva            = 0;

$totalValoareCantitateNegativaProduseFacturate                  = 0;
$costAdaosCantitateNegativaProduseFacturate                     = 0;
$totalTva_neexCantitateNegativaProduseFacturate                 = 0;
$totalVal_cu_tvaCantitateNegativaProduseFacturate               = 0;

$totalValoarePozitivaProduseFacturate               = 0;
$costAdaosPozitivaProduseFacturate                  = 0;
$totalTva_neexPozitivaProduseFacturate              = 0;
$totalVal_cu_tvaPozitivaProduseFacturate            = 0;

$totalValoareProduseFurnizor = 0;
$costAdaosProduseFurnizor = 0;
$totalTva_neexProduseFurnizor = 0;
$totalVal_cu_tvaProduseFurnizor = 0;

$totalValoareProduseFurnizorReturnate = 0;
$costAdaosProduseFurnizorReturnate = 0;
$totalTva_neexProduseFurnizorReturnate = 0;
$totalVal_cu_tvaProduseFurnizorReturnate = 0;


foreach ($invoices as $invoice)
{
    $data = $SupplierInvoice->findFirst(array('supplier_invoice_id'=>$invoice['supplier_invoice_id']));
    $data = $data['results'];
    $invoiceCalculus = new InvoiceSupplierCalculus($data['supplier_invoice_date'], $data['currency_rate']);
    $products               = $SupplierInvoice->getProductsForInvoice($invoice['supplier_invoice_id']);
    foreach ($products as $product)
    {
        $calculus = $invoiceCalculus->getCalculus($product);
        $valoare = $calculus['default_currency_value'];
        $adaos_com = $calculus['adaos_com'];
        $tva_neex  = $calculus['tva_neex'];
        $val_cu_tva = $calculus['val_cu_tva'];

        $totalValoare               +=   $valoare;
        $costAdaos                  +=   $adaos_com;
        $totalTva_neex              +=   $tva_neex;
        $totalVal_cu_tva            +=   $val_cu_tva;
        $totalVal_cu_tva             =  number_format($totalVal_cu_tva,2,'.','');

        if($product['invoice_quantity'] < 0)
        {
            $totalValoareCantitateNegativaProduseFacturate                +=   -1 * abs($valoare);
            $costAdaosCantitateNegativaProduseFacturate                   +=   -1 * abs($adaos_com);
            $totalTva_neexCantitateNegativaProduseFacturate               +=   -1 * abs($tva_neex);
            $totalVal_cu_tvaCantitateNegativaProduseFacturate             +=   -1 * abs($val_cu_tva);
            $totalVal_cu_tvaCantitateNegativaProduseFacturate             =  number_format($totalVal_cu_tvaCantitateNegativaProduseFacturate,2,'.','');
        }
        else
        {
            $totalValoarePozitivaProduseFacturate               +=   abs($valoare);
            $costAdaosPozitivaProduseFacturate                  +=   abs($adaos_com);
            $totalTva_neexPozitivaProduseFacturate              +=   abs($tva_neex);
            $totalVal_cu_tvaPozitivaProduseFacturate            +=   abs($val_cu_tva);
            $totalVal_cu_tvaPozitivaProduseFacturate             =  number_format($totalVal_cu_tvaPozitivaProduseFacturate,2,'.','');
        }

        if($product['product_quantity'] < 0)
        {
            $totalValoareProduseFurnizorReturnate  +=   $valoare;
            $costAdaosProduseFurnizorReturnate +=   $adaos_com;
            $totalTva_neexProduseFurnizorReturnate  +=   $tva_neex;
            $totalVal_cu_tvaProduseFurnizorReturnate +=   $val_cu_tva;
            $totalVal_cu_tvaProduseFurnizorReturnate =  number_format($totalVal_cu_tvaProduseFurnizorReturnate,2,'.','');
        }
        else
        {
            $totalValoareProduseFurnizor +=   $valoare;
            $costAdaosProduseFurnizor +=   $adaos_com;
            $totalTva_neexProduseFurnizor +=   $tva_neex;
            $totalVal_cu_tvaProduseFurnizor +=   $val_cu_tva;
            $totalVal_cu_tvaProduseFurnizor =  number_format($totalVal_cu_tvaProduseFurnizor,2,'.','');
        }

    }
}

拆分思路与实现

1. 封装统计数据结构

把分散的统计变量整合成嵌套数组,避免大量独立变量,让参数传递更清晰:

function initializeInvoiceStats() {
    return [
        'total' => [
            'valoare' => 0,
            'adaos_com' => 0,
            'tva_neex' => 0,
            'val_cu_tva' => 0,
        ],
        'invoice_negative' => [
            'valoare' => 0,
            'adaos_com' => 0,
            'tva_neex' => 0,
            'val_cu_tva' => 0,
        ],
        'invoice_positive' => [
            'valoare' => 0,
            'adaos_com' => 0,
            'tva_neex' => 0,
            'val_cu_tva' => 0,
        ],
        'supplier_normal' => [
            'valoare' => 0,
            'adaos_com' => 0,
            'tva_neex' => 0,
            'val_cu_tva' => 0,
        ],
        'supplier_returned' => [
            'valoare' => 0,
            'adaos_com' => 0,
            'tva_neex' => 0,
            'val_cu_tva' => 0,
        ],
    ];
}

2. 拆分核心子函数

所有子函数通过引用传递统计数组,避免数组拷贝,同时直接修改统计值。

处理单张发票

参数:单条发票数据、Dbm_Supplier实例、统计数组引用

function processSingleInvoice($invoice, $supplierInvoice, &$stats) {
    $data = $supplierInvoice->findFirst(['supplier_invoice_id' => $invoice['supplier_invoice_id']])['results'];
    $invoiceCalculus = new InvoiceSupplierCalculus($data['supplier_invoice_date'], $data['currency_rate']);
    $products = $supplierInvoice->getProductsForInvoice($invoice['supplier_invoice_id']);
    
    foreach ($products as $product) {
        processSingleProduct($product, $invoiceCalculus, $stats);
    }
}

处理单个产品

参数:单条产品数据、InvoiceSupplierCalculus实例、统计数组引用

function processSingleProduct($product, $invoiceCalculus, &$stats) {
    $calculus = $invoiceCalculus->getCalculus($product);
    $calcValues = [
        'valoare' => $calculus['default_currency_value'],
        'adaos_com' => $calculus['adaos_com'],
        'tva_neex' => $calculus['tva_neex'],
        'val_cu_tva' => $calculus['val_cu_tva'],
    ];

    // 更新总统计
    updateStats($stats['total'], $calcValues);
    
    // 按invoice_quantity分类更新
    if ($product['invoice_quantity'] < 0) {
        updateNegativeInvoiceStats($stats['invoice_negative'], $calcValues);
    } else {
        updatePositiveInvoiceStats($stats['invoice_positive'], $calcValues);
    }
    
    // 按product_quantity分类更新(供应商产品/退货)
    if ($product['product_quantity'] < 0) {
        updateStats($stats['supplier_returned'], $calcValues);
    } else {
        updateStats($stats['supplier_normal'], $calcValues);
    }
}

通用统计更新函数

参数:目标统计数组引用、当前产品的计算值数组

function updateStats(&$targetStats, $calcValues) {
    $targetStats['valoare'] += $calcValues['valoare'];
    $targetStats['adaos_com'] += $calcValues['adaos_com'];
    $targetStats['tva_neex'] += $calcValues['tva_neex'];
    $targetStats['val_cu_tva'] = number_format($targetStats['val_cu_tva'] + $calcValues['val_cu_tva'], 2, '.', '');
}

// 针对负数量发票的特殊处理
function updateNegativeInvoiceStats(&$targetStats, $calcValues) {
    $targetStats['valoare'] += -1 * abs($calcValues['valoare']);
    $targetStats['adaos_com'] += -1 * abs($calcValues['adaos_com']);
    $targetStats['tva_neex'] += -1 * abs($calcValues['tva_neex']);
    $targetStats['val_cu_tva'] = number_format($targetStats['val_cu_tva'] + (-1 * abs($calcValues['val_cu_tva'])), 2, '.', '');
}

// 针对正数量发票的特殊处理
function updatePositiveInvoiceStats(&$targetStats, $calcValues) {
    $targetStats['valoare'] += abs($calcValues['valoare']);
    $targetStats['adaos_com'] += abs($calcValues['adaos_com']);
    $targetStats['tva_neex'] += abs($calcValues['tva_neex']);
    $targetStats['val_cu_tva'] = number_format($targetStats['val_cu_tva'] + abs($calcValues['val_cu_tva']), 2, '.', '');
}

3. 简化主流程

$dataset = 'supplier_invoices';
$SupplierInvoice = new Dbm_Supplier($dataset);
$stats = initializeInvoiceStats();

foreach ($invoices as $invoice) {
    processSingleInvoice($invoice, $SupplierInvoice, $stats);
}

// 后续可直接使用$stats数组中的统计结果

参数传递说明

  • 统计数组:始终用引用传递,确保子函数能直接修改原数组,避免性能损耗。
  • 实例对象:传递Dbm_Supplier和InvoiceSupplierCalculus实例,因为子函数需要调用它们的方法查询数据、计算金额。
  • 业务数据:传递单条发票/产品数据,是计算的基础输入。
  • 计算值数组:将产品的四个核心计算值封装成数组传递,减少单个参数的数量,提高可读性。

内容的提问来源于stack exchange,提问作者user3848412

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 04:45:43