HTML 表單中多個同名輸入框 (如動態添加的行、複選框),直接用 name="field" 只能獲取最後一個值。需用 name="field[]" 讓 PHP 解析為數組。
<form method="post" action="">
<table>
<tr>
<th>商品名</th><th>價格</th><th>數量</th>
</tr>
<!-- 第 1 行 -->
<tr class="item-row">
<td><input type="text" name="items[0][name]" required></td>
<td><input type="number" name="items[0][price]" step="0.01" min="0" required></td>
<td><input type="number" name="items[0][qty]" min="1" value="1" required></td>
</tr>
<!-- 第 2 行 (JS 動態添加) -->
<tr class="item-row">
<td><input type="text" name="items[1][name]" required></td>
<td><input type="number" name="items[1][price]" step="0.01" min="0" required></td>
<td><input type="number" name="items[1][qty]" min="1" value="1" required></td>
</tr>
</table>
<button type="button" id="addRow">+ 添加行</button>
<button type="submit">提交訂單</button>
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 獲取二維數組
$items = $_POST['items'] ?? [];
// 驗證結構
if (!is_array($items)) {
die('數據格式錯誤');
}
$validItems = [];
foreach ($items as $index => $item) {
// 過濾非數組項 (防範污染)
if (!is_array($item)) continue;
$name = trim((string)($item['name'] ?? ''));
$price = filter_var($item['price'] ?? '', FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 0]]);
$qty = filter_var($item['qty'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
if ($name === '' || $price === false || $qty === false) {
// 記錄錯誤行號,返回前端提示
continue;
}
$validItems[] = [
'name' => $name,
'price' => $price,
'qty' => $qty,
'subtotal' => $price * $qty,
];
}
if (empty($validItems)) {
die('至少需要一個有效商品');
}
// 業務邏輯:保存訂單、計算總價...
$total = array_sum(array_column($validItems, 'subtotal'));
echo "訂單提交成功,共 " . count($validItems) . " 種商品,總計 ¥" . number_format($total, 2);
// 調試輸出
// var_dump($validItems);
}
<script>
document.getElementById('addRow').addEventListener('click', () => {
const tbody = document.querySelector('table tbody') || document.querySelector('table');
const rows = tbody.querySelectorAll('.item-row');
const newIndex = rows.length; // 0-based 索引
const tr = document.createElement('tr');
tr.className = 'item-row';
tr.innerHTML = `
<td><input type="text" name="items[${newIndex}][name]" required></td>
<td><input type="number" name="items[${newIndex}][price]" step="0.01" min="0" required></td>
<td><input type="number" name="items[${newIndex}][qty]" min="1" value="1" required></td>
<td><button type="button" class="remove-row">刪除</button></td>
`;
tbody.appendChild(tr);
// 綁定刪除事件 (需重新索引或使用 data-index)
tr.querySelector('.remove-row').addEventListener('click', (e) => {
e.target.closest('tr').remove();
reindexRows();
});
});
function reindexRows() {
document.querySelectorAll('.item-row').forEach((row, i) => {
row.querySelectorAll('input').forEach(input => {
// 替換 name 中的索引
input.name = input.name.replace(/items\[\d+\]/, `items[${i}]`);
});
});
}
</script>
<!-- 多選下拉 -->
<select name="tags[]" multiple>
<option value="php">PHP</option>
<option value="js">JavaScript</option>
<option value="go">Go</option>
</select>
<!-- 複選框組 -->
<label><input type="checkbox" name="colors[]" value="red"> 紅</label>
<label><input type="checkbox" name="colors[]" value="green"> 綠</label>
<label><input type="checkbox" name="colors[]" value="blue"> 藍</label>
<?php
$tags = $_POST['tags'] ?? []; // ['php', 'go']
$colors = $_POST['colors'] ?? []; // ['red', 'blue']
// 注意:未勾選時 $_POST 不包含該鍵,而非空數組
// 建議:$tags = (array)($_POST['tags'] ?? []);
現代 SPA/Vue/React 前端常直接發送 JSON,PHP 需手動解析:
<?php
// API 端點
$input = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
exit('Invalid JSON');
}
$items = $input['items'] ?? [];
// 後續驗證邏輯同表單數組
| 錯誤 | 後果 | 修正 |
|---|---|---|
name="item[]" 但 PHP 用 $_POST['item'] 當字符串 | Notice: Array to string conversion | 遍歷數組或 implode(',', $_POST['item']) |
| 動態行索引不連續 (刪除中間行) | PHP 仍正常接收,數組鍵為 0,2,3... | 用 foreach 遍歷,不依賴連續索引 |
| 未驗證數組結構直接入庫 | 污染數據、異常 | 嚴格 filter_var + 結構校驗 |
| 超大數組導致內存/執行時間耗盡 | DoS 風險 | 限制最大行數 (如 1000),分頁提交 |
name="field[]" 是 PHP 解析表單數組的約定語法。關鍵在於:
items[0][name])foreach 遍歷 + filter_var 逐項驗證