ラベル PHP の投稿を表示しています。 すべての投稿を表示
ラベル PHP の投稿を表示しています。 すべての投稿を表示

2013年5月19日日曜日

[CakePHP][pChart]pChart を使用してグラフを描く

pChart を利用して棒グラフとレーダーチャートを描く。
Example: views の exam_all.ctp
<body>
<table>
  <tr>
    <th>Name</th>
    <th>Math</th>
    <th>English</th>
    <th>Physics</th>
  </tr>
  <?php
 foreach ($users as $user) {
  echo "<tr>\n";
  echo "<td>" . $user['User']['username'] . "</td>\n";
  echo "<td>" . $user['Exam']['math'] . "</td>\n";
  echo "<td>" . $user['Exam']['english'] . "</td>\n";
  echo "<td>" . $user['Exam']['physics'] . "</td>\n";
  echo "</tr>\n";
 }
  ?>
</table>
<img src="exam_all_graph" />
<img src="exam_all_radar" />
</body>
Example: exams_controller.php
function exam_all() {
 $users = $this->User->find('all');

 $this->set('users', $users);
 Cache::write('users', $users);
}

function exam_all_graph() {
 /* pChart を使用する */
 App::import('Vendor', 'pchart/pdata');
 App::import('Vendor', 'pchart/pchart');

 $font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";

 /* Cache に保存された値を読み込む */
 $users = Cache::read('users');
 if ($users == false) {
  /* DB から値を取得 */
  $users = $this->User->find('all');
 }
 else {
  /* exam_all_radar() でも使用するので cache を削除しない */
 }

 $mathScore = array();
 $englishScore = array();
 $physicsScore = array();
 $name = array();

 foreach ($users as $user) {
  array_push($mathScore, $user['Exam']['math']);
  array_push($englishScore, $user['Exam']['english']);
  array_push($physicsScore, $user['Exam']['physics']);
  array_push($name, $user['User']['username']);
 }

 $data = new pData;
 $data->AddPoint($mathScore, "math");
 $data->AddPoint($englishScore, "english");
 $data->AddPoint($physicsScore, "physics");
 $data->AddPoint($name, "name");
 $data->AddSerie("math");
 $data->AddSerie("english");
 $data->AddSerie("physics");
 $data->SetAbsciseLabelSerie("name");
 $data->SetSerieName("数学", "math");
 $data->SetSerieName("英語", "english");
 $data->SetSerieName("物理", "physics");

 $chart = new pChart(700, 230);
 $chart->setFontProperties($font_path,8);
 $chart->setGraphArea(50, 30, 680, 200);

 /* グラフに背景色をつける */
 $chart->drawFilledRoundedRectangle(7,7,693,223,5,240,240,240);

 /* グラフ背景に縁をつける */
 $chart->drawRoundedRectangle(5,5,695,225,5,230,230,230);

 /* グラフ領域に背景色をつける */
 $chart->drawGraphArea(255,255,255,TRUE);

 $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_START0,150,150,150,TRUE,0,2,TRUE);

 /* グリッド線を表示する */
 $chart->drawGrid(4,TRUE,230,230,230,50);

 /* chart に data を配置しグラフを描く */
 /* 棒グラフの場合は drawBarGraph */
 $chart->drawBarGraph($data->GetData(), $data->GetDataDescription(), TRUE);

 /* 凡例を追加する */
 $chart->setFontProperties($font_path,8);
 $chart->drawLegend(596,150,$data->GetDataDescription(),255,255,255);

 /* グラフタイトルを追加する */
 $chart->setFontProperties($font_path,10);
 $chart->drawTitle(50,22,"テスト結果",50,50,50,585);

 /* 画像として出力する */
 $chart->Stroke();
}

function exam_all_radar() {
 /* pChart を使用する */
 App::import('Vendor', 'pchart/pdata');
 App::import('Vendor', 'pchart/pchart');

 $font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";

 /* Cache に保存された値を読み込む */
 $users = Cache::read('users');
 if ($users == false) {
  /* DB から値を取得 */
  $users = $this->User->find('all');
 }
 else {
  Cache::delete('users');
 }

 $scores = array();
 $name = array();

 foreach ($users as $user) {
  $score = array();
  $username = $user['User']['username'];
  array_push($score, $user['Exam']['math']);
  array_push($score, $user['Exam']['english']);
  array_push($score, $user['Exam']['physics']);
  $scores[$username] = $score;
  array_push($name, $username);
 }

 $data = new pData;
 $i = 0;
 foreach ($name as $username) {
  $data->AddPoint($scores[$username], "serie" . $i);
  $data->AddSerie("serie" . $i);
  $data->SetSerieName($username, "serie" . $i);
  $i++;
 }
 $data->AddPoint(array("math", "english", "physics"), "subject");
 $data->SetAbsciseLabelSerie("subject");

 $chart = new pChart(400, 400);
 $chart->setFontProperties($font_path,8);
 $chart->setGraphArea(30, 30, 370, 370);

 /* グラフに背景色をつける */
 $chart->drawFilledRoundedRectangle(7,7,393,393,5,240,240,240);

 /* グラフ背景に縁をつける */
 $chart->drawRoundedRectangle(5,5,395,395,5,230,230,230);

 /* chart に data を配置しグラフを描く */
 /* レーダーチャート */
 $chart->drawRadarAxis($data->GetData(), $data->GetDataDescription(), TRUE, 20, 120, 120, 120, 230, 230);
 $chart->drawFilledRadar($data->GetData(), $data->GetDataDescription(), 50, 20);

 /* 凡例を追加する */
 $chart->setFontProperties($font_path,8);
 $chart->drawLegend(15, 15, $data->GetDataDescription(), 255, 255, 255);

 /* グラフタイトルを追加する */
 $chart->setFontProperties($font_path,10);
 $chart->drawTitle(0,22,"テスト結果",50,50,50,400);

 /* 画像として出力する */
 $chart->Stroke();
}

[CakePHP][pChart]CakePHP で pChart を使う

app/vendors に pchat ディレクトリを作成し、その下に pCache.class, pChart.class, pData.class を配置する。ここで拡張子の class を php に変更することで pChart のモジュールが読み込めるようになる。
function exam_graph() {
 /* pChart を使用する */
 App::import('Vendor', 'pchart/pdata');
 App::import('Vendor', 'pchart/pchart');

 $font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";

 /* Cache に保存された値を読み込む */
 $users = Cache::read('users');
 $total = Cache::read('total');

 Cache::delete('users');
 Cache::delete('total');

 $y1data = array();
 $a = array();

 foreach ($users as $user) {
  array_push($y1data, $user['Exam']['math']);
  array_push($a, $user['User']['username']);
 }

 $data = new pData;
 $data->AddPoint($y1data, "math");
 $data->AddPoint($a, "date");
 $data->AddSerie("math");
 $data->SetAbsciseLabelSerie("date");
 $data->SetSerieName("数学", "math");

 $chart = new pChart(700, 230);
 $chart->setFontProperties($font_path,8);
 $chart->setGraphArea(50, 30, 680, 200);
 $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2,TRUE);

 /* chart に data を配置しグラフを描く */
 /* 棒グラフの場合は drawBarGraph */
 $chart->drawBarGraph($data->GetData(), $data->GetDataDescription(), TRUE);

 /* 画像として出力する */
 $chart->Stroke();
}

2013年5月5日日曜日

[pChart]レーダーチャート

レーダーチャートの軸を描く
$chart->drawRadarAxis($data->GetData(), $data->GetDataDescription(), TRUE, 20, 120, 120, 120, 230, 230);
void pChart::drawRadarAxis($Data,$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1);
Table: drawRadarAxis
Parameter説明
$Dataプロットするデータ
$DataDescriptionプロットするデータの Data Description
$MosaicFALSE の場合は網領域が灰色に塗られない
$BorderOffsetグラフ境界からのオフセット値 (px 単位)
$A_R軸色 (R)
$A_G軸色 (G)
$A_B軸色 (B)
$S_R網色 (R)
$S_G網色 (G)
$S_B網色 (B)
$MaxValue最大値
  • $BorderOffset = 0 の場合
    $chart->drawRadarAxis($data->GetData(), $data->GetDataDescription(), TRUE, 0, 120, 120, 120, 230, 230);
  • $BorderOffset = 50 の場合
    $chart->drawRadarAxis($data->GetData(), $data->GetDataDescription(), TRUE, 50, 120, 120, 120, 230, 230);
  • $A_R = 255, $A_G = 0, $A_B = 0 の場合
    $chart->drawRadarAxis($data->GetData(), $data->GetDataDescription(), TRUE, 20, 255, 0, 0, 230, 230);
  • $S_R = 0, $S_G = 255, $S_B = 0 の場合
    $chart->drawRadarAxis($data->GetData(), $data->GetDataDescription(), TRUE, 20, 120, 120, 120, 0, 255, 0);


レーダーチャートを描く(線)
$chart->drawRadar($data->GetData(), $data->GetDataDescription, 20);
void pChart::drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1);
Table: drawRadar
Parameter説明
$Dataプロットするデータ
$DataDescriptionプロットするデータの Data Description
$BorderOffsetグラフ境界からのオフセット値 (px 単位)
$MaxValue最大値
$BorderOffset と $MaxValue は drawRadarAxis() で設定した値と同じにすること。

レーダーチャートを描く(塗りつぶし)
$chart->drawFilledRadar($data->GetData(), $data->GetDataDescription(), 50, 20);
void pChart::drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1);
Table: drawFilledRadar
Parameter説明
$Dataプロットするデータ
$DataDescriptionプロットするデータの Data Description
$Alpha透過度 (下図参照)
$BorderOffsetグラフ境界からのオフセット値 (px 単位)
$MaxValue最大値
$BorderOffset と $MaxValue は drawRadarAxis() で設定した値と同じにすること。
  • $Alpha = 90 の場合
    $chart->drawFilledRadar($data->GetData(), $data->GetDataDescription(), 90, 20);

[pChart]装飾

凡例 (Legend) を追加する
グラフを描画する前に pChart::drawLegend() で凡例を追加する。
凡例に表示される文字列は以下のようにすると割り当てた名前が表示される。
$data = new pData;
$data->SetSerieName("数学", "math");
$font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";

/* 凡例を追加する */
$chart->setFontProperties($font_path,8);
$chart->drawLegend(596,150,$data->GetDataDescription(),255,255,255);

$chart->Stroke();
void pChart::drawLegend($XPos, $YPos, $DataDescription, $R, $G, $B, $Rs=-1, $Gs=-1, $Bs=-1, $Rt=0, $Gt=0, $Bt=0, $Border=FALSE);
Table: drawLegend
Parameter説明
$XPos凡例の X 位置
$YPos凡例の Y 位置
$DataDescription表示する判例の DataDescription
$R背景色 (R)
$G背景色 (G)
$B背景色 (B)
$Rs=-1影色 (R)
$Gs=-1影色 (G)
$bs=-1影色 (B)
$Rt=0文字色 (R)
$Gt=0文字色 (G)
$Bt=0文字色 (B)
$Border=FALSE境界線


タイトルを追加する
グラフを描画する前に pChart::drawTitle() でグラフのタイトルを追加する。
$font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";

/* グラフタイトルを追加する */
$chart->setFontProperties($font_path,10);
$chart->drawTitle(50,22,"数学のテスト結果",50,50,50,585);

$chart->Stroke();
void pChart::drawTitle($XPos, $YPos, $Value, $R, $G, $B, $XPos2=-1, $YPos2=-1, $Shadow=FALSE);
Table: drawTitle
Parameter説明
$XPosタイトルの X 左上位置
$YPosタイトルの Y 左上位置
$Value表示する文字列
$R文字色 (R)
$G文字色 (G)
$B文字色 (B)
$XPos2タイトルの X 右下位置、タイトルは $XPos との水平中心に配置される
$YPos2タイトルの Y 右下位置、タイトルは $YPos との垂直中心に配置される
$Shadow文字の影


グラフ領域の見た目を変更する
  • グラフに背景色をつける pChart::drawFilledRoundedRectangle() でグラフに背景色 (コーナーに丸みあり) をつける。
    /* グラフに背景色をつける */
    $chart->drawFilledRoundedRectangle(7,7,693,223,5,240,240,240);
    void pChart::drawFilledRoundedRectangle($X1, $Y1, $X2, $Y2, $Radius, $R, $G, $B);
    Table: drawFilledRoundedRectangle
    Parameter説明
    $X1左上 X 座標
    $Y1左上 Y 座標
    $X2右下 X 座標
    $Y2右下 Y 座標
    $Radiusコーナーの曲がり具合 (半径)
    $R塗りつぶし色 (R)
    $G塗りつぶし色 (G)
    $B塗りつぶし色 (B)
  • グラフ背景に縁をつける
    pChart::drawRoundedRectangle() でグラフ背景に縁 (コーナーに丸みあり) をつける。
    /* グラフ背景に縁をつける */
    $chart->drawRoundedRectangle(5,5,695,225,5,230,230,230);
    void pChart::drawRoundedRectangle($X1, $Y1, $X2, $Y2, $Radius, $R, $G, $B);
    Table: drawRoundedRectangle
    Parameter説明
    $X1左上 X 座標
    $Y1左上 Y 座標
    $X2右下 X 座標
    $Y2右下 Y 座標
    $Radiusコーナーの曲がり具合 (半径)
    $R線色 (R)
    $G線色 (G)
    $B線色 (B)
  • グラフ領域に背景色をつける
    @Chart::drawGraphArea() でグラフ領域に背景色をつける。
    /* グラフ領域に背景色をつける */
    $chart->drawGraphArea(255,255,255,TRUE);
    void pChart::drawGraphArea($R,$G,$B,$Stripe=FALSE);
    Table: drawGraphArea
    Parameter説明
    $R塗りつぶし色 (R)
    $G塗りつぶし色 (G)
    $B塗りつぶし色 (B)
    $Stripe45 度のストライプ線で背景色を塗る
  • グリッド線を表示する
    /* グリッド線を表示する */
    $chart->drawGrid(4,TRUE,230,230,230,50);
    void pChart::drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=255);
    Table: drawGrid
    Parameter説明
    $LineWidthグリッド線の太さ
    $MosaicTRUE の場合 2 ラインごとに背景色が変更されモザイク状になる (下図参照)
    $Rグリッド色 (R)
    $Gグリッド色 (G)
    $Bグリッド色 (B)
    $Alphaモザイクエリアの透過度
    $Mosaic=FALSE の場合

[pChart]軸の調整

pChart::drawScale() のパラメータにより軸の調整を行う。
void pChart::drawScale($Data, $DataDescription, $ScaleMode, $R, $G, $B, $DrawTicks=TRUE, $Angle=0, $Decimals=1, $WithMargin=FALSE, $SkipLabels=1, $RightScale=FALSE);
Table: drawScale
Parameter説明
$Dataプロットするデータ
$DataDescriptionプロットするデータの DataDescription
$ScaleMode下表参照
$R色 (R)
$G色 (G)
$B色 (B)
$DrawTicksFALSE にすることでラベルが表示されなくなる (下図参照)
$Angleラベルの角度
$Decimals数値桁数
$WithMarginFALSE の場合グラフの表示幅が描画領域いっぱいで使用される (下図参照)
$SkipLabelsX 軸のラベルのスキップ数 (下図参照)
$RightScale
Table: ScaleMode の値
Parameter意味
SCALE_NORMALプロット値の最大・最小値まで表示
SCALE_START0最小値が 0 に設定される
SCALE_ADDALLすべてのプロット値の累積で最大・最小が決まる
SCALE_ADDALLSTART0上項目と同様だが最小値が 0 に設定される
  • SCALE_NOMAL の場合
    $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2,TRUE);
  • SCALE_START0 の場合
    $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_START0,150,150,150,TRUE,0,2,TRUE);
  • $DrawTicks=FALSE の場合
    $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_START0,150,150,150,FALSE,0,2,TRUE);
  • $WidthMargin=FALSE の場合
    $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_START0,150,150,150,TRUE,0,2,FALSE);
  • $SkipLabel=2 の場合
    $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_START0,150,150,150,TRUE,0,2,TRUE, 2);

[pChart]インストール

JpGraph はライセンスが QPL であり、pChart はライセンスが GPL である。
  1. pChart | a PHP Charting library から pChart をダウンロードする。
  2. ダウンロードしたファイルを展開して php/PEAR 以下に pChart ディレクトリを作成し配置する。
  3. pChart ディレクトリ内に必要なファイルがあるので php/PEAR/pChart 直下に配置しなおす。


動作確認
<?php
include("pChart/pData.class");
include("pChart/pChart.class");

$font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";

/* data に plot するデータ情報を入れる */
$data = new pData;
$data->AddPoint(array(1, 4, -3, 5, 10, -2, -8, 2), "serie1");
$data->AddAllSeries();
$data->SetAbsciseLabelSerie();
$data->SetSerieName("test1", "serie1");

/* chart にグラフを描く領域の情報を入れる */
$chart = new pChart(700, 230);
$chart->setFontProperties($font_path,8);
$chart->setGraphArea(50, 30, 680, 200);
$chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2,TRUE);

/* chart に data を配置しグラフを描く */
/* 棒グラフの場合は drawBarGraph */
$chart->drawBarGraph($data->GetData(), $data->GetDataDescription(), TRUE);
/* 線グラフの場合は drawLineGraph */
//$chart->drawLineGraph($data->GetData(),$data->GetDataDescription());
/* plot の場合は drawPlotGraph */
//$chart->drawPlotGraph($data->GetData(),$data->GetDataDescription(),3,2,255,255,255);

/* 画像として出力する */
$chart->Stroke();
?>

2013年5月3日金曜日

[CakePHP]Controller 内でのデバッグ

Controller 内に直接 debug() 関数を埋め込めば値の表示が可能。
<?php

class UsersController extends AppController
{
  var $name = 'Users';

  function index() {
    $users = $this->Users->find('all');
    foreach ($users as $user) {
      debug($user['username'], $showHTML=true);
    }
  }
}

?>

[CakePHP]Controller から複数の Model を使用する

controller 定義内で $uses を用いて使用する Model を指定する
<?php

class UsersController extends AppController
{
  var $name = 'Users';
  var $uses = array('User', 'Profile');

  function index() {
    $users = $this->Users->find('all');
    $profiles = $this->Profile->find('all');
  }
}

?>

[CakePHP]ORDER BY

順序を指定してデータを取り出す
$conditions = array('date >' => '2000-01-01');
$order = 'date';
$scores = $this->Score->find('all', array('conditions' => $conditions, 'order' => $order));

[CakePHP]CkaePHP App::import() の返値

必要なPluginを app/venders の下に置く
$ret = App:import('Vendor', 'geshi/geshi');
var_dump($ret);
として返値を見ることでロードが正常にできているか確認できる。

[CakePHP]add フォームを追加できるページ

/app/views/exams/multi_add.ctp を準備し、<body> 部に以下を記述する。
<div id="add">
</div>
<a href="/php/cake/exams/add" id="link1262083738" onclick=" event.returnValue = false; return false;">Add exam score</a>
<script type="text/javascript">
//<![CDATA[
Event.observe('link1262083738', 'click', function(event) {
 new Ajax.Updater('add','/php/cake/exams/add', {
  asynchronous:true,
  evalScripts:true,
  insertion: Insertion.Top,
  requestHeaders:['X-Update', 'add']
  })
 }, false);
//]]>
</script>
通常は ajax ヘルパーの $ajax->link() を使用すれば add.ctp を <div id="add"> に追加表示することができるが、そのままでは 1 つ分のフォームしか表示できないので自前で Ajax.Updater を書き、オプションに insertion: Insertion.Top を追加することでリンクをクリックする度にフォームが追加されるようにしてみた。

[CakePHP]データ追加ページ

/app/views/exams/add.ctp というページを準備し、<body> 部に以下を記述する。
<?php
echo $form->create('Exam');
echo $form->input('user_id');
echo $form->input('date');
echo $form->input('math');
echo $form->input('english');
echo $form->input('physics');
echo $form->end('Save');
?>
/app/controllers/exams_controller.php を準備し、以下を記述する。
<?
class ExamsController extends AppController
{
 var $name = 'Exams';
 var $uses = array('User', 'Exam');
 var $helpers = array('Html', 'Javascript', 'Ajax');

 function index() {
  $this->set('exams', $this->Exam->find('all'));
 }

 function add() {
  $fields = 'username';
  $users = $this->Exam->User->find('list', array('fields' => $fields));
  $this->set('users', $users);
  if (!empty($this->data)) {
   debug($this->data);

   if ($this->Exam->save($this->data)) {
    $this->flash('Your post has been saved.' , '/examss');
   }
  }
 }
}
?>
$users を view の add.ctp に渡してやることで user_id の部分を user テーブルにある username と紐付けて表示することができる。

[CakePHP]App::import の注意点

モジュール名はすべて小文字で書かないと読み込んでもらえない。
ファイルの実体が app/venders/pcahrt/pData.php であっても pchart/pdata で import しなければならない。
App::import('Vendor', 'pchart/pdata');
App::import('Vendor', 'pchart/pchart');

[CakePHP]Database

Cache を利用して DB へのアクセスを減らす
Table: exams テーブル
iduser_idmathenglishphysics
11493656
22454842
33728091
上記の exams テーブルにアクセスして表を作成し、さらにそのデータから JpGraph でグラフを作成する場合 ExamsController に表を作成するための関数をグラフを作成するための関数を準備する必要がある。
function exam_total() {
 $users = $this->User->find('all');
 foreach ($users as $user) {
  $total[$user['User']['id']] = 0;
  $total[$user['User']['id']] = $user['Exam']['math'] + $user['Exam']['english'] + $user['Exam']['physics'];
 }
 $this->set('users', $users);
 $this->set('total', $total);
 $this->set('graph_path', './exam_total_graph');
}

function exam_total_graph() {
 /* JpGraph を使用する */
 App::import('Vendor', 'jpgraph/jpgraph');
 App::import('Vendor', 'jpgraph/jpgraph_bar');

 /* DB から値を取得 */
 $users = $this->User->find('all');

 $y1data = array();
 $y2data = array();
 $zerodata = array();

 $a = array();

 /* 合計計算 */
 foreach ($users as $user) {
  $total = 0;
  $total = $user['Exam']['math'] + $user['Exam']['english'] + $user['Exam']['physics'];

  array_push($y1data, $user['Exam']['math']);
  array_push($y2data, $total);
  array_push($zerodata, 0);
  array_push($a, $user['User']['username']);
 }

 /* グラフ作成 */
 $graph = new Graph(350, 250, "auto");
 $graph->SetScale("textlin", 0, 100);
 $graph->SetY2Scale("lin", 0, 300);
 /* 右側の Y 軸で値が全部表示できないのでグラフを表示する場所の
  * margin を指定する
  */
 $graph->img->SetMargin(30, 30, 30, 30);

 /* X軸項目追加 */
 $graph->xaxis->SetTickLabels($a);

 /* plot作成 */
 $bplot1 = new BarPlot($y1data);
 $bplot1->value->Show();
 $bplot1->value->SetFormat('%d');

 $bplot2 = new BarPlot($y2data);
 $bplot2->value->Show();
 $bplot2->value->SetFormat('%d');

 /* 単純に bplot1, bplot2 を表示するとグラフが重なってしまうので
  * $zerodata のダミーを間に挟んでグループ化して
  * bplot1, bplot2 が並んでいるように表示する
  */
 $bplotzero = new BarPlot($zerodata);
 $bgplot1 = new GroupBarPlot(array($bplot1, $bplotzero));
 $bgplot2 = new GroupBarPlot(array($bplotzero, $bplot2));

 /* グラフ上に描画 */
 $graph->Add($bgplot1);
 $graph->AddY2($bgplot2);

 /* グラフ表示 */
 $graph->Stroke();
}
この中で DB にアクセスして値を格納し、各人の合計得点を計算する箇所が重複している
$users = $this->User->find('all');
foreach ($users as $user) {
  $total[$user['User']['id']] = 0;
  $total[$user['User']['id']] = $user['Exam']['math'] + $user['Exam']['english'] + $user['Exam']['physics'];
}
Cache 機能を使うことで DB へのアクセスを減らすことが可能となる。
Cache::read($key, $config = null);
Cache::write($key, $value, $config = null);
Cache::delete($key, $config = null);
  • write() で $key に入力した文字列に $value の値を関連付けて保存する。
  • read() で $key に関連付けられた値を読み込む。
  • delete() で $key の値を cache から削除する。
$config は Cache 機能の設定ファイルを指定する。null の場合はデフォルト設定が使用される。
function exam_total() {
 $users = $this->User->find('all');

 foreach ($users as $user) {
  $total[$user['User']['id']] = 0;
  $total[$user['User']['id']] = $user['Exam']['math'] + $user['Exam']['english'] + $user['Exam']['physics'];
 }

 $this->set('users', $users);
 $this->set('total', $total);
 Cache::write('users', $users);
 Cache::write('total', $total);
}

function exam_total_graph() {
 /* JpGraph を使用する */
 App::import('Vendor', 'jpgraph/jpgraph');
 App::import('Vendor', 'jpgraph/jpgraph_bar');

 /* Cache に保存された値を読み込む */
 $users = Cache::read('users');
 $total = Cache::read('total');

 Cache::delete('users');
 Cache::delete('total');

 $y1data = array();
 $y2data = array();
 $zerodata = array();
 $a = array();

 foreach ($users as $user) {
  array_push($y1data, $user['Exam']['math']);
  array_push($y2data, $total[$user['User']['id']]);
  array_push($zerodata, 0);
  array_push($a, $user['User']['username']);
 }

 (以下略)
  • exam_total() の最後で $users, $total を Cache に保存しておき、exam_total() の後に呼び出される exam_total_graph() では cache のデータを利用するように変更して DB へのアクセス回数を減らすことができる。
  • Cache::write() により app/tmp/cache 以下に cake_total, cake_users というファイルが生成される。これを exam_total_graph() 関数で Cache::read() により読み出し、読み出し後は Cache::delete() で削除する。

[CakePHP]Ajax との連携

  1. script.aculo.us の src 以下にある js ファイルを app/webroot/js にコピーする
  2. prototype.js を app/webroot/js にコピーする
  3. index.ctp の <head> 部に以下を追加
    <?php echo $javascript->link('prototype'); ?>
    <?php echo $javascript->link('scriptaculous'); ?>
  4. index.ctp の <body> 部に以下を追加
        <h1>link</h1>
        <div id="user">
        </div>
        <?php echo $ajax->link('View User',
        array('controller' => 'users', 'action' => 'view', 1),
        array('update' => 'user', 'complete' => 'alert("Hello World")' )
        );
        ?>
  5. views/users/view.ctp を作成
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
    <html> <head>
    <meta name="Content-Type" content="text/html; charset=utf-8">
    
    <title></title>
    </head>
    
    <body>
    <h1><?php echo $user['User']['username'] ?></h1>
    <p>
    <small>password: <?php echo $user['User']['password'] ?></small>
    </p>
    
    <p>
    <small>趣味: <?php echo $user['Profile']['hobby'] ?></small>
    </p>
    
    </body> </html>
  6. users_controll.php に以下を追加
    var $helpers = array('Html', 'Javascript', 'Ajax');
    
    function view($id = null)
    {
     $this->User->id = $id;
     $this->set('user', $this->User->read());
    }


Auto complete
  1. script.aculo.us の src 以下にある js ファイルを app/webroot/js にコピーする
  2. prototype.js を app/webroot/js にコピーする
  3. app/views/layouts/ 以下にファイルがないとデフォルトのレイアウトが適用される。デフォルトレイアウトは cake/libs/view/layouts/default.ctp にある。
    ここにある default.ctp を app/views/layouts/ 以下にコピーして以下の箇所を変更する
    <?php
      echo $html->meta('icon');
      echo $html->css('cake.generic');
      echo $scripts_for_layout;
    ?>
  4. prototype.jp と scriptaculous.js を読み込むようにする
    echo $javascript->link('prototype');
    echo $javascript->link('scriptaculous');
  5. charset や html の meta 情報もここで定義されているので各ページには基本的に必要ない。
    index.ctp の <body> 部に以下を追加
    <h1>link</h1>
    <div id="user">
    </div>
    <?php echo $ajax->link('View User',
    array('controller' => 'users', 'action' => 'view', 1),
    array('update' => 'user', 'complete' => 'alert("Hello World")' )
    );
    ?>
    
    <h1>autoComplete</h1>
     <?php echo $form->create('User', array('url' => '/users/index')); ?>
     <?php echo $ajax->autoComplete('User.username', '/users/autoComplete')?>
     <?php echo $form->end('View user')?>
  6. views/users/view.ctp を作成
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
    <html> <head>
    <meta name="Content-Type" content="text/html; charset=utf-8">
    
    <title></title>
    </head>
    
    <body>
    <h1><?php echo $user['User']['username'] ?></h1>
    <p>
    <small>password: <?php echo $user['User']['password'] ?></small>
    </p>
    
    <p>
    <small>趣味: <?php echo $user['Profile']['hobby'] ?></small>
    </p>
    
    </body> </html>
  7. views/users/auto_complete.ctp を作成
    <ul>
      <?php foreach($users as $user): ?>
      <li><?php echo $user['User']['username']; ?></li>
      <?php endforeach; ?>
    </ul>
  8. users_controll.php に以下を追加
    var $helpers = array('Html', 'Javascript', 'Ajax');
    
    function view($id = null)
    {
     $this->User->id = $id;
     $this->set('user', $this->User->read());
    }
    
    function autoComplete() {
     $this->set('users', $this->User->find('all', array(
             'conditions' => array(
                'User.username LIKE' => $this->data['User']['username'].'%'
              ),
             'fields' => array('username')
              )));
     $this->layout = 'ajax';
    }
  9. app/webroot/css に my.css を準備して以下を記述する
    div.auto_complete {
     position: absolute;
     width: 250px;
     background-color: white;
     border: 1px solid #888;
     margin: 0px;
     padding: 0px;
    }
    
    li.selected {
     background-color: #ffb;
    }
  10. app/views/layouts/default.ctp に my.css を読み込ませるようにする
    echo $html->css('cake.generic');
    echo $html->css('my');


Selectで選択させて表を切り替える
プルダウンメニューを表示し、選択を切り替えるとそれに該当するユーザのテスト結果のみを表・グラフで表示するようにする。
  1. index.ctp に以下を追加する。
    <?php
     echo $form->create('user_id');
     echo $form->input('user_id', array('options' => array(1, 2, 3)));
     echo $form->end();
    ?>
    <div id="result">ここに結果が</div><br>
    <?php
     echo $ajax->observeField('user_id',
         array(
               'url' => array('action' => 'exam_table_graph'),
               'frequency' => 0.2,
               'update' => 'result'));
    ?>
  2. index.ctp から Ajax 経由で呼び出される exam_table_graph.ctp を準備する。
    <body>
    <table border="1">
      <tr>
        <th>ID</th>
        <th>user_id</th>
        <th>Date</th>
        <th>math</th>
        <th>english</th>
        <th>physics</th>
      </tr>
      <?php
     foreach ($exams as $exam) {
      echo "<tr>\n";
      echo "<td>" . $exam['Exam']['id']; "</td>\n";
      echo "<td>" . $exam['Exam']['user_id']; "</td>\n";
      echo "<td>" . $exam['Exam']['date']; "</td>\n";
      echo "<td>" . $exam['Exam']['math']; "</td>\n";
      echo "<td>" . $exam['Exam']['english']; "</td>\n";
      echo "<td>" . $exam['Exam']['physics']; "</td>\n";
      echo "</tr>\n";
     }
      ?>
    </table>
    
    <?php
      echo '<img src="' . $graph_path . '"><br>';
    ?>
    
    </body>
  3. exams_controller.php に以下を追加する。
    function exam_table_graph() {
     $user_id = $this->data['user_id'] + 1;
     $conditions = array('user_id' => $user_id);
     $order = 'date';
     $exams = $this->Exam->find('all',
           array('conditions' => $conditions,
          'order' => $order));
     $this->set('exams', $exams);
     $graph_path = './exam_graph/' . $user_id;
     $this->set('graph_path', $graph_path);
     Cache::write('exams' . $user_id, $exams);
    }
    
    function exam_graph($id=null) {
     if (!$id) {
      $id = 1;
     }
     $user_id = $id;
    
     /* Cache に保存された値を読み込む */
     $exams = Cache::read('exams' . $user_id);
     if ($exams == false) {
      $conditions = array('user_id' => $user_id);
      $order = 'date';
      $exams = $this->Exam->find('all',
            array('conditions' => $conditions,
           'order' => $order));
     }
     else {
      Cache::delete('exams' . $user_id);
     }
    
     /* pChart を使用する */
     App::import('Vendor', 'pchart/pdata');
     App::import('Vendor', 'pchart/pchart');
    
     $font_path = "c:\Windows\Fonts\sazanami-gothic.ttf";
    
     $y1data = array();
     $a = array();
    
     foreach ($exams as $exam) {
      array_push($y1data, $exam['Exam']['math']);
      array_push($a, $exam['Exam']['date']);
     }
    
     $data = new pData;
     $data->AddPoint($y1data, "math");
     $data->AddPoint($a, "date");
     $data->AddSerie("math");
     $data->SetAbsciseLabelSerie("date");
     $data->SetSerieName("数学", "math");
    
     $chart = new pChart(400, 230);
     $chart->setFontProperties($font_path,8);
     $chart->setGraphArea(50, 30, 380, 200);
    
     /* グラフに背景色をつける */
     $chart->drawFilledRoundedRectangle(7,7,393,223,5,240,240,240);
    
     /* グラフ背景に縁をつける */
     $chart->drawRoundedRectangle(5,5,395,225,5,230,230,230);
    
     /* グラフ領域に背景色をつける */
     $chart->drawGraphArea(255,255,255,TRUE);
    
     $chart->drawScale($data->GetData(),$data->GetDataDescription(),SCALE_START0,150,150,150,TRUE,0,2,TRUE);
    
     /* グリッド線を表示する */
     $chart->drawGrid(4,TRUE,230,230,230,50);
    
     /* chart に data を配置しグラフを描く */
     /* 棒グラフの場合は drawBarGraph */
     $chart->drawBarGraph($data->GetData(), $data->GetDataDescription(), TRUE);
    
     /* 凡例を追加する */
     $chart->setFontProperties($font_path,8);
     $chart->drawLegend(596,150,$data->GetDataDescription(),255,255,255);
    
     /* グラフタイトルを追加する */
     $chart->setFontProperties($font_path,10);
     $chart->drawTitle(50,22,"数学のテスト結果",50,50,50,585);
    
     /* 画像として出力する */
     $chart->Stroke();
    }


プルダウンメニューで表示する内容を変更する
  1. exams テーブルに以下のデータが入っているとする。 Table: exams テーブル
    iduser_iddatemathenglishphysics
    112008-10-01493656
    222008-10-01454842
    332008-10-01728091
    2132008-12-158600
    2022008-12-153000
    1912008-12-154900
    2432009-03-186300
    2322009-03-185400
    2212009-03-188900
  2. ajax ヘルパーの observeField() メソッドを使用することで user_id のプルダウンメニューを定期的に監視し、変化があった時に exam_table が呼び出される。
  3. exam_table では $this->data を使用してフォームに入力された user_id の値を取得し、該当する user_id のデータのみを返すようにできる。
  4. views/exams/index.ctp に以下を記述
    <?php
     echo $form->create('user_id');
     echo $form->input('user_id', array('options' => array(1, 2, 3)));
     echo $form->end();
    ?>
    <div id="result">ここに結果が</div>
    <?php
     echo $ajax->observeField('user_id',
         array(
               'url' => array('action' => 'exam_table'),
               'frequency' => 0.2,
               'update' => 'result'));
    ?>
  5. views/exams/exams_table.ctp を追加
    <body>
    <table border="1">
      <tr>
        <th>ID</th>
        <th>user_id</th>
        <th>Date</th>
        <th>math</th>
        <th>english</th>
        <th>physics</th>
      </tr>
      <?php
     foreach ($exams as $exam) {
      echo "<tr>\n";
      echo "<td>" . $exam['Exam']['id']; "</td>\n";
      echo "<td>" . $exam['Exam']['user_id']; "</td>\n";
      echo "<td>" . $exam['Exam']['date']; "</td>\n";
      echo "<td>" . $exam['Exam']['math']; "</td>\n";
      echo "<td>" . $exam['Exam']['english']; "</td>\n";
      echo "<td>" . $exam['Exam']['physics']; "</td>\n";
      echo "</tr>\n";
     }
      ?>
    </table>
    </body>
  6. controllers/exams_controller.php に以下を追加
    function exam_table() {
     $conditions = array('user_id' => $this->data['user_id'] + 1);
     $order = 'date';
     $exams = $this->Exam->find('all',
           array('conditions' => $conditions,
          'order' => $order));
     $this->set('exams', $exams);
    }

[CakePHP]基本

CakePHPの基本構成
CakePHPはModel, Controller, Viewの3要素から構成される。
  • Database
    データベースのテーブル名は複数形で定義する。例えばユーザ情報を保持するデータベースであればusersと複数形にする。
  • Model
    /app/models/以下に単数形で定義ファイルを置く。先ほどのusersテーブルに対する定義は/app/models/user.phpに記述する。ファイル名は単数形であることに注意。
    class User extends AppModel
    
  • Controller
    /app/controllers/以下に複数形+_controllerという定義ファイルを置く。usersテーブルに対するコントローラは/app/controllers/users_controller.phpとファイル名が複数形になることに注意。
    class UsersController extends AppController
    
  • View
    /app/views/以下に複数形のディレクトリを作成し、controllerが処理するための入り口ページを置く。先ほどのusers_controller.phpにindexページから入った時の処理を記述してあるならば、/app/views/users/index.ctpというファイルを置く。views下のディレクトリ名が複数形であることに注意。
以上の設定をしてブラウザから/app/users/にアクセスするとUsersで定義したページが見られるようになる。
見た目はviews以下のindex.ctpに従う
表示されるデータはcontrollers以下のusers_controller.phpに従う

データベースアクセスのための基本
データベースのテーブル1つに対してModelが1つ対応する
テーブル名は複数形で付け、idというprimary, auto_incrementのフィールドを準備する


Table: CakePHP の要素
Databaseusersprofilesdiaries
ModelUserProfileDiary
ControllerUsersControllerProfilesControllerDiariesController
Viewapp/views/users/index.ctpapp/views/profiles/index.ctpapp/views/diaries/index.ctp

Table: usersテーブル
idINT(10)Primary key, auto_increment
usernameVARCHAR(32)
passwordVARCHAR(32)
Table: profilesテーブル
idINT(10)Primary key, auto_increment
user_idINT(10)users.idへの外部キー
hobbyVARCHAR(256)
Table: diariesテーブル
idINT(10)Primary key, auto_increment
user_idINT(10)users.idへの外部キー
dateDATETIME
contentTEXT(1000)
  • アソシエーションを定義する
    • アソシエーションによりデータベーステーブルに定義した外部キーを使用してテーブル間のリレーションを構築できる。
      usersにはprofilesが1つとdiariesが複数ぶら下がる構成とする場合models/user.phpに$hasOneと$hasManyを定義する。
      データベースの命名規則がCakePHPのものに従っていればこれだけでOK。
      <?php
      class User extends AppModel
      {
       var $name = 'User';
       var $hasOne = 'Profile';
       var $hasMany = 'Diary';
      }
      ?>
    • 続いてprofilesはusersにぶら下がっている(属している)ことを定義する。
      models/profile.phpに$belongsToを記述する。
      <?php
      class Profile extends AppModel
      {
       var $name = 'Profile';
       var $belongsTo = 'User';
      }
      ?>
    • 同様にdiariesはusersにぶら下がっている(属している)ことを定義する。
      models/diary.phpに$belongsToを記述する。
      <?php
      class Diary extends AppModel
      {
       var $name = 'Diary';
       var $belongsTo = 'User';
      }
      ?>


デバッグ
views/users/index.ctp内に
<?php debug($user, $showHTML=true, $showForm=false); ?>
と書くと以下のように表示してくれる。
Array
(
    [0] => Array
    (
        [User] => Array
            (
                [id] => 1
                [username] => user1
                [password] => password1
            )

        [Profile] => Array
            (
                [id] => 1
                [user_id] => 1
                [hobby] => 読書
            )

        [Diary] => Array
            (
                [0] => Array
                    (
                        [id] => 1
                        [user_id] => 1
                        [date] => 2009-02-20 14:22:06
                        [content] => テストです。
                    )
            )
    )

    [1] => Array
    (
        [User] => Array
            (
                [id] => 2
                [username] => user2
                [password] => password2
            )

        [Profile] => Array
            (
                [id] => 2
                [user_id] => 2
                [hobby] => 映画鑑賞
            )

        [Diary] => Array
            (
                [0] => Array
                    (
                        [id] => 2
                        [user_id] => 2
                        [date] => 2009-02-19 14:22:17
                        [content] => はじめまして
                    )

                [1] => Array
                    (
                        [id] => 3
                        [user_id] => 2
                        [date] => 2009-02-20 14:37:30
                        [content] => 2度目の更新です。
                    )

                [2] => Array
                    (
                        [id] => 4
                        [user_id] => 2
                        [date] => 2009-02-21 14:37:45
                        [content] => 明日の予定は・・・
                    )
            )
    )
)

2013年4月6日土曜日

[PHP][PHPExcel]Cellに属性を付ける

Cellに「折り返して全体を表示」「文字の配置 - 縦位置 = 上詰め」属性を付ける
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>PHP Excel</title>
  </head>
  <body>
<?php
require_once '../PHPExcel_1.7.8/Classes/PHPExcel.php';

// Create new PHPExcel object
printWithTime('Create new PHPExcel object<br/>');
$objPHPExcel = new PHPExcel();
$sheet = $objPHPExcel->setActiveSheetIndex(0);

// フォント
printWithTime('Set default font<br/>');
$sheet->getDefaultStyle()->getFont()->setName('メイリオ');

// cell に「折り返して全体を表示 (Wrap text)」 属性を付ける
$sheet->getDefaultStyle()->getAlignment()->setWrapText(true);

// cell に「文字の配置 - 縦位置 = 上詰め (vertical = top)」属性を付ける
$sheet->getDefaultStyle()->getAlignment()->setVertical('top');

// Add data
printWithTime('Add data<br/>');
$str = "Hello, world.\nThis is a pen.\nGood bye.";
$sheet->setCellValue('A1', 1);
$sheet->setCellValue('B1', $str);

// Save Excel5 file
$outputFilename = 'php_excel3_output.xls';
printWithTime('Write to Excel5 format<br/>');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save($outputFilename);
printWithTime('File written to Excel5 format<br/>');

print "<br/><br/>";
print "Copy $outputFilename to your local PC and open it!<br/>";

/***********************************************************************
 * 時刻と一緒にデバッグ文表示
 ***********************************************************************/
function printWithTime($str)
{
    print "[" . date('H:i:s') . "] " . $str;
}

?>
  </body>
</html>

[PHP][PHPExcel]配列からExcelにデータを書き出す

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>PHP Excel</title>
  </head>
  <body>
<?php
require_once '../PHPExcel_1.7.8/Classes/PHPExcel.php';

// Create new PHPExcel object
printWithTime('Create new PHPExcel object<br/>');
$objPHPExcel = new PHPExcel();

// フォント
printWithTime('Set default font<br/>');
$objPHPExcel->setActiveSheetIndex(0)->getDefaultStyle()->getFont()->setName('メイリオ');

// 配列でデータを準備
$data = array(
    array(1, 'Dog', 'Tokyo', '犬'),
    array(2, 'Cat', 'Chiba', '猫'),
    array(3, 'Bird', 'Saitama', '鳥')
    );

var_dump($data);
print "<br/>";

// Add data
printWithTime('Add data<br/>');
$rowNum = 1;
foreach ($data as $row) {
    $colNum = 0;
    foreach ($row as $d) {
        $objPHPExcel->setActiveSheetIndex(0)->setCellValueByColumnAndRow($colNum++, $rowNum, $d);
    }
    $rowNum++;
}

// Save Excel5 file
$outputFilename = 'php_excel2_output.xls';
printWithTime('Write to Excel5 format<br/>');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save($outputFilename);
printWithTime('File written to Excel5 format<br/>');

print "<br/><br/>";
print "Copy $outputFilename to your local PC and open it!<br/>";

/***********************************************************************
 * 時刻と一緒にデバッグ文表示
 ***********************************************************************/
function printWithTime($str)
{
    print "[" . date('H:i:s') . "] " . $str;
}

?>
  </body>
</html>

[PHP][PHPExcel]Excelファイルに書き出す

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>PHP Excel</title>
  </head>
  <body>
<?php
require_once '../PHPExcel_1.7.8/Classes/PHPExcel.php';

// Create new PHPExcel object
printWithTime('Create new PHPExcel object<br/>');
$objPHPExcel = new PHPExcel();

// Add some data
printWithTime('Add some data<br/>');
$objPHPExcel->setActiveSheetIndex(0)
            ->setCellValue('A1', 'Hello')
            ->setCellValue('B2', 'world!');

// Save Excel5 file
$outputFilename = 'php_excel_output.xls';
printWithTime('Write to Excel5 format<br/>');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save($outputFilename);
printWithTime('File written to Excel5 format<br/>');

print "<br/><br/>";
print "Copy $outputFilename to your local PC and open it!<br/>";

/***********************************************************************
 * 時刻と一緒にデバッグ文表示
 ***********************************************************************/
function printWithTime($str)
{
    print "[" . date('H:i:s') . "] " . $str;
}

?>
  </body>
</html>

[PHP][PHPExcel]インストール

  1. PHPExcel の Downloads から PHPExcel をダウンロードする
  2. ダウンロードしたファイルを解凍してサーバーに置く