Completed
Push — 4.0 ( e0157b...cd2ad2 )
by Ryo
23:19 queued 12:42
created

ProductRepository   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 310
Duplicated Lines 19.03 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 91.13%

Importance

Changes 0
Metric Value
dl 59
loc 310
ccs 113
cts 124
cp 0.9113
rs 8.96
c 0
b 0
f 0
wmc 43
lcom 1
cbo 8

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A findWithSortedClassCategories() 0 22 1
A findProductsWithSortedClassCategories() 0 27 2
C getQueryBuilderBySearchData() 11 75 15
F getQueryBuilderBySearchDataForAdmin() 48 124 24

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) 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\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
119
            ->getQuery()
120 2
            ->getResult();
121
122
        return $products;
123 2
    }
124 2
125
    /**
126
     * get query builder.
127
     *
128
     * @param  array $searchData
129
     *
130 17
     * @return \Doctrine\ORM\QueryBuilder
131 17
     */
132
    public function getQueryBuilderBySearchData($searchData)
133 5
    {
134 5
        $qb = $this->createQueryBuilder('p')
135 5
            ->andWhere('p.Status = 1');
136 5
137 5
        // category
138 5
        $categoryJoin = false;
139 View Code Duplication
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
140 12
            $Categories = $searchData['category_id']->getSelfAndDescendants();
141 1
            if ($Categories) {
142 1
                $qb
143 1
                    ->innerJoin('p.ProductCategories', 'pct')
144 1
                    ->innerJoin('pct.Category', 'c')
145 1
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
146 1
                    ->setParameter('Categories', $Categories);
147
                $categoryJoin = true;
148 11
            }
149
        }
150
151 4
        // name
152
        if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
153
            $keywords = preg_split('/[\s ]+/u', str_replace(['%', '_'], ['\\%', '\\_'], $searchData['name']), -1, PREG_SPLIT_NO_EMPTY);
154
155 4
            foreach ($keywords as $index => $keyword) {
156 4
                $key = sprintf('keyword%s', $index);
157
                $qb
158 7
                    ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR 
159
                        NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR 
160 5
                        EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
161 5
                        $key, $key, $index, $index, $index, $index, $key))
162
                    ->setParameter($key, '%'.$keyword.'%');
163
            }
164 7
        }
165
166
        // Order By
167 17
        // 価格低い順
168
        $config = $this->eccubeConfig;
169
        if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_lower']) {
170
            //@see http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
171
            $qb->addSelect('MIN(pc.price02) as HIDDEN price02_min');
172
            $qb->innerJoin('p.ProductClasses', 'pc');
173
            $qb->andWhere('pc.visible = true');
174
            $qb->groupBy('p.id');
175
            $qb->orderBy('price02_min', 'ASC');
176
            $qb->addOrderBy('p.id', 'DESC');
177 20
        // 価格高い順
178
        } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_higher']) {
179 20
            $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
180 20
            $qb->innerJoin('p.ProductClasses', 'pc');
181
            $qb->andWhere('pc.visible = true');
182
            $qb->groupBy('p.id');
183 20
            $qb->orderBy('price02_max', 'DESC');
184 6
            $qb->addOrderBy('p.id', 'DESC');
185
        // 新着順
186 6
        } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_newer']) {
187 6
            // 在庫切れ商品非表示の設定が有効時対応
188 6
            // @see https://github.com/EC-CUBE/ec-cube/issues/1998
189
            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...
190
                $qb->innerJoin('p.ProductClasses', 'pc');
191
                $qb->andWhere('pc.visible = true');
192
            }
193
            $qb->orderBy('p.create_date', 'DESC');
194
            $qb->addOrderBy('p.id', 'DESC');
195
        } else {
196
            if ($categoryJoin === false) {
197
                $qb
198
                    ->leftJoin('p.ProductCategories', 'pct')
199
                    ->leftJoin('pct.Category', 'c');
200
            }
201
            $qb
202
                ->addOrderBy('p.id', 'DESC');
203
        }
204
205
        return $this->queries->customize(QueryKey::PRODUCT_SEARCH, $qb, $searchData);
206
    }
207
208
    /**
209
     * get query builder.
210
     *
211
     * @param  array $searchData
212 20
     *
213 2
     * @return \Doctrine\ORM\QueryBuilder
214 2
     */
215
    public function getQueryBuilderBySearchDataForAdmin($searchData)
216 2
    {
217 2
        $qb = $this->createQueryBuilder('p')
218 2
            ->addSelect('pc', 'pi', 'tr', 'ps')
219 2
            ->innerJoin('p.ProductClasses', 'pc')
220
            ->leftJoin('p.ProductImage', 'pi')
221
            ->leftJoin('pc.TaxRule', 'tr')
222
            ->leftJoin('pc.ProductStock', 'ps');
223
224 20
        // id
225
        if (isset($searchData['id']) && StringUtil::isNotBlank($searchData['id'])) {
226 2
            $id = preg_match('/^\d{0,10}$/', $searchData['id']) ? $searchData['id'] : null;
227 2
            $qb
228
                ->andWhere('p.id = :id OR p.name LIKE :likeid OR pc.code LIKE :likeid')
229
                ->setParameter('id', $id)
230
                ->setParameter('likeid', '%'.str_replace(['%', '_'], ['\\%', '\\_'], $searchData['id']).'%');
231 20
        }
232
233 1
        // code
234 1
        /*
235
        if (!empty($searchData['code']) && $searchData['code']) {
236
            $qb
237
                ->innerJoin('p.ProductClasses', 'pc')
238 20
                ->andWhere('pc.code LIKE :code')
239
                ->setParameter('code', '%' . $searchData['code'] . '%');
240
        }
241
242
        // name
243
        if (!empty($searchData['name']) && $searchData['name']) {
244
            $keywords = preg_split('/[\s ]+/u', $searchData['name'], -1, PREG_SPLIT_NO_EMPTY);
245 20
            foreach ($keywords as $keyword) {
246 3
                $qb
247 3
                    ->andWhere('p.name LIKE :name')
248 1
                    ->setParameter('name', '%' . $keyword . '%');
249 1
            }
250 2
        }
251 2
       */
252 2
253
        // category
254 View Code Duplication
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
255
            $Categories = $searchData['category_id']->getSelfAndDescendants();
256
            if ($Categories) {
257
                $qb
258
                    ->innerJoin('p.ProductCategories', 'pct')
259 20
                    ->innerJoin('pct.Category', 'c')
260 1
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
261
                    ->setParameter('Categories', $Categories);
262 1
            }
263 1
        }
264
265
        // status
266 20 View Code Duplication
        if (!empty($searchData['status']) && $searchData['status']) {
267 1
            $qb
268
                ->andWhere($qb->expr()->in('p.Status', ':Status'))
269 1
                ->setParameter('Status', $searchData['status']);
270
        }
271 1
272 1
        // link_status
273 View Code Duplication
        if (isset($searchData['link_status']) && !empty($searchData['link_status'])) {
274
            $qb
275
                ->andWhere($qb->expr()->in('p.Status', ':Status'))
276 20
                ->setParameter('Status', $searchData['link_status']);
277 1
        }
278
279 1
        // stock status
280 1
        if (isset($searchData['stock_status'])) {
281
            $qb
282 20
                ->andWhere('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
283 1
                ->setParameter('StockUnlimited', $searchData['stock_status']);
284
        }
285 1
286
        // stock status
287 1
        if (isset($searchData['stock']) && !empty($searchData['stock'])) {
288 1
            switch ($searchData['stock']) {
289
                case [ProductStock::IN_STOCK]:
290
                    $qb->andWhere('pc.stock_unlimited = true OR pc.stock > 0');
291
                    break;
292
                case [ProductStock::OUT_OF_STOCK]:
293 20
                    $qb->andWhere('pc.stock_unlimited = false AND pc.stock <= 0');
294
                    break;
295 20
                default:
296
                    // 共に選択された場合は全権該当するので検索条件に含めない
297
            }
298
        }
299
300
        // crate_date
301 View Code Duplication
        if (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
302
            $date = $searchData['create_date_start'];
303
            $qb
304
                ->andWhere('p.create_date >= :create_date_start')
305
                ->setParameter('create_date_start', $date);
306
        }
307
308 View Code Duplication
        if (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
309
            $date = clone $searchData['create_date_end'];
310
            $date = $date
311
                ->modify('+1 days');
312
            $qb
313
                ->andWhere('p.create_date < :create_date_end')
314
                ->setParameter('create_date_end', $date);
315
        }
316
317
        // update_date
318 View Code Duplication
        if (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
319
            $date = $searchData['update_date_start'];
320
            $qb
321
                ->andWhere('p.update_date >= :update_date_start')
322
                ->setParameter('update_date_start', $date);
323
        }
324 View Code Duplication
        if (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
325
            $date = clone $searchData['update_date_end'];
326
            $date = $date
327
                ->modify('+1 days');
328
            $qb
329
                ->andWhere('p.update_date < :update_date_end')
330
                ->setParameter('update_date_end', $date);
331
        }
332
333
        // Order By
334
        $qb
335
            ->orderBy('p.update_date', 'DESC');
336
337
        return $this->queries->customize(QueryKey::PRODUCT_SEARCH_ADMIN, $qb, $searchData);
338
    }
339
}
340