在開發會員列表、標簽雲、商品屬性值展示等場景時,常需將數據庫單一字段的記錄按「每行 N 列、多行」的網格佈局輸出。本文提供從傳統分頁查詢到現代數組分塊的多種方案,並分析性能與適用場景。
employees (id, name)<?php
// 缺點:多次查詢、N+1 問題、代碼冗長
$perRow = 10;
// 第一行
$result = mysqli_query($conn, "SELECT name FROM employees ORDER BY id LIMIT 0, {$perRow}");
echo '';
while ($row = mysqli_fetch_assoc($result)) {
echo ' ' . $row['name'] . ' ';
}
echo ' ';
// 後續行
$total = mysqli_num_rows(mysqli_query($conn, "SELECT id FROM employees"));
$rows = ceil($total / $perRow);
for ($k = 1; $k < $rows; $k++) {
$offset = $k * $perRow;
$result = mysqli_query($conn, "SELECT name FROM employees ORDER BY id LIMIT {$offset}, {$perRow}");
echo '';
while ($row = mysqli_fetch_assoc($result)) {
echo ' ' . $row['name'] . ' ';
}
echo ' ';
}
1 + ceil(total/perRow) 次查詢,大數據下極慢<?php
// 1. 一次性查詢所有需要的數據
$stmt = $pdo->query("SELECT id, name FROM employees ORDER BY id");
$employees = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 2. 配置參數
$colsPerRow = 10;
// 3. 使用 array_chunk 分塊(核心技巧)
$rows = array_chunk($employees, $colsPerRow);
// 4. 輸出
echo '';
foreach ($rows as $rowIndex => $rowData) {
echo '';
foreach ($rowData as $emp) {
echo '';
echo '';
echo ' ' . htmlspecialchars($emp['name']);
echo ' ';
}
// 最後一行不足補空單元格(可選,保持表格對齊)
$emptyCols = $colsPerRow - count($rowData);
if ($emptyCols > 0) {
echo str_repeat(' ', $emptyCols);
}
echo ' ';
}
echo '
';
array_chunkarray_chunk(array $array, int $length, bool $preserve_keys = false): array
// 將數組分割為多個長度為 $length 的塊,返回二維數組
<?php
function fetchEmployeesChunked(PDO $pdo, int $chunkSize = 1000): Generator
{
$offset = 0;
$pageSize = 1000;
while (true) {
$stmt = $pdo->prepare("SELECT id, name FROM employees ORDER BY id LIMIT :offset, :limit");
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->bindValue(':limit', $pageSize, PDO::PARAM_INT);
$stmt->execute();
$batch = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($batch)) break;
foreach ($batch as $row) {
yield $row;
}
$offset += $pageSize;
}
}
// 使用:邊讀邊輸出,不佔用大量內存
$colsPerRow = 10;
$buffer = [];
echo '';
foreach (fetchEmployeesChunked($pdo) as $emp) {
$buffer[] = $emp;
if (count($buffer) === $colsPerRow) {
echo '';
foreach ($buffer as $e) {
echo ' ' . htmlspecialchars($e['name']) . ' ';
}
echo ' ';
$buffer = [];
}
}
// 處理最後不足一行的數據
if (!empty($buffer)) {
echo '';
foreach ($buffer as $e) {
echo ' ' . htmlspecialchars($e['name']) . ' ';
}
echo str_repeat(' ', $colsPerRow - count($buffer));
echo ' ';
}
echo '
';
<?php
$stmt = $pdo->query("SELECT id, name FROM employees ORDER BY id");
$employees = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo '';
foreach ($employees as $emp) {
echo '';
}
echo '';
.employee-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 12px;
padding: 16px;
}
.employee-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: #fafafa;
border: 1px solid #eee;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
}
.employee-item:hover {
background: #f0f7ff;
border-color: #3b82f6;
}
.employee-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #3b82f6;
}
<?php
// 根據屏幕寬度或用戶偏好動態調整(AJAX 載入時傳參)
$colsPerRow = (int)($_GET['cols'] ?? 10);
// 限制範圍
$colsPerRow = max(3, min(20, $colsPerRow));
array_chunk 是服務端渲染標準解法htmlspecialchars()選對方案,單字段多列顯示從「麻煩的邏輯題」變成「一行代碼的配置事」。