[2020/08/04] PHP PhpSpreadsheet エクセルファイルをPHPの配列データへの読み込みについて(rangeToArray) (No.224)
[2020/08/03] PHP 配列(array)何に使う:$_POST[] のデータを配列に取得 (No.223)
[2020/07/31] PHP 配列(array)の使い方について(「添字配列」「連想配列」) (No.222)
[2020/07/29] PHP PhpSpreadsheet\Chart ワークシートにチャートが存在するエクセルファイル(テンプレート)の系列データ部分を変更し別のファイルとして登録する方法について (No.220)
[2020/07/29] PHP PhpSpreadsheet\Chart ワークシート上に円グラフ・チャート(Pie Chart)とドーナツグラフ・チャート(Donut Chart)、及びレーダーチャート(Radar Chart)を作成する方法について (No.219)
-
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
-
今回は PhpSpreadsheet でエクセルファイルをPHPの配列への「読み込み」について説明したいと思います。
PhpSpreadsheet のワークシートオブジェクトにはシート内に存在する全てのセルのデータをPHPの配列へ読み込む rangeToArray 関数があります。 この関数を使えば、特に気にしなくてもセルの表示される値が取得できます。
■エクセルファイルの「読み込み」その1(セルは値のみ設定)
処理の手順は以下の通りです。
- 最初に IOFactory::load() メソッドで既存エクセルファイルを読込みSpreadsheetオブジェクト作成します。
- Spreadsheetオブジェクトの getSheet() メソッドでワークシートオブジェクトを取得します。
- ワークシートオブジェクトの calculateWorksheetDimension() メソッドでシート全体の領域を示す文字列を取得します。 (この関数は結果として "A1:[最大カラム文字列][最大行]" を返します。)
- ワークシートオブジェクトの rangeToArray() メソッドで全体のセルの値を配列に取得します。
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Worksheet; // [test11.xlsx] ファイルをロードしSpreadsheetオブジェクト作成 $objSpreadsheet = IOFactory::load('./test11.xlsx'); // ワークシートオブジェクトの取得 $objSheet = $objSpreadsheet->getSheet(0); // ワークシート内の最大領域座標("A1:XXXnnn" XXX:最大カラム文字列, nnn:最大行) $strRange = $objSheet->calculateWorksheetDimension(); // ワークシートの全てのデータ取得(配列データとして) $arrData = $objSheet->rangeToArray($strRange); echo "<pre>"; print_r($arrData); echo "</pre>"; ?>
読み込むエクセルファイルは以下のものを使います。
ブラウザの表示は以下の様な感じになります。(エクセル上で空白のセルは、配列の結果としても空白になります。)Array ( [0] => Array ( [0] => No [1] => DATA1 [2] => DATA2 [3] => DATA3 [4] => DATA4 ) [1] => Array ( [0] => 1 [1] => AAAAAAA [2] => BBB [3] => CCC [4] => DDDD ) [2] => Array ( [0] => 2 [1] => aaaa [2] => bbb [3] => ccc [4] => ) [3] => Array ( [0] => 3 [1] => ああああ [2] => いいい [3] => 漢字データ [4] => aaaa ) [4] => Array ( [0] => 4 [1] => [2] => [3] => [4] => ) )
■エクセルファイルの「読み込み」その2(セルに計算式を含む場合)
上記のエクセルファイルの読み込みソースで、以下の様なセルに計算式を含むものがあるものを読込ませてみます。
尚、ソース上で「IOFactory::load('./test11.xlsx');」のファイルの部分を「'./test11-2.xlsx'」とします。
ブラウザの表示は以下の様な感じになります。(金額の計算結果が値として配列に設定されています。)Array ( [0] => Array ( [0] => No [1] => 商品名 [2] => 単価 [3] => 数量 [4] => 金額 ) [1] => Array ( [0] => 1 [1] => 商品1 [2] => 1000 [3] => 10 [4] => 10000 ) [2] => Array ( [0] => 2 [1] => 商品ああ [2] => 100 [3] => 5 [4] => 500 ) [3] => Array ( [0] => 3 [1] => 商品い [2] => 300 [3] => 6 [4] => 1800 ) [4] => Array ( [0] => 4 [1] => 商品う [2] => 500 [3] => 1 [4] => 500 ) )
■エクセルファイルの「読み込み」内容をHTMLのTABLE表示
それでは上記で使用したエクセルファイル「'./test11-2.xlsx'」を読込んで HTML の TABLE タグを使って表形式での表示を行います。
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Worksheet; // [test11-2.xlsx] ファイルをロードしSpreadsheetオブジェクト作成 $objSpreadsheet = IOFactory::load('./test11-2.xlsx'); // ワークシート・オブジェクトの取得 $objSheet = $objSpreadsheet->getSheet(0); // ワークシート内の最大領域座標("A1:XXXnnn" XXX:最大カラム文字列, nnn:最大行) $strRange = $objSheet->calculateWorksheetDimension(); // ワークシートの全てのデータ取得(配列データとして) $arrData = $objSheet->rangeToArray($strRange); ?> <html> <head> <meta charset="utf-8"> <title>test excel array </title> </head> <body> <table border="1"> <thead> <?php $arrWK = $arrData[0]; ?> <tr> <?php for ($ii = 0; $ii <= (count($arrWK) - 1); $ii++) { ?> <th><?php echo $arrWK[$ii]; ?></th> <?php } ?> </tr> </thead> <tbody> <?php for ($i = 1; $i <= (count($arrData) - 1); $i++) { $arrWK = $arrData[$i]; ?> <tr> <?php for ($ii = 0; $ii <= (count($arrWK) - 1); $ii++) { ?> <td><?php echo $arrWK[$ii]; ?></td> <?php } ?> </tr> <?php } ?> </tbody> </table> </body> </html>
ブラウザには以下の様に表示されます。
PR -
配列(array) は「連想配列」であり、その宣言等については以下のページで説明しましたが、 いざ 配列 の使い道は何があるのかは初心者の方には分かりづらいと思います。
⇒PHP 配列(array)の使い方について(「添字配列」「連想配列」)
それで今回は 配列 を使って $_POST[] のデータを配列に取得する方法を説明します。
$_POST[] はウエブページから入力データを PHP に渡す一般的な方法です。
例えば、以下の様な HTML ページが在ったとします。 (「名前」「年齢」「メモ」の項目を入力し SUBMIT ボタンで「arr-post.php」に遷移させます)<html> <head> <meta charset="utf-8"> <title>test array post</title> </head> <body> <form name="test" action="arr-post.php" method="post"> <p>名前:</p><input type="text" name="name" /> <p>年齢:</p><input type="text" name="age" /> <p>メモ:</p><input type="text" name="memo" /> <input type="submit" value="送信" /> </form> </body> </html>
この form タグの遷移先の test.php での $_POST[] の取得は個別に行えば以下の様になります。
各項目ごとに $_POST[] にデータが存在することを確認し、個別の変数に値を設定します。<?php // $_POST["name"] 取得 $strName = ""; if ((isset($_POST["name"]) == true) && ($_POST["name"] != "")) { $strName = $_POST["name"]; } // $_POST["age"] 取得 $strAge = ""; if ((isset($_POST["age"]) == true) && ($_POST["age"] != "")) { $strAge = $_POST["age"]; } // $_POST["memo"] 取得 $strMemo = ""; if ((isset($_POST["memo"]) == true) && ($_POST["memo"] != "")) { $strMemo = $_POST["memo"]; } // 取得データ表示 echo "名前:".$strName."<br />"; echo "年齢:".$strAge."<br />"; echo "メモ:".$strMemo."<br />"; ?>
渡されるデータの個数が少なければこれでもいのですが、数が増えるとソース記述量も増えますので $_POST[] のキー値と、そのデータを格納する配列を導入します。
<?php // $_POST のキー値 $arrKey = array("name", "age", "memo"); // データ格納用配列の初期化 $arrPara = array(); // $_POST のキー値を順次処理する foreach($arrKey as $strKey) { // 各キー毎の格納用配列の初期化 $arrPara[$strKey] = ""; // $_POST の存在チェック if ((isset($_POST[$strKey]) == true) && ($_POST[$strKey] != "")) { // $_POST に存在すれば値を退避 $arrPara[$strKey] = $_POST[$strKey]; } } // 取得データの表示 foreach($arrPara as $strKey => $strPara) { echo "\$arrPara[".$strKey."] = ".$strPara."<br />"; } ?>
このままでもいいのですが、データが文字列として扱うのか、それとも数値として扱うのかを区別したり、 また、文字列データの場合には文字数制限を行ったりできる様にします。
1個のデータのキーに対して、複数の属性を持つことになりますので、属性データも配列で持ちます。
<?php // $_POST のキー値 $arrKey = array( "name" => array("title" => "名前", "attr" => "string", "maxlength" => 10) , "age" => array("title" => "年齢", "attr" => "integer", "maxlength" => 3 , "min" => 0, "max" => 99) , "memo" => array("title" => "メモ", "attr" => "string", "maxlength" => 20) , ); ?>
"title" はデータのタイトル、"attr" はデータ属性、"maxlength" は文字列の最大長で、"min","max" は数値の場合の最大最小値のチェック用です。
この属性配列を使って $_POST[] からデータを取得しながらエラーチェックを行う処理は以下の様になります。
<?php // $_POST のキー値 $arrKey = array( "name" => array("title" => "名前", "attr" => "string", "maxlength" => 10) , "age" => array("title" => "年齢", "attr" => "integer", "maxlength" => 3 , "min" => 0, "max" => 99) , "memo" => array("title" => "メモ", "attr" => "string", "maxlength" => 20) , ); // データ格納用配列の初期化 $arrPara = array(); // エラーメッセージ $strError = ""; // $_POST のキー値を順次処理する foreach($arrKey as $strKey => $arrAttr) { switch ($arrAttr["attr"]) { case "string": $arrPara[$strKey] = ""; if ((isset($_POST[$strKey]) === true) || ($_POST[$strKey] === "")) { $arrPara[$strKey] = $_POST[$strKey]; // 文字列長チェック if (mb_strlen($arrPara[$strKey]) > $arrAttr["maxlength"]) { $strError .= $arrAttr["title"].":データ長エラー<br />"; } } break; case "integer": $arrPara[$strKey] = ""; if ((isset($_POST[$strKey]) === true) || ($_POST[$strKey] === "")) { $arrPara[$strKey] = $_POST[$strKey]; // 文字列長チェック if (mb_strlen($arrPara[$strKey]) > $arrAttr["maxlength"]) { $strError .= $arrAttr["title"].":データ長エラー<br />"; } elseif (is_numeric($arrPara[$strKey]) === false) { // 数値チェックエラー $strError .= $arrAttr["title"].":数値チェックエラー<br />"; } else { // 最小最大値チェック $intAge = intval($arrPara[$strKey]); if ($intAge < $arrAttr["min"] || $arrAttr["max"] < $intAge) { $strError .= $arrAttr["title"].":最小最大値チェックエラー<br />"; } } } break; } } // 取得データの表示 foreach($arrPara as $strKey => $strPara) { echo "\$arrPara[".$strKey."] = ".$strPara."<br />"; } // エラー表示 if ($strError != "") { echo "<br />".$strError; } ?>
ラジオボタン、チェックボックス、セレクトボックス(コンボボックス)などのデータ属性はは考慮していませんので、 皆さんで拡張して下さい。
$_POST[] からデータを取得し配列に格納する処理は行ってきましたが、 それぞれのデータを別の変数に格納する場合にはどうするのかと言いますと、 以下の様に属性配列に格納先変数を 「参照変数」 として記述します。
<?php // 各データ用変数 $strName = ""; $strAge = ""; $strMemo = ""; // $_POST のキー値 $arrKey = array( "name" => array("title" => "名前", "attr" => "string", "save" =>&$strName , "maxlength" => 10) , "age" => array("title" => "年齢", "attr" => "integer", "save" =>&$strAge , "maxlength" => 3 , "min" => 0, "max" => 99) , "memo" => array("title" => "メモ", "attr" => "string", "save" =>&$strMemo , "maxlength" => 20) , ); // エラーメッセージ $strError = ""; // $_POST のキー値を順次処理する foreach($arrKey as $strKey => $arrAttr) { switch ($arrAttr["attr"]) { case "string": $arrAttr["save"] = ""; if ((isset($_POST[$strKey]) === true) || ($_POST[$strKey] === "")) { $arrAttr["save"] = $_POST[$strKey]; // 文字列長チェック if (mb_strlen($arrAttr["save"]) > $arrAttr["maxlength"]) { $strError .= $arrAttr["title"].":データ長エラー<br />"; } } break; case "integer": $arrAttr["save"] = ""; if ((isset($_POST[$strKey]) === true) || ($_POST[$strKey] === "")) { $arrAttr["save"] = $_POST[$strKey]; // 文字列長チェック if (mb_strlen($arrAttr["save"]) > $arrAttr["maxlength"]) { $strError .= $arrAttr["title"].":データ長エラー<br />"; } elseif (is_numeric($arrAttr["save"]) === false) { // 数値チェックエラー $strError .= $arrAttr["title"].":数値チェックエラー<br />"; } else { // 最小最大値チェック $intAge = intval($arrAttr["save"]); if ($intAge < $arrAttr["min"] || $arrAttr["max"] < $intAge) { $strError .= $arrAttr["title"].":最小最大値チェックエラー<br />"; } } } break; } } // 取得データの表示(この処理も属性配列から格納先データを参照) foreach($arrKey as $strKey => $arrAttr) { echo $arrAttr["title"]." = ".$arrAttr["save"]."<br />"; } // エラー表示 if ($strError != "") { echo "<br />".$strError; } ?>
-
今回はPHPのプログラムでは必ず使用するであろうデータ形式である 配列(array) について説明したいと思います。
配列(array) とは1個の変数の中に キー とそれに対応する 値 の組み合わせを複数持っているデータ構造です(リスト構造)。
この 値 の中に各種のデータ型を格納できます。別の配列を格納することで配列の入れ子ができます。
そもそも 配列(array) は何に使うのでしょうか?
配列の中に入れるデータはそれらが関連したデータで、ひとかたまりとして処理できるものです。
例えば以下の様な用途で使ってきました。- 固定的なオプションデータとして配列で持った方が良いデータ(例:一週間の"月"、"火"、"水"...の文字列の配列)
- 連続した数値データを持つ配列データ(数値データの配列から平均、標準偏差などの計算...)
- HTML出力用のSELECTタグ用のVALUE値と表示値の配列データ
- HTML出力用のOPTIONタグ用のVALUE値と表示値の配列データ
- $_POST、$_GET の値を格納するための配列
- データベースのテーブルから取得する一覧データのための配列
- CSVファイル、Excelファイルの読込結果のための配列
- 等々...(拾い上げると各種あります)
プログラムで処理するために繰り返し処理に向いているデータを配列として格納し、 各種の処理を行うことが配列を使う理由になります。
通常プログラム言語の「配列」は キー(添字) が数値(インデックス)である「添字配列」と、キー(添字) を文字列とする「連想配列」の2種類があります。
PHP では「添字配列」と「連想配列」の間に違いはなく、配列型は 1 つだけで、 同じ配列で整数のインデックスと文字列のインデックスを同時に使えます。
これは「添字配列」として使っていた配列が、組み方によっては「連想配列」が混在する様なことにもなりますので、注意が必要です。
インデックスを数値のみとして扱えば「添字配列」として処理でき、インデックスを文字列(数値も含む)で処理すれば「連想配列」として 処理できると考えられます。 (PHPの内部ではインデックスと値の組み合わせの「連想配列」として処理される)
【配列の構文:array()で指定】 $var = array( key1 => value1, key2 => value2, key3 => value3, ... ); ・$var :配列を格納する変数。 ・keyX :キー値(数値でも文字列でもOK) ・valueX:要素値(各種のデータで配列もOK)
それでは「添字配列」と「連想配列」の宣言例から使い方の説明を行います。- 添字配列 の宣言例
- 添字配列 の使い方の例
- 添字配列 の使い方の例その2(foreach文)
- 添字配列 の使い方の例その3(foreach文の注意)
- 添字配列 の使い方の例その4(角括弧構文で作成・修正)
- 連想配列 の宣言例
- 連想配列 の使い方の例その1(foreach文)
- 連想配列 の使い方の例その2(foreach文の注意)
- 連想配列 の使い方の例その3(角括弧構文で作成・修正)
■添字配列 の宣言例
「添字配列」として宣言するには array() の中でキー値の指定を省いて、要素値のみ連続して記述します。
以下のソースでは要素が数値のみの配列、要素が文字列のみの配列、要素が数値,文字列が混在の配列を定義しています。
それぞれを宣言した変数の内容を表示するために var_dump() 関数で処理します。 尚、 var_dump() 出力の前後を <pre> タグで囲むことで、 <br> タグ無しで改行出力をブラウザで改行した様にみられます。<?php // 要素が数値のみの配列 $arr1 = array(10, 20, 30); // 要素が文字列のみの配列 $arr2 = array("10", "20", "30"); // 要素が数値,文字列が混在の配列 $arr3 = array("10", 20, "30"); echo "<pre>"; var_dump($arr1); var_dump($arr2); var_dump($arr3); echo "</pre>"; ?>
これを実行するとブラウザに以下の様に表示されます。
array(3) { [0]=> int(10) [1]=> int(20) [2]=> int(30) } array(3) { [0]=> string(2) "10" [1]=> string(2) "20" [2]=> string(2) "30" } array(3) { [0]=> string(2) "10" [1]=> int(20) [2]=> string(2) "30" }
【var_dump の配列の出力形式】 array(n) { [i]=> int(d) ... [j]=> string(c) "contents" ... } ・array(n) :arrayで配列を示す(n:配列の要素数) ・[i], [j] :キー値(指標) ・int(d) :数値データ (d:要素値) ・string(c) :文字列データ(c:文字列数) ・"contents":文字列の値
■添字配列 の使い方の例
以下の例はよくある配列の反復処理です。 配列の指標を使って「0」から配列の最後の指標までをループで配列の内容を表示処理しています。
この例は必ず指標は「0」から始まり、指標の最後は「配列の要素数-1」であるのが前提です。
(尚、「配列の要素数」を取得するために count() 関数を使用します。)
しかし、この前提が違っているとプログラム実行でエラーが発生しますので、後からこれを回避する方法を記します。<?php // 要素が数値のみの配列 $arr1 = array(10, 20, 30); // 指標を使って「0」から「配列の大きさ-1」までを反復処理 for($i = 0; $i < count($arr1); $i++) { echo "\$arr1[".$i."]=".$arr1[$i]."<br />\n"; } ?>
これを実行するとブラウザに以下の様に表示されます。
$arr1[0]=10 $arr1[1]=20 $arr1[2]=30
■添字配列 の使い方の例その2(foreach文)
配列を反復処理するため前述の for文 とは別の foreach文 があります。構文は以下の通りです。
【foreach文の構文】 foreach (array_expression as $value) 文; foreach (array_expression as $key => $value) 文; ・array_expression:配列(指定された配列の反復処理を行う) ・$value :配列の要素 ・$key :指標(キー値)
array_expression で指定した配列に 関して反復処理を行います。 配列に格納されている要素の値を先頭から $value に代入し、 配列の中身を指し示すポインタを先頭に設定し $value に代入します。この時ポインタが次の要素を示す様に一つ進められます。 これを順次処理することで、配列の要素を順次取得できます。
尚、2番目の形式は、さらに各反復で現在の要素のキーを変数 $key に代入します。<?php // 要素が数値のみの配列 $arr1 = array(10, 20, 30); // 配列の内容を順次取得する「foreach」を使って反復処理 foreach($arr1 as $i => $value) { echo "\$arr1[".$i."]=".$value."<br />\n"; } ?>
これを実行するとブラウザに上記の様に表示されます。(ここでは表示割愛)
尚、指標の取得が必要無ければ「$i =>」の部分を記述しなくてもOKです。<?php // 要素が数値のみの配列 $arr1 = array(10, 20, 30); // 「foreach」で指標の取得無し foreach($arr1 as $value) { echo "DATA=".$value."<br />\n"; } ?>
■添字配列 の使い方の例その3(foreach文の注意)
配列を反復処理で要素を変更したい場合には $value の前に &(アンドマーク) を記述します。
&(アンドマーク) を付加することで反復処理での $value には配列の要素のリファレンス(参照:C言語のポインタの様なもの)が渡されます。<?php // 要素が数値のみの配列 $arr1 = array(10, 20, 30); // 配列の内容を順次取得する「foreach」を使ってループ処理 foreach($arr1 as &$value) { $value = $value * 10; // 要素を10倍する } unset($value); // 最後の要素への参照を解除します echo "<pre>"; print_r($arr1); echo "</pre>"; ?>
これを実行するとブラウザに以下の様に表示されます。配列の各要素が10倍されていることが分かります。
Array ( [0] => 100 [1] => 200 [2] => 300 )
ここで注意が必要なのは unset($value); です。 ここで変数 $value を破棄しないと $value には $arr1 の3番目の要素 $arr1[2] への参照が残ったままで、 不用意に $value に値を設定したりすると $arr1[2] が書き変ってしまいます。
■添字配列 の使い方の例その4(角括弧構文で作成・修正)
配列に対して角括弧構文でキーを指定し要素値を設定することで、配列の更新をすることが出来ます。
キーを指定が無い場合は、指標の中で最大値の次の値を指標として値が追加されます。<?php // 要素が数値のみの配列 $arr1 = array(10, 20, 30); // 角括弧構文でキーを指定し配列の要素を更新 $arr1[2] = 100; // 角括弧構文でキー指定無しで配列の要素を追加 $arr1[] = 200; echo "<pre>"; print_r($arr1); echo "</pre>"; ?>
これを実行するとブラウザに以下の様に表示されます。
配列の3番目の要素が「100」に更新され、4番目の要素として「200」が追加されます。Array ( [0] => 10 [1] => 20 [2] => 100 [3] => 200 )
尚、以下の様に角括弧構文のみで配列を作成することが出来ます。
最初の $arr1[] = で $arr1 が存在していなければ配列として生成され、キー「0」の要素に値が設定されます。
array() 宣言をコメントで消していますが、これがあれば明示的に配列だと宣言できます。(少し冗長なのですが、私はこちらを推奨します。)<?php // 配列宣言 //$arr1 = array(); // 角括弧構文でキー指定無しで配列の要素を追加 $arr1[] = 10; $arr1[] = 20; $arr1[] = 30; echo "<pre>"; print_r($arr1); echo "</pre>"; ?>
■連想配列 の宣言例
今までの 添字配列 ではキー値としていわゆるインデックスとしての数値でしたが、連想配列 としてはキー値に文字列等を用います。
宣言の最初はキーを文字列で行っていますが、最後は数値で宣言しています。<?php // 配列宣言 $arr1 = array( "KEY1" => "11111", "KEY2" => "222", "KEY3" => "3333", "KEY4" => "44444", 0 => 100, // キー:数値!! "KEY5" => "555", ); echo "<pre>"; print_r($arr1); echo "</pre>"; ?>
これを実行するとブラウザに以下の様に表示されます。キーが文字列であっても数値であっても、ちゃんと配列が生成されています。 宣言の中で最後の行でカンマを記述していますが、カンマ無しでもOKです。
Array ( [KEY1] => 11111 [KEY2] => 222 [KEY3] => 3333 [KEY4] => 44444 [0] => 100 [KEY5] => 555 )
■連想配列 の使い方の例その1(foreach文)
添字配列 の時の様に foreach文 を使った例を示します。
<?php // 配列宣言 $arr1 = array( "KEY1" => "11111", "KEY2" => "222", "KEY3" => "3333", "KEY4" => "44444", 0 => 100, "KEY5" => "555", ); // 配列の内容を順次取得する「foreach」を使ってループ処理 foreach($arr1 as $key => $value) { echo "\$arr1[".$key."]=".$value."<br />\n"; } ?>
これを実行するとブラウザに以下の様に表示されます。
$arr1[KEY1]=11111 $arr1[KEY2]=222 $arr1[KEY3]=3333 $arr1[KEY4]=44444 $arr1[0]=100 $arr1[KEY5]=555
■連想配列 の使い方の例その2(foreach文の注意)
添字配列のところで説明しましたが、連想配列でも配列を反復処理で要素を変更したい場合には $value の前に &(アンドマーク) を記述します。 (PHPの配列は連想配列として考えればどちらもおなじことですが)
&(アンドマーク) を付加することで反復処理での $value には配列の要素のリファレンス(参照:C言語のポインタの様なもの)が渡されます。<?php // 要素が数値のみの配列 $arr1 = array( "KEY1" => "11111", "KEY2" => "222", "KEY3" => "3333", "KEY4" => "44444", 0 => 100, "KEY5" => "555", ); // 配列の内容を順次取得する「foreach」を使ってループ処理 foreach($arr1 as &$value) { $value = $value . "AA"; // 要素に文字列 "AA" 付加 } unset($value); // 最後の要素への参照を解除します echo "<pre>"; print_r($arr1); echo "</pre>"; ?>
これを実行するとブラウザに以下の様に表示されます。配列の各要素文字列 "AA" 付加されていることが分かります。
指標「0」の要素は数値でしたが、文字列との連結では数値が文字列 "100" に強制的に変換されて "AA" が付加されることになります。
array(6) { ["KEY1"]=> string(7) "11111AA" ["KEY2"]=> string(5) "222AA" ["KEY3"]=> string(6) "3333AA" ["KEY4"]=> string(7) "44444AA" [0]=> string(5) "100AA" ["KEY5"]=> string(5) "555AA" }
ここで注意が必要なのは unset($value); です。 ここで変数 $value を破棄しないと $value には $arr1 の3番目の要素 $arr1["KEY5"] への参照が残ったままで、 不用意に $value に値を設定したりすると $arr1["KEY5"] が書き変ってしまいます。
■連想配列 の使い方の例その3(角括弧構文で作成・修正)
配列に対して角括弧構文でキーを指定し要素値を設定することで、配列の更新をすることが出来ます。
キーを指定が無い場合は、指標の中で最大値の次の値を指標として値が追加されます。
<?php // 配列宣言 $arr1 = array(); // 角括弧構文でキー指定で配列の要素を追加 $arr1["KEY1"] = "11111"; $arr1["KEY2"] = "222"; $arr1["KEY3"] = "3333"; $arr1["KEY4"] = "44444"; $arr1[0 ] = 100; $arr1["KEY5"] = "555"; // 配列内部表示 echo "<pre>最初の表示<br>"; var_dump($arr1); echo "</pre>"; // キー指定無しで追加 $arr1[] = 200; // 配列内部表示 echo "<pre>追加後の表示<br>"; var_dump($arr1); echo "</pre>"; ?>
これを実行するとブラウザに以下の様に表示されます。
キー指定無しでの配列への追加は、キーの中で数値の最大値に+1されたものがキーとなりその要素に値が設定されます。 (数値キーがなければキーは「0」となります。)
最初の表示 array(6) { ["KEY1"]=> string(5) "11111" ["KEY2"]=> string(3) "222" ["KEY3"]=> string(4) "3333" ["KEY4"]=> string(5) "44444" [0]=> int(100) ["KEY5"]=> string(3) "555" } 追加後の表示 array(7) { ["KEY1"]=> string(5) "11111" ["KEY2"]=> string(3) "222" ["KEY3"]=> string(4) "3333" ["KEY4"]=> string(5) "44444" [0]=> int(100) ["KEY5"]=> string(3) "555" [1]=> int(200) }
-
今回は PhpSpreadsheet\Chart で「Bar Chart:棒グラフ」が作成されているエクセルファイルを読み込み、 そのチャートが参照している系列データ部分を別のデータで書き変えを行う方法を説明します。
おおまかな手順は以下の様になります。- PhpSpreadsheet\IOFactory クラスの createReader メソッドで読込みオブジェクトを生成し、エクセルファイルの読込。 (この時 setIncludeCharts(TRUE) メソッドでチャートの存在を指定する)
- ワークシートの fromArray メソッドで系列データを設定。
- PhpSpreadsheet\IOFactory クラスの createWriter メソッドで書込みオブジェクトを生成し、エクセルファイルの書込。 (この時 setIncludeCharts(TRUE) メソッドでチャートの存在を指定する)
以下の記事のエクセルファイルを元にします。
⇒PHP PhpSpreadsheet\Chart ワークシート上にチャート(Bar Chart:棒グラフ)を作成する方法について
元となるエクセルファイル[test-g-2-1.xlsx]は以下の様になっています。■ワークシートにチャートが存在するエクセルファイル(テンプレート)の系列データ部分を変更し別のファイルとして登録
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\IOFactory; // エクセルファイル読込オブジェクト $objReader = IOFactory::createReader('Xlsx'); $objReader->setIncludeCharts(TRUE); // PHPExcelオブジェクト取得 $objSpreadsheet = $objReader->load("test-g-2-1.xlsx"); // ワークシートオブジェクト取得 $objWorksheet = $objSpreadsheet->getActiveSheet(); // チャート用テストデータ上書 $objWorksheet->fromArray( array( array( 22, 38, 19), array( 25, 49, 20), array( 31, 33, 25), array( 28, 44, 42), array( 40, 31, 13), ), NULL, 'B2' ); // エクセルファイル書込オブジェクト $objWriter = IOFactory::createWriter($objSpreadsheet, 'Xlsx'); $objWriter->setIncludeCharts(TRUE); // エクセルファイル書込 $objWriter->save('test-g-7-1.xlsx'); exit(); ?>
出力されたエクセルファイル[test-g-7-1.xlsx]を見てみると以下の様になります。
チャートが参照している系列データを変更すれば、表示されるチャートのグラフは違ったものになります。
このことを利用すれば、テンプレートのエクセルファイルとしてチャート含んだものを登録しておき、 データ部だけを変えるだけで異なるエクセルファイルを出力することができます。
-
今回は先ず2個のチャート上にそれぞれ「Pie Chart:円グラフ」「Donut Chart:ドーナツグラフ」のグラフを作成し、 その後で「Radar Chart:レーダーチャート」作成します。
■2個のチャート上にそれぞれ「Pie Chart:円グラフ」「Donut Chart:ドーナツグラフ」のグラフを作成
「Pie Chart:円グラフ」「Donut Chart:ドーナツグラフ」は共に系列データ等の指定方法は同じで、表示される図形が円かドーナツかの違いがあるだけです。
系列データとして設定できるのは1種類のデータの系列のみになります。 以下のテストデータを例にとると、縦方向には「商品1」の「2016~2020の1列のデータ」が対象となります。 同一チャート上に「商品1」及び「商品2」のグラフを「棒グラフ」等の様に同時には表示できません。// チャート用テストデータ生成 $objWorksheet->fromArray( array( array('売上', '商品1', '商品2', '商品3'), array( 2016, 12, 18, 15), array( 2017, 15, 19, 10), array( 2018, 21, 23, 20), array( 2019, 18, 14, 12), array( 2020, 20, 21, 23), ) );
以前は PhpSpreadsheet\Chart\PlotArea クラス生成で先頭の引数には「NULL」を指定していましたが、 円グラフの各要素の値を表示するために PhpSpreadsheet\Chart\Layout クラスのオブジェクトを生成し、 指定します。
PhpSpreadsheet\Chart\Layout クラスの setShowVal メソッドで値表示を指定し setShowPercent メソッドでパーセント表記の指定をします。 (結果、値とパーセントの二つとも表示されます)
実際のソースは以下の様になります。
(「Pie Chart:円グラフ」は「商品1」を「Donut Chart:ドーナツグラフ」は「商品2」を対象とします。)<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Chart\Chart; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PhpOffice\PhpSpreadsheet\Chart\PlotArea; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Chart\Legend; use PhpOffice\PhpSpreadsheet\IOFactory; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト取得 $objWorksheet = $objSpreadsheet->getActiveSheet(); // チャート用テストデータ生成 $objWorksheet->fromArray( array( array('売上', '商品1', '商品2', '商品3'), array( 2016, 12, 18, 15), array( 2017, 15, 19, 10), array( 2018, 21, 23, 20), array( 2019, 18, 14, 12), array( 2020, 20, 21, 23), ) ); //===================== // Pie Chart //===================== // 系列ラベルの指定 $arrDataSeriesLabels1 = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', NULL, 1), //商品1 ); // X軸ラベルの指定 $arrCategorysDataSeries1 = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$6', NULL, 5), //2016 to 2020 ); // 描画データの指定 $arrDataSeriesValues1 = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$6', NULL, 5), //商品1 ); // チャート・データシリーズの生成 $objSeries1 = new DataSeries( DataSeries::TYPE_PIECHART, // plotType NULL, // plotGrouping (円グラフはグループ指定必要無し) range(0, count($arrDataSeriesValues1) - 1), // plotOrder $arrDataSeriesLabels1, // plotLabel $arrCategorysDataSeries1, // plotCategory $arrDataSeriesValues1 // plotValues ); // 円グラフ用のレイアウト設定 $objLayout1 = new Layout(); $objLayout1->setShowVal(TRUE); $objLayout1->setShowPercent(TRUE); // プロットエリアにチャート・データシリーズに設定 $objPlotArea1 = new PlotArea($objLayout1, array($objSeries1)); // レジェンド生成(各折れ線の説明を行う) $objLegend1 = new Legend(Legend::POSITION_TOPRIGHT, NULL, false); // チャート・タイトル生成 $objTitle1 = new Title('Pie Chart:商品1'); // チャート生成 $objChart1 = new Chart( 'chart1', // name $objTitle1, // title $objLegend1, // legend $objPlotArea1, // plotArea TRUE, // plotVisibleOnly 0, // displayBlanksAs NULL, // xAxisLabel NULL // yAxisLabel ); // ワークシート内のチャート位置設定 $objChart1->setTopLeftPosition('A8'); // 左上 $objChart1->setBottomRightPosition('G20'); // 右下 // ワークシートにチャート追加 $objWorksheet->addChart($objChart1); //===================== // Donut Chart //===================== // 系列ラベルの指定 $arrDataSeriesLabels2 = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$2', NULL, 1), //商品2 ); // X軸ラベルの指定 $arrCategorysDataSeries2 = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$6', NULL, 5), //2016 to 2020 ); // 描画データの指定 $arrDataSeriesValues2 = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$6', NULL, 5), //商品2 ); // チャート・データシリーズの生成 $objSeries2 = new DataSeries( DataSeries::TYPE_DONUTCHART, // plotType NULL, // plotGrouping (ドーナツグラフはグループ指定必要無し) range(0, count($arrDataSeriesValues2) - 1), // plotOrder $arrDataSeriesLabels2, // plotLabel $arrCategorysDataSeries2, // plotCategory $arrDataSeriesValues2 // plotValues ); // 円グラフ用のレイアウト設定 $objLayout2 = new Layout(); $objLayout2->setShowVal(TRUE); $objLayout2->setShowPercent(TRUE); // プロットエリアにチャート・データシリーズに設定 $objPlotArea2 = new PlotArea($objLayout2, array($objSeries2)); // レジェンド生成(各折れ線の説明を行う) $objLegend2 = new Legend(Legend::POSITION_TOPRIGHT, NULL, false); // チャート・タイトル生成 $objTitle2 = new Title('Donut Chart:商品2'); // チャート生成 $objChart2 = new Chart( 'chart2', // name $objTitle2, // title $objLegend2, // legend $objPlotArea2, // plotArea TRUE, // plotVisibleOnly 0, // displayBlanksAs NULL, // xAxisLabel NULL // yAxisLabel ); // ワークシート内のチャート位置設定 $objChart2->setTopLeftPosition('H8'); // 左上 $objChart2->setBottomRightPosition('N20'); // 右下 // ワークシートにチャート追加 $objWorksheet->addChart($objChart2); // [test-g-6-1.xlsx]:Excel2007形式で保存する $objWriter = IOFactory::createWriter($objSpreadsheet, 'Xlsx'); $objWriter->setIncludeCharts(TRUE); $objWriter->save('test-g-6-1.xlsx'); exit(); ?>
出力されたエクセルファイルを見てみると以下の様になります。
■「Radar Chart:レーダーチャート」のグラフを作成
「Radar Chart:レーダーチャート」のグラフを作成してみます。
「Radar Chart:レーダーチャート」とは折れ線グラフの線を環状に繋げたイメージです。
「系列ラベル」「X軸ラベル」「系列(描画)データ」は各参照系列ごと対応するものを同じ個数設定します。
実際のソースは以下の様になります。<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Chart\Chart; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PhpOffice\PhpSpreadsheet\Chart\PlotArea; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Chart\Legend; use PhpOffice\PhpSpreadsheet\IOFactory; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト取得 $objWorksheet = $objSpreadsheet->getActiveSheet(); // チャート用テストデータ生成 $objWorksheet->fromArray( array( array('売上', '商品1', '商品2', '商品3'), array( 2016, 12, 18, 15), array( 2017, 15, 19, 10), array( 2018, 21, 23, 20), array( 2019, 18, 14, 12), array( 2020, 20, 21, 23), ) ); // 系列ラベルの指定 $arrDataSeriesLabels = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', NULL, 1), //商品1 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', NULL, 1), //商品2 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', NULL, 1), //商品3 ); // X軸ラベルの指定 $arrCategorysDataSeries = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$6', NULL, 5), //2016 to 2020 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$6', NULL, 5), //2016 to 2020 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$6', NULL, 5), //2016 to 2020 ); // 描画データの指定 $arrDataSeriesValues = array( new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$6', NULL, 5), //商品1 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$6', NULL, 5), //商品2 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$6', NULL, 5), //商品3 ); // チャート・データシリーズの生成 $objSeries = new DataSeries( DataSeries::TYPE_RADARCHART, // plotType NULL, // plotGrouping (レーダーチャートはグループ指定必要無し) range(0, count($arrDataSeriesValues) - 1), // plotOrder $arrDataSeriesLabels, // plotLabel $arrCategorysDataSeries, // plotCategory $arrDataSeriesValues, // plotValues NULL, // plotDirection NULL, // smooth line DataSeries::STYLE_MARKER // plotStyle ); // プロットエリアにチャート・データシリーズに設定 $objPlotArea = new PlotArea(NULL, array($objSeries)); // レジェンド生成 $objLegend = new Legend(Legend::POSITION_TOPRIGHT, NULL, false); // チャート・タイトル生成 $objTitle = new Title('Radar Chart'); // チャート生成 $objChart = new Chart( 'chart1', // name $objTitle, // title $objLegend, // legend $objPlotArea, // plotArea TRUE, // plotVisibleOnly 0, // displayBlanksAs NULL, // xAxisLabel NULL // yAxisLabel ); // ワークシート内のチャート位置設定 $objChart->setTopLeftPosition('A8'); // 左上 $objChart->setBottomRightPosition('F20'); // 右下 // ワークシートにチャート追加 $objWorksheet->addChart($objChart); // [test-g-6-2.xlsx]:Excel2007形式で保存する $objWriter = IOFactory::createWriter($objSpreadsheet, 'Xlsx'); $objWriter->setIncludeCharts(TRUE); $objWriter->save('test-g-6-2.xlsx'); exit(); ?>