├── .editorconfig ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Controller │ └── Component │ │ ├── CsvExportComponent.php │ │ └── FixedLengthExportComponent.php ├── Form │ ├── CsvImportForm.php │ └── FixedLengthImportForm.php └── LargeExport │ └── LargeCsvExport.php └── tests ├── TestCase ├── Controller │ └── Component │ │ ├── CsvExportComponentTest.php │ │ └── FixedLengthComponentTest.php └── Form │ ├── CsvImportFormTest.php │ └── FixedLengthImportFormTest.php ├── bootstrap.php └── test_app ├── test1.csv └── test1.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.bat] 14 | end_of_line = crlf 15 | 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /composer.phar 2 | /composer.lock 3 | /phpunit.xml 4 | /vendor 5 | /tmp 6 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | php: 3 | code_rating: true 4 | duplication: true 5 | tools: 6 | php_code_sniffer: 7 | config: 8 | standard: "PSR2" 9 | filter: 10 | paths: 11 | - src/* 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | 9 | sudo: false 10 | 11 | env: 12 | global: 13 | - DEFAULT=1 14 | 15 | matrix: 16 | fast_finish: true 17 | include: 18 | - php: 5.4 19 | 20 | install: 21 | - composer self-update 22 | - composer install --dev 23 | 24 | before_script: 25 | - cp phpunit.xml.dist phpunit.xml 26 | 27 | script: 28 | - sh -c "if [ '$DEFAULT' = '1' ]; then phpunit --stderr; fi" 29 | 30 | notifications: 31 | email: false 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 今後、新しくご利用の方は https://github.com/satthi/csv-combine をご利用ください。# 2 | 3 | # CsvCombine plugin for CakePHP3 # 4 | 5 | [![Build Status](https://travis-ci.org/satthi/csv-combine-plugin-for-CakePHP.svg?branch=master)](https://travis-ci.org/satthi/csv-combine-plugin-for-CakePHP) 6 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/satthi/csv-combine-plugin-for-CakePHP/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/satthi/csv-combine-plugin-for-CakePHP/?branch=master) 7 | 8 | PHP versions 5 9 | CakePHP version 3 10 | 11 | ## 更新履歴 ## 12 | 13 | * 2015/05/01 CakePHP3用に書き換え 14 | * 2016/05/17 固定長対応追加 15 | 16 | ## 特徴 ## 17 | 18 | * 配列 ⇔ CSV・TSVファイルを行う機能 19 | * Cake1.3や2ではファイルのアップロードであったり、保存まで管理していたが自分で使ってなかったので削除しました・・・。 20 | * 固定長に対応しました! 21 | 22 | ## 準備 ## 23 | 24 | ※copmposer対応しました 25 | ``` 26 | "satthi/csv-combine-plugin-for-cakephp": "*" 27 | ``` 28 | 29 | ******************** 30 | ※composerでインストールしないとき 31 | pluginsディレクトリ内にCsvCombineを設置 32 | 33 | bootstrapに以下を記述 34 | ``` 35 | Plugin::load('CsvCombine', ['autoload' => true]); 36 | ``` 37 | ******************** 38 | 39 | ## 使い方(CSV) ## 40 | ```php 41 | loadComponent('CsvCombine.CsvExport'); 54 | } 55 | public function export() 56 | { 57 | $list = [ 58 | [ 59 | 'test1', 60 | 'test2', 61 | 'test3', 62 | ], 63 | [ 64 | 'test4', 65 | 'test5', 66 | 'test6', 67 | ], 68 | ]; 69 | /* 70 | *@array $list 出力のための配列(二次元配列が基本) 71 | *@param $file_name 出力ファイル名(デフォルトはexport.csv) 72 | *@param $delimiter 区切り文字の設定(デフォルトは",") 73 | *@param $directory 一時保存ディレクトリ(デフォルトはTMP,最終的にファイルを削除をする) 74 | *@param $export_encoding 出力するファイルのエンコード(デフォルトはSJIS-win 75 | *@param $array_encoding 入力する配列のエンコード(デフォルトはUTF-8 76 | */ 77 | return $this->CsvExport->export($list); 78 | } 79 | 80 | public function import() 81 | { 82 | $import = new CsvImportForm(); 83 | $file = TMP . 'test.csv'; 84 | $column = [ 85 | 'key1', 86 | 'key2', 87 | 'key3', 88 | ]; 89 | /* 90 | *@array file ファイルパス(必須 91 | *@array $column カラム名を並び順に(必須 92 | *@param $delimiter 区切り文字を設定 (デフォルトは","で"\t"や"|"などを指定することが可能) 93 | *@param $array_encoding 出力する配列のエンコード(デフォルトはUTF-8 94 | *@param $import_encoding 入力するファイルのエンコード(デフォルトはSJIS-win 95 | */ 96 | $result = $import->loadDataCsv($file,$column); 97 | debug($result); 98 | exit; 99 | } 100 | } 101 | 102 | ``` 103 | 104 | ## 使い方(固定長) ## 105 | 106 | ```php 107 | loadComponent('CsvCombine.FixedLengthExport'); 120 | } 121 | 122 | public function export() 123 | { 124 | $list = [ 125 | [ 126 | 'あいう', 127 | 'いいい', 128 | 'uuu', 129 | ], 130 | [ 131 | 'あいう', 132 | 'いいい', 133 | 'uuu', 134 | ], 135 | [ 136 | 'あいう', 137 | 'いいい', 138 | 'uuu', 139 | ], 140 | ]; 141 | $fixed_options = [ 142 | 8, 143 | 10, 144 | 6 145 | ]; 146 | //makeでファイル作成のみ 147 | /* 148 | * export 固定長の出力アクション 149 | * 150 | * @array $list 出力のための配列(二次元配列が基本) 151 | * @array $fixed_options 出力のための固定長の設定(各カラムのバイト数) 152 | * @param $file_name 出力ファイル名(デフォルトはexport.txt) 153 | * @param $line_feed_code 改行コード(デフォルトは\r\n) 154 | * @param $directory 一時保存ディレクトリ(デフォルトはTMP,最終的に削除をする) 155 | * @param $export_encoding 出力するファイルのエンコード(デフォルトはSJIS-win 156 | * @param $array_encoding 入力する配列のエンコード(デフォルトはUTF-8 157 | */ 158 | 159 | //$this->FixedLengthExport->make($list,$fixed_options); 160 | $this->FixedLengthExport->export($list,$fixed_options); 161 | 162 | } 163 | 164 | public function import() 165 | { 166 | $filename = TMP . 'test.txt'; 167 | $column_list = [ 168 | ['name' => 'column1', 'length' => 8], 169 | ['name' => 'column2', 'length' => 10], 170 | ['name' => 'column3', 'length' => 6], 171 | ]; 172 | $import = new FixedLengthImportForm(); 173 | /* 174 | * @text $fileName 固定長テキストファイ 175 | * @array $column_list 各カラム情報(name:カラム名,length:バイト数 デフォルトは空配列 空時には列の数だけ0から連番を振る) 176 | * @param $line_feed_code 改行コード(デフォルトは\r\n) 177 | * @param $array_encoding 出力するする配列のエンコード(デフォルトはUTF-8 178 | * @param $import_encoding 入力するテキストのエンコード(デフォルトはSJIS-win 179 | */ 180 | $result = $import->loadData($filename, $column_list); 181 | debug($result); 182 | exit; 183 | } 184 | } 185 | 186 | ``` 187 | 188 | ## License ## 189 | 190 | The MIT Lisence 191 | 192 | Copyright (c) 2011 Fusic Co., Ltd. (http://fusic.co.jp) 193 | 194 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 195 | 196 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 197 | 198 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 199 | 200 | ## Author ## 201 | 202 | Satoru Hagiwara 203 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "satthi/csv-combine-plugin-for-cakephp", 3 | "description": "CakePHP CsvCombine", 4 | "type": "cakephp-plugin", 5 | "keywords": ["cakephp", "csv"], 6 | "homepage": "https://github.com/satthi/csv-combine-plugin-for-CakePHP", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Satoru Hagiwara", 11 | "role": "Author" 12 | } 13 | ], 14 | "support": { 15 | "source": "https://github.com/satthi/csv-combine-plugin-for-CakePHP" 16 | }, 17 | "require": { 18 | "cakephp/cakephp": "~3.0" 19 | }, 20 | "require-dev": { 21 | "cakephp/cakephp-codesniffer": "dev-master", 22 | "phpunit/phpunit": "*" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "CsvCombine\\": "src" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "CsvCombine\\Test\\": "tests", 32 | "CsvCombine\\Test\\App\\": "tests/test_app/App" 33 | } 34 | }, 35 | "suggest": { 36 | }, 37 | "extra": { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ./tests/TestCase 17 | 18 | 19 | 20 | 21 | 22 | 33 | 34 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/Controller/Component/CsvExportComponent.php: -------------------------------------------------------------------------------- 1 | _controller = $event->subject(); 27 | } 28 | 29 | /* 30 | * export CSVの出力アクション 31 | * 32 | * @param array $list 出力のための配列(二次元配列が基本) 33 | * @param string $file_name 出力ファイル名(デフォルトはexport.csv) 34 | * @param string $delimiter 区切り文字の設定(デフォルトは",") 35 | * @param string $directory 一時保存ディレクトリ(デフォルトはTMP,最終的に削除をする) 36 | * @param string $export_encoding 入力するファイルのエンコード(デフォルトはSJIS-win 37 | * @param string $array_encoding 出力する配列のエンコード(デフォルトはUTF-8 38 | */ 39 | public function export($list, $file_name = 'export.csv', $delimiter = ",", $directory = TMP,$export_encoding = 'SJIS-win',$array_encoding = 'UTF-8') 40 | { 41 | //layoutを切って autoRenderも外しておく 42 | $this->_controller->viewBuilder()->layout('ajax'); 43 | $this->_controller->autoRender = false; 44 | 45 | //headerのセット 46 | $save_directory = $this->make($list, $file_name , $delimiter , $directory ,$export_encoding ,$array_encoding); 47 | // 日本語ファイル名出力のため 48 | setlocale(LC_ALL, 'ja_JP.UTF-8'); 49 | $basename = basename($save_directory); 50 | 51 | // ファイル名の変換(IE対応) 52 | if (strstr(env('HTTP_USER_AGENT'), 'MSIE') || strstr(env('HTTP_USER_AGENT'), 'Trident') || strstr(env('HTTP_USER_AGENT'), 'Edge')) { 53 | $basename = mb_convert_encoding($basename, "SJIS", "UTF-8"); 54 | } 55 | $this->_controller->response->file($save_directory, ['download' => true, 'name' => $basename]); 56 | } 57 | 58 | /* 59 | * make CSVの生成アクション 60 | * 61 | * @param array $list 出力のための配列(二次元配列が基本) 62 | * @param string $file_name 出力ファイル名(デフォルトはexport.csv) 63 | * @param string $delimiter 区切り文字の設定(デフォルトは",") 64 | * @param string $directory 一時保存ディレクトリ(デフォルトはTMP,最終的に削除をする) 65 | * @param string $export_encoding 入力するファイルのエンコード(デフォルトはSJIS-win 66 | * @param string $array_encoding 出力する配列のエンコード(デフォルトはUTF-8 67 | */ 68 | public function make($list, $file_name = 'export.csv', $delimiter = ",", $directory = TMP,$export_encoding = 'SJIS-win',$array_encoding = 'UTF-8') 69 | { 70 | Configure::write('debug', 0); 71 | ini_set("memory_limit", -1); 72 | set_time_limit(0); 73 | $csv_list = array(); 74 | mb_convert_variables($export_encoding, $array_encoding, $list); 75 | //$listにカンマか"がいた時の対応 76 | if (isset($list)) { 77 | if (is_array($list)) { 78 | foreach ($list as $k => $list1) { 79 | if (is_array($list1)) { 80 | foreach ($list1 as $m => $v) { 81 | if (is_array($v)){ 82 | //3次元以上の配列の時はエラー 83 | throw new MethodNotAllowedException('array layer error'); 84 | } 85 | $csv_list[$k][$m] = $this->_parseCsv($v, $delimiter); 86 | } 87 | } else { 88 | //1次元の時は1列目に値が入る。 89 | $csv_list[0][$k] = $this->_parseCsv($list1, $delimiter); 90 | } 91 | } 92 | } else { 93 | //文字列の時は1カラムに値が入るだけ。 94 | $csv_list[0][0] = $this->_parseCsv($list, $delimiter); 95 | } 96 | } 97 | 98 | $save_directory = $directory . $file_name; 99 | $fp = fopen($save_directory, 'w'); 100 | foreach ($csv_list as $fields) { 101 | fputs($fp, implode($delimiter, $fields) . "\r\n"); 102 | } 103 | 104 | fclose($fp); 105 | 106 | return $save_directory; 107 | } 108 | 109 | /* 110 | * _parseCsv 111 | * csv(など)の形式に変更 112 | * 113 | * @param string $v 変換する値 114 | * @param string $delimiter 区切り文字 115 | */ 116 | private function _parseCsv($v, $delimiter) 117 | { 118 | //区切り文字・改行・ダブルクオートの時 119 | if (preg_match('/[' . $delimiter . '\\n"]/', $v)) { 120 | $v = str_replace('"', '""', $v); 121 | $v = '"' . $v . '"'; 122 | } 123 | return $v; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/Controller/Component/FixedLengthExportComponent.php: -------------------------------------------------------------------------------- 1 | 'export.txt', 21 | 'line_feed_code' => "\r\n", 22 | 'directory' => TMP, 23 | 'export_encoding' => 'SJIS-win', 24 | 'array_encoding' => 'UTF-8', 25 | 'extra_fixed_options' => [] 26 | ]; 27 | private $_textData = ''; 28 | 29 | /** 30 | * コンポーネント初期化 31 | * 32 | * @access public 33 | */ 34 | public function startup(Event $event) { 35 | $this->_controller = $event->subject(); 36 | } 37 | 38 | /* 39 | * export 固定長の出力アクション 40 | * 41 | * @param array $list 出力のための配列(二次元配列が基本) 42 | * @param array $fixed_options 出力のための固定長の設定(各カラムのバイト数及び型) 43 | * @param array $options 下記パラメータを必要に応じて設定 44 | * file_name 出力ファイル名(デフォルトはexport.txt) 45 | * line_feed_code 改行コード(デフォルトは\r\n) 46 | * $directory 一時保存ディレクトリ(デフォルトはTMP,最終的に削除をする) 47 | * export_encoding 出力するファイルのエンコード(デフォルトはSJIS-win 48 | * array_encoding 入力する配列のエンコード(デフォルトはUTF-8 49 | * extra_fixed_options 出力のための固定長の設定(列によって桁数が異なる場合の設定) 50 | */ 51 | public function export($list, $fixed_options, $options) 52 | { 53 | $options = array_merge($this->_defaultOptions,$options); 54 | extract($options); 55 | 56 | //layoutを切って autoRenderも外しておく 57 | $this->_controller->viewBuilder()->layout('ajax'); 58 | $this->_controller->autoRender = false; 59 | 60 | //headerのセット 61 | $save_directory = $this->make($list, $fixed_options, $options); 62 | // 日本語ファイル名出力のため 63 | setlocale(LC_ALL, 'ja_JP.UTF-8'); 64 | $basename = basename($save_directory); 65 | 66 | // ファイル名の変換(IE対応) 67 | if (strstr(env('HTTP_USER_AGENT'), 'MSIE') || strstr(env('HTTP_USER_AGENT'), 'Trident') || strstr(env('HTTP_USER_AGENT'), 'Edge')) { 68 | $basename = mb_convert_encoding($basename, "SJIS", "UTF-8"); 69 | } 70 | $this->_controller->response->file($save_directory, ['download' => true, 'name' => $basename]); 71 | } 72 | 73 | /* 74 | * make 固定長の作成アクション 75 | * 76 | * @param array $list 出力のための配列(二次元配列が基本) 77 | * @param array $fixed_options 出力のための固定長の設定(各カラムのバイト数) 78 | * @param array $options 下記パラメータを必要に応じて設定 79 | * file_name 出力ファイル名(デフォルトはexport.txt) 80 | * line_feed_code 改行コード(デフォルトは\r\n) 81 | * $directory 一時保存ディレクトリ(デフォルトはTMP,最終的に削除をする) 82 | * export_encoding 出力するファイルのエンコード(デフォルトはSJIS-win 83 | * array_encoding 入力する配列のエンコード(デフォルトはUTF-8 84 | * extra_fixed_options 出力のための固定長の設定(列によって桁数が異なる場合の設定) 85 | */ 86 | public function make($list, $fixed_options, $options) 87 | { 88 | Configure::write('debug', 0); 89 | ini_set("memory_limit", -1); 90 | set_time_limit(0); 91 | 92 | $this->_textData = ''; 93 | $options = array_merge($this->_defaultOptions,$options); 94 | extract($options); 95 | 96 | mb_convert_variables($export_encoding, $array_encoding, $list); 97 | 98 | // keyを振りなおしておく。 99 | $list = array_merge($list); 100 | $list_count = count($list); 101 | //$listにカンマか"がいた時の対応 102 | $return_text = ''; 103 | foreach ($list as $row => $list_val) { 104 | $column_options = $fixed_options; 105 | if (array_key_exists($row + 1, $extra_fixed_options)) { 106 | $column_options = $extra_fixed_options[$row + 1]; 107 | } elseif (array_key_exists($row - $list_count, $extra_fixed_options)) { 108 | $column_options = $extra_fixed_options[$row - $list_count]; 109 | } 110 | 111 | foreach ($column_options as $fixed_option_key => $fixed_info) { 112 | if (!array_key_exists($fixed_option_key, $list_val)) { 113 | //必要なデータが存在しないエラー 114 | throw new MethodNotAllowedException('data not exist'); 115 | } else if (strlen($list_val[$fixed_option_key]) > $fixed_info['length']) { 116 | throw new MethodNotAllowedException('length error'); 117 | } 118 | 119 | if ($fixed_info['type'] == 'text') { 120 | $return_text .= str_pad($list_val[$fixed_option_key], $fixed_info['length']); 121 | } elseif ($fixed_info['type'] == 'integer') { 122 | $return_text .= sprintf('%0' . $fixed_info['length'] . 's', ($list_val[$fixed_option_key])); 123 | } else { 124 | throw new MethodNotAllowedException('type error'); 125 | } 126 | } 127 | $return_text .= $line_feed_code; 128 | } 129 | 130 | $this->_textData = $return_text; 131 | $save_directory = $directory . $file_name; 132 | $fp = fopen($save_directory, 'w'); 133 | fwrite($fp, $return_text); 134 | 135 | fclose($fp); 136 | 137 | return $save_directory; 138 | } 139 | 140 | /* 141 | * getRawData ファイルに出力した生テキストデータを取得 142 | */ 143 | public function getRawData() 144 | { 145 | return $this->_textData; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Form/CsvImportForm.php: -------------------------------------------------------------------------------- 1 | fgetcsv_reg($file,65536,$delimiter)){//CSVファイルを","区切りで配列に 35 | mb_convert_variables($array_encoding,$import_encoding,$data); 36 | $csvData[] = $data; 37 | } 38 | 39 | $i = 0; 40 | foreach ($csvData as $line) { 41 | $this_data = array(); 42 | if (empty($column_list)) { 43 | $this_column_list = array(); 44 | $line_count = 0; 45 | foreach ($line as $line_v) { 46 | $this_column_list[] = $line_count; 47 | $line_count++; 48 | } 49 | } else { 50 | $this_column_list = $column_list; 51 | } 52 | foreach ($this_column_list as $k => $v) { 53 | if (isset($line[$k])) { 54 | //先頭と末尾の"を削除 55 | $b = $line[$k]; 56 | //カラムの数だけセット 57 | $this_data = Hash::merge( 58 | $this_data, 59 | array($v => $b) 60 | ); 61 | } else { 62 | $this_data = Hash::merge( 63 | $this_data, 64 | array($v => '') 65 | ); 66 | } 67 | } 68 | 69 | $data[$i] = $this_data; 70 | $i++; 71 | } 72 | } catch (\Exception $e) { 73 | return false; 74 | } 75 | 76 | return $data; 77 | } 78 | 79 | /** 80 | * fgetcsv_reg 81 | * 82 | * this is a port of the original code written by yossy. 83 | * 84 | * @author yossy 85 | * @author hagiwara 86 | * 87 | * @param resource $handle 88 | * @param integer $length 89 | * @param string $d 90 | * @param string $e 91 | * @see http://yossy.iimp.jp/wp/?p=56 92 | * @return array 93 | */ 94 | private function fgetcsv_reg (&$handle, $length = null, $d = ',', $e = '"') 95 | { 96 | $d = preg_quote($d); 97 | $e = preg_quote($e); 98 | $_line = ""; 99 | $eof = false; // Added for PHP Warning. 100 | while ( $eof != true ) { 101 | $_line .= (empty($length) ? fgets($handle) : fgets($handle, $length)); 102 | $itemcnt = preg_match_all('/'.$e.'/', $_line, $dummy); 103 | if ($itemcnt % 2 == 0) $eof = true; 104 | } 105 | $_csv_line = preg_replace('/(?:\\r\\n|[\\r\\n])?$/', $d, trim($_line)); 106 | $_csv_pattern = '/('.$e.'[^'.$e.']*(?:'.$e.$e.'[^'.$e.']*)*'.$e.'|[^'.$d.']*)'.$d.'/'; 107 | 108 | preg_match_all($_csv_pattern, $_csv_line, $_csv_matches); 109 | 110 | $_csv_data = $_csv_matches[1]; 111 | 112 | for ( $_csv_i=0; $_csv_i "\r\n", 11 | 'directory' => TMP, 12 | 'array_encoding' => 'UTF-8', 13 | 'import_encoding' => 'SJIS-win', 14 | 'extra_fixed_options' => [] 15 | ]; 16 | 17 | /* 18 | * loadData 固定長読み込みアクション 19 | * 20 | * @param string $fileName 固定長テキストファイ 21 | * @param array $column_list 各カラム情報(name:カラム名,length:バイト数) 22 | * @param array $options 下記パラメータを必要に応じて設定 23 | * line_feed_code 改行コード(デフォルトは\r\n) 24 | * array_encoding 出力するする配列のエンコード(デフォルトはUTF-8 25 | * import_encoding 入力するテキストのエンコード(デフォルトはSJIS-win 26 | * extra_fixed_options 出力のための固定長の設定(列によって桁数が異なる場合の設定) 27 | */ 28 | public function loadData($fileName, $fixed_options, $options = []) 29 | { 30 | $options = array_merge($this->_defaultOptions,$options); 31 | extract($options); 32 | 33 | $fp = fopen($fileName,'r'); 34 | $data = fread($fp, filesize($fileName)); 35 | fclose($fp); 36 | 37 | return $this->loadDataBody($data, $fixed_options, $options); 38 | } 39 | 40 | /* 41 | * loadDataBody 固定長内容読み込みアクション 42 | * 43 | * @param string $data 固定長テキストデータ 44 | * @param array $column_list 各カラム情報(name:カラム名,length:バイト数) 45 | * @param array $options 下記パラメータを必要に応じて設定 46 | * line_feed_code 改行コード(デフォルトは\r\n) 47 | * array_encoding 出力するする配列のエンコード(デフォルトはUTF-8 48 | * import_encoding 入力するテキストのエンコード(デフォルトはSJIS-win 49 | * extra_fixed_options 出力のための固定長の設定(列によって桁数が異なる場合の設定) 50 | */ 51 | public function loadDataBody($data, $fixed_options, $options = []) 52 | { 53 | $options = array_merge($this->_defaultOptions,$options); 54 | extract($options); 55 | 56 | $return_info = []; 57 | //まずは分割 58 | $data_explode = explode($line_feed_code, $data); 59 | $list_count = count($data_explode); 60 | foreach ($data_explode as $row => $text) { 61 | //空行は無視 62 | if (strlen($text) === 0) { 63 | continue; 64 | } 65 | $start_point = 0; 66 | $column_list = $fixed_options; 67 | if (array_key_exists($row + 1, $extra_fixed_options)) { 68 | $column_list = $extra_fixed_options[$row + 1]; 69 | } elseif (array_key_exists($row - $list_count + 1, $extra_fixed_options)) { 70 | $column_list = $extra_fixed_options[$row - $list_count + 1]; 71 | } 72 | 73 | foreach ($column_list as $column_info) { 74 | $return_info[$row][$column_info['name']] = rtrim(substr($text, $start_point, $column_info['length'])); 75 | $start_point += $column_info['length']; 76 | } 77 | } 78 | 79 | //最後にまとめて文字コードを変換 80 | mb_convert_variables($array_encoding, $import_encoding, $return_info); 81 | 82 | return $return_info; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/LargeExport/LargeCsvExport.php: -------------------------------------------------------------------------------- 1 | ',', 19 | 'export_encoding' => 'SJIS-win', 20 | 'array_encoding' => 'UTF-8', 21 | 'write_span' => 1, 22 | ]; 23 | private $settings; 24 | private $tmpRowText = ''; 25 | private $rowCount = 0; 26 | 27 | /** 28 | * __construct 29 | * 30 | */ 31 | public function __construct($settings = []) 32 | { 33 | // 設定値 34 | $this->settings = array_merge( 35 | $this->defaultSettings, 36 | $settings 37 | ); 38 | $this->tmpCsvFp = new File($this->getTmpFileName()); 39 | } 40 | 41 | /** 42 | * getTmpFileName 43 | * 一時ファイルの取得 44 | */ 45 | private function getTmpFileName() 46 | { 47 | $tmpFileName = TMP . 'csv_file_' . Security::hash(time() . rand()); 48 | // ファイルが存在した場合は何もしない 49 | if (file_exists($tmpFileName)) { 50 | return $this->getTmpFileName(); 51 | } 52 | return $tmpFileName; 53 | } 54 | 55 | /** 56 | * addRow 57 | * 都度都度ファイルに追記をしていく 58 | */ 59 | public function addRow($lists) 60 | { 61 | if (!is_array($lists)) { 62 | throw new MethodNotAllowedException('$list must be array.'); 63 | } 64 | $this->rowCount++; 65 | $csvRow = $this->parseCsv($lists); 66 | $this->tmpRowText .= $csvRow; 67 | if ($this->rowCount % $this->settings['write_span'] == 0) { 68 | $this->writeRow(); 69 | } 70 | } 71 | 72 | /** 73 | * writeRow 74 | * 行の書き込み 75 | */ 76 | private function writeRow() 77 | { 78 | $this->tmpCsvFp->write($this->tmpRowText, 'a'); 79 | // 一時的なテキストの初期化 80 | $this->tmpRowText = ''; 81 | } 82 | 83 | /** 84 | * read 85 | * csvテキストデータの読み込み(及び一時ファイルの削除) 86 | */ 87 | public function read() 88 | { 89 | // 書き込みの残りがあれば書き込む 90 | $this->writeRow(); 91 | 92 | $csvText = $this->tmpCsvFp->read(); 93 | // ファイル削除 94 | $this->tmpCsvFp->delete(); 95 | $this->tmpCsvFp->close(); 96 | return $csvText; 97 | } 98 | 99 | /** 100 | * getPath 101 | * ファイルパスの取得。readメソッドでメモリで落ちる場合はパスを取得してrequest->fileでDLする 102 | */ 103 | public function getPath() 104 | { 105 | // 書き込みの残りがあれば書き込む 106 | $this->writeRow(); 107 | return $this->tmpCsvFp->pwd(); 108 | } 109 | 110 | /* 111 | * _parseCsv 112 | * csv(など)の形式に変更 113 | * 114 | * @param array $lists 変換する値 115 | * @param string 1行分のCSVテキストデータ 116 | */ 117 | private function parseCsv($lists) 118 | { 119 | // 文字コードの変換 120 | mb_convert_variables($this->settings['export_encoding'], $this->settings['array_encoding'], $lists); 121 | foreach ($lists as $listKey => $list) { 122 | //区切り文字・改行・ダブルクオートの時 123 | if (preg_match('/[' . $this->settings['delimiter'] . '\\n"]/', $list)) { 124 | $list = str_replace('"', '""', $list); 125 | $lists[$listKey] = '"' . $list . '"'; 126 | } 127 | } 128 | // カンマ区切り 129 | return implode($this->settings['delimiter'], $lists) . "\r\n"; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /tests/TestCase/Controller/Component/CsvExportComponentTest.php: -------------------------------------------------------------------------------- 1 | Controller = new Controller(); 28 | $this->ComponentRegistry = new ComponentRegistry($this->Controller); 29 | $this->CsvExport = new CsvExportComponent($this->ComponentRegistry); 30 | 31 | $this->test1_csv_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/test_app/test1.csv'; 32 | $this->test2_csv_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/test_app/test2.csv'; 33 | } 34 | 35 | /** 36 | * tearDown method 37 | * 38 | * @return void 39 | */ 40 | public function tearDown() 41 | { 42 | unset($this->CsvExport); 43 | 44 | parent::tearDown(); 45 | 46 | //不要なファイルを削除する 47 | unlink($this->test2_csv_path); 48 | } 49 | 50 | /** 51 | * Test initialize method 52 | * 53 | * @return void 54 | */ 55 | public function test_make() 56 | { 57 | $test2_csv_path_pathinfo = pathinfo($this->test2_csv_path); 58 | //CSV1と同じ内容を作成 59 | $lists = [ 60 | [ 61 | '1', 62 | '2', 63 | '3', 64 | ], 65 | [ 66 | 'あ', 67 | 'い', 68 | 'う', 69 | ], 70 | [ 71 | '"hoge', 72 | "\r\n", 73 | '', 74 | ], 75 | ]; 76 | 77 | 78 | $this->CsvExport->make($lists, $test2_csv_path_pathinfo['basename'], ',', $test2_csv_path_pathinfo['dirname'] . '/'); 79 | 80 | $csv1_fp = fopen($this->test1_csv_path ,'r'); 81 | $csv1 = fread($csv1_fp, filesize($this->test1_csv_path)); 82 | fclose($csv1_fp); 83 | 84 | $csv2_fp = fopen($this->test2_csv_path ,'r'); 85 | $csv2 = fread($csv2_fp, filesize($this->test2_csv_path)); 86 | fclose($csv2_fp); 87 | 88 | //同じ内容で作成ができているかを確認 89 | $this->assertEquals($csv1, $csv2); 90 | 91 | 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /tests/TestCase/Controller/Component/FixedLengthComponentTest.php: -------------------------------------------------------------------------------- 1 | Controller = new Controller(); 28 | $this->ComponentRegistry = new ComponentRegistry($this->Controller); 29 | $this->FixedLengthExport = new FixedLengthExportComponent($this->ComponentRegistry); 30 | 31 | $this->test1_fixed_length_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/test_app/test1.txt'; 32 | $this->test2_fixed_length_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/test_app/test2.txt'; 33 | } 34 | 35 | /** 36 | * tearDown method 37 | * 38 | * @return void 39 | */ 40 | public function tearDown() 41 | { 42 | unset($this->FixedLengthExport); 43 | 44 | parent::tearDown(); 45 | 46 | //不要なファイルを削除する 47 | unlink($this->test2_fixed_length_path); 48 | } 49 | 50 | /** 51 | * Test initialize method 52 | * 53 | * @return void 54 | */ 55 | public function test_make() 56 | { 57 | $test2_fixed_length_path_pathinfo = pathinfo($this->test2_fixed_length_path); 58 | //CSV1と同じ内容を作成 59 | $list = [ 60 | [ 61 | 'あいう', 62 | 'いいい', 63 | 'uu', 64 | 'u', 65 | ], 66 | [ 67 | 'いうえ', 68 | 'ううう', 69 | 'eee', 70 | ], 71 | [ 72 | 'ab', 73 | 'cde', 74 | 'fggf', 75 | 'おお', 76 | ], 77 | ]; 78 | $fixed_options = [ 79 | ['length' => 8, 'type' => 'text'], 80 | ['length' => 10, 'type' => 'text'], 81 | ['length' => 6, 'type' => 'text'], 82 | ]; 83 | $header_options = [ 84 | ['length' => 8, 'type' => 'text'], 85 | ['length' => 10, 'type' => 'text'], 86 | ['length' => 2, 'type' => 'text'], 87 | ['length' => 4, 'type' => 'text'], 88 | ]; 89 | $footer_options = [ 90 | ['length' => 2, 'type' => 'text'], 91 | ['length' => 6, 'type' => 'text'], 92 | ['length' => 10, 'type' => 'text'], 93 | ['length' => 6, 'type' => 'text'], 94 | ]; 95 | $options = [ 96 | 'file_name' => $test2_fixed_length_path_pathinfo['basename'], 97 | 'directory' => $test2_fixed_length_path_pathinfo['dirname'] . '/', 98 | 'extra_fixed_options' => [ 99 | 1 => $header_options, 100 | -1 => $footer_options, 101 | ] 102 | ]; 103 | $this->FixedLengthExport->make($list, $fixed_options, $options); 104 | 105 | $fixed_length1_fp = fopen($this->test1_fixed_length_path ,'r'); 106 | $fixed_length1 = fread($fixed_length1_fp, filesize($this->test1_fixed_length_path)); 107 | fclose($fixed_length1_fp); 108 | 109 | $fixed_length2_fp = fopen($this->test2_fixed_length_path ,'r'); 110 | $fixed_length2 = fread($fixed_length2_fp, filesize($this->test2_fixed_length_path)); 111 | fclose($fixed_length2_fp); 112 | 113 | //同じ内容で作成ができているかを確認 114 | $this->assertEquals($fixed_length1, $fixed_length2); 115 | 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /tests/TestCase/Form/CsvImportFormTest.php: -------------------------------------------------------------------------------- 1 | Form = new CsvImportForm(); 22 | } 23 | 24 | /** 25 | * tearDown method 26 | * 27 | * @return void 28 | */ 29 | public function tearDown() 30 | { 31 | unset($this->Form); 32 | 33 | parent::tearDown(); 34 | } 35 | 36 | /** 37 | * Test initialize method 38 | * 39 | * @return void 40 | */ 41 | public function test_loadCsv() 42 | { 43 | $test1_csv_path = dirname(dirname(dirname(__FILE__))) . '/test_app/test1.csv'; 44 | $column = [ 45 | 'column1', 46 | 'column2', 47 | 'column3', 48 | ]; 49 | $csvData = $this->Form->loadDataCsv($test1_csv_path, $column); 50 | //テストファイル 51 | //1行目 52 | $result1 = [ 53 | 'column1' => '1', 54 | 'column2' => '2', 55 | 'column3' => '3' 56 | ]; 57 | $this->assertTrue( 58 | $csvData[0] === $result1 59 | ); 60 | 61 | //2行目 62 | $result2 = [ 63 | 'column1' => 'あ', 64 | 'column2' => 'い', 65 | 'column3' => 'う' 66 | ]; 67 | $this->assertTrue( 68 | $csvData[1] === $result2 69 | ); 70 | 71 | //3行目 72 | $result3 = [ 73 | 'column1' => '"hoge', 74 | 'column2' => "\r\n", 75 | 'column3' => '' 76 | ]; 77 | $this->assertTrue( 78 | $csvData[2] === $result3 79 | ); 80 | } 81 | 82 | /** 83 | * Test initialize method 84 | * 85 | * @return void 86 | */ 87 | public function test_loadCsvEmpty() 88 | { 89 | $test1_csv_path = dirname(dirname(dirname(__FILE__))) . '/test_app/test1.csv'; 90 | $column = [ 91 | 'column1', 92 | 'column2', 93 | 'column3', 94 | ]; 95 | $csvData = $this->Form->loadDataCsv($test1_csv_path); 96 | //テストファイル 97 | //1行目 98 | $result1 = [ 99 | '1', 100 | '2', 101 | '3' 102 | ]; 103 | $this->assertTrue( 104 | $csvData[0] === $result1 105 | ); 106 | 107 | //2行目 108 | $result2 = [ 109 | 'あ', 110 | 'い', 111 | 'う' 112 | ]; 113 | $this->assertTrue( 114 | $csvData[1] === $result2 115 | ); 116 | 117 | //3行目 118 | $result3 = [ 119 | '"hoge', 120 | "\r\n", 121 | '' 122 | ]; 123 | $this->assertTrue( 124 | $csvData[2] === $result3 125 | ); 126 | } 127 | 128 | 129 | } 130 | -------------------------------------------------------------------------------- /tests/TestCase/Form/FixedLengthImportFormTest.php: -------------------------------------------------------------------------------- 1 | Form = new FixedLengthImportForm(); 22 | } 23 | 24 | /** 25 | * tearDown method 26 | * 27 | * @return void 28 | */ 29 | public function tearDown() 30 | { 31 | unset($this->Form); 32 | 33 | parent::tearDown(); 34 | } 35 | 36 | /** 37 | * Test initialize method 38 | * 39 | * @return void 40 | */ 41 | public function test_loadCsv() 42 | { 43 | $test1_fixed_length_path = dirname(dirname(dirname(__FILE__))) . '/test_app/test1.txt'; 44 | $column_list = [ 45 | ['name' => 'column1', 'length' => 8], 46 | ['name' => 'column2', 'length' => 10], 47 | ['name' => 'column3', 'length' => 6], 48 | ]; 49 | $extra_list = [ 50 | //ヘッダー 51 | 1 => [ 52 | ['name' => 'columna', 'length' => 4], 53 | ['name' => 'columnb', 'length' => 8], 54 | ['name' => 'columnc', 'length' => 12], 55 | ], 56 | //フッター 57 | -1 => [ 58 | ['name' => 'columnx', 'length' => 2], 59 | ['name' => 'columny', 'length' => 12], 60 | ['name' => 'columnz', 'length' => 10], 61 | ] 62 | ]; 63 | $options = ['extra_fixed_options' => $extra_list]; 64 | $fixedLengthData = $this->Form->loadData($test1_fixed_length_path, $column_list, $options); 65 | //テストファイル 66 | //1行目 67 | $result1 = [ 68 | 'columna' => 'あい', 69 | 'columnb' => 'う いい', 70 | 'columnc' => 'い uuu' 71 | ]; 72 | $this->assertTrue( 73 | $fixedLengthData[0] === $result1 74 | ); 75 | 76 | //2行目 77 | $result2 = [ 78 | 'column1' => 'いうえ', 79 | 'column2' => 'ううう', 80 | 'column3' => 'eee' 81 | ]; 82 | $this->assertTrue( 83 | $fixedLengthData[1] === $result2 84 | ); 85 | 86 | //3行目 87 | $result3 = [ 88 | 'columnx' => 'ab', 89 | 'columny' => 'cde fggf', 90 | 'columnz' => ' おお' 91 | ]; 92 | $this->assertTrue( 93 | $fixedLengthData[2] === $result3 94 | ); 95 | } 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'App']); 51 | Cake\Core\Configure::write('debug', 2); 52 | 53 | $Tmp = new \Cake\Filesystem\Folder(TMP); 54 | $Tmp->create(TMP . 'cache/models', 0777); 55 | $Tmp->create(TMP . 'cache/persistent', 0777); 56 | $Tmp->create(TMP . 'cache/views', 0777); 57 | 58 | $cache = [ 59 | 'default' => [ 60 | 'engine' => 'File', 61 | 'path' => CACHE 62 | ], 63 | '_cake_core_' => [ 64 | 'className' => 'File', 65 | 'prefix' => 'search_myapp_cake_core_', 66 | 'path' => CACHE . 'persistent/', 67 | 'serialize' => true, 68 | 'duration' => '+10 seconds' 69 | ], 70 | '_cake_model_' => [ 71 | 'className' => 'File', 72 | 'prefix' => 'search_my_app_cake_model_', 73 | 'path' => CACHE . 'models/', 74 | 'serialize' => 'File', 75 | 'duration' => '+10 seconds' 76 | ] 77 | ]; 78 | 79 | Cake\Cache\Cache::config($cache); 80 | 81 | // Ensure default test connection is defined -------------------------------------------------------------------------------- /tests/test_app/test1.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satthi/csv-combine-plugin-for-CakePHP/0297f8607d94f2975a25f9c435ce795662763b72/tests/test_app/test1.csv -------------------------------------------------------------------------------- /tests/test_app/test1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satthi/csv-combine-plugin-for-CakePHP/0297f8607d94f2975a25f9c435ce795662763b72/tests/test_app/test1.txt --------------------------------------------------------------------------------