PHP でグラフ画像を作成する

Web アプリケーションなら、JavaScript でも高品質なグラフを描画することができますし、画像として保存することができるものもあります。
ただ今回、

  • サーバサイドでレポートを生成する
  • レポートは PDF ファイル
  • レポートにグラフを埋め込む

という要件があり、サーバサイドでグラフ画像を生成することになりました。
グラフを生成するためのライブラリはいくつかあるようですが、今回は pChart を使用しました。 公式はこちら↓

www.pchart.net

最初にライブラリを入手します。こちら からダウンロードします。 この記事を書いている時点では 2.1.4 が最新でしたので、これを落としてきます。 中身はこのようになっています。
pChart2.1.4
├─cache
├─class
├─data
├─examples
├─fonts
└─palettes


コードを書きます。この3ファイルを取り込みます。

require_once 'pChart2.1.4/class/pData.class.php';
require_once 'pChart2.1.4/class/pDraw.class.php';
require_once 'pChart2.1.4/class/pImage.class.php';


主に使用するクラスは pData と pImage になります。(が pDraw も require が必要です)
「pData の内容を pImage を使って出力する」というイメージです。
グラフのデータを pData で設定します。

$data = new pData();
$data->addPoints([1, 2, 3, 4], 'data1');
$data->addPoints([5, 6, 7, 8], 'data2');


pData を生成して addPoints でデータを設定します。そのデータが何なのか、名前も一緒に指定します。気温の変化であれば、
addPoints([20.1, 22.3, 21.8, 23.4], 'temperature'); などのようになるでしょう。 今回欲しかったのは積み上げ棒グラフなので、複数回 addPoints しています。


ラベルの設定も addPoints で行います。

$data->addPoints(['A', 'B', 'C', 'D'], 'labels');
$data->setAbscissa('labels');


ラベル用のデータにつけた名前を、setAbscissa に指定します。
データの設定はこれで終わりです。次に pImage です。

$image = new pImage(800, 400, $data);
$image->setGraphArea(50, 30, 700, 360);
$image->setFontProperties(['FontName'=>'pChart2.1.4/fonts/calibri.ttf', 'FontSize'=>15]);


pImage のコンストラクタに、画像のサイズと pData を渡します。さらに、その画像中のどこがグラフの領域になるかを setGraphArea で指定します。
この例なら、左から50px上から30pxの点と、左から700px上から360pxの点で表される領域です。
そして setFontProperties で使用するフォントファイルを指定します。この例ではライブラリに付属のものを指定していますが、日本語を使用したい場合は IPA フォントなどのパスを指定します。 OTF も使用できます。

グラフを描画します。

$image->drawScale(['Mode'=>SCALE_MODE_ADDALL]);
$image->drawStackedBarChart();
$image->autoOutput('graph.png');


drawScale には様々なオプションを指定することができ、目盛りを制御することができます。
drawStackedBarChart で積み上げ棒グラフの描画を行います。グラフの種類によってコールするメソッドを切り替えます。
autoOutput に出力先ファイルパスを渡し、画像ファイルとして出力します。これでグラフ画像が得られます。

コードの全体と出力された画像は以下になります。

<?php
require_once 'pChart2.1.4/class/pData.class.php';
require_once 'pChart2.1.4/class/pDraw.class.php';
require_once 'pChart2.1.4/class/pImage.class.php';

$data = new pData();
$data->addPoints([1, 2, 3, 4], 'data1');
$data->addPoints([5, 6, 7, 8], 'data2');
$data->addPoints(['A', 'B', 'C', 'D'], 'labels');
$data->setAbscissa('labels');

$image = new pImage(800, 400, $data);
$image->setGraphArea(50, 30, 700, 360);
$image->setFontProperties(['FontName'=>'pChart2.1.4/fonts/calibri.ttf', 'FontSize'=>15]);
$image->drawScale(['Mode'=>SCALE_MODE_ADDALL]);
$image->drawStackedBarChart();
$image->autoOutput('graph.png');

f:id:venturenet:20180615145133p:plain



最後に、pChart には 1.X 系と 2. X系が存在し、この両者に互換性はありません。 ネット上で pChart について調べ物をするときには、そのページがどちらのバージョンのことを書いているのか、気をつけてみるようにした方がよいかと思います。


docforce2でSalesforceのオブジェクト設計書を自動生成する

データベーススキーマを設計する場合は、ER図を書いたり何らかの設計書を書くというのが普通かと思いますが、Salesforce では簡単にオブジェクトの構造を構築することができるため、良し悪しは別にして設計書の類は作成せずに開発を進めることも多いかと思います。
しかし後から設計書を要求されたり、あるいはオブジェクト構造を確認するのにドキュメントの方がやはりわかりやすいということで設計書が欲しくなることがあります。
こんなときは、DRY原則に従って Salesforce 上のオブジェクトからドキュメントを自動生成できると便利です。 今回はそのツールの1つ、docforce2 の使い方を備忘録として残しておきます。

Javaランタイムのインストール

Java のランタイムがインストールされていない場合は、まずここからダウンロードしてインストールします。

MavensMate のインストール

MavensMate(現在は開発終了) で出力されるデータが必要になるため、ここからダウンロードしてインストールします。
MavensMate-app-v0.0.10-win-x64-setup.zipでよいでしょう。

MavensMate でデータ出力

MavensMate を起動します。
最初にワークスペースディレクトリを指定します。メニューから Settings を開き、データを置くディレクトリを Workspaces で指定します。ここでは D:\temp\mm とします。

f:id:venturenet:20180608113557p:plain f:id:venturenet:20180608113609p:plain

次に、プロジェクトを作成します。 メニューから New Project を選択して New Project のタブを開き、ワークスペース、プロジェクト名、Salesforce のユーザ名、パスワード、本番/サンドボックス/開発環境のどれであるかを指定します。
これらの情報を入力したら、Project Metadata のタブを開きます。ここで、

  • ApprovalProcess
  • CustomObject
  • Workflow

の3つを選択しますが、それぞれのツリーを開いて少し待っているとその要素が表示されるので、その中から必要なものを選択します。 ただしここでは全部選択するのが無難かもしれません。 選択したら Create Project ボタンをクリックしプロジェクトを作成します。

f:id:venturenet:20180608114331p:plain f:id:venturenet:20180608114341p:plain

プロジェクトが作成されると、ワークスペースとして指定したディレクトリにデータが出力されます。

docforce2 のインストール

GitHub - nyasba/docforce2: salesforce document generator から git で引っ張るか zip をダウンロードします。 ここでは zip でダウンロードして、D:\temp\docforce2-master に設置したものとします。

docforce2 の設定と実行

基本的には ここ に書かれている通りです。 出力対象のオブジェクトを設定するために、zip で展開したディレクトリの直下にある docforce.yml を開きます。
inputBaseDir には、先程出力したデータのパスを指定します。D:\temp\mm\<プロジェクト名>\src になります。
outputDir には出力先ディレクトリを指定します。

inputBaseDir : "D:/temp/mm/Test/src"
outputDir : "output"
resources :
    - resource :
          object : "objects/Account.object"
          approvalProcesses : 
          workflow : "workflows/Account.workflow"
          author: "test"


  • 承認プロセスがなくても approvalProcesses のキーだけは指定する
  • ワークフローがなくても、workflow のキーと値は指定する
  • インデントがずれないように注意する (ずれるとエラーになります)

という点に気をつけましょう。

設定ができたら、コマンドラインで実行します。

D:\temp\docforce2-master>gradlew run


環境、設定に誤りがなければ、これでオブジェクトごとに Excel ファイルで設計書が出力されます。



SQLite で CSV データをインポートする

SQLiteCSV データをインポートする方法をご紹介します。
CUIGUI 、どちらもとても簡単にできます。

コマンドライン

対象のテーブルは以下で、データベースファイルは user.sqlite とします。

CREATE TABLE user (user_id, user_name, user_age);


インポート元の CSV データは以下。user.csv とします。
ファイルは UTF-8 で作成しておきます。

1,山田,20
2,佐藤,30
3,鈴木,40


これをインポートするには、まずコマンドラインツールを立ち上げます。
コマンドラインツールは ここ からダウンロードできます。 Window の場合は sqlite-tools-~ で始まるものをダウンロードします。

> sqlite3 user.sqlite

次にモードを CSV にします。

sqlite> .mode csv

あとはインポートするだけです。

sqlite> .import user.csv user


GUI (SQLiteStudio)

次は SQLiteStudio での例です。対象のデータベース、CSV データは同じものを使います。

SQLiteStudio を起動して user.sqlite を開いたら、user テーブルを右クリックして Import into the table を選択します。

f:id:venturenet:20180601160946p:plain

ウィザードが開くので、 Next ボタンをクリックします。
次の画面で、インポート元 CSV ファイルのパスやファイルの文字エンコーディング方式、セパレータなどを指定します。
セパレータにカンマ以外のものを指定すれば、タブ区切りファイルなどもインポートできるということです。
Finish ボタンをクリックして完了です。

f:id:venturenet:20180601161627p:plain

インポート時には、インポート対象のテーブルは存在していなくてもOKです。
その場合は、インポートのウィザードを開いてインポート先のテーブル名(まだ存在しないテーブル名)を入力すると、そのテーブルを自動的に作成してくれます。
CSV ファイルの1行目にヘッダがあれば、それをカラム名として扱うこともできます。



SQLite のクライアントツール

SQLite については何度か記事にしていますが、SQLite はちょっと使いにとても便利な軽量データベースです。
コマンドラインツールもありますが、ちょちょいと使う分には GUI があると便利です。 今回はよく使っている SQLiteStudio をご紹介したいと思います。

SQLiteStudio はこちらから SQLiteStudio

他にも便利なクライアントツールがあるかもしれませんが、普段これを使っていて特に困ることはありません。十分な機能を備えています。

実行ファイルは SQLiteStudio.exe で、これを起動すればウィンドウが立ち上がります。

f:id:venturenet:20180525145404p:plain

SQLite のデータベースのファイル名には特に制限はありません。 そのため SQLite のデータベースのファイル名にはいつも拡張子 .sqlite をつけると決めていますので、.sqlite をダブルクリックしたら SQLiteStudio.exe が起動するように設定しています。

こうしておけば、新しいファイルの拡張子を .sqlite で作成し、それをダブルクリックすれば SQLiteStudio.exe で空のデータベースが開きます。

f:id:venturenet:20180525150459p:plain a.sqlite というファイルを作成してダブルクリックしたところ。左ペインを見ると、最初から a という空のデータベースが作成されていることがわかる。

複雑な GUI ではないですし、各ボタンにマウスを乗せればそのボタンの説明が表示されますので、マニュアルを読まなくても直感的に操作できるのではないかと思います。
例えばだいたいの操作において、

  • 緑色のチェックボタンは確定 (コミット)
  • 赤色の×ボタンは取り消し (ロールバック)
  • 緑色の+は追加
  • 赤色の-は削除

など、わかりやすいアイコンになっています。

SQLite は、Excel のような表計算ソフトで対応しきれないケースでは本当に便利です。 ぜひご利用になられてみてはいかがでしょうか。


PhantomJS でWebサイトの画面キャプチャをとる

PHP で単にスクレイピングをするだけなら、file_get_contents して解析したり、 curl を使ったり、Simple HTML DOM Parser(http://simplehtmldom.sourceforge.net/) を使ったり、 DOMDocument を使ったり、様々が方法があります。

しかしサイトの画像キャプチャを取得するとなると、PHP のみでは一筋縄ではいきません。
今回は PhantomJS を利用する際のメモになります。

PhantomJS とは:

PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.
http://phantomjs.org/

ということで、ひらたくいえば JavaScript で動かすヘッドレスブラウザということになります。


まずはこちら http://phantomjs.org/download.html から必要なものをダウンロードします。 その時点での最新版(Windows であれば phantomjs-2.1.1-windows.zip)をダウンロードします。

圧縮ファイルを解凍すると、bin/phantomjs.exe という実行ファイルがあり、これが本体になります。 これを任意のフォルダに配置します。

使い方は簡単です。JavaScript コードを書いたファイルを引数に指定して、起動します。

phantomjs.exe hello.js

公式の Hello World はこうです。

hello.js

console.log('Hello, world!');
phantom.exit();

phantom というオブジェクトがいて、exit() を呼ぶことで処理を終了するようです。 これを呼ばないと終了しないので注意しましょう。
APIのドキュメントも準備されていますので、必要に応じて参照します。 API | PhantomJS


本題です。
画面キャプチャを取ってみます。コードはずばり以下の通りです。

var page = require('webpage').create();

page.open('https://news.yahoo.co.jp/', function(status) {
  page.render('cap.png');
  phantom.exit();
});

open メソッドに指定したURLにアクセスし、ページがロードされると指定したコールバックが呼び出されます。
ここではチェックしていませんが status には 'success' か 'fail' が入ってくるので、それを見てエラー処理を行うことができます。

さらに render メソッドで出力先の画像ファイルパスを指定すると、そのファイルに画面キャプチャが出力されます。

キャプチャする範囲を指定する場合は clipRect を使用します。

page.clipRect = { top:0, left:0, width:760, height:400 };
page.render('cap.png');

また、デフォルトのユーザエージェント文字列だと意図しないレンダリングになる可能性がありますが、 その場合は任意のユーザエージェント文字列を指定することができます。

// 当たり前ですが open の前に設定します
page.settings.userAgent = ...任意のユーザエージェント文字列...;
page.open('https://news.yahoo.co.jp/', function(status) {



PHP から使う場合には PhantomJS のライブラリもありますが、 単に exec で phantomjs の実行ファイルを起動することでも利用できます。

exec("phantomjs.exe {$filePath}", $output, $resultCode);




C# で SQLite を使う (Windows)

前回はPHPSQLite を扱う方法をご紹介しました。今回は、 C#SQLite を扱う方法をご紹介したいと思います。
Visual Studio 2017 を使用します。

Visual Studio での依存ライブラリの管理は NuGet が便利です。NuGet を使って、SQLite に必要なパッケージをインストールします。

f:id:venturenet:20180511134756p:plain

パッケージマネージャが開いたら 参照 を選択して "SQLite" と打ち込んで検索し、検索結果から System.Data.SQLite.Core を選択してインストールします。

f:id:venturenet:20180511134842p:plain

準備はこれだけです。

コードも簡単です。まずはデータベースファイルを開きます。
ファイル名を指定すればOKです。

using (var conn = new SQLiteConnection("Data Source=C:\\temp\\db.sqlite"))
{
    conn.Open();


SQL文は、コマンドを作成して設定します。

using (SQLiteCommand command = conn.CreateCommand())
{
    command.CommandText = "SELECT * FROM some_table WHERE id=:id";
    // プレースホルダのバインドはこのように
    command.Parameters.Add("id", System.Data.DbType.String).Value = "hoge";


結果の1行を、キーをカラム名、値はすべて文字列の Dictionary で取得する場合は、以下のようにできます。

using (var reader = command.ExecuteReader())
{
    var schemaTable = reader.GetSchemaTable();
    
    while (reader.Read())
    {
        var row = new Dictionary<string, string>();

        foreach (DataRow schemaRow in schemaTable.Rows)
        {
            var columnName = schemaRow[schemaTable.Columns["columnName"]].ToString();
            row.Add(columnName, reader[columnName].ToString());
        }
    }
}



環境や構成によっては、"DLL 'SQLite.Interop.dll' を読み込めません:指定されたモジュールが見つかりません。 (HRESULT からの例外:0x8007007E)" といったエラーが出る場合があります。 この場合は、<ソリューションのルート>\packages\System.Data.SQLite.Core.<バージョン>\build\<.NETバージョン>\<プロセッサアーキテクチャ> フォルダの下にある SQLite.Interop.dll を実行ファイルがある場所にコピーして置くと動作するかもしれません。
.NETバージョンとプロセッサアーキテクチャは適切なものを選択してください。例えば .NET 4.6 の x86 であれば、build\net46\x86 の中にあるものを使います。


このように簡単に SQLite を使用することができますが、ここでご紹介した機能はごくごく一部です。
System.Data.SQLite についてのより深い情報を知りたい場合は こちら から。

PHP で SQLite を使う

CSVなどのデータでも強力な検索機能を使用したいことがあります。
データ量が少ない場合は、Excel を使って VLOOKUP 関数や INDEX 関数、MATCH 関数を駆使することで乗り切れる場合もありますが、 万~数十万オーダのデータになるとなかなか厳しくなってくるかと思います。

そんなときに便利なのが SQLite です。 SQLite はデータベースが1ファイルととても軽量であり、PHP であれば 5.3 以降はデフォルトで使用できます。 軽量であるにも関わらず、SQL も必要そうなものはすべて使用可能です。WITH句だって使用できます。
数十万件くらいのデータならなんなく扱えます。

PHP での基本的な使い方は以下の通りです。

// $filename にはデータベースファイル名を指定する
$sqlite = new SQLite3($filename);

// $queryにはSQL文を指定する
// プリペアに失敗した場合は false が返る
$pstmt = $sqlite->prepare('SELECT id, name FROM some_table WHERE id=:id');

// パラメタに値をセットする
$pstmt->bindValue('id', 4);

// 実行!
$result = $pstmt->execute();

while ($record = $result->fetchArray(SQLITE3_ASSOC)) {
    // $record['name'] のようにカラム名を指定してデータを取る
}


もちろん、上のコード以外にもたくさんの機能、オプションがあります。公式のドキュメントを参照しましょう。



さて、ゴールデンウィークも後半、いかがおすごしでしょうか。
今日はみどりの日、こういった仕事をしていますとなかなか自然にふれる機会が少ないと思います。
たまには大自然にふれ、リフレッシュしてみてはいかがでしょうか。