Failed Conditions
Branch experimental/sf (68db07)
by Kentaro
42:17 queued 33:39
created

CustomerRepository   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 380
Duplicated Lines 18.16 %

Coupling/Cohesion

Components 3
Dependencies 8

Test Coverage

Coverage 96%

Importance

Changes 0
Metric Value
dl 69
loc 380
rs 4.5599
c 0
b 0
f 0
ccs 144
cts 150
cp 0.96
wmc 58
lcom 3
cbo 8

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
A newCustomer() 0 11 1
F getQueryBuilderBySearchData() 51 151 41
A getUniqueSecretKey() 9 9 2
A getUniqueResetKey() 9 9 2
A getProvisionalCustomerBySecretKey() 0 7 1
A getRegularCustomerByEmail() 0 7 1
A getRegularCustomerByResetKey() 0 11 1
A getResetPassword() 0 4 1
B updateBuyData() 0 46 6
A getNonWithdrawingCustomers() 0 9 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 CustomerRepository 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 CustomerRepository, 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\ORM\EntityManagerInterface;
17
use Eccube\Common\EccubeConfig;
18
use Eccube\Doctrine\Query\Queries;
19
use Eccube\Entity\Customer;
20
use Eccube\Entity\Master\CustomerStatus;
21
use Eccube\Entity\Master\OrderStatus;
22
use Eccube\Util\StringUtil;
23
use Symfony\Bridge\Doctrine\RegistryInterface;
24
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
25
26
/**
27
 * CustomerRepository
28
 *
29
 * This class was generated by the Doctrine ORM. Add your own custom
30
 * repository methods below.
31
 */
32
class CustomerRepository extends AbstractRepository
33
{
34
    /**
35
     * @var Queries
36
     */
37
    protected $queries;
38
39
    /**
40
     * @var EntityManagerInterface
41
     */
42
    protected $entityManager;
43
44
    /**
45
     * @var OrderRepository
46
     */
47
    protected $orderRepository;
48
49
    /**
50
     * @var EccubeConfig
51
     */
52
    protected $eccubeConfig;
53
54
    /**
55
     * @var EncoderFactoryInterface
56
     */
57
    protected $encoderFactory;
58
59
    /**
60
     * CustomerRepository constructor.
61
     *
62
     * @param RegistryInterface $registry
0 ignored issues
show
introduced by
Expected 7 spaces after parameter type; 1 found
Loading history...
63
     * @param Queries $queries
0 ignored issues
show
introduced by
Expected 17 spaces after parameter type; 1 found
Loading history...
64
     * @param EntityManagerInterface $entityManager
0 ignored issues
show
introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
65
     * @param OrderRepository $orderRepository
0 ignored issues
show
introduced by
Expected 9 spaces after parameter type; 1 found
Loading history...
66
     * @param EncoderFactoryInterface $encoderFactory
67
     * @param EccubeConfig $eccubeConfig
0 ignored issues
show
introduced by
Expected 12 spaces after parameter type; 1 found
Loading history...
68
     */
69 723
    public function __construct(
70
        RegistryInterface $registry,
71
        Queries $queries,
72
        EntityManagerInterface $entityManager,
73
        OrderRepository $orderRepository,
74
        EncoderFactoryInterface $encoderFactory,
75
        EccubeConfig $eccubeConfig
76
    ) {
77 723
        parent::__construct($registry, Customer::class);
78
79 723
        $this->queries = $queries;
80 723
        $this->entityManager = $entityManager;
81 723
        $this->orderRepository = $orderRepository;
82 723
        $this->encoderFactory = $encoderFactory;
83 723
        $this->eccubeConfig = $eccubeConfig;
84
    }
85
86 8
    public function newCustomer()
87
    {
88 8
        $CustomerStatus = $this->getEntityManager()
89 8
            ->find(CustomerStatus::class, CustomerStatus::PROVISIONAL);
90
91 8
        $Customer = new \Eccube\Entity\Customer();
92
        $Customer
93 8
            ->setStatus($CustomerStatus);
94
95 8
        return $Customer;
96
    }
97
98 41
    public function getQueryBuilderBySearchData($searchData)
99
    {
100 41
        $qb = $this->createQueryBuilder('c')
101 41
            ->select('c');
102
103 41
        if (isset($searchData['multi']) && StringUtil::isNotBlank($searchData['multi'])) {
104
            //スペース除去
105 14
            $clean_key_multi = preg_replace('/\s+|[ ]+/u', '', $searchData['multi']);
106 14
            $id = preg_match('/^\d{0,10}$/', $clean_key_multi) ? $clean_key_multi : null;
107
            $qb
108 14
                ->andWhere('c.id = :customer_id OR CONCAT(c.name01, c.name02) LIKE :name OR CONCAT(c.kana01, c.kana02) LIKE :kana OR c.email LIKE :email')
109 14
                ->setParameter('customer_id', $id)
110 14
                ->setParameter('name', '%'.$clean_key_multi.'%')
111 14
                ->setParameter('kana', '%'.$clean_key_multi.'%')
112 14
                ->setParameter('email', '%'.$clean_key_multi.'%');
113
        }
114
115
        // Pref
116 41
        if (!empty($searchData['pref']) && $searchData['pref']) {
117
            $qb
118 1
                ->andWhere('c.Pref = :pref')
119 1
                ->setParameter('pref', $searchData['pref']->getId());
120
        }
121
122
        // sex
123 41
        if (!empty($searchData['sex']) && count($searchData['sex']) > 0) {
124 2
            $sexs = [];
125 2
            foreach ($searchData['sex'] as $sex) {
126 2
                $sexs[] = $sex->getId();
127
            }
128
129
            $qb
130 2
                ->andWhere($qb->expr()->in('c.Sex', ':sexs'))
131 2
                ->setParameter('sexs', $sexs);
132
        }
133
134 41
        if (!empty($searchData['birth_month']) && $searchData['birth_month']) {
135
            $qb
136 1
                ->andWhere('EXTRACT(MONTH FROM c.birth) = :birth_month')
137 1
                ->setParameter('birth_month', $searchData['birth_month']);
138
        }
139
140
        // birth
141 41
        if (!empty($searchData['birth_start']) && $searchData['birth_start']) {
142
            $qb
143 2
                ->andWhere('c.birth >= :birth_start')
144 2
                ->setParameter('birth_start', $searchData['birth_start']);
145
        }
146 41 View Code Duplication
        if (!empty($searchData['birth_end']) && $searchData['birth_end']) {
147 2
            $date = clone $searchData['birth_end'];
148 2
            $date->modify('+1 days');
149
            $qb
150 2
                ->andWhere('c.birth < :birth_end')
151 2
                ->setParameter('birth_end', $date);
152
        }
153
154
        // tel
155 41 View Code Duplication
        if (isset($searchData['tel']) && StringUtil::isNotBlank($searchData['tel'])) {
156 1
            $tel = preg_replace('/[^0-9]/ ', '', $searchData['tel']);
157
            $qb
158 1
                ->andWhere('CONCAT(c.tel01, c.tel02, c.tel03) LIKE :tel')
159 1
                ->setParameter('tel', '%'.$tel.'%');
160
        }
161
162
        // buy_total
163 41 View Code Duplication
        if (isset($searchData['buy_total_start']) && StringUtil::isNotBlank($searchData['buy_total_start'])) {
164
            $qb
165 1
                ->andWhere('c.buy_total >= :buy_total_start')
166 1
                ->setParameter('buy_total_start', $searchData['buy_total_start']);
167
        }
168 41 View Code Duplication
        if (isset($searchData['buy_total_end']) && StringUtil::isNotBlank($searchData['buy_total_end'])) {
169
            $qb
170 1
                ->andWhere('c.buy_total <= :buy_total_end')
171 1
                ->setParameter('buy_total_end', $searchData['buy_total_end']);
172
        }
173
174
        // buy_times
175 41
        if (!empty($searchData['buy_times_start']) && $searchData['buy_times_start']) {
176
            $qb
177 1
                ->andWhere('c.buy_times >= :buy_times_start')
178 1
                ->setParameter('buy_times_start', $searchData['buy_times_start']);
179
        }
180 41
        if (!empty($searchData['buy_times_end']) && $searchData['buy_times_end']) {
181
            $qb
182 1
                ->andWhere('c.buy_times <= :buy_times_end')
183 1
                ->setParameter('buy_times_end', $searchData['buy_times_end']);
184
        }
185
186
        // create_date
187 41
        if (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
188
            $qb
189 1
                ->andWhere('c.create_date >= :create_date_start')
190 1
                ->setParameter('create_date_start', $searchData['create_date_start']);
191
        }
192 41 View Code Duplication
        if (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
193 1
            $date = clone $searchData['create_date_end'];
194 1
            $date->modify('+1 days');
195
            $qb
196 1
                ->andWhere('c.create_date < :create_date_end')
197 1
                ->setParameter('create_date_end', $date);
198
        }
199
200
        // update_date
201 41
        if (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
202
            $qb
203 1
                ->andWhere('c.update_date >= :update_date_start')
204 1
                ->setParameter('update_date_start', $searchData['update_date_start']);
205
        }
206 41 View Code Duplication
        if (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
207 1
            $date = clone $searchData['update_date_end'];
208 1
            $date->modify('+1 days');
209
            $qb
210 1
                ->andWhere('c.update_date < :update_date_end')
211 1
                ->setParameter('update_date_end', $date);
212
        }
213
214
        // last_buy
215 41
        if (!empty($searchData['last_buy_start']) && $searchData['last_buy_start']) {
216
            $qb
217 1
                ->andWhere('c.last_buy_date >= :last_buy_start')
218 1
                ->setParameter('last_buy_start', $searchData['last_buy_start']);
219
        }
220 41 View Code Duplication
        if (!empty($searchData['last_buy_end']) && $searchData['last_buy_end']) {
221 1
            $date = clone $searchData['last_buy_end'];
222 1
            $date->modify('+1 days');
223
            $qb
224 1
                ->andWhere('c.last_buy_date < :last_buy_end')
225 1
                ->setParameter('last_buy_end', $date);
226
        }
227
228
        // status
229 41
        if (!empty($searchData['customer_status']) && count($searchData['customer_status']) > 0) {
230
            $qb
231 3
                ->andWhere($qb->expr()->in('c.Status', ':statuses'))
232 3
                ->setParameter('statuses', $searchData['customer_status']);
233
        }
234
235
        // buy_product_name
236 41 View Code Duplication
        if (isset($searchData['buy_product_name']) && StringUtil::isNotBlank($searchData['buy_product_name'])) {
237
            $qb
238 2
                ->leftJoin('c.Orders', 'o')
239 2
                ->leftJoin('o.OrderItems', 'oi')
240 2
                ->andWhere('oi.product_name LIKE :buy_product_name')
241 2
                ->setParameter('buy_product_name', '%'.$searchData['buy_product_name'].'%');
242
        }
243
244
        // Order By
245 41
        $qb->addOrderBy('c.update_date', 'DESC');
246
247 41
        return $this->queries->customize(QueryKey::CUSTOMER_SEARCH, $qb, $searchData);
248
    }
249
250
    /**
251
     * ユニークなシークレットキーを返す.
252
     *
253
     * @return string
254
     */
255 281 View Code Duplication
    public function getUniqueSecretKey()
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...
256
    {
257
        do {
258 281
            $key = StringUtil::random(32);
259 281
            $Customer = $this->findOneBy(['secret_key' => $key]);
260 281
        } while ($Customer);
261
262 281
        return $key;
263
    }
264
265
    /**
266
     * ユニークなパスワードリセットキーを返す
267
     *
268
     * @return string
269
     */
270 View Code Duplication
    public function getUniqueResetKey()
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...
271
    {
272
        do {
273
            $key = StringUtil::random(32);
274
            $Customer = $this->findOneBy(['reset_key' => $key]);
275
        } while ($Customer);
276
277
        return $key;
278
    }
279
280
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$secretKey" missing
Loading history...
281
     * 仮会員をシークレットキーで検索する.
282
     *
283
     * @param $secretKey
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
284
     *
285
     * @return null|Customer 見つからない場合はnullを返す.
286
     */
287 4
    public function getProvisionalCustomerBySecretKey($secretKey)
288
    {
289 4
        return $this->findOneBy([
290 4
            'secret_key' => $secretKey,
291 4
            'Status' => CustomerStatus::PROVISIONAL,
292
        ]);
293
    }
294
295
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$email" missing
Loading history...
296
     * 本会員をemailで検索する.
297
     *
298
     * @param $email
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
299
     *
300
     * @return null|Customer 見つからない場合はnullを返す.
301
     */
302 1
    public function getRegularCustomerByEmail($email)
303
    {
304 1
        return $this->findOneBy([
305 1
            'email' => $email,
306 1
            'Status' => CustomerStatus::REGULAR,
307
        ]);
308
    }
309
310
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$resetKey" missing
Loading history...
311
     * 本会員をリセットキーで検索する.
312
     *
313
     * @param $resetKey
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
314
     *
315
     * @return null|Customer 見つからない場合はnullを返す.
316
     */
317 3
    public function getRegularCustomerByResetKey($resetKey)
318
    {
319 3
        return $this->createQueryBuilder('c')
320 3
            ->where('c.reset_key = :reset_key AND c.Status = :status AND c.reset_expire >= :reset_expire')
321 3
            ->setParameter('reset_key', $resetKey)
322 3
            ->setParameter('status', CustomerStatus::REGULAR)
323 3
            ->setParameter('reset_expire', new \DateTime())
324 3
            ->setMaxResults(1)
325 3
            ->getQuery()
326 3
            ->getOneOrNullResult();
327
    }
328
329
    /**
330
     * リセット用パスワードを生成する.
331
     *
332
     * @return string
333
     */
334 2
    public function getResetPassword()
335
    {
336 2
        return StringUtil::random(8);
337
    }
338
339
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$orderStatusId" missing
Loading history...
340
     * 会員の初回購入時間、購入時間、購入回数、購入金額を更新する
341
     *
342
     * @param $app
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
343
     * @param  Customer $Customer
0 ignored issues
show
introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
introduced by
Doc comment for parameter $Customer does not match actual variable name $orderStatusId
Loading history...
344
     * @param  $orderStatusId
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
345
     * @param  $isNewOrder
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
346
     */
347 1
    public function updateBuyData(Customer $Customer, $orderStatusId)
348
    {
349
        // 会員の場合、初回購入時間・購入時間・購入回数・購入金額を更新
350
351
        $arr = [
352 1
            OrderStatus::NEW,
353
            OrderStatus::PAY_WAIT,
354
            OrderStatus::BACK_ORDER,
355
            OrderStatus::DELIVERED,
356
            OrderStatus::PAID,
357
        ];
358
359 1
        $result = $this->orderRepository->getCustomerCount($Customer, $arr);
360
361 1
        if (!empty($result)) {
362 1
            $data = $result[0];
363
364 1
            $now = new \DateTime();
365
366 1
            $firstBuyDate = $Customer->getFirstBuyDate();
367 1
            if (empty($firstBuyDate)) {
368 1
                $Customer->setFirstBuyDate($now);
369
            }
370
371 1
            if ($orderStatusId == OrderStatus::CANCEL ||
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
372 1
                $orderStatusId == OrderStatus::PENDING ||
373 1
                $orderStatusId == OrderStatus::PROCESSING
374
            ) {
375
                // キャンセル、決済処理中、購入処理中は購入時間は更新しない
376
            } else {
377 1
                $Customer->setLastBuyDate($now);
378
            }
379
380 1
            $Customer->setBuyTimes($data['buy_times']);
381 1
            $Customer->setBuyTotal($data['buy_total']);
382
        } else {
383
            // 受注データが存在しなければ初期化
384 1
            $Customer->setFirstBuyDate(null);
385 1
            $Customer->setLastBuyDate(null);
386 1
            $Customer->setBuyTimes(0);
387 1
            $Customer->setBuyTotal(0);
388
        }
389
390 1
        $this->entityManager->persist($Customer);
391 1
        $this->entityManager->flush();
392
    }
393
394
    /**
395
     * 仮会員, 本会員の会員を返す.
396
     * Eccube\Entity\CustomerのUniqueEntityバリデーションで使用しています.
397
     *
398
     * @param array $criteria
399
     *
400
     * @return Customer[]
401
     */
402 46
    public function getNonWithdrawingCustomers(array $criteria = [])
403
    {
404 46
        $criteria['Status'] = [
405 46
            CustomerStatus::PROVISIONAL,
406 46
            CustomerStatus::REGULAR,
407
        ];
408
409 46
        return $this->findBy($criteria);
410
    }
411
}
412