首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP array_multisort 表格排序實戰:可排序欄位、AJAX 無刷新、狀態保持

     編輯:PHP 相關知識

需求場景:前台可點擊欄位標題排序的資料表

使用者期望點擊「姓名」、「價格」、「日期」等表頭即可切換升/降序,且不重新整理頁面。後端需支援多欄位、多方向、型別感知排序。

後端核心:通用排序服務

<?php
// sort_service.php
class TableSorter {
    private array $data;
    private array $allowedColumns;
    private array $numericColumns;
    private string $defaultColumn;
    private string $defaultOrder;

    public function __construct(array $data, array $config = []) {
        $this->data = $data;
        $this->allowedColumns = $config['allowed'] ?? array_keys($data[0] ?? []);
        $this->numericColumns = $config['numeric'] ?? [];
        $this->defaultColumn = $config['default_col'] ?? ($this->allowedColumns[0] ?? 'id');
        $this->defaultOrder  = $config['default_order'] ?? 'asc';
    }

    public function sort(string $col = '', string $order = ''): array {
        $col = $this->allowedColumns[0] ?? '';
        if ($col && in_array($col, $this->allowedColumns, true)) {
            $this->defaultColumn = $col;
        }
        $order = strtolower($order) === 'desc' ? 'desc' : 'asc';
        $this->defaultOrder = $order;

        if (empty($this->data)) return [];

        // 建立平行陣列
        $sortKeys = array_column($this->data, $this->defaultColumn);
        $flags = in_array($this->defaultColumn, $this->numericColumns, true)
            ? SORT_NUMERIC : (SORT_NATURAL | SORT_FLAG_CASE);

        $sortOrder = $order === 'desc' ? SORT_DESC : SORT_ASC;
        array_multisort($sortKeys, $sortOrder, $flags, $this->data);

        return $this->data;
    }

    public function getState(): array {
        return ['col' => $this->defaultColumn, 'order' => $this->defaultOrder];
    }
}

// API 端點
header('Content-Type: application/json; charset=utf-8');
require_once 'db.php'; // 取得 $pdo

$stmt = $pdo->query("SELECT id, product, price, stock, created_at FROM products");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

$sorter = new TableSorter($rows, [
    'allowed' => ['id', 'product', 'price', 'stock', 'created_at'],
    'numeric' => ['id', 'price', 'stock'],
    'default_col' => 'id',
    'default_order' => 'desc',
]);

$col = $_GET['sort'] ?? '';
$order = $_GET['order'] ?? '';
$data = $sorter->sort($col, $order);

echo json_encode([
    'success' => true,
    'data' => $data,
    'state' => $sorter->getState(),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

前端:Vue 3 / Alpine.js 輕量實作

<!-- index.html -->
<div x-data="tableApp()" x-init="load()">
  <table class="sortable">
    <thead>
      <template x-for="col in columns" :key="col.key">
        <th @click="sort(col.key)" :class="{sorted: state.col === col.key}">
          {{ col.label }}
          <span x-show="state.col === col.key">
            {{ state.order === 'asc' ? '▲' : '▼' }}
          </span>
        </th>
      </template>
    </thead>
    <tbody>
      <template x-for="row in rows" :key="row.id">
        <tr><td>{{ row.id }}</td><td>{{ row.product }}</td><td>{{ row.price }}</td><td>{{ row.stock }}</td><td>{{ row.created_at }}</td></tr>
      </template>
    </tbody>
  </table>
</div>

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script>
function tableApp() {
  return {
    columns: [
      {key: 'id', label: 'ID'},
      {key: 'product', label: '商品'},
      {key: 'price', label: '價格'},
      {key: 'stock', label: '庫存'},
      {key: 'created_at', label: '建立時間'},
    ],
    rows: [],
    state: {col: 'id', order: 'desc'},
    async load() {
      const url = new URL('api.php', window.location.origin);
      url.searchParams.set('sort', this.state.col);
      url.searchParams.set('order', this.state.order);
      const res = await fetch(url);
      const {data, state} = await res.json();
      this.rows = data;
      this.state = state;
    },
    sort(col) {
      if (this.state.col === col) {
        this.state.order = this.state.order === 'asc' ? 'desc' : 'asc';
      } else {
        this.state.col = col;
        this.state.order = 'asc';
      }
      this.load();
    }
  };
}
</script>

進階:URL 狀態同步(可分享、可重整)

// 在 sort() 中加入:
const url = new URL(window.location);
url.searchParams.set('sort', this.state.col);
url.searchParams.set('order', this.state.order);
window.history.pushState({}, '', url); // 不重載頁面更新網址

// x-init="load()" 改為:
x-init="load(new URLSearchParams(window.location.search))"
// load(params) 內:
const sort = params.get('sort') || this.state.col;
const order = params.get('order') || this.state.order;
this.state = {col: sort, order: order};
await this.load();

現在使用者可將排序後的網址分享給同事、重整頁面保持狀態。

資料庫層優化:大資料集下推排序

若資料超過 5,000 筆,前端分頁 + 後端 SQL 排序更佳:

$allowed = ['id','product','price','stock','created_at'];
$col = in_array($_GET['sort'] ?? '', $allowed, true) ? $_GET['sort'] : 'id';
$order = ($_GET['order'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

$sql = "SELECT * FROM products ORDER BY {$col} {$order} LIMIT 50 OFFSET ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([($_GET['page'] ?? 1 - 1) * 50]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

原則: 能在 SQL ORDER BY 解決的,不要搬到 PHP 記憶體排序。

總結

  • 後端封裝 TableSorter 統一驗證欄位、型別旗標、預設值。
  • 前端用 Alpine.js/Vue 等輕量框架綁定點擊、圖示、狀態。
  • URL 同步實現可分享、可書籤、可重整的排序狀態。
  • 大資料集務必下推到資料庫,PHP 只處理當頁資料。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved