新しいフォルダー

気になったことや勉強したことについて書いていきます

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;