Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
25.00% |
4 / 16 |
CRAP | |
38.02% |
192 / 505 |
CsvImportController | |
0.00% |
0 / 1 |
|
25.00% |
4 / 16 |
9061.98 | |
38.02% |
192 / 505 |
csvProduct | |
0.00% |
0 / 1 |
1389.22 | |
35.43% |
62 / 175 |
|||
csvCategory | |
0.00% |
0 / 1 |
59.72 | |
55.56% |
40 / 72 |
|||
csvTemplate | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
anonymous function | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 1 |
|||
render | |
0.00% |
0 / 1 |
5.51 | |
72.73% |
8 / 11 |
|||
getImportData | |
0.00% |
0 / 1 |
6.12 | |
85.00% |
17 / 20 |
|||
createProductImage | |
0.00% |
0 / 1 |
14.72 | |
12.50% |
2 / 16 |
|||
createProductCategory | |
0.00% |
0 / 1 |
6.29 | |
80.00% |
20 / 25 |
|||
createProductTag | |
0.00% |
0 / 1 |
32.09 | |
20.00% |
4 / 20 |
|||
createProductClass | |
0.00% |
0 / 1 |
152.14 | |
48.61% |
35 / 72 |
|||
updateProductClass | |
0.00% |
0 / 1 |
1122 | |
0.00% |
0 / 73 |
|||
addErrors | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
getErrors | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
hasErrors | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getProductCsvHeader | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getCategoryCsvHeader | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
<?php | |
/* | |
* This file is part of EC-CUBE | |
* | |
* Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved. | |
* | |
* http://www.lockon.co.jp/ | |
* | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation; either version 2 | |
* of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
*/ | |
namespace Eccube\Controller\Admin\Product; | |
use Eccube\Application; | |
use Eccube\Common\Constant; | |
use Eccube\Entity\Category; | |
use Eccube\Entity\Product; | |
use Eccube\Entity\ProductCategory; | |
use Eccube\Entity\ProductClass; | |
use Eccube\Entity\ProductImage; | |
use Eccube\Entity\ProductStock; | |
use Eccube\Entity\ProductTag; | |
use Eccube\Exception\CsvImportException; | |
use Eccube\Service\CsvImportService; | |
use Eccube\Util\Str; | |
use Symfony\Component\Filesystem\Filesystem; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\StreamedResponse; | |
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | |
class CsvImportController | |
{ | |
private $errors = array(); | |
private $fileName; | |
private $em; | |
private $productTwig = 'Product/csv_product.twig'; | |
private $categoryTwig = 'Product/csv_category.twig'; | |
/** | |
* 商品登録CSVアップロード | |
*/ | |
public function csvProduct(Application $app, Request $request) | |
{ | |
$form = $app['form.factory']->createBuilder('admin_csv_import')->getForm(); | |
$headers = $this->getProductCsvHeader(); | |
if ('POST' === $request->getMethod()) { | |
$form->handleRequest($request); | |
if ($form->isValid()) { | |
$formFile = $form['import_file']->getData(); | |
if (!empty($formFile)) { | |
log_info('商品CSV登録開始'); | |
$data = $this->getImportData($app, $formFile); | |
if ($data === false) { | |
$this->addErrors('CSVのフォーマットが一致しません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
$keys = array_keys($headers); | |
$columnHeaders = $data->getColumnHeaders(); | |
if ($keys !== $columnHeaders) { | |
$this->addErrors('CSVのフォーマットが一致しません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
$size = count($data); | |
if ($size < 1) { | |
$this->addErrors('CSVデータが存在しません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
$headerSize = count($keys); | |
$this->em = $app['orm.em']; | |
$this->em->getConfiguration()->setSQLLogger(null); | |
$this->em->getConnection()->beginTransaction(); | |
$BaseInfo = $app['eccube.repository.base_info']->get(); | |
// CSVファイルの登録処理 | |
foreach ($data as $row) { | |
if ($headerSize != count($row)) { | |
$this->addErrors(($data->key() + 1) . '行目のCSVフォーマットが一致しません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
if ($row['商品ID'] == '') { | |
$Product = new Product(); | |
$this->em->persist($Product); | |
} else { | |
if (preg_match('/^\d+$/', $row['商品ID'])) { | |
$Product = $app['eccube.repository.product']->find($row['商品ID']); | |
if (!$Product) { | |
$this->addErrors(($data->key() + 1) . '行目の商品IDが存在しません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の商品IDが存在しません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
} | |
if ($row['公開ステータス(ID)'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の公開ステータス(ID)が設定されていません。'); | |
} else { | |
if (preg_match('/^\d+$/', $row['公開ステータス(ID)'])) { | |
$Disp = $app['eccube.repository.master.disp']->find($row['公開ステータス(ID)']); | |
if (!$Disp) { | |
$this->addErrors(($data->key() + 1) . '行目の公開ステータス(ID)が存在しません。'); | |
} else { | |
$Product->setStatus($Disp); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の公開ステータス(ID)が存在しません。'); | |
} | |
} | |
if (Str::isBlank($row['商品名'])) { | |
$this->addErrors(($data->key() + 1) . '行目の商品名が設定されていません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} else { | |
$Product->setName(Str::trimAll($row['商品名'])); | |
} | |
if (Str::isNotBlank($row['ショップ用メモ欄'])) { | |
$Product->setNote(Str::trimAll($row['ショップ用メモ欄'])); | |
} else { | |
$Product->setNote(null); | |
} | |
if (Str::isNotBlank($row['商品説明(一覧)'])) { | |
$Product->setDescriptionList(Str::trimAll($row['商品説明(一覧)'])); | |
} else { | |
$Product->setDescriptionList(null); | |
} | |
if (Str::isNotBlank($row['商品説明(詳細)'])) { | |
$Product->setDescriptionDetail(Str::trimAll($row['商品説明(詳細)'])); | |
} else { | |
$Product->setDescriptionDetail(null); | |
} | |
if (Str::isNotBlank($row['検索ワード'])) { | |
$Product->setSearchWord(Str::trimAll($row['検索ワード'])); | |
} else { | |
$Product->setSearchWord(null); | |
} | |
if (Str::isNotBlank($row['フリーエリア'])) { | |
$Product->setFreeArea(Str::trimAll($row['フリーエリア'])); | |
} else { | |
$Product->setFreeArea(null); | |
} | |
if ($row['商品削除フラグ'] == '') { | |
$Product->setDelFlg(Constant::DISABLED); | |
} else { | |
if ($row['商品削除フラグ'] == (string)Constant::DISABLED || $row['商品削除フラグ'] == (string)Constant::ENABLED) { | |
$Product->setDelFlg($row['商品削除フラグ']); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の商品削除フラグが設定されていません。'); | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
} | |
// 商品画像登録 | |
$this->createProductImage($row, $Product); | |
$this->em->flush($Product); | |
// 商品カテゴリ登録 | |
$this->createProductCategory($row, $Product, $app, $data); | |
//タグ登録 | |
$this->createProductTag($row, $Product, $app, $data); | |
// 商品規格が存在しなければ新規登録 | |
$ProductClasses = $Product->getProductClasses(); | |
if ($ProductClasses->count() < 1) { | |
// 規格分類1(ID)がセットされていると規格なし商品、規格あり商品を作成 | |
$ProductClassOrg = $this->createProductClass($row, $Product, $app, $data); | |
if ($BaseInfo->getOptionProductDeliveryFee() == Constant::ENABLED) { | |
if ($row['送料'] != '') { | |
$deliveryFee = str_replace(',', '', $row['送料']); | |
if (preg_match('/^\d+$/', $deliveryFee) && $deliveryFee >= 0) { | |
$ProductClassOrg->setDeliveryFee($deliveryFee); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の送料は0以上の数値を設定してください。'); | |
} | |
} | |
} | |
if ($row['規格分類1(ID)'] != '') { | |
if ($row['規格分類1(ID)'] == $row['規格分類2(ID)']) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)と規格分類2(ID)には同じ値を使用できません。'); | |
} else { | |
// 商品規格あり | |
// 企画分類あり商品を作成 | |
$ProductClass = clone $ProductClassOrg; | |
$ProductStock = clone $ProductClassOrg->getProductStock(); | |
// 規格分類1、規格分類2がnullであるデータの削除フラグを1にセット | |
$ProductClassOrg->setDelFlg(Constant::ENABLED); | |
// 規格分類1、2をそれぞれセットし作成 | |
$ClassCategory1 = null; | |
if (preg_match('/^\d+$/', $row['規格分類1(ID)'])) { | |
$ClassCategory1 = $app['eccube.repository.class_category']->find($row['規格分類1(ID)']); | |
if (!$ClassCategory1) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} else { | |
$ProductClass->setClassCategory1($ClassCategory1); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} | |
if ($row['規格分類2(ID)'] != '') { | |
if (preg_match('/^\d+$/', $row['規格分類2(ID)'])) { | |
$ClassCategory2 = $app['eccube.repository.class_category']->find($row['規格分類2(ID)']); | |
if (!$ClassCategory2) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)が存在しません。'); | |
} else { | |
if ($ClassCategory1 && | |
($ClassCategory1->getClassName()->getId() == $ClassCategory2->getClassName()->getId()) | |
) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)と規格分類2(ID)の規格名が同じです。'); | |
} else { | |
$ProductClass->setClassCategory2($ClassCategory2); | |
} | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)が存在しません。'); | |
} | |
} | |
$ProductClass->setProductStock($ProductStock); | |
$ProductStock->setProductClass($ProductClass); | |
$this->em->persist($ProductClass); | |
$this->em->persist($ProductStock); | |
} | |
} else { | |
if ($row['規格分類2(ID)'] != '') { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} | |
} | |
} else { | |
// 商品規格の更新 | |
$flag = false; | |
$classCategoryId1 = $row['規格分類1(ID)'] == '' ? null : $row['規格分類1(ID)']; | |
$classCategoryId2 = $row['規格分類2(ID)'] == '' ? null : $row['規格分類2(ID)']; | |
foreach ($ProductClasses as $pc) { | |
$classCategory1 = is_null($pc->getClassCategory1()) ? null : $pc->getClassCategory1()->getId(); | |
$classCategory2 = is_null($pc->getClassCategory2()) ? null : $pc->getClassCategory2()->getId(); | |
// 登録されている商品規格を更新 | |
if ($classCategory1 == $classCategoryId1 && | |
$classCategory2 == $classCategoryId2 | |
) { | |
$this->updateProductClass($row, $Product, $pc, $app, $data); | |
if ($BaseInfo->getOptionProductDeliveryFee() == Constant::ENABLED) { | |
if ($row['送料'] != '') { | |
$deliveryFee = str_replace(',', '', $row['送料']); | |
if (preg_match('/^\d+$/', $deliveryFee) && $deliveryFee >= 0) { | |
$pc->setDeliveryFee($deliveryFee); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の送料は0以上の数値を設定してください。'); | |
} | |
} | |
} | |
$flag = true; | |
break; | |
} | |
} | |
// 商品規格を登録 | |
if (!$flag) { | |
$pc = $ProductClasses[0]; | |
if ($pc->getClassCategory1() == null && | |
$pc->getClassCategory2() == null | |
) { | |
// 規格分類1、規格分類2がnullであるデータの削除フラグを1にセット | |
$pc->setDelFlg(Constant::ENABLED); | |
} | |
if ($row['規格分類1(ID)'] == $row['規格分類2(ID)']) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)と規格分類2(ID)には同じ値を使用できません。'); | |
} else { | |
// 必ず規格分類1がセットされている | |
// 規格分類1、2をそれぞれセットし作成 | |
$ClassCategory1 = null; | |
if (preg_match('/^\d+$/', $classCategoryId1)) { | |
$ClassCategory1 = $app['eccube.repository.class_category']->find($classCategoryId1); | |
if (!$ClassCategory1) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} | |
$ClassCategory2 = null; | |
if ($row['規格分類2(ID)'] != '') { | |
if ($pc->getClassCategory1() != null && $pc->getClassCategory2() == null) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)は設定できません。'); | |
} else { | |
if (preg_match('/^\d+$/', $classCategoryId2)) { | |
$ClassCategory2 = $app['eccube.repository.class_category']->find($classCategoryId2); | |
if (!$ClassCategory2) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)が存在しません。'); | |
} else { | |
if ($ClassCategory1 && | |
($ClassCategory1->getClassName()->getId() == $ClassCategory2->getClassName()->getId()) | |
) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)と規格分類2(ID)の規格名が同じです。'); | |
} | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)が存在しません。'); | |
} | |
} | |
} else { | |
if ($pc->getClassCategory1() != null && $pc->getClassCategory2() != null) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)に値を設定してください。'); | |
} | |
} | |
$ProductClass = $this->createProductClass($row, $Product, $app, $data, $ClassCategory1, $ClassCategory2); | |
if ($BaseInfo->getOptionProductDeliveryFee() == Constant::ENABLED) { | |
if ($row['送料'] != '') { | |
$deliveryFee = str_replace(',', '', $row['送料']); | |
if (preg_match('/^\d+$/', $deliveryFee) && $deliveryFee >= 0) { | |
$ProductClass->setDeliveryFee($deliveryFee); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の送料は0以上の数値を設定してください。'); | |
} | |
} | |
} | |
$Product->addProductClass($ProductClass); | |
} | |
} | |
} | |
if ($this->hasErrors()) { | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
$this->em->persist($Product); | |
} | |
$this->em->flush(); | |
$this->em->getConnection()->commit(); | |
log_info('商品CSV登録完了'); | |
$app->addSuccess('admin.product.csv_import.save.complete', 'admin'); | |
} | |
} | |
} | |
return $this->render($app, $form, $headers, $this->productTwig); | |
} | |
/** | |
* カテゴリ登録CSVアップロード | |
*/ | |
public function csvCategory(Application $app, Request $request) | |
{ | |
$form = $app['form.factory']->createBuilder('admin_csv_import')->getForm(); | |
$headers = $this->getCategoryCsvHeader(); | |
if ('POST' === $request->getMethod()) { | |
$form->handleRequest($request); | |
if ($form->isValid()) { | |
$formFile = $form['import_file']->getData(); | |
if (!empty($formFile)) { | |
log_info('カテゴリCSV登録開始'); | |
$data = $this->getImportData($app, $formFile); | |
if ($data === false) { | |
$this->addErrors('CSVのフォーマットが一致しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$keys = array_keys($headers); | |
$columnHeaders = $data->getColumnHeaders(); | |
if ($keys !== $columnHeaders) { | |
$this->addErrors('CSVのフォーマットが一致しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$size = count($data); | |
if ($size < 1) { | |
$this->addErrors('CSVデータが存在しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$headerSize = count($keys); | |
$this->em = $app['orm.em']; | |
$this->em->getConfiguration()->setSQLLogger(null); | |
$this->em->getConnection()->beginTransaction(); | |
// CSVファイルの登録処理 | |
foreach ($data as $row) { | |
if ($headerSize != count($row)) { | |
$this->addErrors(($data->key() + 1) . '行目のCSVフォーマットが一致しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
if ($row['カテゴリID'] == '') { | |
$Category = new Category(); | |
} else { | |
if (!preg_match('/^\d+$/', $row['カテゴリID'])) { | |
$this->addErrors(($data->key() + 1) . '行目のカテゴリIDが存在しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$Category = $app['eccube.repository.category']->find($row['カテゴリID']); | |
if (!$Category) { | |
$this->addErrors(($data->key() + 1) . '行目のカテゴリIDが存在しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
if ($row['カテゴリID'] == $row['親カテゴリID']) { | |
$this->addErrors(($data->key() + 1) . '行目のカテゴリIDと親カテゴリIDが同じです。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
} | |
if (Str::isBlank($row['カテゴリ名'])) { | |
$this->addErrors(($data->key() + 1) . '行目のカテゴリ名が設定されていません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} else { | |
$Category->setName(Str::trimAll($row['カテゴリ名'])); | |
} | |
if ($row['親カテゴリID'] != '') { | |
if (!preg_match('/^\d+$/', $row['親カテゴリID'])) { | |
$this->addErrors(($data->key() + 1) . '行目の親カテゴリIDが存在しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$ParentCategory = $app['eccube.repository.category']->find($row['親カテゴリID']); | |
if (!$ParentCategory) { | |
$this->addErrors(($data->key() + 1) . '行目の親カテゴリIDが存在しません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
} else { | |
$ParentCategory = null; | |
} | |
$Category->setParent($ParentCategory); | |
if ($ParentCategory) { | |
$Category->setLevel($ParentCategory->getLevel() + 1); | |
} else { | |
$Category->setLevel(1); | |
} | |
if ($app['config']['category_nest_level'] < $Category->getLevel()) { | |
$this->addErrors(($data->key() + 1) . '行目のカテゴリが最大レベルを超えているため設定できません。'); | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$status = $app['eccube.repository.category']->save($Category); | |
if (!$status) { | |
$this->addErrors(($data->key() + 1) . '行目のカテゴリが設定できません。'); | |
} | |
if ($this->hasErrors()) { | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
$this->em->persist($Category); | |
} | |
$this->em->flush(); | |
$this->em->getConnection()->commit(); | |
log_info('カテゴリCSV登録完了'); | |
$app->addSuccess('admin.category.csv_import.save.complete', 'admin'); | |
} | |
} | |
} | |
return $this->render($app, $form, $headers, $this->categoryTwig); | |
} | |
/** | |
* アップロード用CSV雛形ファイルダウンロード | |
*/ | |
public function csvTemplate(Application $app, Request $request, $type) | |
{ | |
set_time_limit(0); | |
$response = new StreamedResponse(); | |
if ($type == 'product') { | |
$headers = $this->getProductCsvHeader(); | |
$filename = 'product.csv'; | |
} else if ($type == 'category') { | |
$headers = $this->getCategoryCsvHeader(); | |
$filename = 'category.csv'; | |
} else { | |
throw new NotFoundHttpException(); | |
} | |
$response->setCallback(function () use ($app, $request, $headers) { | |
// ヘッダ行の出力 | |
$row = array(); | |
foreach ($headers as $key => $value) { | |
$row[] = mb_convert_encoding($key, $app['config']['csv_export_encoding'], 'UTF-8'); | |
} | |
$fp = fopen('php://output', 'w'); | |
fputcsv($fp, $row, $app['config']['csv_export_separator']); | |
fclose($fp); | |
}); | |
$response->headers->set('Content-Type', 'application/octet-stream'); | |
$response->headers->set('Content-Disposition', 'attachment; filename=' . $filename); | |
$response->send(); | |
return $response; | |
} | |
/** | |
* 登録、更新時のエラー画面表示 | |
* | |
*/ | |
protected function render($app, $form, $headers, $twig) | |
{ | |
if ($this->hasErrors()) { | |
if ($this->em) { | |
$this->em->getConnection()->rollback(); | |
} | |
} | |
if (!empty($this->fileName)) { | |
try { | |
$fs = new Filesystem(); | |
$fs->remove($app['config']['csv_temp_realdir'] . '/' . $this->fileName); | |
} catch (\Exception $e) { | |
// エラーが発生しても無視する | |
} | |
} | |
return $app->render($twig, array( | |
'form' => $form->createView(), | |
'headers' => $headers, | |
'errors' => $this->errors, | |
)); | |
} | |
/** | |
* アップロードされたCSVファイルの行ごとの処理 | |
* | |
* @param $formFile | |
* @return CsvImportService | |
*/ | |
protected function getImportData($app, $formFile) | |
{ | |
// アップロードされたCSVファイルを一時ディレクトリに保存 | |
$this->fileName = 'upload_' . Str::random() . '.' . $formFile->getClientOriginalExtension(); | |
$formFile->move($app['config']['csv_temp_realdir'], $this->fileName); | |
$file = file_get_contents($app['config']['csv_temp_realdir'] . '/' . $this->fileName); | |
if ('\\' === DIRECTORY_SEPARATOR && PHP_VERSION_ID >= 70000) { | |
// Windows 環境の PHP7 の場合はファイルエンコーディングを CP932 に合わせる | |
// see https://github.com/EC-CUBE/ec-cube/issues/1780 | |
setlocale(LC_ALL, ''); // 既定のロケールに設定 | |
if (mb_detect_encoding($file) === 'UTF-8') { // UTF-8 を検出したら SJIS-win に変換 | |
$file = mb_convert_encoding($file, 'SJIS-win', 'UTF-8'); | |
} | |
} else { | |
// アップロードされたファイルがUTF-8以外は文字コード変換を行う | |
$encode = Str::characterEncoding(substr($file, 0, 6)); | |
if ($encode != 'UTF-8') { | |
$file = mb_convert_encoding($file, 'UTF-8', $encode); | |
} | |
} | |
$file = Str::convertLineFeed($file); | |
$tmp = tmpfile(); | |
fwrite($tmp, $file); | |
rewind($tmp); | |
$meta = stream_get_meta_data($tmp); | |
$file = new \SplFileObject($meta['uri']); | |
set_time_limit(0); | |
// アップロードされたCSVファイルを行ごとに取得 | |
$data = new CsvImportService($file, $app['config']['csv_import_delimiter'], $app['config']['csv_import_enclosure']); | |
$ret = $data->setHeaderRowNumber(0); | |
return ($ret !== false) ? $data : false; | |
} | |
/** | |
* 商品画像の削除、登録 | |
*/ | |
protected function createProductImage($row, Product $Product) | |
{ | |
if ($row['商品画像'] != '') { | |
// 画像の削除 | |
$ProductImages = $Product->getProductImage(); | |
foreach ($ProductImages as $ProductImage) { | |
$Product->removeProductImage($ProductImage); | |
$this->em->remove($ProductImage); | |
} | |
// 画像の登録 | |
$images = explode(',', $row['商品画像']); | |
$rank = 1; | |
foreach ($images as $image) { | |
$ProductImage = new ProductImage(); | |
$ProductImage->setFileName(Str::trimAll($image)); | |
$ProductImage->setProduct($Product); | |
$ProductImage->setRank($rank); | |
$Product->addProductImage($ProductImage); | |
$rank++; | |
$this->em->persist($ProductImage); | |
} | |
} | |
} | |
/** | |
* 商品カテゴリの削除、登録 | |
*/ | |
protected function createProductCategory($row, Product $Product, $app, $data) | |
{ | |
// カテゴリの削除 | |
$ProductCategories = $Product->getProductCategories(); | |
foreach ($ProductCategories as $ProductCategory) { | |
$Product->removeProductCategory($ProductCategory); | |
$this->em->remove($ProductCategory); | |
$this->em->flush($ProductCategory); | |
} | |
if ($row['商品カテゴリ(ID)'] == '') { | |
// 入力されていなければ削除のみ | |
return; | |
} | |
// カテゴリの登録 | |
$categories = explode(',', $row['商品カテゴリ(ID)']); | |
$rank = 1; | |
foreach ($categories as $category) { | |
if (preg_match('/^\d+$/', $category)) { | |
$Category = $app['eccube.repository.category']->find($category); | |
if (!$Category) { | |
$this->addErrors(($data->key() + 1).'行目の商品カテゴリ(ID)「'.$category.'」が存在しません。'); | |
} else { | |
$ProductCategory = new ProductCategory(); | |
$ProductCategory->setProductId($Product->getId()); | |
$ProductCategory->setCategoryId($Category->getId()); | |
$ProductCategory->setProduct($Product); | |
$ProductCategory->setCategory($Category); | |
$ProductCategory->setRank($rank); | |
$Product->addProductCategory($ProductCategory); | |
$rank++; | |
$this->em->persist($ProductCategory); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1).'行目の商品カテゴリ(ID)「'.$category.'」が存在しません。'); | |
} | |
} | |
} | |
/** | |
* タグの登録 | |
* | |
* @param array $row | |
* @param Product $Product | |
* @param Application $app | |
* @param CsvImportService $data | |
*/ | |
protected function createProductTag($row, Product $Product, $app, $data) | |
{ | |
// タグの削除 | |
$ProductTags = $Product->getProductTag(); | |
foreach ($ProductTags as $ProductTags) { | |
$Product->removeProductTag($ProductTags); | |
$this->em->remove($ProductTags); | |
} | |
if ($row['タグ(ID)'] == '') { | |
return; | |
} | |
// タグの登録 | |
$tags = explode(',', $row['タグ(ID)']); | |
foreach ($tags as $tag_id) { | |
$Tag = null; | |
if (preg_match('/^\d+$/', $tag_id)) { | |
$Tag = $app['eccube.repository.master.tag']->find($tag_id); | |
if ($Tag) { | |
$ProductTags = new ProductTag(); | |
$ProductTags | |
->setProduct($Product) | |
->setTag($Tag); | |
$Product->addProductTag($ProductTags); | |
$this->em->persist($ProductTags); | |
} | |
} | |
if (!$Tag) { | |
$this->addErrors(($data->key() + 1) . '行目のタグ(ID)「' . $tag_id . '」が存在しません。'); | |
} | |
} | |
} | |
/** | |
* 商品規格分類1、商品規格分類2がnullとなる商品規格情報を作成 | |
*/ | |
protected function createProductClass($row, Product $Product, $app, $data, $ClassCategory1 = null, $ClassCategory2 = null) | |
{ | |
// 規格分類1、規格分類2がnullとなる商品を作成 | |
$ProductClass = new ProductClass(); | |
$ProductClass->setProduct($Product); | |
if ($row['商品種別(ID)'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の商品種別(ID)が設定されていません。'); | |
} else { | |
if (preg_match('/^\d+$/', $row['商品種別(ID)'])) { | |
$ProductType = $app['eccube.repository.master.product_type']->find($row['商品種別(ID)']); | |
if (!$ProductType) { | |
$this->addErrors(($data->key() + 1) . '行目の商品種別(ID)が存在しません。'); | |
} else { | |
$ProductClass->setProductType($ProductType); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の商品種別(ID)が存在しません。'); | |
} | |
} | |
$ProductClass->setClassCategory1($ClassCategory1); | |
$ProductClass->setClassCategory2($ClassCategory2); | |
if ($row['発送日目安(ID)'] != '') { | |
if (preg_match('/^\d+$/', $row['発送日目安(ID)'])) { | |
$DeliveryDate = $app['eccube.repository.delivery_date']->find($row['発送日目安(ID)']); | |
if (!$DeliveryDate) { | |
$this->addErrors(($data->key() + 1) . '行目の発送日目安(ID)が存在しません。'); | |
} else { | |
$ProductClass->setDeliveryDate($DeliveryDate); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の発送日目安(ID)が存在しません。'); | |
} | |
} | |
if (Str::isNotBlank($row['商品コード'])) { | |
$ProductClass->setCode(Str::trimAll($row['商品コード'])); | |
} else { | |
$ProductClass->setCode(null); | |
} | |
if ($row['在庫数無制限フラグ'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数無制限フラグが設定されていません。'); | |
} else { | |
if ($row['在庫数無制限フラグ'] == (string) Constant::DISABLED) { | |
$ProductClass->setStockUnlimited(Constant::DISABLED); | |
// 在庫数が設定されていなければエラー | |
if ($row['在庫数'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数が設定されていません。'); | |
} else { | |
$stock = str_replace(',', '', $row['在庫数']); | |
if (preg_match('/^\d+$/', $stock) && $stock >= 0) { | |
$ProductClass->setStock($stock); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数は0以上の数値を設定してください。'); | |
} | |
} | |
} else if ($row['在庫数無制限フラグ'] == (string) Constant::ENABLED) { | |
$ProductClass->setStockUnlimited(Constant::ENABLED); | |
$ProductClass->setStock(null); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数無制限フラグが設定されていません。'); | |
} | |
} | |
if ($row['販売制限数'] != '') { | |
$saleLimit = str_replace(',', '', $row['販売制限数']); | |
if (preg_match('/^\d+$/', $saleLimit) && $saleLimit >= 0) { | |
$ProductClass->setSaleLimit($saleLimit); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の販売制限数は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['通常価格'] != '') { | |
$price01 = str_replace(',', '', $row['通常価格']); | |
if (preg_match('/^\d+$/', $price01) && $price01 >= 0) { | |
$ProductClass->setPrice01($price01); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の通常価格は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['販売価格'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の販売価格が設定されていません。'); | |
} else { | |
$price02 = str_replace(',', '', $row['販売価格']); | |
if (preg_match('/^\d+$/', $price02) && $price02 >= 0) { | |
$ProductClass->setPrice02($price02); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の販売価格は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['送料'] != '') { | |
$delivery_fee = str_replace(',', '', $row['送料']); | |
if (preg_match('/^\d+$/', $delivery_fee) && $delivery_fee >= 0) { | |
$ProductClass->setDeliveryFee($delivery_fee); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の送料は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['商品規格削除フラグ'] == '') { | |
$ProductClass->setDelFlg(Constant::DISABLED); | |
} else { | |
if ($row['商品規格削除フラグ'] == (string) Constant::DISABLED || $row['商品規格削除フラグ'] == (string) Constant::ENABLED) { | |
$ProductClass->setDelFlg($row['商品規格削除フラグ']); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の商品規格削除フラグが設定されていません。'); | |
} | |
} | |
$Product->addProductClass($ProductClass); | |
$ProductStock = new ProductStock(); | |
$ProductClass->setProductStock($ProductStock); | |
$ProductStock->setProductClass($ProductClass); | |
if (!$ProductClass->getStockUnlimited()) { | |
$ProductStock->setStock($ProductClass->getStock()); | |
} else { | |
// 在庫無制限時はnullを設定 | |
$ProductStock->setStock(null); | |
} | |
$this->em->persist($ProductClass); | |
$this->em->persist($ProductStock); | |
return $ProductClass; | |
} | |
/** | |
* 商品規格情報を更新 | |
*/ | |
protected function updateProductClass($row, Product $Product, ProductClass $ProductClass, $app, $data) | |
{ | |
$ProductClass->setProduct($Product); | |
if ($row['商品種別(ID)'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の商品種別(ID)が設定されていません。'); | |
} else { | |
if (preg_match('/^\d+$/', $row['商品種別(ID)'])) { | |
$ProductType = $app['eccube.repository.master.product_type']->find($row['商品種別(ID)']); | |
if (!$ProductType) { | |
$this->addErrors(($data->key() + 1) . '行目の商品種別(ID)が存在しません。'); | |
} else { | |
$ProductClass->setProductType($ProductType); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の商品種別(ID)が存在しません。'); | |
} | |
} | |
// 規格分類1、2をそれぞれセットし作成 | |
if ($row['規格分類1(ID)'] != '') { | |
if (preg_match('/^\d+$/', $row['規格分類1(ID)'])) { | |
$ClassCategory = $app['eccube.repository.class_category']->find($row['規格分類1(ID)']); | |
if (!$ClassCategory) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} else { | |
$ProductClass->setClassCategory1($ClassCategory); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類1(ID)が存在しません。'); | |
} | |
} | |
if ($row['規格分類2(ID)'] != '') { | |
if (preg_match('/^\d+$/', $row['規格分類2(ID)'])) { | |
$ClassCategory = $app['eccube.repository.class_category']->find($row['規格分類2(ID)']); | |
if (!$ClassCategory) { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)が存在しません。'); | |
} else { | |
$ProductClass->setClassCategory2($ClassCategory); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の規格分類2(ID)が存在しません。'); | |
} | |
} | |
if ($row['発送日目安(ID)'] != '') { | |
if (preg_match('/^\d+$/', $row['発送日目安(ID)'])) { | |
$DeliveryDate = $app['eccube.repository.delivery_date']->find($row['発送日目安(ID)']); | |
if (!$DeliveryDate) { | |
$this->addErrors(($data->key() + 1) . '行目の発送日目安(ID)が存在しません。'); | |
} else { | |
$ProductClass->setDeliveryDate($DeliveryDate); | |
} | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の発送日目安(ID)が存在しません。'); | |
} | |
} | |
if (Str::isNotBlank($row['商品コード'])) { | |
$ProductClass->setCode(Str::trimAll($row['商品コード'])); | |
} else { | |
$ProductClass->setCode(null); | |
} | |
if ($row['在庫数無制限フラグ'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数無制限フラグが設定されていません。'); | |
} else { | |
if ($row['在庫数無制限フラグ'] == (string) Constant::DISABLED) { | |
$ProductClass->setStockUnlimited(Constant::DISABLED); | |
// 在庫数が設定されていなければエラー | |
if ($row['在庫数'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数が設定されていません。'); | |
} else { | |
$stock = str_replace(',', '', $row['在庫数']); | |
if (preg_match('/^\d+$/', $stock) && $stock >= 0) { | |
$ProductClass->setStock($row['在庫数']); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数は0以上の数値を設定してください。'); | |
} | |
} | |
} else if ($row['在庫数無制限フラグ'] == (string) Constant::ENABLED) { | |
$ProductClass->setStockUnlimited(Constant::ENABLED); | |
$ProductClass->setStock(null); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の在庫数無制限フラグが設定されていません。'); | |
} | |
} | |
if ($row['販売制限数'] != '') { | |
$saleLimit = str_replace(',', '', $row['販売制限数']); | |
if (preg_match('/^\d+$/', $saleLimit) && $saleLimit >= 0) { | |
$ProductClass->setSaleLimit($saleLimit); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の販売制限数は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['通常価格'] != '') { | |
$price01 = str_replace(',', '', $row['通常価格']); | |
if (preg_match('/^\d+$/', $price01) && $price01 >= 0) { | |
$ProductClass->setPrice01($price01); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の通常価格は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['販売価格'] == '') { | |
$this->addErrors(($data->key() + 1) . '行目の販売価格が設定されていません。'); | |
} else { | |
$price02 = str_replace(',', '', $row['販売価格']); | |
if (preg_match('/^\d+$/', $price02) && $price02 >= 0) { | |
$ProductClass->setPrice02($price02); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の販売価格は0以上の数値を設定してください。'); | |
} | |
} | |
if ($row['商品規格削除フラグ'] == '') { | |
$ProductClass->setDelFlg(Constant::DISABLED); | |
} else { | |
if ($row['商品規格削除フラグ'] == (string) Constant::DISABLED || $row['商品規格削除フラグ'] == (string) Constant::ENABLED) { | |
$ProductClass->setDelFlg($row['商品規格削除フラグ']); | |
} else { | |
$this->addErrors(($data->key() + 1) . '行目の商品規格削除フラグが設定されていません。'); | |
} | |
} | |
$ProductStock = $ProductClass->getProductStock(); | |
if (!$ProductClass->getStockUnlimited()) { | |
$ProductStock->setStock($ProductClass->getStock()); | |
} else { | |
// 在庫無制限時はnullを設定 | |
$ProductStock->setStock(null); | |
} | |
return $ProductClass; | |
} | |
/** | |
* 登録、更新時のエラー画面表示 | |
* | |
*/ | |
protected function addErrors($message) | |
{ | |
$e = new CsvImportException($message); | |
$this->errors[] = $e; | |
} | |
/** | |
* @return array | |
*/ | |
protected function getErrors() | |
{ | |
return $this->errors; | |
} | |
/** | |
* | |
* @return boolean | |
*/ | |
protected function hasErrors() | |
{ | |
return count($this->getErrors()) > 0; | |
} | |
/** | |
* 商品登録CSVヘッダー定義 | |
*/ | |
private function getProductCsvHeader() | |
{ | |
return array( | |
'商品ID' => 'id', | |
'公開ステータス(ID)' => 'status', | |
'商品名' => 'name', | |
'ショップ用メモ欄' => 'note', | |
'商品説明(一覧)' => 'description_list', | |
'商品説明(詳細)' => 'description_detail', | |
'検索ワード' => 'search_word', | |
'フリーエリア' => 'free_area', | |
'商品削除フラグ' => 'product_del_flg', | |
'商品画像' => 'product_image', | |
'商品カテゴリ(ID)' => 'product_category', | |
'タグ(ID)' => 'product_tag', | |
'商品種別(ID)' => 'product_type', | |
'規格分類1(ID)' => 'class_category1', | |
'規格分類2(ID)' => 'class_category2', | |
'発送日目安(ID)' => 'deliveryFee', | |
'商品コード' => 'product_code', | |
'在庫数' => 'stock', | |
'在庫数無制限フラグ' => 'stock_unlimited', | |
'販売制限数' => 'sale_limit', | |
'通常価格' => 'price01', | |
'販売価格' => 'price02', | |
'送料' => 'delivery_fee', | |
'商品規格削除フラグ' => 'product_class_del_flg', | |
); | |
} | |
/** | |
* カテゴリCSVヘッダー定義 | |
*/ | |
private function getCategoryCsvHeader() | |
{ | |
return array( | |
'カテゴリID' => 'id', | |
'カテゴリ名' => 'category_name', | |
'親カテゴリID' => 'parent_category_id', | |
); | |
} | |
} |