source: branches/version-2_13-dev/data/class/pages/admin/products/LC_Page_Admin_Products_UploadCSVCategory.php @ 22856

Revision 22856, 21.5 KB checked in by Seasoft, 11 years ago (diff)

#2043 (typo修正・ソース整形・ソースコメントの改善 for 2.13.0)

  • 主に空白・空白行の調整。もう少し整えたいが、一旦現状コミット。
  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2/*
3 * This file is part of EC-CUBE
4 *
5 * Copyright(c) 2000-2013 LOCKON CO.,LTD. All Rights Reserved.
6 *
7 * http://www.lockon.co.jp/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 */
23
24require_once CLASS_EX_REALDIR . 'page_extends/admin/LC_Page_Admin_Ex.php';
25
26/**
27 * カテゴリ登録CSVのページクラス
28 *
29 * LC_Page_Admin_Products_UploadCSV をカスタマイズする場合はこのクラスを編集する.
30 *
31 * @package Page
32 * @author LOCKON CO.,LTD.
33 * @version $$Id$$
34 */
35class LC_Page_Admin_Products_UploadCSVCategory extends LC_Page_Admin_Ex
36{
37    /** エラー情報 **/
38    var $arrErr;
39
40    /** 表示用項目 **/
41    var $arrTitle;
42
43    /** 結果行情報 **/
44    var $arrRowResult;
45
46    /** エラー行情報 **/
47    var $arrRowErr;
48
49    /** TAGエラーチェックフィールド情報 */
50    var $arrTagCheckItem;
51
52    /** テーブルカラム情報 (登録処理用) **/
53    var $arrRegistColumn;
54
55    /** 登録フォームカラム情報 **/
56    var $arrFormKeyList;
57
58
59    /**
60     * Page を初期化する.
61     *
62     * @return void
63     */
64    function init()
65    {
66        parent::init();
67        $this->tpl_mainpage = 'products/upload_csv_category.tpl';
68        $this->tpl_mainno   = 'products';
69        $this->tpl_subno    = 'upload_csv_category';
70        $this->tpl_maintitle = '商品管理';
71        $this->tpl_subtitle = 'カテゴリ登録CSV';
72        $this->csv_id = '5';
73
74        $masterData = new SC_DB_MasterData_Ex();
75        $this->arrAllowedTag = $masterData->getMasterData('mtb_allowed_tag');
76        $this->arrTagCheckItem = array();
77    }
78
79    /**
80     * Page のプロセス.
81     *
82     * @return void
83     */
84    function process()
85    {
86        $this->action();
87        $this->sendResponse();
88    }
89
90    /**
91     * Page のアクション.
92     *
93     * @return void
94     */
95    function action()
96    {
97        // CSV管理ヘルパー
98        $objCSV = new SC_Helper_CSV_Ex();
99        // CSV構造読み込み
100        $arrCSVFrame = $objCSV->sfGetCsvOutput($this->csv_id);
101
102        // CSV構造がインポート可能かのチェック
103        if (!$objCSV->sfIsImportCSVFrame($arrCSVFrame)) {
104            // 無効なフォーマットなので初期状態に強制変更
105            $arrCSVFrame = $objCSV->sfGetCsvOutput($this->csv_id, '', array(), 'no');
106            $this->tpl_is_format_default = true;
107        }
108        // CSV構造は更新可能なフォーマットかのフラグ取得
109        $this->tpl_is_update = $objCSV->sfIsUpdateCSVFrame($arrCSVFrame);
110
111        // CSVファイルアップロード情報の初期化
112        $objUpFile = new SC_UploadFile_Ex(IMAGE_TEMP_REALDIR, IMAGE_SAVE_REALDIR);
113        $this->lfInitFile($objUpFile);
114
115        // パラメーター情報の初期化
116        $objFormParam = new SC_FormParam_Ex();
117        $this->lfInitParam($objFormParam, $arrCSVFrame);
118
119        $this->max_upload_csv_size = SC_Utils_Ex::getUnitDataSize(CSV_SIZE);
120
121        $objFormParam->setHtmlDispNameArray();
122        $this->arrTitle = $objFormParam->getHtmlDispNameArray();
123
124        switch ($this->getMode()) {
125            case 'csv_upload':
126                $this->doUploadCsv($objFormParam, $objUpFile);
127                break;
128            default:
129                break;
130        }
131
132    }
133
134    /**
135     * 登録/編集結果のメッセージをプロパティへ追加する
136     *
137     * @param integer $line_count 行数
138     * @param stirng $message メッセージ
139     * @return void
140     */
141    function addRowResult($line_count, $message)
142    {
143        $this->arrRowResult[] = $line_count . '行目:' . $message;
144    }
145
146    /**
147     * 登録/編集結果のエラーメッセージをプロパティへ追加する
148     *
149     * @param integer $line_count 行数
150     * @param stirng $message メッセージ
151     * @return void
152     */
153    function addRowErr($line_count, $message)
154    {
155        $this->arrRowErr[] = $line_count . '行目:' . $message;
156    }
157
158    /**
159     * CSVアップロードを実行する
160     *
161     * @param SC_FormParam  $objFormParam
162     * @param SC_UploadFile $objUpFile
163     * @param SC_Helper_DB  $objDb
164     * @return void
165     */
166    function doUploadCsv(&$objFormParam, &$objUpFile)
167    {
168        // ファイルアップロードのチェック
169        $objUpFile->makeTempFile('csv_file');
170        $arrErr = $objUpFile->checkExists();
171        if (count($arrErr) > 0) {
172            $this->arrErr = $arrErr;
173            return;
174        }
175        // 一時ファイル名の取得
176        $filepath = $objUpFile->getTempFilePath('csv_file');
177        // CSVファイルの文字コード変換
178        $enc_filepath = SC_Utils_Ex::sfEncodeFile($filepath, CHAR_CODE, CSV_TEMP_REALDIR);
179        // CSVファイルのオープン
180        $fp = fopen($enc_filepath, 'r');
181        // 失敗した場合はエラー表示
182        if (!$fp) {
183            SC_Utils_Ex::sfDispError('');
184        }
185
186        // 登録先テーブル カラム情報の初期化
187        $this->lfInitTableInfo();
188
189        // 登録フォーム カラム情報
190        $this->arrFormKeyList = $objFormParam->getKeyList();
191
192        // 登録対象の列数
193        $col_max_count = $objFormParam->getCount();
194        // 行数
195        $line_count = 0;
196
197        $objQuery =& SC_Query_Ex::getSingletonInstance();
198        $objQuery->begin();
199
200        $errFlag = false;
201
202        while (!feof($fp)) {
203            $arrCSV = fgetcsv($fp, CSV_LINE_MAX);
204            // 行カウント
205            $line_count++;
206            // ヘッダ行はスキップ
207            if ($line_count == 1) {
208                continue;
209            }
210            // 空行はスキップ
211            if (empty($arrCSV)) {
212                continue;
213            }
214            // 列数が異なる場合はエラー
215            $col_count = count($arrCSV);
216            if ($col_max_count != $col_count) {
217                $this->addRowErr($line_count, '※ 項目数が' . $col_count . '個検出されました。項目数は' . $col_max_count . '個になります。');
218                $errFlag = true;
219                break;
220            }
221            // シーケンス配列を格納する。
222            $objFormParam->setParam($arrCSV, true);
223            // 入力値の変換
224            $objFormParam->convParam();
225            // <br>なしでエラー取得する。
226            $arrCSVErr = $this->lfCheckError($objFormParam);
227
228            // 入力エラーチェック
229            if (count($arrCSVErr) > 0) {
230                foreach ($arrCSVErr as $err) {
231                    $this->addRowErr($line_count, $err);
232                }
233                $errFlag = true;
234                break;
235            }
236
237            $category_id = $this->lfRegistCategory($objQuery, $line_count, $objFormParam);
238            $this->addRowResult($line_count, 'カテゴリID:'.$category_id . ' / カテゴリ名:' . $objFormParam->getValue('category_name'));
239        }
240
241        // 実行結果画面を表示
242        $this->tpl_mainpage = 'products/upload_csv_category_complete.tpl';
243
244        fclose($fp);
245
246        if ($errFlag) {
247            $objQuery->rollback();
248            return;
249        }
250
251        $objQuery->commit();
252
253        // カテゴリ件数を更新
254        SC_Helper_DB_Ex::sfCountCategory($objQuery);
255
256        return;
257    }
258
259    /**
260     * デストラクタ.
261     *
262     * @return void
263     */
264    function destroy()
265    {
266        parent::destroy();
267    }
268
269    /**
270     * ファイル情報の初期化を行う.
271     *
272     * @return void
273     */
274    function lfInitFile(&$objUpFile)
275    {
276        $objUpFile->addFile('CSVファイル', 'csv_file', array('csv'), CSV_SIZE, true, 0, 0, false);
277    }
278
279    /**
280     * 入力情報の初期化を行う.
281     *
282     * @param array CSV構造設定配列
283     * @return void
284     */
285    function lfInitParam(&$objFormParam, &$arrCSVFrame)
286    {
287        // 固有の初期値調整
288        $arrCSVFrame = $this->lfSetParamDefaultValue($arrCSVFrame);
289        // CSV項目毎の処理
290        foreach ($arrCSVFrame as $item) {
291            if ($item['status'] == CSV_COLUMN_STATUS_FLG_DISABLE) continue;
292            //サブクエリ構造の場合は AS名 を使用
293            if (preg_match_all('/\(.+\) as (.+)$/i', $item['col'], $match, PREG_SET_ORDER)) {
294                $col = $match[0][1];
295            } else {
296                $col = $item['col'];
297            }
298            // HTML_TAG_CHECKは別途実行なので除去し、別保存しておく
299            if (strpos(strtoupper($item['error_check_types']), 'HTML_TAG_CHECK') !== FALSE) {
300                $this->arrTagCheckItem[] = $item;
301                $error_check_types = str_replace('HTML_TAG_CHECK', '', $item['error_check_types']);
302            } else {
303                $error_check_types = $item['error_check_types'];
304            }
305            $arrErrorCheckTypes = explode(',', $error_check_types);
306            foreach ($arrErrorCheckTypes as $key => $val) {
307                if (trim($val) == '') {
308                    unset($arrErrorCheckTypes[$key]);
309                } else {
310                    $arrErrorCheckTypes[$key] = trim($val);
311                }
312            }
313            // パラメーター登録
314            $objFormParam->addParam(
315                    $item['disp_name']
316                    , $col
317                    , constant($item['size_const_type'])
318                    , $item['mb_convert_kana_option']
319                    , $arrErrorCheckTypes
320                    , $item['default']
321                    , ($item['rw_flg'] != CSV_COLUMN_RW_FLG_READ_ONLY) ? true : false
322                    );
323        }
324    }
325
326    /**
327     * 入力チェックを行う.
328     *
329     * @return void
330     */
331    function lfCheckError(&$objFormParam)
332    {
333        // 入力データを渡す。
334        $arrRet =  $objFormParam->getHashArray();
335        $objErr = new SC_CheckError_Ex($arrRet);
336        $objErr->arrErr = $objFormParam->checkError(false);
337        // HTMLタグチェックの実行
338        foreach ($this->arrTagCheckItem as $item) {
339            $objErr->doFunc(array($item['disp_name'], $item['col'], $this->arrAllowedTag), array('HTML_TAG_CHECK'));
340        }
341        // このフォーム特有の複雑系のエラーチェックを行う
342        if (count($objErr->arrErr) == 0) {
343            $objErr->arrErr = $this->lfCheckErrorDetail($arrRet, $objErr->arrErr);
344        }
345
346        return $objErr->arrErr;
347    }
348
349    /**
350     * 保存先テーブル情報の初期化を行う.
351     *
352     * @return void
353     */
354    function lfInitTableInfo()
355    {
356        $objQuery =& SC_Query_Ex::getSingletonInstance();
357        $this->arrRegistColumn = $objQuery->listTableFields('dtb_category');
358    }
359
360    /**
361     * カテゴリ登録を行う.
362     *
363     * FIXME: 登録の実処理自体は、LC_Page_Admin_Products_Categoryと共通化して欲しい。
364     *
365     * @param SC_Query $objQuery SC_Queryインスタンス
366     * @param string|integer $line 処理中の行数
367     * @return integer カテゴリID
368     */
369    function lfRegistCategory($objQuery, $line, &$objFormParam)
370    {
371        // 登録データ対象取得
372        $arrList = $objFormParam->getHashArray();
373        // 登録時間を生成(DBのCURRENT_TIMESTAMPだとcommitした際、すべて同一の時間になってしまう)
374        $arrList['update_date'] = $this->lfGetDbFormatTimeWithLine($line);
375
376        // 登録情報を生成する。
377        // テーブルのカラムに存在しているもののうち、Form投入設定されていないデータは上書きしない。
378        $sqlval = SC_Utils_Ex::sfArrayIntersectKeys($arrList, $this->arrRegistColumn);
379
380        // 必須入力では無い項目だが、空文字では問題のある特殊なカラム値の初期値設定
381        $sqlval = $this->lfSetCategoryDefaultData($sqlval);
382
383        if ($sqlval['category_id'] != '') {
384            // 同じidが存在すればupdate存在しなければinsert
385            $where = 'category_id = ?';
386            $category_exists = $objQuery->exists('dtb_category', $where, array($sqlval['category_id']));
387            if ($category_exists) {
388                // UPDATEの実行
389                $where = 'category_id = ?';
390                $objQuery->update('dtb_category', $sqlval, $where, array($sqlval['category_id']));
391            } else {
392                $sqlval['create_date'] = $arrList['update_date'];
393                // 新規登録
394                $category_id = $this->registerCategory($sqlval['parent_category_id'],
395                                        $sqlval['category_name'],
396                                        $_SESSION['member_id'],
397                                        $sqlval['category_id']);
398            }
399            $category_id = $sqlval['category_id'];
400            // TODO: 削除時処理
401        } else {
402            // 新規登録
403            $category_id = $this->registerCategory($sqlval['parent_category_id'],
404                                        $sqlval['category_name'],
405                                        $_SESSION['member_id']);
406        }
407
408        return $category_id;
409    }
410
411    /**
412     * 初期値の設定
413     *
414     * @param array $arrCSVFrame CSV構造配列
415     * @return array $arrCSVFrame CSV構造配列
416     */
417    function lfSetParamDefaultValue(&$arrCSVFrame)
418    {
419        foreach ($arrCSVFrame as $key => $val) {
420            switch ($val['col']) {
421                case 'parent_category_id':
422                    $arrCSVFrame[$key]['default'] = '0';
423                    break;
424                case 'del_flg':
425                    $arrCSVFrame[$key]['default'] = '0';
426                    break;
427                default:
428                    break;
429            }
430        }
431
432        return $arrCSVFrame;
433    }
434
435    /**
436     * データ登録前に特殊な値の持ち方をする部分のデータ部分の初期値補正を行う
437     *
438     * @param array $sqlval 商品登録情報配列
439     * @return $sqlval 登録情報配列
440     */
441    function lfSetCategoryDefaultData(&$sqlval)
442    {
443        if ($sqlval['del_flg'] == '') {
444            $sqlval['del_flg'] = '0'; //有効
445        }
446        if ($sqlval['creator_id'] == '') {
447            $sqlval['creator_id'] = $_SESSION['member_id'];
448        }
449        if ($sqlval['parent_category_id'] == '') {
450            $sqlval['parent_category_id'] = (string)'0';
451        }
452
453        return $sqlval;
454    }
455
456    /**
457     * このフォーム特有の複雑な入力チェックを行う.
458     *
459     * @param array 確認対象データ
460     * @param array エラー配列
461     * @return array エラー配列
462     */
463    function lfCheckErrorDetail($item, $arrErr)
464    {
465        $objQuery =& SC_Query_Ex::getSingletonInstance();
466        /*
467        // カテゴリIDの存在チェック
468        if (!$this->lfIsDbRecord('dtb_category', 'category_id', $item)) {
469            $arrErr['category_id'] = '※ 指定のカテゴリIDは、登録されていません。';
470        }
471        */
472        // 親カテゴリIDの存在チェック
473        if (array_search('parent_category_id', $this->arrFormKeyList) !== FALSE
474            && $item['parent_category_id'] != ''
475            && $item['parent_category_id'] != '0'
476            && !SC_Helper_DB_Ex::sfIsRecord('dtb_category', 'category_id', array($item['parent_category_id']))
477        ) {
478            $arrErr['parent_category_id'] = '※ 指定の親カテゴリID(' . $item['parent_category_id'] . ')は、存在しません。';
479        }
480        // 削除フラグのチェック
481        if (array_search('del_flg', $this->arrFormKeyList) !== FALSE
482            && $item['del_flg'] != ''
483        ) {
484            if (!($item['del_flg'] == '0' or $item['del_flg'] == '1')) {
485                $arrErr['del_flg'] = '※ 削除フラグは「0」(有効)、「1」(削除)のみが有効な値です。';
486            }
487        }
488        // 重複チェック 同じカテゴリ内に同名の存在は許可されない
489        if (array_search('category_name', $this->arrFormKeyList) !== FALSE
490            && $item['category_name'] != ''
491        ) {
492            $parent_category_id = $item['parent_category_id'];
493            if ($parent_category_id == '') {
494                $parent_category_id = (string)'0';
495            }
496            $where = 'parent_category_id = ? AND category_id <> ? AND category_name = ?';
497            $exists = $objQuery->exists('dtb_category',
498                        $where,
499                        array($parent_category_id,
500                                $item['category_id'],
501                                $item['category_name']));
502            if ($exists) {
503                $arrErr['category_name'] = '※ 既に同名のカテゴリが存在します。';
504            }
505        }
506        // 登録数上限チェック
507        $where = 'del_flg = 0';
508        $count = $objQuery->count('dtb_category', $where);
509        if ($count >= CATEGORY_MAX) {
510            $item['category_name'] = '※ カテゴリの登録最大数を超えました。';
511        }
512        // 階層上限チェック
513        if (array_search('parent_category_id', $this->arrFormKeyList) !== FALSE
514                and $item['parent_category_id'] != '') {
515            $level = $objQuery->get('level', 'dtb_category', 'category_id = ?', array($parent_category_id));
516            if ($level >= LEVEL_MAX) {
517                $arrErr['parent_category_id'] = '※ ' . LEVEL_MAX . '階層以上の登録はできません。';
518            }
519        }
520
521        return $arrErr;
522    }
523
524    /**
525     * カテゴリを登録する
526     *
527     * @param integer 親カテゴリID
528     * @param string カテゴリ名
529     * @param integer 作成者のID
530     * @param integer 指定カテゴリID
531     * @return integer カテゴリID
532     */
533    function registerCategory($parent_category_id, $category_name, $creator_id, $category_id = null)
534    {
535        $objQuery =& SC_Query_Ex::getSingletonInstance();
536
537        $rank = null;
538        if ($parent_category_id == 0) {
539            // ROOT階層で最大のランクを取得する。
540            $where = 'parent_category_id = ?';
541            $rank = $objQuery->max('rank', 'dtb_category', $where, array($parent_category_id)) + 1;
542        } else {
543            // 親のランクを自分のランクとする。
544            $where = 'category_id = ?';
545            $rank = $objQuery->get('rank', 'dtb_category', $where, array($parent_category_id));
546            // 追加レコードのランク以上のレコードを一つあげる。
547            $where = 'rank >= ?';
548            $arrRawSql = array(
549                'rank' => '(rank + 1)',
550            );
551            $objQuery->update('dtb_category', array(), $where, array($rank), $arrRawSql);
552        }
553
554        $where = 'category_id = ?';
555        // 自分のレベルを取得する(親のレベル + 1)
556        $level = $objQuery->get('level', 'dtb_category', $where, array($parent_category_id)) + 1;
557
558        $arrCategory = array();
559        $arrCategory['category_name'] = $category_name;
560        $arrCategory['parent_category_id'] = $parent_category_id;
561        $arrCategory['create_date'] = 'CURRENT_TIMESTAMP';
562        $arrCategory['update_date'] = 'CURRENT_TIMESTAMP';
563        $arrCategory['creator_id']  = $creator_id;
564        $arrCategory['rank']        = $rank;
565        $arrCategory['level']       = $level;
566        //カテゴリIDが指定されていればそれを利用する
567        if (isset($category_id)) {
568            $arrCategory['category_id'] = $category_id;
569            // シーケンスの調整
570            $seq_count = $objQuery->currVal('dtb_category_category_id');
571            if ($seq_count < $arrCategory['category_id']) {
572                $objQuery->setVal('dtb_category_category_id', $arrCategory['category_id'] + 1);
573            }
574        } else {
575            $arrCategory['category_id'] = $objQuery->nextVal('dtb_category_category_id');
576        }
577        $objQuery->insert('dtb_category', $arrCategory);
578
579        return $arrCategory['category_id'];
580    }
581
582    /**
583     * 指定された行番号をmicrotimeに付与してDB保存用の時間を生成する。
584     * トランザクション内のCURRENT_TIMESTAMPは全てcommit()時の時間に統一されてしまう為。
585     *
586     * @param string $line_no 行番号
587     * @return string $time DB保存用の時間文字列
588     */
589    function lfGetDbFormatTimeWithLine($line_no = '')
590    {
591        $time = date('Y-m-d H:i:s');
592        // 秒以下を生成
593        if ($line_no != '') {
594            $microtime = sprintf('%06d', $line_no);
595            $time .= ".$microtime";
596        }
597
598        return $time;
599    }
600
601    /**
602     * 指定されたキーと値の有効性のDB確認
603     *
604     * @param string $table テーブル名
605     * @param string $keyname キー名
606     * @param array  $item 入力データ配列
607     * @return boolean true:有効なデータがある false:有効ではない
608     */
609    function lfIsDbRecord($table, $keyname, $item)
610    {
611        if (array_search($keyname, $this->arrFormKeyList) !== FALSE  //入力対象である
612            && $item[$keyname] != ''   // 空ではない
613            && !SC_Helper_DB_Ex::sfIsRecord($table, $keyname, (array)$item[$keyname]) //DBに存在するか
614        ) {
615            return false;
616        }
617
618        return true;
619    }
620
621}
Note: See TracBrowser for help on using the repository browser.