カテゴリーアーカイブ PHP

村上 著者:村上

【PHP】PHPExcelでグラフを一から作る方法

昨日もPHPExcelについての記事を書きましたが、本日も引き続き同じネタについてまとめ。
当初はグラフ入りテンプレートを使ってグラフを描写する予定でしたが、思っていた以上に「複雑なグラフ」のボーダーラインが低く、どうしても作りたいグラフが消されてしまうので、プログラムでグラフ作成をすることにしました。

 

ライブラリ導入から、現在のシート選択の部分は割愛します。
やっていることは意外と単純で、まず、グラフの表示範囲や参照先のセルを指定します。

//グラフ描画設定
$options = [
    'title' => 'グラフタイトル',
    'topPos' => 'A3',   //描画するグラフの左上の位置
    'bottomPos' => 'I24',   //描画するグラフの右下の位置
    'labelPos1' => $sheet.'!$B$2',  //グラフのデータラベル
    'xAxisRange' => $sheet.'!$A$3:$A$12',   //グラフのx軸の値の範囲
    'dataValueRange1' => $sheet.'!$B$3:$B$12'  //実際のデータ範囲
];
なお、$sheet は、現在アクティブなエクセルのシートを指定します。

実際にグラフを書いているのは下記のコード。

// グラフのデータラベル設定
$dataSeriesLabels = [
    new PHPExcel_Chart_DataSeriesValues('String', $options['labelPos1'], null, 1)
];

// グラフのX軸項目設定
$xAxisTickValues = [
    new PHPExcel_Chart_DataSeriesValues('String', $options['xAxisRange'], null, 10)
];

// グラフの系列設定
$dataSeriesValues = [
    new PHPExcel_Chart_DataSeriesValues('Number', $options['dataValueRange1'], null, 10)
];

//  グラフ描画設定
$series1 = new PHPExcel_Chart_DataSeries(
    PHPExcel_Chart_DataSeries::TYPE_BARCHART,               // 横棒グラフ
    PHPExcel_Chart_DataSeries::GROUPING_CLUSTERED,          // plotGrouping
    range(0, count($dataSeriesValues)-1),                  // plotOrder
    $dataSeriesLabels,                                     // plotLabel
    $xAxisTickValues,                                      // plotCategory
    $dataSeriesValues                                      // plotValues
);
$series1->setPlotDirection(PHPExcel_Chart_DataSeries::DIRECTION_BAR);

//  プロットエリアの設定
$plotarea1 = new PHPExcel_Chart_PlotArea(null, [$series1]);
//  グラフ内での凡例のポジション設定
$legend1 = new PHPExcel_Chart_Legend(PHPExcel_Chart_Legend::POSITION_BOTTOM, null, false);
//  y軸のラベル設定
$yAxisLabel1 = new PHPExcel_Chart_Title('');
//  タイトルがある場合、タイトルの設定
$title1 = new PHPExcel_Chart_Title($options['title']);

//  グラフ作成
$chart1 = new PHPExcel_Chart('chart1',       // グラフの名前
                             $title1,        // グラフタイトル
                             $legend1,       // 凡例
                             $plotarea1,     // グラフのプロットエリア
                             false,           // 表示されているセルだけをプロット
                             0,              // 空白セルをどうするか
                             null,           // x軸のラベル
                             null            // y軸のラベル
);

//  グラフがワークシートのどこに描画されるかの設定
$chart1->setTopLeftPosition($options['topPos']);
$chart1->setBottomRightPosition($options['bottomPos']);

//ワークシートにグラフの追加
$objWorksheet->addChart($chart1);

こちらのコードでは横棒グラフを作成しています。
表示位置やタイトルを任意で決められるので、なかなか自由度は高めですね。

 

作成できるグラフの種類も豊富で、棒グラフ以外にも、折れ線グラフやレーダーチャート、あとは設定名的に察するに、バブルチャートなんかも使えそうでした。
…が、よく使うのは棒グラフ・折れ線グラフ・円グラフぐらいかな。

便利&簡単なPHPExcelですが、若干ハマっている箇所もあり、どうしても縦軸の項目名とグラフの凡例が正しく表示されません…。
データ自体は指定してあるので、設定が間違っているのだと思っています。
もし、この不具合がわかる方いらっしゃいましたら、どうぞコメントでご指摘をお願い致します。

  • この記事いいね! (0)
村上 著者:村上

【PHP】PHPExcelでグラフ入りのテンプレートを使用する方法

以前、「【PHP】PHPExcelを使うときにセルの書式設定を変更する方法」という記事を投稿しましたが、本日の記事は、またこの PHPExcel について。
今回は、PHPExcelでテンプレートファイルを使う際に、グラフが入っていた場合の処理についてまとめです。

今回参考にした投稿はこちら。

PHPExcelでテンプレートファイルからグラフを作ってみよう – Qiita
https://qiita.com/tutida/items/2d163ed7662bb499a082

やりたいことがすべて書いてありました…!

 

早速、コードを書いていきますが、PHPExcelオブジェクト生成から、シートの選択まではこんな感じ

// PHPExcelオブジェクトを生成する
$book = new PHPExcel();
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
// 読み込むテンプレートファイルにグラフが含まれていることを宣言
$objReader->setIncludeCharts(TRUE);
// テンプレート読み込み
$book = $objReader->load('テンプレートのエクセルファイルのパス');
$book->setActiveSheetIndex(0);
// カレントシート選択
$sheet = $book->getActiveSheet();

上記のコードで重要なのは、5行目の「読み込むテンプレートファイルにグラフが含まれていることを宣言する」、という

$objReader->setIncludeCharts(TRUE);

上記の記述です。
この一行を入れないと、読み込んだテンプレートファイルからグラフが消えてしまいます。

また、読み込み時だけでなく、下記のようにエクセルファイルの出力・保存の時にも同じ記述を入れる必要があります。

// ファイル名
$filename = 'PHPExcel-sample.xlsx';
$writer = PHPExcel_IOFactory::createWriter($book, 'Excel2007');
// 書き出すエクセルファイルにグラフが含まれていることを宣言
$writer->setIncludeCharts(TRUE);
$writer->save("/tmp/".$file);
File::download("/tmp/".$file, $file);

こんな感じ。
出力時にも、エクセルファイルにグラフが含まれていることを宣言する5行目の記述を必ず入れましょう。

 

ただ、グラフを扱う際には注意が必要で、あまり複雑な指定をすると、グラフが含まれていることを宣言していてもグラフが消える場合があります。
現に、私もそれで悩まされています。
グラフ入りのテンプレートを入れる場合は、極力簡単でシンプルなものを使うようにしましょう。

なお、グラフが消える・簡略化される条件は下記のようなものがあげられるとのこと。

  • X軸を設定している
  • Y軸を2種類設定している場合、2つ目は消える
  • 詳細な設定はほぼ消える

…かなり簡単なものしか扱えなさそうですね。
これならいっそ、テンプレートから読み込まず、PHPExcelでグラフを作成したほうがいいかもしれません。

ゼロからグラフを作成する方法については、冒頭で挙げた投稿を書かれた方が記事を挙げているので、こちらが参考になるかと思います。

PHPExcelでゼロからグラフを書いてみよう – Qiita
https://qiita.com/tutida/items/643baa8832beedd53499

  • この記事いいね! (0)
村上 著者:村上

【PHP】DateTimeで現在日時から○日後の日付を取得する方法

後輩に聞かれたので、私と彼のための覚え書きのメモとして。

PHPには、日付と時刻を扱う DateTimeクラスがありますが、こちらを用いて、現在の日時から○日後や、○時間前の日付・時刻を取得する方法です。

DateTimeクラスについて、PHPのマニュアルはこちらから。

DateTime クラス – Manual
http://php.net/manual/ja/class.datetime.php

 

○日後を扱う前に、まず現在の日時を取得する方法は下記の通り。

// 現在の日時を取得
$now = new DateTime();
echo $now->format('Y-m-d H:i:s'); // 2017-12-05 19:05:00

DateTimeの引数を空で指定すると、現在の日時を取得できます。
それを任意のフォーマットに変換しています。

また日付文字列を引数に指定する事も出来ます。

// 日付文字列で指定した日時を取得
$date = new DateTime("2017-06-01 06:00:00");
echo $date->format('Y-m-d H:i:s'); // 2017-06-01 06:00:00

日付文字列で指定した日時を取得できます。

 

で、本題はここから。
○日後という相対的な日時を取得したい場合、modify というものを使うことにより、DateTimeで取得した日付を変更できます。

// 現在の日時の1日後の日時を取得
$date = new DateTime();
$date->modify('+1 day');
echo $date->format('Y-m-d H:i:s');  // 現在の日時の1日後の日付

または、この書き方でもOK。

// 日付文字列で指定した日時の1日後の日時を取得
$date = new DateTime("2017-06-01 06:00:00 + 1 day");
echo $date->format('Y-m-d H:i:s'); // 2017-07-01 06:00:00

上記はGoogle Chromeでしか動作検証していませんが、1日後の日付が正しく取得できました。

それ以外にも、「– 1 day」と書けば1日前、「+ 3 hour」と書けば3時間後、「+ 1 week」と書けば1週間後の日時がそれぞれ取得できます。
日付の変更は、使用頻度はそこまで高くはないのですが、ふとした拍子に使ったりするので、頭の片隅ででも覚えて置きたいですね。

 

また、今回 DateTimeクラスを調べていて知ったのですが、数値を指定して日時を取得することもできるとのこと。
このやり方については私はまだ実際に使ったことありません。

// 数値を指定して日時を取得
$date = new DateTime();
$date->setDate(2017,6,1);
$date->setTime(6,0,0);
echo $date->format('Y-m-d H:i:s'); // 2017-06-01 06:00:00

セレクトボックスから年・月・日をそれぞれ選んで、それらを日付に変換するときとかに使えそうですね。
が、今回はDatepickerを使っているので、使うことはなさそうです。

  • この記事いいね! (0)
村上 著者:村上

【未解決】FuelPHPでストアドプロシージャを実行してデータを取得したい

タイトルにある通り、今回の記事はまだ未実装、というか実現できていないことについて。
ということで、もし解決策をご存知の方がいらっしゃれば、何卒ご教授くださいますようお願い致します。

 

ストアドプロシージャについては、こちらの記事がわかりやすいかと。

ストアドプロシージャの基本的ななにか – Qiita
https://qiita.com/setsuna82001/items/e742338eb93e3a48ba46

こちらの記事によると、ストアドプロシージャとは「データベース上での一連の処理に、名前をつけて関数のように呼び出して使用できるもの」とのこと。
データベース上で動作が完結するので、開発言語に依存しません。

 

FuelPHPについて、公式サイトはこちらから。

FuelPHPドキュメント
http://fuelphp.jp/docs/1.7/

FuelPHPは、PHP5.3以上を対象に開発されたWebフレームワークです。
他のフレームワークに比べて規約が少なく、また高速で軽量なため、使いやすいのが特徴です。

こちらのサイトもご参考に。

高速で軽量なフレームワークFuelPHPを使う
http://www.buildinsider.net/web/bookphplib100/096

 

そして、今回実現したいのは、この両方を利用して、データベースからデータを取得する方法についてです。
実行するストアドプロシージャはこんな感じ。

1> EXEC dbo.user_month_report @year =2017, @month =12;
2> go

※実際に動作させているものとは異なります。

こちらをターミナルで実行すると、望んだとおりの結果が得られます。
したがって、ストアドプロシージャ自体には問題はなさそう。

が、こちらを下記のように FuelPHP で実行すると、結果が何故か bool(true) が返ってきます。

$result = DB::query("EXEC dbo.user_month_report @year =2017, @month =12;");
$result->execute();

ストアドプロシージャに問題がないようなので、十中八九 FuelPHP が原因というのは分かるのですが、その解決策がどうしてもわかりません。
FuelPHPのドキュメントにも、ストアドプロシージャの実行方法についての記述がないため、そもそも対応していないのでしょうか。

ちなみに、FuelPHPのフォーラムに、「DB Class with mysql – select from stored procedure」という、今回の状況に似たような投稿があったのは見つけました。
その中で、DB::query()の第二引数の$typeに SQL クエリタイプとして「DB::SELECT」を指定するといいとの記述があったので試してみましたが、残念ながら結果は変わりませんでした。

参考にしたフォーラムの投稿はこちら。

DB Class with mysql – select from stored procedure
https://fuelphp.com/forums/discussion/4299

 
新しいライブラリやフレームワークを使うのは、やはり難しいですね。

  • この記事いいね! (0)
村上 著者:村上

【PHP】指定した値の範囲でランダムな少数の値を生成する方法

ランダムな整数の生成なら、rand() 関数が用意されていますが、今回は少数値が必要だったので、生成方法をご紹介。
PHPのマニュアルに掲載されていたものですから、少し調べればわかるやり方ではありますが、今後も使うことがあるだろうという事で、備忘録も兼ねています。

 

まずはランダムな整数の生成から。
こちらは簡単で、rand() 関数を使用するだけ。

// rand() 関数
int rand ( int $min , int $max );

// 実行例
$random = rand(1, 10);

引数は省略可ですが、指定した場合は、この範囲内で収まる値で乱数を返してくれます。
上記の実行例だと、1から10の範囲内の乱数が生成されます。

PHPマニュアルはこちらからご確認ください。
http://php.net/manual/ja/function.rand.php

 

お次はランダムな少数値の生成方法。
この場合は、mt_getrandmax()という、mt_rand() 関数が呼ばれた際に返される最大の値を返す関数を利用しています。

なお、mt_rand() はランダムな整数を生成する関数です。
乱数を返す機能としては rand() 関数との違いはなさそうですが、PHPマニュアルによると、rand()よりも4倍以上高速に乱数を生成してくれるのだとか。

ランダムな少数を返す関数は下記の通り。

// ランダムな少数値を返す
function randomFloat($min = 0, $max = 1) {
    return $min + mt_rand() / mt_getrandmax() * ($max - $min);
}

// 実行例
$random = randomFloat(0, 100);

こちらを実行すると、0から100の範囲でランダムな少数値を返してくれます。
ちなみに、こちらの関数も引数は省略可です。

 

と、いう事で、ランダムな少数値の生成方法でした。
なお、0から1の間で乱数を返す関数でしたら、lcg_value() という関数もあるそうでしたが、こちらは乱数の精度があまりよくないとのこと。
また、値の範囲が0から1というのも、少し汎用性が悪い場合がありそうですから、小数値の乱数生成なら上記の関数がおすすめです。

  • この記事いいね! (0)
村上 著者:村上

【備忘録】多次元の連想配列を複数の条件でソートする方法

PHPで多次元の連想配列のソートをする機会があったので、備忘録として。

ソートには、「array_multisort()」という複数または多次元の配列をソートできる関数を利用します。

array_multisort()関数のマニュアルについてはこちらから。
http://php.net/manual/ja/function.array-multisort.php

 

今回はサンプルとして、下記のようなデータを使用します。
こちらのデータを、年齢誕生月の昇順で並び替えを行います。

$data = array(
    array("no" => "1", "name" => "田中", "age" => 30, "birth_month" => 6),
    array("no" => "2", "name" => "鈴木", "age" => 15, "birth_month" => 8),
    array("no" => "3", "name" => "山田", "age" => 22, "birth_month" => 11),
    array("no" => "4", "name" => "遠藤", "age" => 30, "birth_month" => 9)
);

 

まず、ソートしたい列のデータを取り出し、ソート用の配列を作ります。
今回は、年齢と誕生月でソートしたいので、$tmp_age$tmp_bmonthという配列を用意し、それぞれに年齢と誕生月のデータを格納します。

foreach ($data as $key => $value) {
    $tmp_age[$key] = $value["age"];  // $tmp_age に年齢データ格納
    $tmp_bmonth[$key] = $value["birth_month"];  // $tmp_bmont に誕生月データ格納
}

 

ソート用の配列に各データを格納出来たら、array_multisort()関数でソートします。
関数の引数には、「ソートしたい列データの配列」、「ソート順」、「ソート条件」という組み合わせを条件分だけ繰り返し、最後にソートしたい連想配列を指定します。
なお、「ソート順」、「ソート条件」については省略可能で、省略した場合は昇順になります。

array_multisort($tmp_age, SORT_ASC,  // 年齢データを昇順でソート
                $tmp_bmonth, SORT_ASC,  // 誕生月データを昇順でソート
                $data);  // ソートしたい連想配列

なお array_multisort()は、成功した場合には TRUE を、失敗した場合に FALSE を返します。

 

ソート後の連想配列を var_dump で取り出してみた結果がこちら。

array(4) {
  [0]=>
  array(4) {
    ["no"]=>
    string(1) "2"
    ["name"]=>
    string(6) "鈴木"
    ["age"]=>
    int(15)
    ["birth_month"]=>
    int(8)
  }
  [1]=>
  array(4) {
    ["no"]=>
    string(1) "3"
    ["name"]=>
    string(6) "山田"
    ["age"]=>
    int(22)
    ["birth_month"]=>
    int(11)
  }
  [2]=>
  array(4) {
    ["no"]=>
    string(1) "1"
    ["name"]=>
    string(6) "田中"
    ["age"]=>
    int(30)
    ["birth_month"]=>
    int(6)
  }
  [3]=>
  array(4) {
    ["no"]=>
    string(1) "4"
    ["name"]=>
    string(6) "遠藤"
    ["age"]=>
    int(30)
    ["birth_month"]=>
    int(9)
  }
}

年齢・誕生月で正しくソートできました。

 

単に連想配列の昇順・降順のみでしたら、asort()arsort() を利用すれば良いのですが、多次元の連想配列で、かつソートの条件が複数の場合は、ソート用の配列が要るなど、少しややこしかったですね。
が、今後利用する機会が多々ありそうなやり方でしたので、きちんと覚えたいと思います。

  • この記事いいね! (0)