Failed Conditions
Push — experimental/3.1 ( 3d2ede...2919b9 )
by Yangsin
28:59
created

CustomerRepository   C

Complexity

Total Complexity 62

Size/Duplication

Total Lines 416
Duplicated Lines 20.67 %

Coupling/Cohesion

Components 3
Dependencies 9

Test Coverage

Coverage 99.35%

Importance

Changes 0
Metric Value
dl 86
loc 416
ccs 153
cts 154
cp 0.9935
rs 5.9493
c 0
b 0
f 0
wmc 62
lcom 3
cbo 9

13 Methods

Rating   Name   Duplication   Size   Complexity  
A newCustomer() 0 11 1
A loadUserByUsername() 0 13 2
A refreshUser() 8 8 2
A supportsClass() 0 4 1
F getQueryBuilderBySearchData() 60 150 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 47 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
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
24
25
namespace Eccube\Repository;
26
27
use Doctrine\ORM\EntityManager;
28
use Eccube\Annotation\Inject;
29
use Eccube\Annotation\Repository;
30
use Eccube\Doctrine\Query\Queries;
31
use Eccube\Entity\Customer;
32
use Eccube\Entity\Master\CustomerStatus;
33
use Eccube\Util\Str;
34
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
35
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
36
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
37
use Symfony\Component\Security\Core\User\UserInterface;
38
use Symfony\Component\Security\Core\User\UserProviderInterface;
39
40
/**
41
 * CustomerRepository
42
 *
43
 * This class was generated by the Doctrine ORM. Add your own custom
44
 * repository methods below.
45
 *
46
 * @Repository
47
 */
48
class CustomerRepository extends AbstractRepository implements UserProviderInterface
49
{
50
    /**
51
     * @Inject("eccube.queries")
52
     * @var Queries
53
     */
54
    protected $queries;
55
56
    /**
57
     * @Inject("orm.em")
58
     * @var EntityManager
59
     */
60
    protected $entityManager;
61
62
    /**
63
     * @Inject(OrderRepository::class)
64
     * @var OrderRepository
65
     */
66
    protected $orderRepository;
67
68
    /**
69
     * @Inject("config")
70
     * @var array
71
     */
72
    protected $appConfig;
73
74
    /**
75
     * @Inject("security.encoder_factory")
76
     * @var EncoderFactory
77
     */
78
    protected $encoderFactory;
79
80 8
    public function newCustomer()
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
81
    {
82 8
        $CustomerStatus = $this->getEntityManager()
83 8
            ->find(CustomerStatus::class, CustomerStatus::PROVISIONAL);
84
85 8
        $Customer = new \Eccube\Entity\Customer();
86
        $Customer
87 8
            ->setStatus($CustomerStatus);
88
89 8
        return $Customer;
90
    }
91
92
    /**
93
     * Loads the user for the given username.
94
     *
95
     * This method must throw UsernameNotFoundException if the user is not
96
     * found.
97
     *
98
     * @param string $username The username
99
     *
100
     * @return UserInterface
101
     *
102
     * @see UsernameNotFoundException
103
     *
104
     * @throws UsernameNotFoundException if the user is not found
105
     */
106 37
    public function loadUserByUsername($username)
107
    {
108 37
        $Customer = $this->findOneBy([
109 37
            'email' => $username,
110
            'Status' => CustomerStatus::REGULAR,
111
        ]);
112
113 37
        if (is_null($Customer)) {
114 1
            throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
115
        }
116
117 36
        return $Customer;
118
    }
119
120
    /**
121
     * Refreshes the user for the account interface.
122
     *
123
     * It is up to the implementation to decide if the user data should be
124
     * totally reloaded (e.g. from the database), or if the UserInterface
125
     * object can just be merged into some internal array of users / identity
126
     * map.
127
     *
128
     * @param UserInterface $user
129
     *
130
     * @return UserInterface
131
     *
132
     * @throws UnsupportedUserException if the account is not supported
133
     */
134 35 View Code Duplication
    public function refreshUser(UserInterface $user)
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...
135
    {
136 35
        if (!$user instanceof Customer) {
137 1
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
138
        }
139
140 34
        return $this->loadUserByUsername($user->getUsername());
141
    }
142
143
    /**
144
     * Whether this provider supports the given user class.
145
     *
146
     * @param string $class
147
     *
148
     * @return bool
149
     */
150 1
    public function supportsClass($class)
151
    {
152 1
        return $class === Customer::class;
153
    }
154
155 39
    public function getQueryBuilderBySearchData($searchData)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
156
    {
157 39
        $qb = $this->createQueryBuilder('c')
158 39
            ->select('c');
159
160 39
        if (isset($searchData['multi']) && Str::isNotBlank($searchData['multi'])) {
161
            //スペース除去
162 16
            $clean_key_multi = preg_replace('/\s+|[ ]+/u', '', $searchData['multi']);
163 16
            $id = preg_match('/^\d+$/', $clean_key_multi) ? $clean_key_multi : null;
164
            $qb
165 16
                ->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')
166 16
                ->setParameter('customer_id', $id)
167 16
                ->setParameter('name', '%'.$clean_key_multi.'%')
168 16
                ->setParameter('kana', '%'.$clean_key_multi.'%')
169 16
                ->setParameter('email', '%'.$clean_key_multi.'%');
170
        }
171
172
        // Pref
173 39 View Code Duplication
        if (!empty($searchData['pref']) && $searchData['pref']) {
174
            $qb
175 1
                ->andWhere('c.Pref = :pref')
176 1
                ->setParameter('pref', $searchData['pref']->getId());
177
        }
178
179
        // sex
180 39
        if (!empty($searchData['sex']) && count($searchData['sex']) > 0) {
181 2
            $sexs = array();
182 2
            foreach ($searchData['sex'] as $sex) {
183 2
                $sexs[] = $sex->getId();
184
            }
185
186
            $qb
187 2
                ->andWhere($qb->expr()->in('c.Sex', ':sexs'))
188 2
                ->setParameter('sexs', $sexs);
189
        }
190
191 39
        if (!empty($searchData['birth_month']) && $searchData['birth_month']) {
192
            $qb
193 1
                ->andWhere('EXTRACT(MONTH FROM c.birth) = :birth_month')
194 1
                ->setParameter('birth_month', $searchData['birth_month']);
195
        }
196
197
        // birth
198 39
        if (!empty($searchData['birth_start']) && $searchData['birth_start']) {
199
            $qb
200 2
                ->andWhere('c.birth >= :birth_start')
201 2
                ->setParameter('birth_start', $searchData['birth_start']);
202
        }
203 39 View Code Duplication
        if (!empty($searchData['birth_end']) && $searchData['birth_end']) {
204 2
            $date = clone $searchData['birth_end'];
205 2
            $date->modify('+1 days');
206
            $qb
207 2
                ->andWhere('c.birth < :birth_end')
208 2
                ->setParameter('birth_end', $date);
209
        }
210
211
        // tel
212 39 View Code Duplication
        if (isset($searchData['tel']) && Str::isNotBlank($searchData['tel'])) {
213
            $qb
214 1
                ->andWhere('CONCAT(c.tel01, c.tel02, c.tel03) LIKE :tel')
215 1
                ->setParameter('tel', '%'.$searchData['tel'].'%');
216
        }
217
218
        // buy_total
219 39 View Code Duplication
        if (isset($searchData['buy_total_start']) && Str::isNotBlank($searchData['buy_total_start'])) {
220
            $qb
221 1
                ->andWhere('c.buy_total >= :buy_total_start')
222 1
                ->setParameter('buy_total_start', $searchData['buy_total_start']);
223
        }
224 39 View Code Duplication
        if (isset($searchData['buy_total_end']) && Str::isNotBlank($searchData['buy_total_end'])) {
225
            $qb
226 1
                ->andWhere('c.buy_total <= :buy_total_end')
227 1
                ->setParameter('buy_total_end', $searchData['buy_total_end']);
228
        }
229
230
        // buy_times
231 39
        if (!empty($searchData['buy_times_start']) && $searchData['buy_times_start']) {
232
            $qb
233 1
                ->andWhere('c.buy_times >= :buy_times_start')
234 1
                ->setParameter('buy_times_start', $searchData['buy_times_start']);
235
        }
236 39
        if (!empty($searchData['buy_times_end']) && $searchData['buy_times_end']) {
237
            $qb
238 1
                ->andWhere('c.buy_times <= :buy_times_end')
239 1
                ->setParameter('buy_times_end', $searchData['buy_times_end']);
240
        }
241
242
        // create_date
243 39
        if (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
244
            $qb
245 1
                ->andWhere('c.create_date >= :create_date_start')
246 1
                ->setParameter('create_date_start', $searchData['create_date_start']);
247
        }
248 39 View Code Duplication
        if (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
249 1
            $date = clone $searchData['create_date_end'];
250 1
            $date->modify('+1 days');
251
            $qb
252 1
                ->andWhere('c.create_date < :create_date_end')
253 1
                ->setParameter('create_date_end', $date);
254
        }
255
256
        // update_date
257 39
        if (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
258
            $qb
259 1
                ->andWhere('c.update_date >= :update_date_start')
260 1
                ->setParameter('update_date_start', $searchData['update_date_start']);
261
        }
262 39 View Code Duplication
        if (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
263 1
            $date = clone $searchData['update_date_end'];
264 1
            $date->modify('+1 days');
265
            $qb
266 1
                ->andWhere('c.update_date < :update_date_end')
267 1
                ->setParameter('update_date_end', $date);
268
        }
269
270
        // last_buy
271 39 View Code Duplication
        if (!empty($searchData['last_buy_start']) && $searchData['last_buy_start']) {
272
            $qb
273 1
                ->andWhere('c.last_buy_date >= :last_buy_start')
274 1
                ->setParameter('last_buy_start', $searchData['last_buy_start']);
275
        }
276 39 View Code Duplication
        if (!empty($searchData['last_buy_end']) && $searchData['last_buy_end']) {
277 1
            $date = clone $searchData['last_buy_end'];
278 1
            $date->modify('+1 days');
279
            $qb
280 1
                ->andWhere('c.last_buy_date < :last_buy_end')
281 1
                ->setParameter('last_buy_end', $date);
282
        }
283
284
        // status
285 39
        if (!empty($searchData['customer_status']) && count($searchData['customer_status']) > 0) {
286
            $qb
287 2
                ->andWhere($qb->expr()->in('c.Status', ':statuses'))
288 2
                ->setParameter('statuses', $searchData['customer_status']);
289
        }
290
291
        // buy_product_name、buy_product_code
292 39 View Code Duplication
        if (isset($searchData['buy_product_code']) && Str::isNotBlank($searchData['buy_product_code'])) {
293
            $qb
294 1
                ->leftJoin('c.Orders', 'o')
295 1
                ->leftJoin('o.OrderDetails', 'od')
296 1
                ->andWhere('od.product_name LIKE :buy_product_name OR od.product_code LIKE :buy_product_name')
297 1
                ->setParameter('buy_product_name', '%'.$searchData['buy_product_code'].'%');
298
        }
299
300
        // Order By
301 39
        $qb->addOrderBy('c.update_date', 'DESC');
302
303 39
        return $this->queries->customize(QueryKey::CUSTOMER_SEARCH, $qb, $searchData);
304
    }
305
306
    /**
307
     * ユニークなシークレットキーを返す.
308
     *
309
     * @return string
310
     */
311 234 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...
312
    {
313
        do {
314 234
            $key = Str::random(32);
315 234
            $Customer = $this->findOneBy(['secret_key' => $key]);
316 234
        } while ($Customer);
317
318 234
        return $key;
319
    }
320
321
    /**
322
     * ユニークなパスワードリセットキーを返す
323
     *
324
     * @return string
325
     */
326 1 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...
327
    {
328
        do {
329 1
            $key = Str::random(32);
330 1
            $Customer = $this->findOneBy(['reset_key' => $key]);
331 1
        } while ($Customer);
332
333 1
        return $key;
334
    }
335
336
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$secretKey" missing
Loading history...
337
     * 仮会員をシークレットキーで検索する.
338
     *
339
     * @param $secretKey
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
340
     * @return null|Customer 見つからない場合はnullを返す.
341
     */
342 4
    public function getProvisionalCustomerBySecretKey($secretKey)
343
    {
344 4
        return $this->findOneBy([
345 4
            'secret_key' => $secretKey,
346
            'Status' => CustomerStatus::PROVISIONAL,
347
        ]);
348
    }
349
350
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$email" missing
Loading history...
351
     * 本会員をemailで検索する.
352
     *
353
     * @param $email
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
354
     * @return null|Customer 見つからない場合はnullを返す.
355
     */
356 2
    public function getRegularCustomerByEmail($email)
357
    {
358 2
        return $this->findOneBy([
359 2
            'email' => $email,
360
            'Status' => CustomerStatus::REGULAR,
361
        ]);
362
    }
363
364
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$resetKey" missing
Loading history...
365
     * 本会員をリセットキーで検索する.
366
     *
367
     * @param $resetKey
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
368
     * @return null|Customer 見つからない場合はnullを返す.
369
     */
370 4
    public function getRegularCustomerByResetKey($resetKey)
371
    {
372 4
        return $this->createQueryBuilder('c')
373 4
            ->where('c.reset_key = :reset_key AND c.Status = :status AND c.reset_expire >= :reset_expire')
374 4
            ->setParameter('reset_key', $resetKey)
375 4
            ->setParameter('status', CustomerStatus::REGULAR)
376 4
            ->setParameter('reset_expire', new \DateTime())
377 4
            ->setMaxResults(1)
378 4
            ->getQuery()
379 4
            ->getOneOrNullResult();
380
    }
381
382
    /**
383
     * リセット用パスワードを生成する.
384
     *
385
     * @return string
386
     */
387 3
    public function getResetPassword()
388
    {
389 3
        return Str::random(8);
390
    }
391
392
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$orderStatusId" missing
Loading history...
393
     * 会員の初回購入時間、購入時間、購入回数、購入金額を更新する
394
     *
395
     * @param $app
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
396
     * @param  Customer $Customer
0 ignored issues
show
introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
397
     * @param  $orderStatusId
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
398
     */
399 2
    public function updateBuyData($app, Customer $Customer, $orderStatusId)
0 ignored issues
show
Unused Code introduced by
The parameter $app is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
400
    {
401
        // 会員の場合、初回購入時間・購入時間・購入回数・購入金額を更新
402
403
        $arr = array(
404 2
            $this->appConfig['order_new'],
405 2
            $this->appConfig['order_pay_wait'],
406 2
            $this->appConfig['order_back_order'],
407 2
            $this->appConfig['order_deliv'],
408 2
            $this->appConfig['order_pre_end'],
409
        );
410
411 2
        $result = $this->orderRepository->getCustomerCount($Customer, $arr);
412
413 2
        if (!empty($result)) {
414 1
            $data = $result[0];
415
416 1
            $now = new \DateTime();
417
418 1
            $firstBuyDate = $Customer->getFirstBuyDate();
419 1
            if (empty($firstBuyDate)) {
420 1
                $Customer->setFirstBuyDate($now);
421
            }
422
423 1
            if ($orderStatusId == $this->appConfig['order_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...
424 1
                $orderStatusId == $this->appConfig['order_pending'] ||
425 1
                $orderStatusId == $this->appConfig['order_processing']
426
            ) {
427
                // キャンセル、決済処理中、購入処理中は購入時間は更新しない
428
            } else {
429 1
                $Customer->setLastBuyDate($now);
430
            }
431
432 1
            $Customer->setBuyTimes($data['buy_times']);
433 1
            $Customer->setBuyTotal($data['buy_total']);
434
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
435
        } else {
436
            // 受注データが存在しなければ初期化
437 2
            $Customer->setFirstBuyDate(null);
438 2
            $Customer->setLastBuyDate(null);
439 2
            $Customer->setBuyTimes(0);
440 2
            $Customer->setBuyTotal(0);
441
        }
442
443 2
        $this->entityManager->persist($Customer);
444 2
        $this->entityManager->flush();
445
    }
446
447
    /**
448
     * 仮会員, 本会員の会員を返す.
449
     * Eccube\Entity\CustomerのUniqueEntityバリデーションで使用しています.
450
     *
451
     * @param array $criteria
452
     * @return Customer[]
453
     */
454 46
    public function getNonWithdrawingCustomers(array $criteria = [])
455
    {
456 46
        $criteria['Status'] = [
457
            CustomerStatus::PROVISIONAL,
458
            CustomerStatus::REGULAR,
459
        ];
460
461 46
        return $this->findBy($criteria);
462
    }
463
}
464