Failed Conditions
Push — dev/local-plugin-cache ( 16070e )
by Kiyotaka
08:36 queued 47s
created

ProductRepository   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 311
Duplicated Lines 18.97 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 59
loc 311
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 28 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
     */
49
    public function __construct(
50
        RegistryInterface $registry,
51
        Queries $queries,
52
        EccubeConfig $eccubeConfig
53
    ) {
54
        parent::__construct($registry, Product::class);
55
        $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
     */
66
    public function findWithSortedClassCategories($productId)
67
    {
68
        $qb = $this->createQueryBuilder('p');
69
        $qb->addSelect(['pc', 'cc1', 'cc2', 'pi', 'pt'])
70
            ->innerJoin('p.ProductClasses', 'pc')
71
            ->leftJoin('pc.ClassCategory1', 'cc1')
72
            ->leftJoin('pc.ClassCategory2', 'cc2')
73
            ->leftJoin('p.ProductImage', 'pi')
74
            ->leftJoin('p.ProductTag', 'pt')
75
            ->where('p.id = :id')
76
            ->andWhere('pc.visible = :visible')
77
            ->setParameter('id', $productId)
78
            ->setParameter('visible', true)
79
            ->orderBy('cc1.sort_no', 'DESC')
80
            ->addOrderBy('cc2.sort_no', 'DESC');
81
82
        $product = $qb
83
            ->getQuery()
84
            ->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
     *
95
     * @return ArrayCollection|array
96
     */
97
    public function findProductsWithSortedClassCategories(array $ids, $indexBy = null)
98
    {
99
        if (count($ids) < 1) {
100
            return [];
101
        }
102
        $qb = $this->createQueryBuilder('p', $indexBy);
103
        $qb->addSelect(['pc', 'cc1', 'cc2', 'pi', 'pt', 'tr', 'ps'])
104
            ->innerJoin('p.ProductClasses', 'pc')
105
            // XXX Joined 'TaxRule' and 'ProductStock' to prevent lazy loading
106
            ->leftJoin('pc.TaxRule', 'tr')
107
            ->innerJoin('pc.ProductStock', 'ps')
108
            ->leftJoin('pc.ClassCategory1', 'cc1')
109
            ->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
            ->setParameter('visible', true)
115
            ->orderBy('cc1.sort_no', 'DESC')
116
            ->addOrderBy('cc2.sort_no', 'DESC');
117
118
        $products = $qb
119
            ->getQuery()
120
            ->useResultCache(true, $this->eccubeConfig['eccube_result_cache_lifetime_short'])
121
            ->getResult();
122
123
        return $products;
124
    }
125
126
    /**
127
     * get query builder.
128
     *
129
     * @param  array $searchData
130
     *
131
     * @return \Doctrine\ORM\QueryBuilder
132
     */
133
    public function getQueryBuilderBySearchData($searchData)
134
    {
135
        $qb = $this->createQueryBuilder('p')
136
            ->andWhere('p.Status = 1');
137
138
        // category
139
        $categoryJoin = false;
140 View Code Duplication
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
141
            $Categories = $searchData['category_id']->getSelfAndDescendants();
142
            if ($Categories) {
143
                $qb
144
                    ->innerJoin('p.ProductCategories', 'pct')
145
                    ->innerJoin('pct.Category', 'c')
146
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
147
                    ->setParameter('Categories', $Categories);
148
                $categoryJoin = true;
149
            }
150
        }
151
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
156
            foreach ($keywords as $index => $keyword) {
157
                $key = sprintf('keyword%s', $index);
158
                $qb
159
                    ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR 
160
                        NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR 
161
                        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
            }
165
        }
166
167
        // 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
            $qb->addOrderBy('p.id', 'DESC');
178
        // 価格高い順
179
        } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_higher']) {
180
            $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
181
            $qb->innerJoin('p.ProductClasses', 'pc');
182
            $qb->andWhere('pc.visible = true');
183
            $qb->groupBy('p.id');
184
            $qb->orderBy('price02_max', 'DESC');
185
            $qb->addOrderBy('p.id', 'DESC');
186
        // 新着順
187
        } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_newer']) {
188
            // 在庫切れ商品非表示の設定が有効時対応
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
     * @param  array $searchData
213
     *
214
     * @return \Doctrine\ORM\QueryBuilder
215
     */
216
    public function getQueryBuilderBySearchDataForAdmin($searchData)
217
    {
218
        $qb = $this->createQueryBuilder('p')
219
            ->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
225
        // id
226
        if (isset($searchData['id']) && StringUtil::isNotBlank($searchData['id'])) {
227
            $id = preg_match('/^\d{0,10}$/', $searchData['id']) ? $searchData['id'] : null;
228
            $qb
229
                ->andWhere('p.id = :id OR p.name LIKE :likeid OR pc.code LIKE :likeid')
230
                ->setParameter('id', $id)
231
                ->setParameter('likeid', '%'.str_replace(['%', '_'], ['\\%', '\\_'], $searchData['id']).'%');
232
        }
233
234
        // code
235
        /*
236
        if (!empty($searchData['code']) && $searchData['code']) {
237
            $qb
238
                ->innerJoin('p.ProductClasses', 'pc')
239
                ->andWhere('pc.code LIKE :code')
240
                ->setParameter('code', '%' . $searchData['code'] . '%');
241
        }
242
243
        // name
244
        if (!empty($searchData['name']) && $searchData['name']) {
245
            $keywords = preg_split('/[\s ]+/u', $searchData['name'], -1, PREG_SPLIT_NO_EMPTY);
246
            foreach ($keywords as $keyword) {
247
                $qb
248
                    ->andWhere('p.name LIKE :name')
249
                    ->setParameter('name', '%' . $keyword . '%');
250
            }
251
        }
252
       */
253
254
        // category
255 View Code Duplication
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
256
            $Categories = $searchData['category_id']->getSelfAndDescendants();
257
            if ($Categories) {
258
                $qb
259
                    ->innerJoin('p.ProductCategories', 'pct')
260
                    ->innerJoin('pct.Category', 'c')
261
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
262
                    ->setParameter('Categories', $Categories);
263
            }
264
        }
265
266
        // status
267 View Code Duplication
        if (!empty($searchData['status']) && $searchData['status']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
268
            $qb
269
                ->andWhere($qb->expr()->in('p.Status', ':Status'))
270
                ->setParameter('Status', $searchData['status']);
271
        }
272
273
        // link_status
274 View Code Duplication
        if (isset($searchData['link_status']) && !empty($searchData['link_status'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
275
            $qb
276
                ->andWhere($qb->expr()->in('p.Status', ':Status'))
277
                ->setParameter('Status', $searchData['link_status']);
278
        }
279
280
        // stock status
281
        if (isset($searchData['stock_status'])) {
282
            $qb
283
                ->andWhere('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
284
                ->setParameter('StockUnlimited', $searchData['stock_status']);
285
        }
286
287
        // stock status
288
        if (isset($searchData['stock']) && !empty($searchData['stock'])) {
289
            switch ($searchData['stock']) {
290
                case [ProductStock::IN_STOCK]:
291
                    $qb->andWhere('pc.stock_unlimited = true OR pc.stock > 0');
292
                    break;
293
                case [ProductStock::OUT_OF_STOCK]:
294
                    $qb->andWhere('pc.stock_unlimited = false AND pc.stock <= 0');
295
                    break;
296
                default:
297
                    // 共に選択された場合は全権該当するので検索条件に含めない
298
            }
299
        }
300
301
        // crate_date
302 View Code Duplication
        if (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
303
            $date = $searchData['create_date_start'];
304
            $qb
305
                ->andWhere('p.create_date >= :create_date_start')
306
                ->setParameter('create_date_start', $date);
307
        }
308
309 View Code Duplication
        if (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
310
            $date = clone $searchData['create_date_end'];
311
            $date = $date
312
                ->modify('+1 days');
313
            $qb
314
                ->andWhere('p.create_date < :create_date_end')
315
                ->setParameter('create_date_end', $date);
316
        }
317
318
        // update_date
319 View Code Duplication
        if (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
320
            $date = $searchData['update_date_start'];
321
            $qb
322
                ->andWhere('p.update_date >= :update_date_start')
323
                ->setParameter('update_date_start', $date);
324
        }
325 View Code Duplication
        if (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
326
            $date = clone $searchData['update_date_end'];
327
            $date = $date
328
                ->modify('+1 days');
329
            $qb
330
                ->andWhere('p.update_date < :update_date_end')
331
                ->setParameter('update_date_end', $date);
332
        }
333
334
        // Order By
335
        $qb
336
            ->orderBy('p.update_date', 'DESC');
337
338
        return $this->queries->customize(QueryKey::PRODUCT_SEARCH_ADMIN, $qb, $searchData);
339
    }
340
}
341