
本文介绍一种高效、可维护的 php 实现方案:仅一次数据库查询,将歌曲按首字母自动分组,并均分为左右两列(每列最多 13 个字母),彻底避免为 a–z 重复书写 26 次 sql 和模板逻辑。
本文介绍一种高效、可维护的 php 实现方案:仅一次数据库查询,将歌曲按首字母自动分组,并均分为左右两列(每列最多 13 个字母),彻底避免为 a–z 重复书写 26 次 sql 和模板逻辑。
在构建歌单类应用(如在线吉他谱库、合唱曲目索引页)时,常需将大量歌曲按标题首字母归类,并以双栏布局呈现——左栏 A–M,右栏 N–Z。若采用原始思路:为每个字母单独写一条 WHERE title LIKE ‘A%’ 查询 + 单独变量 $songTitleA, $songTitleB……不仅代码冗长难维护,还极易因拼写错误或遗漏导致某字母数据丢失,更违背“DRY(Don’t Repeat Yourself)”原则。
以下方案完全重构流程,核心思想是 “一次查询、内存分组、结构化渲染”,兼顾性能、可读性与扩展性:
✅ 步骤一:单次查询获取全部有序数据
<?php require_once 'config.php'; // ✅ 关键优化:只执行一次查询,按标题升序排列 $sql = "SELECT id, title, chord FROM song ORDER BY title ASC"; $stmt = $conn->prepare($sql); $stmt->execute(); $songs = $stmt->fetchAll(PDO::FETCH_ASSOC); ?>
⚠️ 注意:确保 title 字段无前导空格或不可见字符(可加 TRIM() 或在入库时清洗)。若需兼容大小写混合(如“a Song”),建议统一转大写后再取首字母:strtoupper($song[‘title’][0])。
✅ 步骤二:内存中按首字母动态分组
<?php $grouped = []; foreach ($songs as $song) {// 安全提取首字母(处理空标题 / 非字母开头)$firstChar = !empty($song['title']) ? strtoupper($song['title'][0]) : '#'; // 过滤非字母字符(如数字、符号),归入 '#' 组(可后续单独处理)$letter = ctype_alpha($firstChar) ? $firstChar : '#'; $grouped[$letter][] = $song;} // 按字母顺序排序分组键(确保 A, B, C…… 显示顺序正确)ksort($grouped); ?>
✅ 步骤三:均分两栏(按字母数量,非歌曲数)
<?php // 将字母分组数组切分为左右两部分:左栏最多 13 个字母,右栏余下 $midpoint = (int) ceil(count($grouped) / 2); $columns = array_chunk($grouped, $midpoint, true); $leftColumn = $columns[0] ?? []; $rightColumn = $columns[1] ?? []; ?>
? 说明:array_chunk(…, true) 保留键名(即字母),确保 [‘A’ => […], ‘B’ => […]] 结构不丢失;ceil() 保证当总字母数为奇数(如 25)时,左栏多 1 个(13 vs 12),符合常见排版习惯。
立即学习 “PHP 免费学习笔记(深入)”;
✅ 步骤四:封装可复用的渲染函数
<?php function renderColumn(array $lettersGroup, string $columnClass = 'column'): string {$html = "<div class="{$columnClass}">n"; $html .= " <div class="table-wrapper">n"; $html .= " <table class="fl-table">n"; foreach ($lettersGroup as $letter => $songs) {// 表头:字母 + 空白占位列 $html .= " <tr>n"; $html .= " <th style="color:black; width:80%">{$letter}</th>n"; $html .= " <th style="color:white;"></th>n"; $html .= " </tr>n"; // 歌曲行:标题 + 和弦(带详情链接)foreach ($songs as $song) {$html .= " <tr>n"; $html .= " <td><a style="margin-left:10px" href="details.php?id={$song['id']}">{$song['title']}</a></td>n"; $html .= " <td><span style="margin-left:10px">{$song['chord']}</span></td>n"; $html .= " </tr>n"; } } $html .= " </table>n"; $html .= " </div>n"; $html .= "</div>n"; return $html; } ?>
✅ 步骤五:最终输出(简洁、无重复)
<div class="song-columns"> <?= renderColumn($leftColumn, 'column left') ?> <?= renderColumn($rightColumn, 'column right') ?> </div>
? 进阶提示与注意事项
- 性能友好 :全程无 N+1 查询,仅 1 次 DB 请求 + PHP 内存操作,即使千级歌曲也毫秒级响应。
- 健壮性增强 :对空标题、数字开头(如“1999”)、特殊符号(如“& Band”)做了归类处理,避免键名错误。
- 样式解耦 :HTML 结构与业务逻辑分离,CSS 可通过 .left / .right 独立控制宽度、间距、响应式断点。
- 扩展灵活 :如需改为三栏、按歌曲数均分、或添加“# 其他”汇总栏,只需调整 array_chunk 参数或预处理 $grouped 数组。
- 安全加固 :实际生产环境务必对 $song[‘title’] 和 $song[‘chord’] 做 htmlspecialchars() 转义,防止 XSS:
htmlspecialchars($song['title'], ENT_QUOTES, 'UTF-8')
此方案将原本 26×(SQL + 变量 + 循环)的硬编码,压缩为 4 个清晰逻辑块,代码量减少 80% 以上,且具备自解释性与长期可维护性——这才是现代 PHP Web 开发应有的工程实践。