PHPUNIT Exceptionのテストをする場合の注意点
テストかいてて混乱したので備忘録として残しておく。
まず、PHPUNITではassert~の判定でfalseになった場合Exceptionを吐いている。
そのため、try{ 処理 } catch {Exception $e} だけだと判定によるExceptionなのか、実際にthrowされたExceptionなのか区別ができなくなる。
try{
mogemoge(); ←☆このメソッドではthrow newExceptionをする
$this->assertTrue(False); ←● assertでfalse判定
} catch(PHPUnit_Framework_AssertionFailedError $e){
●ここに飛んでくる
} catch(Exception $e) {
☆ここに飛んでくる
}
※☆の処理でExceptionを吐いた時点で●の処理は実行されない
Catchで取り分けることで意図的なExceptionとassertによるExceptionを切り分けることができる。
データをシリアライズしてDBに一時保存する
前回(CakePHPでCSVをアップロードしてDB更新 - 新しいフォルダー)
の続き。
CSVをアップロードするときに変更点を確認する画面を作る。
画面遷移としては
CSVを選択・アップロード → 確認画面 → 完了画面
となる。
その場合に、最初にアップロードされたCSVのデータをどう持ち回るか思案。
結果的に一時保存用のDBを作り、そこにsession_idをKEYにしたシリアライズしたデータをぶっこみました。
CREATE TABLE `tmp_upload` (
`session_id` varchar(64) NOT NULL DEFAULT '',
`table_name` varchar(32) DEFAULT NULL,
`value` text,
PRIMARY KEY (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
消すことを考えた場合、有効期限用の日付カラムを作り
アップロードの処理をする前に上記のテーブルの期限切れデータを消す処理を走らせる事で不要カラムを消すことができる。
$serializeData = serialize($updateItemData);
//一時保存
$this->TmpUpload->set(array(
'session_id' => session_id(),
'table_name' => $tableName,
'value' => $serializeData
));
$this->TmpUpload->save();//確認画面用のViewに必要なデータを渡す
$this->set('tableName', $tableName);
$this->set('columnName', $columnName);
$this->set('tableDatas', $changeColumns);
確認画面のSUBMITを受け取ったら
session_idをKEYにレコードを取得し、アンシリアライズ後にデータの更新を行う。
PHP 正規表現(int/float/datetime)
バリデートメソッドを作ったのでメモって置く。
間違ってたらサーセン
テスト用の配列
$testInput = array(
//int
array('integer' => '123'),
array('integer' => 123),
array('integer' => -10),
array('integer' => '-10'), //ここまでOK
array('integer' => '123.01'),//ここまでNG
array('integer' => 123.01),
array('integer' => 'abc'),
array('integer' => 'テスト'),
//float
array('float' => '123'),
array('float' => 123),
array('float' => -10),
array('float' => '-10'),
array('float' => '123.01'),
array('float' => 123.01), //ここまでOK
array('float' => 'abc'), //ここからNG
array('float' => 'テスト'),
//datetime
array('datetime' => '123'),
array('datetime' => 123),
array('datetime' => -10),
array('datetime' => '-10'),
array('datetime' => '123.01'),
array('datetime' => 123.01),
array('datetime' => 'abc'),
array('datetime' => 'テスト'),
array('datetime' => '2000-10-1a 10:10'),
array('datetime' => '2000-10-10/10:10'),
array('datetime' => '2000-10-10-10:10'),
array('datetime' => '2000-10-10'),
array('datetime' => '20-10-10 1:1:1'), //ここまでNG
array('datetime' => '2000-10-10 1:0'), //ここからOK
array('datetime' => '2000-1-1 10:10'),
array('datetime' => '2000/10-10 10:10:00'),
array('datetime' => '2000-10/10 10:10:00')
);
チェック用の処理
foreach($testInput as $data){
foreach($data as $key => $value){
switch($key){
case 'integer':
echo (preg_match("/^[-]?[0-9]+$/", $value))
? "int_ok:$value<br>":"int_ng:$value<br>";
break;
case 'float':
case 'double':
echo (preg_match("/^[-]?[0-9]*[.]?[0-9]+$/", $value))
? "float_ok:$value<br>":"float_ng:$value<br>";
break;
case 'datetime':
echo (preg_match("/^[0-9]{4}[-\/][0-9]{1,2}[-\/][0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?$/", $value))
? "datetime_ok:$value<br>":"datetime_ng:$value<br>";
break;
}
}
}
CakePHPでCSVをアップロードしてDB更新
エラーチェックはしてません。
Controller
public function upload(){
//フォームでテーブル名を送りつける。
$tableName = $this->request->data['selectTable'];//対象テーブル読み込み
$this->loadModel($tableName);
//該当テーブルのカラム名と属性を取得
$tableType = $this->$tableName->getColumnTypes();
//エンコード※1
$fileName = self::fileEncode();
$fp = fopen($fileName, 'rb');
//saveAllする配列
$updateItemData = array();
//データ取得
while ($row = fgetcsv($fp)) {
//カラムが異なっていたらエラー
if(count($tableType) == count($row)){//1レコード分の配列
$record = array();
//添字
$i = 0;//一括保存用に整形
foreach($tableType as $key => $value){
//例:$record['name'] = 'username'
$record[$key] = $row[$i++];
}
$updateItemData[$tableName][] = $record;
} else {
//カラム数が異なるのでエラー
throw new RuntimeException('Invalid column detected');
}//一括保存
$this->$tableName->saveAll($updateItemData[$tableName]);
}
※1 エンコード
public function fileEncode(){
$detectOrder = 'ASCII,JIS,UTF-8,CP51932,SJIS-win';
setlocale(LC_ALL, 'ja_JP.UTF-8');//ファイルのパスを取得
$tmpName = $this->request->param('form')['upfile']['tmp_name'];
$buffer = file_get_contents($tmpName);
if (!$encoding = mb_detect_encoding($buffer, $detectOrder, true)) {
// 文字コードの自動判定に失敗
unset($buffer);
throw new RuntimeException('Character set detection failed');
}
file_put_contents($tmpName, mb_convert_encoding($buffer, 'UTF-8', $encoding));
unset($buffer);
return $tmpName;
}
出典元は忘れてしまいました…
CakePHPでテーブルデータをCSVとしてダウンロード
エラーチェック等は入れてません
Controller
//適当にフォームからDLするテーブル名を送る
$selectTable = $this->request->data['selectTable'];
//モデルをロード
$this->loadModel($selectTable);
//テーブルのカラム名を取得
$tableType = $this->$selectTable->getColumnTypes();//テーブルデータを取得※1
$tableData = $this->$selectTable->getAll();
//CSVの先頭行(カラム名)のために整形
$columnName = array();
foreach($tableType as $key => $value){
$columnName = $key;
}//CSV出力※2
self::csvSet($columnName, $tableData, $selectTable);
※1 MODEL内のデータ取得のメソッド
public function getAll(){
$sql = "SELECT * FROM テーブル名";
$tableData = $this->query($sql);
$returnData = array();//データの整形
foreach($tableData as $data){
$returnData = $data[テーブル名];
}
return $returnData;
}
※2 csvSetメソッド
public function csvSet($columnName, $datas, $name = 'csv'){
//自動選択のテンプレートを使わない$this->layout = false;
//ファイル名の設定
$filename = $name."_".date('YmdHis');
// テンプレートにセット
$this->set('fileName', $filename);
$this->set('th', $columnName);
$this->set('td', $datas);//テンプレート呼び出し※3
$this->render('/Layouts/csv');
}
※3 CSV出力のテンプレート(View/Layouts/csv.ctp)
<?php
$this->Csv->addRow($th);
foreach($td as $t) {
foreach($t as $tData){
$this->Csv->addField($tData);
}
$this->Csv->endRow();
}$this->Csv->setFilename($fileName);
echo $this->Csv->render(true, 'sjis', 'utf-8');
exit;