如何将多行选中的 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 放行。
备注:内容仅供参考。
