authenticate($request, $action); if ($apiKey instanceof \WP_REST_Response) { return $apiKey; } // All physical tables of the restaurant (combinations are a separate // model and are not included). No SQL ordering: the query builder emits // one "ORDER BY" per orderBy() call, so chaining breaks the SQL — we sort // in PHP afterwards, the same way FloorPlanController does. $tables = Table::where('restaurant_id', $apiKey->restaurant_id)->get(); // Area id -> name map (one query, instead of lazy-loading per table). $areaNames = []; foreach (Area::where('restaurant_id', $apiKey->restaurant_id)->get() as $area) { $areaNames[intval($area->id)] = $area->name; } $list = []; foreach ($tables as $table) { $areaId = $table->area_id ? intval($table->area_id) : null; $list[] = [ 'id' => intval($table->id), 'name' => $table->name, 'area_id' => $areaId, 'area_name' => ($areaId !== null && isset($areaNames[$areaId])) ? $areaNames[$areaId] : null, 'min_seats' => intval($table->min_seats), 'max_seats' => intval($table->max_seats), ]; } // Group by area, then natural-sort by table name ("2" < "10" < "EXT-1"). usort($list, function ($a, $b) { $areaCmp = strnatcasecmp((string) $a['area_name'], (string) $b['area_name']); if ($areaCmp !== 0) { return $areaCmp; } return strnatcasecmp((string) $a['name'], (string) $b['name']); }); $data = [ 'count' => count($list), 'tables' => $list, ]; return $this->successResponse($apiKey, $action, $request, $data, 200); } }