テーブルに関しては前々回に作成&郵便番号データを放り込んだ。ということで、今回からは実際に作成していってみる。
今回やることは以下の通り。
1.コントローラとアクションを作成(index()とaddress()アクション)
2.Formヘルパーを使ってindex.ctpに郵便番号での検索ページを作成
3.モデルのbeforeFind()、afterFind()を使って郵便番号のデータを加工
4.郵便番号は前方一致で検索できるようにする
5.address.ctpにて郵便番号からの住所結果を出力
6.検索結果が存在しない場合はindex()へと戻す
最終的には住所などから郵便番号を出すという機能を実装するつもりだけど、まずは郵便番号を入力したら住所を出す、という機能を作り、その後機能を付け足していくという形で作成していきます。
コントローラーを作成
まずは、どうなるかわからないので空のアクションだけを作成する。
//app/Controller/PostalCodesController <?php class PostalCodesController extends AppController{ public $helpers = array('Html','Form','Session'); //* public function index(){ } public function address(){ } } ?>
ヘルパーの使用に関して
ヘルパーというのはViewファイルで使用するユーティリティーツールなんだけど、使用する際にはコントローラ内にpublic $helpersを記載して指定をする。
【使い方】 public $helpers = array("ヘルパーの名前");
上記のではHtmlヘルパー、Formヘルパー、Sessionヘルパーを指定している。
といっても、実をいうとこ場合なら特に書かなくても問題ないっぽい。というのも、この3つのヘルパーはlib/Controller/Controller.php内でデフォルトで指定されているみたいで、指定から外すこともできない。
$helpersを記載しない → Session,Html,Formヘルパーが指定される $helpers = array(); → それでもSession,Html,Formヘルパーが指定される $helpers = array('Time'); → Session,Html,Form,Timeヘルパーが指定される //アクション内でこんなことしたらエラーが起きた public function index(){ unset($this->helpers[0]); } //または public $helpers = array('Session'); public function index(){ unset($this->helpers['Session']); }
というわけで、無理に外そうとするのはやめようね。まあ、こんなこと誰もしないと思うけど。
ビュー(index.ctp)を作成
コントローラーを作成したら、次はビューファイルを作成していく。
ひとまず、index.ctpを作成。ここではFormヘルパーを使って郵便番号入力フォームを作成する。
//View/PostalCodes/index.ctp <H1>郵便番号検索</H1> <?php //フォームを作成。 //命名規約に沿ってたら特に引数は必要ないっぽい。 print $this->Form->create(); //フォーム要素を作成。下記の場合はテキストボックスが作成される。 print $this->Form->input('zipcode' , array('label'=>'郵便番号')); //フォームを終了する。 //下のように文字列を引数で渡したら送信ボタンが作成される。 print $this->Form->end('送信'); ?>
アクセスしてみる。
http://localhost/cakePHPfolder/postal_codes/
Formヘルパーの説明
とっても便利みたいなんだけど、詳しいことは別の機会に調べるとして今回は簡単に調べたことだけをまとめます。
このフォームヘルパーを使うと、View、Controller、Model全てに連動できる形のフォームデータを作成してくれるみたい。更にはフォーム要素のデフォルト値などもコントローラ内で簡単に指定することができる。
$this->Form->create(string $modelname , array $options);
$modelname
名前の通り使用するモデルの名前を記載する。デフォルトはnull。何も指定しないと、命名規約に沿ったモデル名が自動的に入る。
$options
他の設定情報を配列で記載する。
主な$optionの値 | ||
---|---|---|
オプション名 | 説明 | デフォルト |
type | メソッド指定 | post |
action | 送信先をアクションで指定 | 現在のページ |
url | 送信先をURLで指定 | 現在のページ |
<?php //上の$this->Form->create();は下記のと同じ print $this->Form->create( 'PostalCode', array( 'action'=>'index', 'url' =>'/postl_codes/index' ) ); ?>
$this->Form->input(string $fieldname , array $options);
他にもtext()やselect()などもあるけど、基本はinput()で全部できるっぽいから、とりあえずこれだけ。
$fieldsname
フィールド名を記載すると、cakePHP内でこの要素とモデルのフィールドとを関連付けしてくれる。
また、フィールド名やその属性によって、要素の属性も自動的に決めてくれる。
基本──text
boolean型またはtinyint(1)──checkbox
text型──textarea
フィールド名がpassword、passwd、psword──password
date型──select(年、月、日)
datetime型──select(年、月、日、時、分)
time型──select(時、分)
もちろんこれらは第2引数の$optionsで指定もできる。
$options
こちらも配列で指定。いろいろあるけど、主に使いそうなのだけ。
主な$optionの値 | ||
---|---|---|
オプション名 | 説明 | |
type | 要素の属性を指定 | |
div | 要素を囲むDIVの属性を指定 falseにするとDIVで囲まない | |
label | ラベルを指定 falseにするとラベルを表示しない | |
error | エラー時に表示する属性を指定 falseにすると表示しない | |
before | 要素の先頭に表示するものを指定 | |
separator | ラベルとフォーム要素の後に入るっぽい。 radioなど複数の要素がある場合にこれで区切れる | |
between | ラベルとフォーム要素の間に入るっぽい。 | |
after | 要素の最後に表示するものを指定 | |
class | 要素のクラス名を指定 | |
id | 要素のIDを指定 | |
default | 要素の初期値を指定 ただ、これはコントローラ内でも指定できる。 またcheckboxの場合は使えず、'checked'=>true/falseとする | |
options | selectまたはrabelの値を指定(array('値'=>'表示名')) | |
selected | select要素の初期値を指定。 これもコントローラ内でも指定できる |
$this->Form->end($options)
これでフォームを閉じる。
$optionsに文字を指定すればその文字の投稿ボタンが表示される。
何も指定しなければ、そのまま閉じるだけ。また、$optionsは配列でも指定でき、label、value、class、id、divなどが指定できる。
オプションのdefaultでも指定できるが、コントローラ内で以下のようにすることでも指定ができる。
//PostalCode $this->request->data('PostalCode.zipcode','000-0000'); //または $this->request->data('PostalCode',array('zipcode'=>'000-0000')); //または $this->request->data['PostalCode']['zipcode'] = '000-0000';
3番目の方法を使えば、モデルから取得したデータをそのままぶっこむこともできる。
index()アクションをいじる
上の作成の仕方の場合、$this->Form->create()のデフォルトによりPOSTデータがそのままindex()へと送られる。
というわけで、index()内を編集してポストデータが送られたら送られたデータを解析し、別の動作をするよう編集する。
//PostalCodesControler.php内 public function index(){ //POSTリクエストの場合の動作 if($this->request->is('post')){ $data = $this->request->data['PostalCode']['zipcode']; if(!preg_match("/([0-9]{3})\-?([0-9]{4})/i" , $data){ $this->Session->setFlash("入力が正しくありません。郵便番号は7桁の半角数字か***-****形式で入力して下さい"); }else{ $this->redirect(array('action'=>'address',$data)); } } }
本当は前方一致で検索できるようにしたいんだけど、まずは7桁での検索から作成してみます。
なお、上記の説明は以下の通り。
今POSTリクエストかどうかをこれで判断する。なお、$this->request->is()では以下の判別ができるとても便利な関数。
$this->request->is('文字');の主な一覧 | |
---|---|
post | POSTリクエストか |
get | GETリクエストか |
put | PUTリクエストか |
delete | Deleteリクエストか |
ajax | Ajaxリクエストか |
mobile | 携帯からのリクエストか |
なお、putに関しては、ビュー出力時に$this->request->dataの中にデータが設定されてあった状態で、フォームがPOST送信された場合になる。
deleteは$this->Form->create()の中で'type'=>'delete'と設定した場合になるっぽい。
(putも'type'=>'put'とすることでそうなる)。
POSTでの送信内容はこの中に格納される。
なお、上記では配列として取り出しているが、関数で取り出すこともできる。
$data = $this->request->data('PostalCode.zipcode');
最初のだと、たとえば$this->request->data['PostalCode']['zipcode']が存在しなかった場合(空ではなくそもそもその配列のキーが存在しなかった場合)エラーが起きるから上のように関数で呼び出して存在するかどうか確かめる方がいいかも。
また、この中にデータを格納することで、フォームのデフォルト値を設定することもできる。
(指定の仕方はFormヘルパーの使い方の部分に記載しました)
1回かぎりのメッセージを入力できるみたい。上記の場合なら、もし正規表現のルールに沿ってない場合、
みたいに出力される。
その名前の通り、リダイレクトしてくれる。上記の場合なら、http://localhost/cakephpFolder/postal_codes/address/$dataにリダイレクトされる。
(このURLの場合、cakePHP内で$dataがaddress()アクションの第一引数として渡される)
address()アクションとaddres.ctpファイルを作る
ここでは渡された引数を元に、郵便番号を検索してデータを吐き出す。また、データがなかったらindex()へと戻すように指定する。
(今は7桁でしか郵便番号検索できないようにしてるけど、次回複数検索もできるようにするつもりだから、それを想定して複数の検索結果を表示できるようにしてます)
//Controller/PostalCodesController.php内 public function address($zipcode = 0){ $options = array('conditions'=>array('zipcode'=>$zipcode)); if(!$result = $this->PostalCode->find('all',$options)){ $this->Session->setFlash("ヒットしませんでした"); $this->redirect($this->referer(array('action'=>'index'))); }else{ $this->set(compact('result')); } } //View/PostalCodes/address.ctp内 <?php $count = 0; $cause = array(0=>'変更なし',1=>'市政・区政・町政・分区・政令指定都市施行',2=>'住居表示の実施',3=>'区画整理',4=>'郵便区調整等',5=>'訂正',6=>'廃止'); $this->Html->addCrumb('検索',array('action'=>'index')); $this->Html->addCrumb('検索結果'); ?> <?php print $this->Html->getCrumbs(">"); ?> <hr> <H1>検索結果</H1> <?php foreach($result as $rows): ?> 【<?php print ++$count; ?>】 <table border='1' style='margin-bottom:20px; width:700px;'> <tr><th width='200'>郵便番号</th><td><b style="color:#292999;"><?php print $rows['PostalCode']['zipcode']; ?></b></td></tr> <tr><th width='200'>JISコード</th><td><?php print $rows['PostalCode']['jiscode']; ?></td></tr> <tr><th width='200'>都道府県</th><td><?php print $rows['PostalCode']['state']; ?>(<?php print $rows['PostalCode']['state_kana']; ?>)</td></tr> <tr><th width='200'>市区町村</th><td><?php print $rows['PostalCode']['city']; ?>(<?php print $rows['PostalCode']['city_kana']; ?>)</td></tr> <tr><th width='200'>町域名</th><td><?php print $rows['PostalCode']['street']; ?>(<?php print $rows['PostalCode']['street_kana']; ?>)</td></tr> <tr><th width='200'>変更</th><td><?php print $cause[$rows['PostalCode']['cause']]; ?></td></tr> </table> <?php endforeach; ?> <?php print $this->Html->link('戻る',array('action'=>'index')); ?>
また、初めて使うのをひとつひとつ紹介していく。
これが、モデルのデータ(つまりはMysqlのデータ)を取り出すcakePHP関数となる。実際は
$this->モデル名->find($type , $options)
という書き方になる。
これは、相当色んなことができるっぽいので詳細はFormヘルパー同様また別の機会にまとめるとして、今は$options = array('conditions'=>array('zipcode'=>$zipcode));にて郵便番号を指定し、'all'で検索結果を全部出力するということだけ。
これもかなり使える関数。HTTP_REFERERが読み取れた場合はそれを返し、読み取れなかった場合は中の配列を返す。
これを使うことで、あちこちのアクションからこのaddressにアクセスしたとしても、それぞれのアクションへと簡単に返すことができる。
一番使う関数になるのかも。Viewページで使う変数をこの関数で渡す。
この場合は$resultをViewページへと渡すことができる。
(なお、compactはPHP標準関数です。念のため)
$this->Html->getCrumbs(">");
これも結構便利そうな関数。トピックパス(俗に言うパンくずリスト)を作成してくれる。
直感的に解るけど、リンクを作成できる。これも、また別の機会に詳しい使い方をまとめようかな。
渡される郵便番号データを検索前と検索後に加工
さて、これで郵便番号で住所が検索できる、わけじゃない。
というのも、面倒にも今回郵便番号データをdecimal型にしちゃってるからだ。だから、次にはモデル内にbeforeFind()とafterFind()関数を作り、その中で郵便番号データを加工する。
この2つの関数は、作成しておくと検索開始時と検索後の出力時にいろいろな指示を与えられる。
//Model/PostalCode.php内 public function beforeFind($queryData){ if(!empty($queryData['conditions']['zipcode']) and preg_match("/^(\d{3})\-?(\d{4})$/i",$queryData['conditions']['zipcode'],$match)){ $queryData['conditions']['zipcode'] = (int)($match[1].$match[2]) / 10000000; } return $queryData; } public function afterFind($result){ foreach($result as $key => $row){ if(!empty($row[$this->alias]['zipcode']) && preg_match("/^(\d{3})(\d{4})$/i",str_pad($row[$this->alias]['zipcode'] * 10000000 , 7 , '0' , STR_PAD_LEFT), $match)){ $result[$key][$this->alias]['zipcode'] = $match[1] . '-' . $match[2]; } } return $result; }
(2012/05/17追記)
モデル内またはAppModel内で自身のモデルを記載する際、直接記載するのではなくこの変数を使うようにした方がいいみたい。
モデルの名前を別のに指定していた場合でも、これだとエラーが起きなくなる。
これで、$this->PostalCode->find()時にconditions配列の中にzipcodeが指定されていたら小数点の形へと変形してくれ、データを取り出す時には郵便番号の形へと変換してくれる。
検索してみる
さて、これで内部的に変更してくれ、こっちは郵便番号を郵便番号のフォーマットのまま検索できるようになった。
というわけで、東京都中央区(103-0001)を検索してみる。
うまくいった\(^o^)/
公開するかぎりはあまりいい加減なことは書けないし、今回作成する際に相当いろいろと調べて(それがブログを作った目的でもあるんだけど)かなり時間がかかってしまったが、なんとかcakePHP2.1で簡単な検索だけはできるようになった。
次回は、郵便番号を前方一致で検索できるように作り替えます。
本当は住所から郵便番号を検索することが目的だけど、それはまだもうちょっと先の話になりそう。
【参考リンク】
CookBook2.x(英語版)
FormHelper Class Info:
CakeRequest Class Info:
0 件のコメント:
コメントを投稿