PHPExcel 專案已於 2017 年歸檔,不再更新。 官方繼任者為 PhpSpreadsheet (同一作者團隊),支援 PHP 7.1+、嚴格型別、PSR-4、更豐富的圖表 API。本文以 **PhpSpreadsheet** 為基礎講解,代碼完全相容最新版。
composer require phpoffice/phpspreadsheet
PhpSpreadsheet 圖表物件層級:
<?php
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('銷售數據');
// 寫入標題列
$sheet->fromArray([
['月份', '產品A', '產品B', '產品C'],
['1月', 1200, 950, 800],
['2月', 1350, 1020, 850],
['3月', 1480, 1100, 920],
['4月', 1550, 1180, 980],
['5月', 1620, 1250, 1050],
['6月', 1700, 1300, 1100],
], null, 'A1'); // null = 不覆蓋現有格式,從 A1 開始寫入
?>
<?php
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
// 類別軸 (X軸) 標籤:A2:A7 (月份)
$categories = new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_STRING,
'工作表1!$A$2:$A$7', // 絕對參考
null, 6 // 資料點數量
);
// 系列 1:產品A (B2:B7)
$seriesA = new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_NUMBER,
'工作表1!$B$2:$B$7',
null, 6
);
// 系列 2:產品B (C2:C7)
$seriesB = new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_NUMBER,
'工作表1!$C$2:$C$7',
null, 6
);
// 系列 3:產品C (D2:D7)
$seriesC = new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_NUMBER,
'工作表1!$D$2:$D$7',
null, 6
);
// 建立 DataSeries:群組長條圖 (BAR_CLUSTERED)
$dataSeries = new DataSeries(
DataSeries::TYPE_BARCHART, // 圖表類型常數
DataSeries::GROUPING_CLUSTERED, // 群組模式:簇狀/堆疊/百分比堆疊
range(0, 2), // 系列索引 (0,1,2 對應下方三個系列)
[], // 系列標籤 (可選,留空自動取標題列)
[$categories], // 類別軸標籤陣列
[$seriesA, $seriesB, $seriesC] // 數值系列陣列
);
// 設定系列標題 (對應 B1, C1, D1)
$dataSeries->setPlotDirection(DataSeries::DIRECTION_COL); // 垂直長條 (預設水平)
$dataSeries->setPlotLabel(['工作表1!$B$1', '工作表1!$C$1', '工作表1!$D$1']);
?>
<?php
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
// PlotArea:包含座標軸、圖例、標題、DataSeries
$plotArea = new PlotArea(
null, // Layout (佈局,null 使用預設)
[$dataSeries] // DataSeries 陣列
);
// 圖表標題
$title = new Title('2024 上半年產品銷售趨勢');
// 圖例位置
$legend = new Legend(Legend::POSITION_RIGHT, null, false);
// 建立 Chart 物件
$chart = new Chart(
'chart1', // 內部名稱
$title, // Title 物件
$legend, // Legend 物件
$plotArea, // PlotArea 物件
true, // plotVisibleOnly (只繪製可見儲存格)
0, // displayBlanksAs (0=gap, 1=zero, 2=span)
null, // xAxisLabel
null, // yAxisLabel
new Layout() // 整體佈局
);
// 設定圖表位置與大小 (儲存格座標)
$chart->setTopLeftPosition('F2'); // 左上角錨點
$chart->setBottomRightPosition('N18'); // 右下角錨點 (寬約 9 欄 x 16 列)
// 加入工作表
$sheet->addChart($chart);
?>
<?php
$writer = new Xlsx($spreadsheet);
$writer->setIncludeCharts(true); // 關鍵:必須啟用,否則圖表不會寫入
$writer->save('sales_chart.xlsx');
echo "檔案已產生:sales_chart.xlsx\n";
?>
| 圖表類型 | DataSeries::TYPE_* 常數 | 適用場景 |
|---|---|---|
| 群組長條圖 | TYPE_BARCHART + GROUPING_CLUSTERED | 多系列分類比較 |
| 堆疊長條圖 | TYPE_BARCHART + GROUPING_STACKED | 部分佔整體比例 |
| 百分比堆疊長條圖 | TYPE_BARCHART + GROUPING_PERCENT_STACKED | 各系列佔比隨時間變化 |
| 折線圖 | TYPE_LINECHART + GROUPING_STANDARD | 趨勢、時間序列 |
| 平滑折線圖 | TYPE_LINECHART + GROUPING_SMOOTH | 平滑趨勢曲線 |
| 圓餅圖 | TYPE_PIECHART | 單系列佔比 |
| 環狀圖 | TYPE_DOUGHNUTCHART | 多系列佔比 (環狀) |
| 散佈圖 | TYPE_SCATTERCHART | 兩數值變數相關性 |
| 面積圖 | TYPE_AREACHART | 累積總量趨勢 |
| 雷達圖 | TYPE_RADARCHART | 多維度指標對比 |
| 混合圖表 | PlotArea 含多個不同 TYPE 的 DataSeries | 雙軸、長條+折線組合 |
<?php
// 系列 1:長條圖 (銷售額) - 主軸
$barSeries = new DataSeries(
DataSeries::TYPE_BARCHART,
DataSeries::GROUPING_CLUSTERED,
[0],
[],
[$categories],
[$seriesSales]
);
$barSeries->setPlotLabel(['工作表1!$B$1']);
// 系列 2:折線圖 (成長率) - 次軸
$lineSeries = new DataSeries(
DataSeries::TYPE_LINECHART,
DataSeries::GROUPING_STANDARD,
[1],
[],
[$categories],
[$seriesGrowth]
);
$lineSeries->setPlotLabel(['工作表1!$C$1']);
// PlotArea 同時包含兩個 DataSeries
$plotArea = new PlotArea(null, [$barSeries, $lineSeries]);
// 次軸設定需在 Chart/PlotArea 層級處理 (PhpSpreadsheet 1.20+ 支援)
$chart = new Chart('combo1', $title, $legend, $plotArea, true, 0, null, null, new Layout());
// 註:雙軸完整支援需較新版本,若不支援可考慮輸出後用 Excel 手動調整或改用 Python openpyxl
?>
Cell::setValueBinder(new AdvancedValueBinder()) 加速寫入;或分批寫入、關閉未需的快取。Cache\Serializer 或 Cache\Redis:Settings::setCacheStorage(new \PhpOffice\PhpSpreadsheet\Cache\Redis($redisClient));PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph (需安裝 JpGraph 庫) 或轉換為 PDF 後用 ImageMagick 截圖。| 現象 | 原因與解決 |
|---|---|
| 輸出檔案無圖表 | 未呼叫 $writer->setIncludeCharts(true);或圖表錨點位置超出工作表範圍 |
| Excel 開啟提示「圖表資料遺失」 | DataSeries 參考範圍錯誤 (工作表名稱含空格需單引號、絕對參考 $)、系列索引不匹配 |
| 類別軸顯示數字而非標籤 | DataSeriesValues 類型應為 DATASERIES_TYPE_STRING,且範圍正確包含標題列 |
| 中文亂碼 | 確保 PHP 檔案 UTF-8 編碼、資料庫連線 charset=utf8mb4、PhpSpreadsheet 內部自動處理 UTF-8 |
| 記憶體耗盡 | 啟用快取存儲、分批處理、增加 memory_limit |
PhpSpreadsheet 提供完整、物件導向的圖表 API,涵蓋從簡單長條圖到複雜混合圖表的需求。掌握「資料範圍 → DataSeriesValues → DataSeries → PlotArea → Chart → Worksheet → Writer」這條建構鏈,並善用 setIncludeCharts(true)、正確的錨點定位、系列索引對應,即可在 PHP 中程式化產出專業級 Excel 圖表報表。建議搭配 官方文檔 Charts 章節 深入探索進階功能。