在作業管理系統中,學生登入個人中心需查看「已提交」與「未提交」兩類作業清單。核心涉及三張資料表的關聯關係:
stu_no (學號)、stu_id (主鍵)、clas_id (所屬班級)clas_id、與 Task (作業表) 為多對多關聯stu_id、task_id、提交內容等邏輯鏈條:學生 → 班級 → 班級所有作業 → 依提交表篩選已/未交。
// Student 模型中
public function getTasks($stuno)
{
$student = self::get(['stu_no' => $stuno]);
if (!$student) return [];
$class = Clas::get(['clas_id' => $student['clas_id']]);
return $class ? $class->task : []; // 依賴模型關聯定義
}
利用 ThinkPHP 模型關聯 (belongsToMany 或 hasMany),直接透過 $class->task 獲取作業集合,回傳二維陣列。
public function getUnSubmitTasks($stuno)
{
$student = self::get(['stu_no' => $stuno]);
if (!$student) return [];
$allTasks = $this->getTasks($stuno);
$stuId = $student['stu_id'];
// 優化:一次查詢該生所有已提交 task_id,避免 N+1 問題
$submittedIds = Submit::where('stu_id', $stuId)
->column('task_id'); // 返回一維陣列 [task_id1, task_id2, ...]
// 過濾:保留不在已提交清單中的作業
return array_filter($allTasks, function ($task) use ($submittedIds) {
return !in_array($task['task_id'], $submittedIds);
});
}
效能優化說明:原範例在迴圈中逐筆查詢 Submit::get() 會造成 N+1 查詢問題。改用 column() 一次撈取所有已提交 ID,再用 array_filter 過濾,資料庫查詢從 N+1 降為 2 次。
public function getSubmitTasks($stuno)
{
$allTasks = $this->getTasks($stuno);
$unsubmitted = $this->getUnSubmitTasks($stuno);
// 建立未提交 task_id 查找表 (O(1) 查找)
$unsubmittedIds = array_column($unsubmitted, 'task_id');
return array_filter($allTasks, function ($task) use ($unsubmittedIds) {
return !in_array($task['task_id'], $unsubmittedIds);
});
}
同樣採用 array_column + in_array 替代雙重迴圈,時間複雜度從 O(n²) 降為 O(n)。
LEFT JOIN + CASE WHEN 或 IF,一次查詢即可標記每筆作業的提交狀態,完全避免 PHP 端陣列運算。getTasks 結果,降低資料庫負載。limit/offset 或游標分頁,避免一次載入過多資料。Clas::with('task')->get(...) 減少查詢次數。本文展示了從基礎邏輯到效能優化的完整演進。核心原則:善用資料庫集合運算替代應用層迴圈查詢、利用陣列索引加速查找。ThinkPHP5 的模型關聯與查詢構建器提供了優雅的語法支撐,開發者只需關注業務邏輯與 SQL 效能即可構建高效的作業管理查詢功能。