fc2ブログ

CakePHPで何かを作ろうとする 9日目 , 10日目(やっと完成)

一昨日の続き。昨日はブログを書く時間が無かったので2日分まとめて書く。

9日目(昨日)
計算式ごとの回答タイム平均値をテーブルから取得する処理をコーディング。
「ユーザーIDがログインユーザーのIDで、パターンが足し算(add)のレコードの、計算式と回答タイム平均値をタイムが大きい順に最大100件取得」
という処理をモデルのfind()メソッドで書いたら以下のようになった。
// ans_dataテーブルからデータ取得
$getdata = $this->AnsDatum->find("all",array(
      "conditions" => array("AnsDatum.userid" => $userId, "AnsDatum.pattern" => "add"),
      "fields" => array("AnsDatum.expression","round(avg(AnsDatum.time),3) as avgtime"),
      "group" => array("AnsDatum.expression"),
      "order" => array("avg(AnsDatum.time) DESC"),
      "limit" => 100
    ));
このとき、n番目のレコードの値は
AnsDatum.expression は $getdata[(n-1)]["AnsDatum"]["expression"] に、
round(avg(AnsDatum.time),3) は $getdata[(n-1)][0]["avgtime"] に格納される。
group by して関数を使った場合の格納形式についてはCakePHPマニュアルに書かれている。(日本語版マニュアルには、現在この記述は無い)

10日目(今日)
計算式ごとの回答タイム平均値を表示する画面をコーディング。
久しぶりにCSS解説ページを見たが、凝ったことはやらずに「display: table; などを使ってテーブル表示して、レコードが増えたら float: left; でテーブル追加」で妥協した。

これで一応完成したので、コントローラー、モデル、ビューとJavaScript(修正箇所のみ)の全分岐を通すテストを行った。小さなバグが3つぐらいあった。

10日間CakePHPプログラミングを行って、CakePHPは結構理解できたと思う。
CakePHPの機能で十分で、PHPの関数はあまり使わなかったので、PHPの理解は怪しい。
スポンサーサイト



CakePHPで何かを作ろうとする 8日目

昨日の続きだが、状況確認も兼ねて今作ろうとしているものを改めて書いておく。

先日作った簡単な計算の特訓ページに、次の機能を追加する。
・ユーザー登録
・ログイン/ログアウト
・ログイン済かチェック
・式ごとの回答タイムをデータベースに登録
・データベースに登録された回答タイムを表示

やりたいことは「計算式ごとの回答タイム平均値を表示」なのだが、そのためには回答タイムを登録する必要があり、誰のデータかを識別するためにユーザー登録やログインが必要になる。

今日はユーザー登録機能とログアウト機能を作った。
また、式ごとの回答タイムをデータベースに登録する際の入力チェック部分を作った。
POSTデータに 6 + 7,0.625 という形で式とタイムが格納されており、以下のように正規表現を使ってチェックした。
foreach($_POST as $key => $value){
  if(preg_match("/^\\d+$/",$key) < 1){
    $this->log("不正なkey: " . $key, "debug");
    $chkFlg = false;  // チェックエラーフラグを立てて、入力チェックを続行
  }
  if(preg_match("/^(\\d+\\s*(?:\\+|-|\\*|\\/)\\s*\\d+),(\\d+\\.?\\d*)$/",$value,$match) < 1){
    $this->log("不正なvalue: " . $value, "debug");
    $chkFlg = false;  // チェックエラーフラグを立てて、入力チェックを続行
  }else{
    $this->log("POSTdata 式: " . $match[1] . " タイム: ". $match[2], "debug");
    $inExpres[] = $match[1];  // 式を配列へ格納
    $inTime[] = $match[2];  // タイムを配列へ格納
  }
}
この前正規表現について色々と調べたので、正規表現を書くのはそれほど苦労しなかった。でも (?: を使うのは初めてだ。
ereg()はPHP 5.3.0から非推奨」とマニュアルに書かれていたので、preg_match()を使用した。

現在の状況だが、「データベースに登録された回答タイムを表示」以外の機能は完成。
ただし、完成した機能の入力チェック処理、エラー処理に漏れが無いか見直す必要あり。
また、テストが不十分なのでせめて分岐を全部通すぐらいのテストはしたい。

明日で完成すればいいと思うが、テストとデバッグだけで丸一日かかりそうな気がする。

CakePHPで何かを作ろうとする 7日目

この前の続き。CakePHPのプログラミングは3日ぶりだ。
キリのいいところまで作ったらHTML5の勉強に入りたいのだが、用事があったり体調が良くなかったりで、なかなかPHPプログラミングが終わらない。

今日はログイン絡みの入力データチェック部分をコーディングした。
・クッキーに格納されているユーザIDがusersテーブルに存在するか。
・ログイン画面から入力されたデータを、モデルのvalidates()メソッドでチェック。
・ログイン画面から入力されたユーザーIDはusersテーブルに存在するか、パスワードはusersテーブルのデータと一致するか。

ログイン画面で入力されたデータのサニタイズを行なおうとしたが、CakePHPマニュアル
CakePHP already protects you against SQL Injection if you use CakePHP's ORM methods (such as find() and save()) and proper array notation (ie. array('field' => $value)) instead of raw SQL. For sanitization against XSS its generally better to save raw HTML in database without modification and sanitize at the time of output/display.
と書かれていた。(日本語版マニュアルには、現在この記述は無い)
つまり、find() や save() のような CakePHP が提供するORM(オブジェクト関連マッピング)メソッドを用いて、きちんと array('field' => $value) のように引数を設定していれば、CakePHP が勝手にSQLインジェクション対策をしてくれる。
自分が作っているログイン機能の仕様上、HTMLサニタイズは不要なので、結局サニタイズのコーディングは無し。

今日は1時間半しかプログラミングできなかった。明日はもっと時間を取りたい。

[jQuery] 配列データを一旦格納し、イベント発生時にAjaxで送信する

この前、「後でブログに書きたい」と書いてたことを今更まとめる。

JavaScriptで配列データを作成し、そのデータをすぐに送信するのではなく、送信ボタンを押したときにAjaxで送信するようなケースを考える。
この場合、作成した配列データをどこかに格納しなければいけない。
配列をグローバル変数にして格納してもいいのだが、jQueryのdataメソッドが便利なのでそれを使用する。

処理の概要は、
1. サーバーへ送信する配列データを作成
2. 作成した配列データを jQueryのdataメソッドでDOM要素に格納
3. 送信ボタン押下時に、jQueryのdataメソッドでDOM要素から配列データを取り出す
4. 取り出した配列データを jQuery.ajax の引数に設定してサーバーへ送信
となる。

まず、サーバーへ送信する配列データを作成し、DOM要素に格納する箇所のソースコード例
  var senddata = {};  // サーバーへ送信するデータ
  for(var i=0;i<10;i++){
    // 送信データとして乱数を作成
    senddata[i] = Math.floor(Math.random()*10) + 1;
  }
  // サーバーへ送信ボタン追加
  var $div = $("#div1");
  var $button = $("<button/>").addClass("sbtn").attr("id","sbtn");
  $button.text("サーバーへ送信");
  $button.data("senddata",senddata);  // 送信ボタンに送信データを格納
  $button.click(ajaxsend);
  $div.append($button);
ここで、サーバーへ送信する配列のデータを、大カッコ [ ] ではなく中カッコ { } で宣言している。
jQueryのdataメソッドでは任意のObjectを格納できるのでどちらでも良いのだが、jQuery.ajaxの引数として連想配列(ハッシュ)を設定する必要があるため、ここでは { } で宣言している。
そのため、厳密には配列ではなく「0からの連番をキー値とする連想配列」となる。

次に、送信ボタン押下時にDOM要素から配列データを取り出し、Ajaxで送信する箇所のソースコード例
var ajaxsend = function(){
  $.ajax({url: "./ajaxtest.php",  // 実行されるURL
    type: "POST",
    data: $("#sbtn").data("senddata")  // 送信データ
  });
};

受信した側では、PHPなら
foreach($_POST as $key => $value)
などで配列データを取り出せばよい。

自分が作成した、一通りの動作を確認できるテストコードは以下のとおり。(拡張子zipでアップロードできなかかったので、リンク先を保存して拡張子をzipに変えてください)
jQueryのdataメソッド+Ajaxテストコード
サーバー側のコードとして ajaxtest.php も作ったが、データ送受信確認用コードなので入力チェックやエラー処理などは無い。

以上。この記事を書くためにテストコードを作成したのだが、あまり意味無かったかなぁ。

[CakePHP] URLにwebrootを指定したときに表示される画面を変える方法

せっかくなので、昨日調べたことをまとめる。

CakePHPを使用する場合、Webサーバーではドキュメントルートを /app/webroot に設定することが多いと思う。
別にルートでなくてもよいのだが、/app/webroot に当たるURLをブラウザで開くと、以下のようなCakePHP設定チェック画面が表示される。
CakePHP設定チェック画面

これは /app/config/core.php で Configure::write('debug', 2); と設定されている場合で、
Configure::write('debug', 0); と設定されている場合は以下のような画面が表示される。
webroot指定時(debug=0)

この画面を変更する方法だが、上のCakePHP設定チェック画面に書かれているように /app/views/pages/home.ctp を作成すればよい。
デフォルトではこのディレクトリの中身はemptyなので、/cake/libs/view/pages/home.ctp が表示される。(core.phpの debug の値によって表示画面が変わるのは、この home.ctp 内で制御しているから)

以下のソースコードを /app/views/pages/home.ctp に記述すれば、index.htmlにリダイレクトされる。
<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: ./index.html");

そもそもなぜルートを指定すると /cake/libs/view/pages/home.ctp が表示されるかと言うと、/app/config/routes.php で
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
と設定されているから。
そのため、これを修正して表示画面を変えることもできる。
例えば、
Router::connect('/', array('controller' => 'tests'));
とすると、ブラウザでルートを指定したときtestsコントローラーのindexアクションが実行される。

デフォルトで表示されるCakePHP設定チェック画面に
To change the content of this page, create: APP/views/pages/home.ctp.
と書かれているからそれで終了なはずなのに、なぜか自分は1時間ぐらい調べた。

CakePHPで何かを作ろうとする 5日目,6日目

この前の続き。昨日は CakePHP についての記事を書いたので、自分のプログラミングについて書けなかった。
今日は2日分書く。

5日目(昨日)
AjaxのJavaScript側の処理を修正した。
jQuery.post を使った箇所を jQuery.ajax に変更。
送信ボタンを押してから実行される処理を beforeSend に、
通信成功時に実行される処理を success に、通信エラー時に実行される処理を error に記述。
送信ボタンの連打を防ぎ、結果が返ってくるまで「送信中です」と画面に表示したかったので。

Ajaxで送信したデータを、CakePHPのモデルを使ってテーブルに格納するところまで出来た。
テーブルの確認やデータ編集に Scaffolding を使い、調べたところを昨日の記事にまとめたところで力尽きた。

6日目(今日)
ログイン画面で入力するデータのバリデーション機能を追加した。
CakePHPのマニュアルを見ながらやったが、現在日本語マニュアルは大事なページが見られないので、英語版も読んだ。

ふと、「ブラウザでwebルート(http://localhost/)を入力すると CakePHP の設定チェック画面が表示されるが、別のページを表示させるにはどうすればいいんだろう」と思い立って調べた。
調べればすぐにわかるので大したことでは無いのだが、せっかくだから後でブログにまとめるか。

これが昨日今日でやったこと。脱線するから物づくりが進まない。

[CakePHP] Scaffolding の使い方をまとめた(既存のコントローラーがある場合など)

CakePHP の Scaffolding について、自分用のメモも兼ねて基礎の基礎をまとめた。
<注意>以下の内容は CakePHP1.3 についてです。他のバージョンでは検証してません。

1. Scaffolding とは
CakePHP の Scaffolding という機能を使用することにより、テーブルのデータを表示、編集、削除ができる画面が生成される。
↓Scaffolding により生成された画面Scaffolding により生成された画面
Scaffolding の詳細については、CakePHPマニュアルを参照。
マニュアルには「アプリケーションのスキャフォールディングは、開発者がオブジェクトを生成・取り出し・更新・削除することができる基本的なアプリケーションを定義し作成するという技術です。」と書かれており、テーブルのデータ編集以外にも色々なことができそうだが、マニュアルにはほとんど記述が無い。

2. Scaffolding の使い方
2.1 該当テーブルに対して、既存のコントローラーが無い場合
testsという名前のテーブルについて Scaffolding を使用するには、/app/controllers に tests_controller.php という名前のファイルを作成し、以下のソースコードを記述すればよい。
<?php
class TestsController extends AppController {
  public $scaffold;
}
モデルのファイルを作成する必要は無い。(作成してもよい)
サーバーがlocalhostでルートディレクトリが/app/webrootの場合、http://localhost/tests にアクセスすれば上の画像のような画面が表示される。

2.2 該当テーブルに対して、既存のコントローラーがある場合
testsという名前のテーブルについて Scaffolding を使用したいが、既に tests_controller.php ファイルが作成されている場合は、別名のコントローラーを作成する。
ここでは別名を tests_tbl_controller.php とする。/app/controllers にこの名前のファイルを作成し、以下のソースコードを記述すればよい。
<?php
class TestsTblController extends AppController {
  public $uses = array("Test");
  public $scaffold;
}
コントローラー名とモデル名が異なるので、uses属性にモデル名を記述する。
この場合もモデルのファイルを作成する必要は無い。(作成してもよい)
サーバーがlocalhostでルートディレクトリが/app/webrootの場合、http://localhost/tests_tbl にアクセスすれば以下のような画面が表示される(画像からはヘッダやフッタを省いた)。
Scaffolding により生成された画面 2.2

2.3 該当テーブル名が単数形で、既存の複数形のテーブル名が無い場合
CakePHP の規約では、モデル名の複数形がテーブル名になる。
testという単数形のテーブル名について Scaffolding を使用したい場合は、モデルでテーブル名を指定する。
/app/models に test.php という名前のファイルを作成し、以下のソースコードを記述すればよい。
<?php
class Test extends AppModel {
  public $useTable = "Test";
}
useTable属性にテーブル名を記述する。
Scaffolding により以下のような画面が生成される(画像からはヘッダやフッタを省いた)。
testテーブルの画面のため、上記testsテーブルとはフィールド名が異なっている。
Scaffolding により生成された画面 2.3
画面に Tests と表示されるのは、コントローラーのファイル名が tests_controller.php のため。

2.4 該当テーブル名が単数形で、既存の複数形のテーブル名がある場合
test という名前のテーブルについて Scaffolding を使用したいが、既に testsという名前のテーブルがある場合は、別名のモデルを作成する。
コントローラー名も重ならないように別名にするべき。
コントローラーのファイル名を test_tbl_controller.php とし、モデルのファイル名を test_tbl.php とすると、コントローラーのソースコードは以下のようになる。
<?php
class TestTblController extends AppController {
  public $uses = array("TestTbl");
  public $scaffold;
}
モデルのソースコードは以下のようになる。
class TestTbl extends AppModel {
public $useTable = "Test";
}

Scaffolding により以下のような画面が生成される(画像からはヘッダやフッタを省いた)。
Scaffolding により生成された画面 2.4

紛らわしいテーブル名はバグの元なので、単数形と複数形のテーブル名の混在はできるだけ避けた方がよい。

以上。
自分が一番書きたかったことは「2.2 該当テーブルに対して、既存のコントローラーがある場合」で、それ以外はついでに調べた。

CakePHPで何かを作ろうとする 4日目

この前の続き。2日も間があいてしまった。

今日は久しぶりにAjaxのコーディングをした。
JavaScriptは、CakePHPのAjaxヘルパーを使わずにjQueryでコーディングした。
ヘルパーを使うのは、PHPで動的にJavaScriptを組むときに考えよう。
Ajaxヘルパーを使うためには prototype.js を読み込む必要があるので、あまり使う気にならない。

「サーバーへ送信」ボタンクリックで jQuery.post するのだが、その際JavaScript内でのデータ渡しに jQuery.data を使った。これが結構便利なので、後でちゃんとブログに書きたい。

CakePHP側でxmlでレスポンスを返すコーディングに手間取った。
xml用のレイアウトとビューを作れば良いのだが、CakePHPの機能をちょっと調べた。
XMLヘルパー は、シリアライズが便利そうだ。
リクエストハンドラコンポーネントでレスポンスヘッダを設定できるようだが、うまくいかなかったのでコントローラーのheaderメソッドで設定した。

Ajaxでブラウザからデータを受け取って、xmlをブラウザに返すところまではできた。
次は受け取ったデータをデータベースに格納する機能のコーディングかな。
データのバリデーションやサニタイズは後回し。

CakePHPで何かを作ろうとする 3日目

昨日の続き。

今日やったことは、

・コントローラーでCookieからデータを読み取り、ユーザーIDがセットされていなかったらログイン画面を表示
$this->Cookie->read() の結果が NULL だったら $this->render("login") するだけ。

・ログイン画面を作成
CakePHPのフォームヘルパーでサクっと作成。
こんな感じ。
<div>
<?php echo $this->Form->create("User",array("url" => "/tokkun_adds/login")); ?>
<?php echo $this->Form->input("userid",array("label" => "ユーザーID")); ?>
<?php echo $form->end("送信"); ?>
</div>
create()の引数を array("action" => "login") とすると /users/login を探してしまうので、url を指定。

・コントローラーのloginアクション作成
usersテーブルを作ってそこにユーザーIDを格納しているので、App::import("Model", "User"); でUserモデルをインポート。
フォームで入力されたuseridが、usersテーブルに存在するかチェック。
存在する場合、CookieにユーザーIDをセットして、indexへリダイレクト。


これだけ。初めてCakePHPのモデルを作ったので3時間ぐらいかかった。
AuthComponent を使って認証しようかとマニュアルを読んでみたが、理解するのに時間がかかりそうなので今日のところは自前でログイン画面を作った。
デバッグをどうしようかと考えたが、デバッグログを出力することにした。これが一番簡単そうなので。
$this->log("出力するメッセージ",LOG_DEBUG) で cakephp\app\tmp\logs\debug.log に出力される。
CakePHPマニュアル(Logging)に書かれていて、実際ちゃんと出力されるのだが、これは古い情報のようだ。
英語版では
$this->log("出力するメッセージ", "debug") か CakeLog::write("debug", "出力するメッセージ") のどちらか
と書かれている。

今日は、PHPでは配列のサイズをcount()で取得することがわかった。実際にプログラミングしてみないとわからないことがたくさんある。

CakePHPでCookieを読み書きする

昨日の続き。

今日は1時間半プログラミングした。やったことは
・CakePHPのレイアウトを少し修正。scriptタグをHtml->script()で記述。レイアウトの一部をビューとして抜き出す。
・CakePHPのコントローラーに、Cookieの読み書き処理を追加。
これだけ。
何をするのにもマニュアルを調べるから、とても時間がかかる。

CakePHPには、CookieComponentというコンポーネントがある。
このコンポーネントは、デフォルトで暗号化してCookieに書き込んでくれる。
Cookieを読み込むときも、暗号化されていることを意識する必要は無い。
Cookie名は暗号化されないので、"UserID"とかそのものズバリの名前はつけない方がいい。

明日はもうちょっとプログラミングに時間を取れるといいな。
ダラダラとWebを見る時間を削れば簡単に取れるんだけど。
プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター