PAYATHON 2026

如何将多行选中的 ids 传递到 print page

支付老李

结论

插入成功后,可以让接口把本次插入成功的 ids 返回给 JavaScript,再通过查询参数跳转到打印页面。打印接口读取并校验这些 ids,然后使用 CodeIgniter 的 where_in() 查询对应记录。

如果可以调整数据库结构,更稳妥的方式是为每次批量插入生成唯一的 batch_id,打印页面只接收这个批次号。这样既能避免 URL 过长,也能防止因 mr_no 重复而查到历史数据。

方案一:直接传递选中的 ids

1. 提交选中的 ids

使用 FormData 时,数组字段名要写成 due_ids[]:

$('#allocate').click(function (event) {
    event.preventDefault();

    var allVals = [];

    $('input[name="selectedBilties"]:checked').each(function () {
        allVals.push($(this).val());
    });

    if (allVals.length === 0) {
        alertify.error('Please select at least one record.');
        return;
    }

    var formData = new FormData();
    formData.append('agent', $('#agent').val());
    formData.append('due_to', $('#due_to').val());

    allVals.forEach(function (id) {
        formData.append('due_ids[]', id);
    });

    $.ajax({
        url: 'crossing/payment_rec_allocation/PaymentRecAllocationController/createPaymentAllocation',
        type: 'POST',
        dataType: 'json',
        data: formData,
        contentType: false,
        cache: false,
        processData: false,

        success: function (response) {
            var result = response.PaymentRecAllocation;

            if (result.form_status !== 'true') {
                alertify.error(result.form_message);
                return;
            }

            alertify.confirm(
                'Payment Recovery Allocation',
                'Do you want to print?',
                function () {
                    var ids = result.ids.join(',');

                    window.location.href =
                        'crossing/payment_rec_allocation/PaymentRecAllocationController/' +
                        'printCollectionRecPage?ids=' +
                        encodeURIComponent(ids);
                },
                function () {
                    window.location.reload();
                }
            );
        },

        error: function () {
            alertify.error('Request failed.');
        }
    });
});

原代码中,JavaScript 提交的字段是 rec_type,PHP 读取的却是 due_to。两边的字段名必须一致,否则 $this->input->post('due_to') 得到的会是空值。

跳转后的 URL 类似:

printCollectionRecPage?ids=101,105,109

下面这行代码可以删除:

setTimeout(location.reload.bind(location), 2000);

页面已经开始跳转,此时再刷新原页面没有作用,还可能造成重复请求。

2. 插入接口返回成功插入的 ids

批量插入最好放在数据库事务中,避免只插入部分记录。也不能只根据最后一次循环产生的 $modelResult 判断整个批次是否成功。

public function createPaymentAllocation()
{
    $biltyIds = $this->input->post('due_ids');
    $agent    = $this->input->post('agent');
    $dueTo    = $this->input->post('due_to');

    if (!is_array($biltyIds)) {
        $biltyIds = array();
    }

    $biltyIds = array_values(array_unique(array_filter(
        $biltyIds,
        static function ($id) {
            return ctype_digit((string) $id);
        }
    )));

    if (empty($biltyIds)) {
        echo json_encode(array(
            'PaymentRecAllocation' => array(
                'form_status'  => 'false',
                'form_message' => 'No valid records were selected.'
            )
        ));
        return;
    }

    $this->db->trans_start();

    foreach ($biltyIds as $biltyId) {
        $insertData = array(
            'pay_rec_agent' => $agent,
            'pay_dueto'     => $dueTo,
            'mr_no'         => $biltyId
        );

        $this->PayRecAllModel->inserPaymentAllocation($insertData);
    }

    $this->db->trans_complete();

    if ($this->db->trans_status() === false) {
        $response = array(
            'PaymentRecAllocation' => array(
                'form_status'  => 'false',
                'form_message' => 'Something went wrong.'
            )
        );
    } else {
        $response = array(
            'PaymentRecAllocation' => array(
                'form_status'  => 'true',
                'form_message' => 'Payment Recovery has been successfully allocated.',
                'ids'          => $biltyIds
            )
        );
    }

    echo json_encode($response);
}

Model 可以继续使用现有的插入方法:

public function inserPaymentAllocation($data)
{
    return $this->db->insert('payment_rec_allocn', $data);
}

3. 打印 Controller 接收 ids

public function printCollectionRecPage()
{
    $ids = $this->input->get('ids', true);
    $ids = explode(',', (string) $ids);

    $ids = array_values(array_unique(array_filter(
        $ids,
        static function ($id) {
            return ctype_digit((string) $id);
        }
    )));

    if (empty($ids)) {
        show_error('No valid records were supplied for printing.', 400);
        return;
    }

    $data = array();
    $data['collnR'] = $this->PayRecAllModel->printCollectionRecPage($ids);

    $this->load->view('template/header');
    $this->load->view(
        'crossing/payment_rec_allocation/printCollectionRecovery',
        $data
    );
    $this->load->view('template/footer');
}

不要把未经校验的查询参数直接拼接到 SQL 中。即使这些 ids 最初来自当前页面,用户仍然可以手动修改打印 URL。

4. Model 使用 where_in()

public function printCollectionRecPage(array $ids)
{
    $this->db->select('payment_rec_allocn.*, crossing_cash_memo.*');
    $this->db->from('payment_rec_allocn');
    $this->db->join(
        'crossing_cash_memo',
        'payment_rec_allocn.mr_no = crossing_cash_memo.mr_no'
    );
    $this->db->where('crossing_cash_memo.total !=', 0);
    $this->db->where_in('payment_rec_allocn.mr_no', $ids);

    return $this->db->get()->result();
}

假设传入的 ids 是 101,105,109,打印页面只会查询这三个 mr_no。

更推荐:使用唯一的 batch_id

直接传 ids 存在两个问题:

  • 选择的记录较多时,URL 可能过长。
  • 如果同一个 mr_no 可以多次插入 payment_rec_allocn,where_in('mr_no', $ids) 可能把以前插入的记录也查出来。

更可靠的方式是在表中增加 batch_id 字段。用户每次点击分配时生成一个唯一批次号,并将它写入本次插入的每一行。

假设数据库中已经增加了这个字段:

ALTER TABLE payment_rec_allocn
ADD COLUMN batch_id VARCHAR(64) NOT NULL;

插入时生成批次号:

$batchId = bin2hex(random_bytes(16));

foreach ($biltyIds as $biltyId) {
    $insertData = array(
        'batch_id'      => $batchId,
        'pay_rec_agent' => $agent,
        'pay_dueto'     => $dueTo,
        'mr_no'         => $biltyId
    );

    $this->PayRecAllModel->inserPaymentAllocation($insertData);
}

成功响应只需返回批次号:

'PaymentRecAllocation' => array(
    'form_status'  => 'true',
    'form_message' => 'Payment Recovery has been successfully allocated.',
    'batch_id'     => $batchId
)

JavaScript 跳转:

window.location.href =
    'crossing/payment_rec_allocation/PaymentRecAllocationController/' +
    'printCollectionRecPage?batch_id=' +
    encodeURIComponent(result.batch_id);

打印 Controller:

public function printCollectionRecPage()
{
    $batchId = $this->input->get('batch_id', true);

    if (!is_string($batchId) || !preg_match('/^[a-f0-9]{32}$/', $batchId)) {
        show_error('Invalid print batch.', 400);
        return;
    }

    $data['collnR'] = $this->PayRecAllModel
        ->printCollectionRecPage($batchId);

    $this->load->view('template/header');
    $this->load->view(
        'crossing/payment_rec_allocation/printCollectionRecovery',
        $data
    );
    $this->load->view('template/footer');
}

对应的 Model:

public function printCollectionRecPage($batchId)
{
    $this->db->select('payment_rec_allocn.*, crossing_cash_memo.*');
    $this->db->from('payment_rec_allocn');
    $this->db->join(
        'crossing_cash_memo',
        'payment_rec_allocn.mr_no = crossing_cash_memo.mr_no'
    );
    $this->db->where('payment_rec_allocn.batch_id', $batchId);
    $this->db->where('crossing_cash_memo.total !=', 0);

    return $this->db->get()->result();
}

如果允许调整数据库结构,应优先使用 batch_id。它直接对应“本次批量插入”,比传递一组业务 ids 更准确,也便于重新打开或重复打印。如果系统包含敏感数据,还需要验证当前登录用户是否有权查看该批次,不能只凭 batch_id 放行。

备注:内容仅供参考。