Completed
Pull Request — 4.0 (#4641)
by Kiyotaka
05:07
created

ProductRepository   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 337
Duplicated Lines 23.44 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 91.13%

Importance

Changes 0
Metric Value
dl 79
loc 337
ccs 113
cts 124
cp 0.9113
rs 6.4799
c 0
b 0
f 0
wmc 54
lcom 1
cbo 8

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A findWithSortedClassCategories() 0 22 1
A findProductsWithSortedClassCategories() 0 28 2
C getQueryBuilderBySearchData() 11 75 15
F getQueryBuilderBySearchDataForAdmin() 68 150 35

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 ProductRepository 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 ProductRepository, 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) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.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\Repository;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Eccube\Common\EccubeConfig;
18
use Eccube\Doctrine\Query\Queries;
19
use Eccube\Entity\Product;
20
use Eccube\Entity\ProductStock;
21
use Eccube\Util\StringUtil;
22
use Symfony\Bridge\Doctrine\RegistryInterface;
23
24
/**
25
 * ProductRepository
26
 *
27
 * This class was generated by the Doctrine ORM. Add your own custom
28
 * repository methods below.
29
 */
30
class ProductRepository extends AbstractRepository
31
{
32
    /**
33
     * @var Queries
34
     */
35
    protected $queries;
36
37
    /**
38
     * @var EccubeConfig
39
     */
40
    protected $eccubeConfig;
41
42
    /**
43
     * ProductRepository constructor.
44
     *
45
     * @param RegistryInterface $registry
46
     * @param Queries $queries
47
     * @param EccubeConfig $eccubeConfig
48 204
     */
49
    public function __construct(
50
        RegistryInterface $registry,
51
        Queries $queries,
52
        EccubeConfig $eccubeConfig
53 204
    ) {
54 204
        parent::__construct($registry, Product::class);
55 204
        $this->queries = $queries;
56
        $this->eccubeConfig = $eccubeConfig;
57
    }
58
59
    /**
60
     * Find the Product with sorted ClassCategories.
61
     *
62
     * @param integer $productId
63
     *
64
     * @return Product
65 15
     */
66
    public function findWithSortedClassCategories($productId)
67 15
    {
68 15
        $qb = $this->createQueryBuilder('p');
69 15
        $qb->addSelect(['pc', 'cc1', 'cc2', 'pi', 'pt'])
70 15
            ->innerJoin('p.ProductClasses', 'pc')
71 15
            ->leftJoin('pc.ClassCategory1', 'cc1')
72 15
            ->leftJoin('pc.ClassCategory2', 'cc2')
73 15
            ->leftJoin('p.ProductImage', 'pi')
74 15
            ->leftJoin('p.ProductTag', 'pt')
75 15
            ->where('p.id = :id')
76 15
            ->andWhere('pc.visible = :visible')
77
            ->setParameter('id', $productId)
78 15
            ->setParameter('visible', true)
79 15
            ->orderBy('cc1.sort_no', 'DESC')
80
            ->addOrderBy('cc2.sort_no', 'DESC');
81 15
82 15
        $product = $qb
83
            ->getQuery()
84 15
            ->getSingleResult();
85
86
        return $product;
87
    }
88
89
    /**
90
     * Find the Products with sorted ClassCategories.
91
     *
92
     * @param array $ids Product in ids
93
     * @param string $indexBy The index for the from.
94 17
     *
95
     * @return ArrayCollection|array
96 17
     */
97 17
    public function findProductsWithSortedClassCategories(array $ids, $indexBy = null)
98
    {
99
        if (count($ids) < 1) {
100 17
            return [];
101 17
        }
102 3
        $qb = $this->createQueryBuilder('p', $indexBy);
103 3
        $qb->addSelect(['pc', 'cc1', 'cc2', 'pi', 'pt', 'tr', 'ps'])
104
            ->innerJoin('p.ProductClasses', 'pc')
105 3
            // XXX Joined 'TaxRule' and 'ProductStock' to prevent lazy loading
106 3
            ->leftJoin('pc.TaxRule', 'tr')
107 3
            ->innerJoin('pc.ProductStock', 'ps')
108 3
            ->leftJoin('pc.ClassCategory1', 'cc1')
109 3
            ->leftJoin('pc.ClassCategory2', 'cc2')
110
            ->leftJoin('p.ProductImage', 'pi')
111
            ->leftJoin('p.ProductTag', 'pt')
112
            ->where($qb->expr()->in('p.id', $ids))
113
            ->andWhere('pc.visible = :visible')
114 17
            ->setParameter('visible', true)
115 2
            ->orderBy('cc1.sort_no', 'DESC')
116
            ->addOrderBy('cc2.sort_no', 'DESC');
117 2
118 2
        $products = $qb
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ORM\AbstractQuery::useResultCache() has been deprecated with message: 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
119
            ->getQuery()
120 2
            ->useResultCache(true, $this->eccubeConfig['eccube_result_cache_lifetime_short'])
121
            ->getResult();
122
123 2
        return $products;
124 2
    }
125
126
    /**
127
     * get query builder.
128
     *
129
     * @param  array $searchData
130 17
     *
131 17
     * @return \Doctrine\ORM\QueryBuilder
132
     */
133 5
    public function getQueryBuilderBySearchData($searchData)
134 5
    {
135 5
        $qb = $this->createQueryBuilder('p')
136 5
            ->andWhere('p.Status = 1');
137 5
138 5
        // category
139
        $categoryJoin = false;
140 12 View Code Duplication
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
141 1
            $Categories = $searchData['category_id']->getSelfAndDescendants();
142 1
            if ($Categories) {
143 1
                $qb
144 1
                    ->innerJoin('p.ProductCategories', 'pct')
145 1
                    ->innerJoin('pct.Category', 'c')
146 1
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
147
                    ->setParameter('Categories', $Categories);
148 11
                $categoryJoin = true;
149
            }
150
        }
151 4
152
        // name
153
        if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
154
            $keywords = preg_split('/[\s ]+/u', str_replace(['%', '_'], ['\\%', '\\_'], $searchData['name']), -1, PREG_SPLIT_NO_EMPTY);
155 4
156 4
            foreach ($keywords as $index => $keyword) {
157
                $key = sprintf('keyword%s', $index);
158 7
                $qb
159
                    ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR
160 5
                        NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR
161 5
                        EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
162
                        $key, $key, $index, $index, $index, $index, $key))
163
                    ->setParameter($key, '%'.$keyword.'%');
164 7
            }
165
        }
166
167 17
        // Order By
168
        // 価格低い順
169
        $config = $this->eccubeConfig;
170
        if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_lower']) {
171
            //@see http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
172
            $qb->addSelect('MIN(pc.price02) as HIDDEN price02_min');
173
            $qb->innerJoin('p.ProductClasses', 'pc');
174
            $qb->andWhere('pc.visible = true');
175
            $qb->groupBy('p.id');
176
            $qb->orderBy('price02_min', 'ASC');
177 20
            $qb->addOrderBy('p.id', 'DESC');
178
        // 価格高い順
179 20
        } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_higher']) {
180 20
            $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
181
            $qb->innerJoin('p.ProductClasses', 'pc');
182
            $qb->andWhere('pc.visible = true');
183 20
            $qb->groupBy('p.id');
184 6
            $qb->orderBy('price02_max', 'DESC');
185
            $qb->addOrderBy('p.id', 'DESC');
186 6
        // 新着順
187 6
        } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_newer']) {
188 6
            // 在庫切れ商品非表示の設定が有効時対応
189
            // @see https://github.com/EC-CUBE/ec-cube/issues/1998
190
            if ($this->getEntityManager()->getFilters()->isEnabled('option_nostock_hidden') == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
191
                $qb->innerJoin('p.ProductClasses', 'pc');
192
                $qb->andWhere('pc.visible = true');
193
            }
194
            $qb->orderBy('p.create_date', 'DESC');
195
            $qb->addOrderBy('p.id', 'DESC');
196
        } else {
197
            if ($categoryJoin === false) {
198
                $qb
199
                    ->leftJoin('p.ProductCategories', 'pct')
200
                    ->leftJoin('pct.Category', 'c');
201
            }
202
            $qb
203
                ->addOrderBy('p.id', 'DESC');
204
        }
205
206
        return $this->queries->customize(QueryKey::PRODUCT_SEARCH, $qb, $searchData);
207
    }
208
209
    /**
210
     * get query builder.
211
     *
212 20
     * @param  array $searchData
213 2
     *
214 2
     * @return \Doctrine\ORM\QueryBuilder
215
     */
216 2
    public function getQueryBuilderBySearchDataForAdmin($searchData)
217 2
    {
218 2
        $qb = $this->createQueryBuilder('p')
219 2
            ->addSelect('pc', 'pi', 'tr', 'ps')
220
            ->innerJoin('p.ProductClasses', 'pc')
221
            ->leftJoin('p.ProductImage', 'pi')
222
            ->leftJoin('pc.TaxRule', 'tr')
223
            ->leftJoin('pc.ProductStock', 'ps')
224 20
            ->andWhere('pc.visible = :visible')
225
            ->setParameter('visible', true);
226 2
227 2
        // id
228
        if (isset($searchData['id']) && StringUtil::isNotBlank($searchData['id'])) {
229
            $id = preg_match('/^\d{0,10}$/', $searchData['id'])  ? $searchData['id'] : null;
230
            if ($id && $id > '2147483647' && $this->isPostgreSQL()) {
231 20
                $id = null;
232
            }
233 1
            $qb
234 1
                ->andWhere('p.id = :id OR p.name LIKE :likeid OR pc.code LIKE :likeid')
235
                ->setParameter('id', $id)
236
                ->setParameter('likeid', '%'.str_replace(['%', '_'], ['\\%', '\\_'], $searchData['id']).'%');
237
        }
238 20
239
        // code
240
        /*
241
        if (!empty($searchData['code']) && $searchData['code']) {
242
            $qb
243
                ->innerJoin('p.ProductClasses', 'pc')
244
                ->andWhere('pc.code LIKE :code')
245 20
                ->setParameter('code', '%' . $searchData['code'] . '%');
246 3
        }
247 3
248 1
        // name
249 1
        if (!empty($searchData['name']) && $searchData['name']) {
250 2
            $keywords = preg_split('/[\s ]+/u', $searchData['name'], -1, PREG_SPLIT_NO_EMPTY);
251 2
            foreach ($keywords as $keyword) {
252 2
                $qb
253
                    ->andWhere('p.name LIKE :name')
254
                    ->setParameter('name', '%' . $keyword . '%');
255
            }
256
        }
257
       */
258
259 20
        // category
260 1 View Code Duplication
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
261
            $Categories = $searchData['category_id']->getSelfAndDescendants();
262 1
            if ($Categories) {
263 1
                $qb
264
                    ->innerJoin('p.ProductCategories', 'pct')
265
                    ->innerJoin('pct.Category', 'c')
266 20
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
267 1
                    ->setParameter('Categories', $Categories);
268
            }
269 1
        }
270
271 1
        // status
272 1 View Code Duplication
        if (!empty($searchData['status']) && $searchData['status']) {
273
            $qb
274
                ->andWhere($qb->expr()->in('p.Status', ':Status'))
275
                ->setParameter('Status', $searchData['status']);
276 20
        }
277 1
278
        // link_status
279 1 View Code Duplication
        if (isset($searchData['link_status']) && !empty($searchData['link_status'])) {
280 1
            $qb
281
                ->andWhere($qb->expr()->in('p.Status', ':Status'))
282 20
                ->setParameter('Status', $searchData['link_status']);
283 1
        }
284
285 1
        // stock status
286
        if (isset($searchData['stock_status'])) {
287 1
            $qb
288 1
                ->andWhere('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
289
                ->setParameter('StockUnlimited', $searchData['stock_status']);
290
        }
291
292
        // stock status
293 20
        if (isset($searchData['stock']) && !empty($searchData['stock'])) {
294
            switch ($searchData['stock']) {
295 20
                case [ProductStock::IN_STOCK]:
296
                    $qb->andWhere('pc.stock_unlimited = true OR pc.stock > 0');
297
                    break;
298
                case [ProductStock::OUT_OF_STOCK]:
299
                    $qb->andWhere('pc.stock_unlimited = false AND pc.stock <= 0');
300
                    break;
301
                default:
302
                    // 共に選択された場合は全権該当するので検索条件に含めない
303
            }
304
        }
305
306
        // crate_date
307 View Code Duplication
        if (!empty($searchData['create_datetime_start']) && $searchData['create_datetime_start']) {
308
            $date = $searchData['create_datetime_start'];
309
            $qb
310
                ->andWhere('p.create_date >= :create_date_start')
311
                ->setParameter('create_date_start', $date);
312
        } elseif (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
313
            $date = $searchData['create_date_start'];
314
            $qb
315
                ->andWhere('p.create_date >= :create_date_start')
316
                ->setParameter('create_date_start', $date);
317
        }
318
319 View Code Duplication
        if (!empty($searchData['create_datetime_end']) && $searchData['create_datetime_end']) {
320
            $date = $searchData['create_datetime_end'];
321
            $qb
322
                ->andWhere('p.create_date < :create_date_end')
323
                ->setParameter('create_date_end', $date);
324
        } elseif (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
325
            $date = clone $searchData['create_date_end'];
326
            $date = $date
327
                ->modify('+1 days');
328
            $qb
329
                ->andWhere('p.create_date < :create_date_end')
330
                ->setParameter('create_date_end', $date);
331
        }
332
333
        // update_date
334 View Code Duplication
        if (!empty($searchData['update_datetime_start']) && $searchData['update_datetime_start']) {
335
            $date = $searchData['update_datetime_start'];
336
            $qb
337
                ->andWhere('p.update_date >= :update_date_start')
338
                ->setParameter('update_date_start', $date);
339
        } elseif (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
340
            $date = $searchData['update_date_start'];
341
            $qb
342
                ->andWhere('p.update_date >= :update_date_start')
343
                ->setParameter('update_date_start', $date);
344
        }
345
346 View Code Duplication
        if (!empty($searchData['update_datetime_end']) && $searchData['update_datetime_end']) {
347
            $date = $searchData['update_datetime_end'];
348
            $qb
349
                ->andWhere('p.update_date < :update_date_end')
350
                ->setParameter('update_date_end', $date);
351
        } elseif (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
352
            $date = clone $searchData['update_date_end'];
353
            $date = $date
354
                ->modify('+1 days');
355
            $qb
356
                ->andWhere('p.update_date < :update_date_end')
357
                ->setParameter('update_date_end', $date);
358
        }
359
360
        // Order By
361
        $qb
362
            ->orderBy('p.update_date', 'DESC');
363
364
        return $this->queries->customize(QueryKey::PRODUCT_SEARCH_ADMIN, $qb, $searchData);
365
    }
366
}
367