Completed
Push — experimental/sf ( 7e7cd6...2a520b )
by
unknown
273:54 queued 265:51
created

CsvExportService   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 424
Duplicated Lines 11.32 %

Coupling/Cohesion

Components 2
Dependencies 14

Test Coverage

Coverage 72%

Importance

Changes 0
Metric Value
dl 48
loc 424
ccs 90
cts 125
cp 0.72
rs 9.1199
c 0
b 0
f 0
wmc 41
lcom 2
cbo 14

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 1
A setConfig() 0 4 1
A setCsvRepository() 0 4 1
A setCsvTypeRepository() 0 4 1
A setOrderRepository() 0 4 1
A setCustomerRepository() 0 4 1
A setProductRepository() 0 4 1
A setEntityManager() 0 4 1
A getEntityManager() 0 4 1
A initCsvType() 0 17 2
A getProductQueryBuilder() 16 16 1
A setExportQueryBuilder() 0 4 1
A getCsvs() 0 4 1
A exportHeader() 0 15 4
A exportData() 0 18 4
B getData() 0 42 9
A getConvertEncodhingCallback() 0 10 1
A fopen() 0 6 3
A fputcsv() 0 8 2
A fclose() 0 7 2
A getOrderQueryBuilder() 16 16 1
A getCustomerQueryBuilder() 16 16 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CsvExportService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CsvExportService, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Service;
15
16
use Doctrine\Common\Util\ClassUtils;
17
use Doctrine\ORM\EntityManagerInterface;
18
use Eccube\Common\EccubeConfig;
19
use Eccube\Form\Type\Admin\SearchProductType;
20
use Eccube\Repository\CsvRepository;
21
use Eccube\Repository\CustomerRepository;
22
use Eccube\Repository\Master\CsvTypeRepository;
23
use Eccube\Repository\OrderRepository;
24
use Eccube\Repository\ProductRepository;
25
use Eccube\Repository\ShippingRepository;
26
use Eccube\Util\EntityUtil;
27
use Eccube\Util\FormUtil;
28
use Symfony\Component\Form\FormFactoryInterface;
29
use Symfony\Component\HttpFoundation\Request;
30
use Doctrine\ORM\QueryBuilder;
31
use Eccube\Entity\Master\CsvType;
32
use Eccube\Entity\Csv;
33
34
class CsvExportService
0 ignored issues
show
introduced by
Missing class doc comment
Loading history...
35
{
36
    /**
37
     * @var resource
38
     */
39
    protected $fp;
40
41
    /**
42
     * @var boolean
43
     */
44
    protected $closed = false;
45
46
    /**
47
     * @var \Closure
48
     */
49
    protected $convertEncodingCallBack;
50
51
    /**
52
     * @var EntityManagerInterface
53
     */
54
    protected $entityManager;
55
56
    /**
57
     * @var QueryBuilder;
58
     */
59
    protected $qb;
60
61
    /**
62
     * @var EccubeConfig
63
     */
64
    protected $eccubeConfig;
65
66
    /**
67
     * @var CsvType
68
     */
69
    protected $CsvType;
70
71
    /**
72
     * @var Csv[]
73
     */
74
    protected $Csvs;
75
76
    /**
77
     * @var CsvRepository
78
     */
79
    protected $csvRepository;
80
81
    /**
82
     * @var CsvTypeRepository
83
     */
84
    protected $csvTypeRepository;
85
86
    /**
87
     * @var OrderRepository
88
     */
89
    protected $orderRepository;
90
91
    /**
92
     * @var ShippingRepository
93
     */
94
    protected $shippingRepository;
95
96
    /**
97
     * @var CustomerRepository
98
     */
99
    protected $customerRepository;
100
101
    /**
102
     * @var ProductRepository
103
     */
104
    protected $productRepository;
105
106
    /**
107
     * @var FormFactoryInterface
108
     */
109
    protected $formFactory;
110
111
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$shippingRepository" missing
Loading history...
introduced by
Doc comment for parameter "$productRepository" missing
Loading history...
introduced by
Doc comment for parameter "$formFactory" missing
Loading history...
112
     * CsvExportService constructor.
113
     *
114
     * @param EntityManagerInterface $entityManager
115
     * @param CsvRepository $csvRepository
0 ignored issues
show
introduced by
Expected 10 spaces after parameter type; 1 found
Loading history...
116
     * @param CsvTypeRepository $csvTypeRepository
0 ignored issues
show
introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
117
     * @param OrderRepository $orderRepository
0 ignored issues
show
introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
118
     * @param CustomerRepository $customerRepository
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
introduced by
Doc comment for parameter $customerRepository does not match actual variable name $shippingRepository
Loading history...
119
     * @param EccubeConfig $eccubeConfig
0 ignored issues
show
introduced by
Expected 11 spaces after parameter type; 1 found
Loading history...
introduced by
Doc comment for parameter $eccubeConfig does not match actual variable name $customerRepository
Loading history...
120
     */
121 62
    public function __construct(
122
        EntityManagerInterface $entityManager,
123
        CsvRepository $csvRepository,
124
        CsvTypeRepository $csvTypeRepository,
125
        OrderRepository $orderRepository,
126
        ShippingRepository $shippingRepository,
127
        CustomerRepository $customerRepository,
128
        ProductRepository $productRepository,
129
        EccubeConfig $eccubeConfig,
130
        FormFactoryInterface $formFactory
131
    ) {
132 62
        $this->entityManager = $entityManager;
133 62
        $this->csvRepository = $csvRepository;
134 62
        $this->csvTypeRepository = $csvTypeRepository;
135 62
        $this->orderRepository = $orderRepository;
136 62
        $this->shippingRepository = $shippingRepository;
137 62
        $this->customerRepository = $customerRepository;
138 62
        $this->eccubeConfig = $eccubeConfig;
139 62
        $this->productRepository = $productRepository;
140 62
        $this->formFactory = $formFactory;
141
    }
142
143
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$config" missing
Loading history...
144
     * @param $config
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
145
     */
146
    public function setConfig($config)
147
    {
148
        $this->eccubeConfig = $config;
149
    }
150
151
    /**
152
     * @param CsvRepository $csvRepository
153
     */
154
    public function setCsvRepository(CsvRepository $csvRepository)
155
    {
156
        $this->csvRepository = $csvRepository;
157
    }
158
159
    /**
160
     * @param CsvTypeRepository $csvTypeRepository
161
     */
162
    public function setCsvTypeRepository(CsvTypeRepository $csvTypeRepository)
163
    {
164
        $this->csvTypeRepository = $csvTypeRepository;
165
    }
166
167
    /**
168
     * @param OrderRepository $orderRepository
169
     */
170
    public function setOrderRepository(OrderRepository $orderRepository)
171
    {
172
        $this->orderRepository = $orderRepository;
173
    }
174
175
    /**
176
     * @param CustomerRepository $customerRepository
177
     */
178
    public function setCustomerRepository(CustomerRepository $customerRepository)
179
    {
180
        $this->customerRepository = $customerRepository;
181
    }
182
183
    /**
184
     * @param ProductRepository $productRepository
185
     */
186
    public function setProductRepository(ProductRepository $productRepository)
187
    {
188
        $this->productRepository = $productRepository;
189
    }
190
191
    /**
192
     * @param EntityManagerInterface $entityManager
193
     */
194
    public function setEntityManager(EntityManagerInterface $entityManager)
195
    {
196
        $this->entityManager = $entityManager;
197
    }
198
199
    /**
200
     * @return EntityManagerInterface
201
     */
202
    public function getEntityManager()
203
    {
204
        return $this->entityManager;
205
    }
206
207
    /**
208
     * @param QueryBuilder $qb
209
     */
210 4
    public function setExportQueryBuilder(QueryBuilder $qb)
211
    {
212 4
        $this->qb = $qb;
213
    }
214
215
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$CsvType" missing
Loading history...
216
     * Csv種別からServiceの初期化を行う.
217
     *
218
     * @param $CsvType|integer
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
219
     */
220 5
    public function initCsvType($CsvType)
221
    {
222 5
        if ($CsvType instanceof CsvType) {
223
            $this->CsvType = $CsvType;
224
        } else {
225 5
            $this->CsvType = $this->csvTypeRepository->find($CsvType);
226
        }
227
228
        $criteria = [
229 5
            'CsvType' => $CsvType,
230
            'enabled' => true,
231
        ];
232
        $orderBy = [
233 5
            'sort_no' => 'ASC',
234
        ];
235 5
        $this->Csvs = $this->csvRepository->findBy($criteria, $orderBy);
236
    }
237
238
    /**
239
     * @return Csv[]
240
     */
241 4
    public function getCsvs()
242
    {
243 4
        return $this->Csvs;
244
    }
245
246
    /**
247
     * ヘッダ行を出力する.
248
     * このメソッドを使う場合は, 事前にinitCsvType($CsvType)で初期化しておく必要がある.
249
     */
250 4
    public function exportHeader()
251
    {
252 4
        if (is_null($this->CsvType) || is_null($this->Csvs)) {
253
            throw new \LogicException('init csv type incomplete.');
254
        }
255
256 4
        $row = [];
257 4
        foreach ($this->Csvs as $Csv) {
258 4
            $row[] = $Csv->getDispName();
259
        }
260
261 4
        $this->fopen();
262 4
        $this->fputcsv($row);
263 4
        $this->fclose();
264
    }
265
266
    /**
267
     * クエリビルダにもとづいてデータ行を出力する.
268
     * このメソッドを使う場合は, 事前にsetExportQueryBuilder($qb)で出力対象のクエリビルダをわたしておく必要がある.
269
     *
270
     * @param \Closure $closure
271
     */
272 4
    public function exportData(\Closure $closure)
273
    {
274 4
        if (is_null($this->qb) || is_null($this->entityManager)) {
275
            throw new \LogicException('query builder not set.');
276
        }
277
278 4
        $this->fopen();
279
280 4
        $query = $this->qb->getQuery();
281 4
        foreach ($query->getResult() as $iteratableResult) {
282 4
            $closure($iteratableResult, $this);
283 4
            $this->entityManager->detach($iteratableResult);
284 4
            $query->free();
285 4
            flush();
286
        }
287
288 4
        $this->fclose();
289
    }
290
291
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$entity" missing
Loading history...
292
     * CSV出力項目と比較し, 合致するデータを返す.
293
     *
294
     * @param \Eccube\Entity\Csv $Csv
295
     * @param $entity
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
296
     *
297
     * @return mixed|null|string|void
298
     */
299 4
    public function getData(Csv $Csv, $entity)
300
    {
301
        // エンティティ名が一致するかどうかチェック.
302 4
        $csvEntityName = str_replace('\\\\', '\\', $Csv->getEntityName());
303 4
        $entityName = ClassUtils::getClass($entity);
304 4
        if ($csvEntityName !== $entityName) {
305 2
            return null;
306
        }
307
308
        // カラム名がエンティティに存在するかどうかをチェック.
309 4
        if (!$entity->offsetExists($Csv->getFieldName())) {
310 2
            return null;
311
        }
312
313
        // データを取得.
314 4
        $data = $entity->offsetGet($Csv->getFieldName());
315
316
        // one to one の場合は, dtb_csv.referece_field_nameと比較し, 合致する結果を取得する.
317 4
        if ($data instanceof \Eccube\Entity\AbstractEntity) {
318 4
            if (EntityUtil::isNotEmpty($data)) {
319 4
                return $data->offsetGet($Csv->getReferenceFieldName());
320
            }
321 4
        } elseif ($data instanceof \Doctrine\Common\Collections\Collection) {
322
            // one to manyの場合は, カンマ区切りに変換する.
323
            $array = [];
324
            foreach ($data as $elem) {
325
                if (EntityUtil::isNotEmpty($elem)) {
326
                    $array[] = $elem->offsetGet($Csv->getReferenceFieldName());
327
                }
328
            }
329
330
            return implode($this->eccubeConfig['eccube_csv_export_multidata_separator'], $array);
331 4
        } elseif ($data instanceof \DateTime) {
332
            // datetimeの場合は文字列に変換する.
333 3
            return $data->format($this->eccubeConfig['eccube_csv_export_date_format']);
334
        } else {
335
            // スカラ値の場合はそのまま.
336 4
            return $data;
337
        }
338
339
        return null;
340
    }
341
342
    /**
343
     * 文字エンコーディングの変換を行うコールバック関数を返す.
344
     *
345
     * @return \Closure
346
     */
347 5
    public function getConvertEncodhingCallback()
348
    {
349 5
        $config = $this->eccubeConfig;
350
351 5
        return function ($value) use ($config) {
352 5
            return mb_convert_encoding(
353 5
                (string) $value, $config['eccube_csv_export_encoding'], 'UTF-8'
354
            );
355 5
        };
356
    }
357
358 5
    public function fopen()
359
    {
360 5
        if (is_null($this->fp) || $this->closed) {
361 3
            $this->fp = fopen('php://output', 'w');
362
        }
363
    }
364
365
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$row" missing
Loading history...
366
     * @param $row
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
367
     * @param null $callback
0 ignored issues
show
Bug introduced by
There is no parameter named $callback. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
introduced by
Superfluous parameter comment
Loading history...
368
     */
369 5
    public function fputcsv($row)
370
    {
371 5
        if (is_null($this->convertEncodingCallBack)) {
372 5
            $this->convertEncodingCallBack = $this->getConvertEncodhingCallback();
373
        }
374
375 5
        fputcsv($this->fp, array_map($this->convertEncodingCallBack, $row), $this->eccubeConfig['eccube_csv_export_separator']);
376
    }
377
378 5
    public function fclose()
379
    {
380 5
        if (!$this->closed) {
381 5
            fclose($this->fp);
382 5
            $this->closed = true;
383
        }
384
    }
385
386
    /**
387
     * 受注検索用のクエリビルダを返す.
388
     *
389
     * @param Request $request
390
     *
391
     * @return \Doctrine\ORM\QueryBuilder
392
     */
393 1 View Code Duplication
    public function getOrderQueryBuilder(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
394
    {
395 1
        $session = $request->getSession();
396 1
        $builder = $this->formFactory
397 1
            ->createBuilder(SearchProductType::class);
398 1
        $searchForm = $builder->getForm();
399
400 1
        $viewData = $session->get('eccube.admin.order.search', []);
401 1
        $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
402
403
        // 受注データのクエリビルダを構築.
404 1
        $qb = $this->orderRepository
405 1
            ->getQueryBuilderBySearchDataForAdmin($searchData);
406
407 1
        return $qb;
408
    }
409
410
    /**
411
     * 会員検索用のクエリビルダを返す.
412
     *
413
     * @param Request $request
414
     *
415
     * @return \Doctrine\ORM\QueryBuilder
416
     */
417 1 View Code Duplication
    public function getCustomerQueryBuilder(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
418
    {
419 1
        $session = $request->getSession();
420 1
        $builder = $this->formFactory
421 1
            ->createBuilder(SearchProductType::class);
422 1
        $searchForm = $builder->getForm();
423
424 1
        $viewData = $session->get('eccube.admin.customer.search', []);
425 1
        $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
426
427
        // 会員データのクエリビルダを構築.
428 1
        $qb = $this->customerRepository
429 1
            ->getQueryBuilderBySearchData($searchData);
430
431 1
        return $qb;
432
    }
433
434
    /**
435
     * 商品検索用のクエリビルダを返す.
436
     *
437
     * @param Request $request
438
     *
439
     * @return \Doctrine\ORM\QueryBuilder
440
     */
441 View Code Duplication
    public function getProductQueryBuilder(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
442
    {
443
        $session = $request->getSession();
444
        $builder = $this->formFactory
445
            ->createBuilder(SearchProductType::class);
446
        $searchForm = $builder->getForm();
447
448
        $viewData = $session->get('eccube.admin.product.search', []);
449
        $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
450
451
        // 商品データのクエリビルダを構築.
452
        $qb = $this->productRepository
453
            ->getQueryBuilderBySearchDataForAdmin($searchData);
454
455
        return $qb;
456
    }
457
}
458