Failed Conditions
Pull Request — 4.0 (#4619)
by Hideki
05:05
created

CustomerRepository   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 366
Duplicated Lines 21.86 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 94.48%

Importance

Changes 0
Metric Value
dl 80
loc 366
ccs 137
cts 145
cp 0.9448
rs 3.2
c 0
b 0
f 0
wmc 65
lcom 2
cbo 8

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
A newCustomer() 0 11 1
F getQueryBuilderBySearchData() 62 184 53
A getUniqueSecretKey() 9 9 2
A getUniqueResetKey() 9 9 2
A getProvisionalCustomerBySecretKey() 0 7 1
A getRegularCustomerByEmail() 0 7 1
A getRegularCustomerByResetKey() 0 18 2
A getResetPassword() 0 4 1
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) 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\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\Util\StringUtil;
22
use Symfony\Bridge\Doctrine\RegistryInterface;
23
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
24
25
/**
26
 * CustomerRepository
27
 *
28
 * This class was generated by the Doctrine ORM. Add your own custom
29
 * repository methods below.
30
 */
31
class CustomerRepository extends AbstractRepository
32
{
33
    /**
34
     * @var Queries
35
     */
36
    protected $queries;
37
38
    /**
39
     * @var EntityManagerInterface
40
     */
41
    protected $entityManager;
42
43
    /**
44
     * @var OrderRepository
45
     */
46
    protected $orderRepository;
47
48
    /**
49
     * @var EccubeConfig
50
     */
51
    protected $eccubeConfig;
52
53
    /**
54
     * @var EncoderFactoryInterface
55
     */
56
    protected $encoderFactory;
57
58
    /**
59
     * CustomerRepository constructor.
60
     *
61
     * @param RegistryInterface $registry
62
     * @param Queries $queries
63
     * @param EntityManagerInterface $entityManager
64
     * @param OrderRepository $orderRepository
65
     * @param EncoderFactoryInterface $encoderFactory
66
     * @param EccubeConfig $eccubeConfig
67
     */
68
    public function __construct(
69 804
        RegistryInterface $registry,
70
        Queries $queries,
71
        EntityManagerInterface $entityManager,
72
        OrderRepository $orderRepository,
73
        EncoderFactoryInterface $encoderFactory,
74
        EccubeConfig $eccubeConfig
75
    ) {
76
        parent::__construct($registry, Customer::class);
77 804
78
        $this->queries = $queries;
79 804
        $this->entityManager = $entityManager;
80 804
        $this->orderRepository = $orderRepository;
81 804
        $this->encoderFactory = $encoderFactory;
82 804
        $this->eccubeConfig = $eccubeConfig;
83 804
    }
84
85
    public function newCustomer()
86 8
    {
87
        $CustomerStatus = $this->getEntityManager()
88 8
            ->find(CustomerStatus::class, CustomerStatus::PROVISIONAL);
89 8
90
        $Customer = new \Eccube\Entity\Customer();
91 8
        $Customer
92
            ->setStatus($CustomerStatus);
0 ignored issues
show
Bug introduced by
It seems like $CustomerStatus defined by $this->getEntityManager(...merStatus::PROVISIONAL) on line 87 can also be of type object; however, Eccube\Entity\Customer::setStatus() does only seem to accept null|object<Eccube\Entity\Master\CustomerStatus>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
93 8
94
        return $Customer;
95 8
    }
96
97
    public function getQueryBuilderBySearchData($searchData)
98 40
    {
99
        $qb = $this->createQueryBuilder('c')
100 40
            ->select('c');
101 40
102
        if (isset($searchData['multi']) && StringUtil::isNotBlank($searchData['multi'])) {
103 40
            //スペース除去
104
            $clean_key_multi = preg_replace('/\s+|[ ]+/u', '', $searchData['multi']);
105 13
            $id = preg_match('/^\d{0,10}$/', $clean_key_multi) ? $clean_key_multi : null;
106 13
            $qb
107
                ->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')
108 13
                ->setParameter('customer_id', $id)
109 13
                ->setParameter('name', '%'.$clean_key_multi.'%')
110 13
                ->setParameter('kana', '%'.$clean_key_multi.'%')
111 13
                ->setParameter('email', '%'.$clean_key_multi.'%');
112 13
        }
113
114
        // Pref
115
        if (!empty($searchData['pref']) && $searchData['pref']) {
116 40
            $qb
117
                ->andWhere('c.Pref = :pref')
118 1
                ->setParameter('pref', $searchData['pref']->getId());
119 1
        }
120
121
        // sex
122
        if (!empty($searchData['sex']) && count($searchData['sex']) > 0) {
123 40
            $sexs = [];
124 2
            foreach ($searchData['sex'] as $sex) {
125 2
                $sexs[] = $sex->getId();
126 2
            }
127
128
            $qb
129
                ->andWhere($qb->expr()->in('c.Sex', ':sexs'))
130 2
                ->setParameter('sexs', $sexs);
131 2
        }
132
133
        if (!empty($searchData['birth_month']) && $searchData['birth_month']) {
134 40
            $qb
135
                ->andWhere('EXTRACT(MONTH FROM c.birth) = :birth_month')
136 1
                ->setParameter('birth_month', $searchData['birth_month']);
137 1
        }
138
139
        // birth
140
        if (!empty($searchData['birth_start']) && $searchData['birth_start']) {
141 40
            $qb
142
                ->andWhere('c.birth >= :birth_start')
143 2
                ->setParameter('birth_start', $searchData['birth_start']);
144 2
        }
145
        if (!empty($searchData['birth_end']) && $searchData['birth_end']) {
146 40
            $date = clone $searchData['birth_end'];
147 2
            $date->modify('+1 days');
148 2
            $qb
149
                ->andWhere('c.birth < :birth_end')
150 2
                ->setParameter('birth_end', $date);
151 2
        }
152
153
        // tel
154 View Code Duplication
        if (isset($searchData['phone_number']) && StringUtil::isNotBlank($searchData['phone_number'])) {
155 40
            $tel = preg_replace('/[^0-9]/ ', '', $searchData['phone_number']);
156 1
            $qb
157
                ->andWhere('c.phone_number LIKE :phone_number')
158 1
                ->setParameter('phone_number', '%'.$tel.'%');
159 1
        }
160
161
        // buy_total
162 View Code Duplication
        if (isset($searchData['buy_total_start']) && StringUtil::isNotBlank($searchData['buy_total_start'])) {
163 40
            $qb
164
                ->andWhere('c.buy_total >= :buy_total_start')
165 1
                ->setParameter('buy_total_start', $searchData['buy_total_start']);
166 1
        }
167 View Code Duplication
        if (isset($searchData['buy_total_end']) && StringUtil::isNotBlank($searchData['buy_total_end'])) {
168 40
            $qb
169
                ->andWhere('c.buy_total <= :buy_total_end')
170 1
                ->setParameter('buy_total_end', $searchData['buy_total_end']);
171 1
        }
172
173
        // buy_times
174 View Code Duplication
        if (isset($searchData['buy_times_start']) && StringUtil::isNotBlank($searchData['buy_times_start'])) {
175 40
            $qb
176
                ->andWhere('c.buy_times >= :buy_times_start')
177 1
                ->setParameter('buy_times_start', $searchData['buy_times_start']);
178 1
        }
179 View Code Duplication
        if (isset($searchData['buy_times_end']) && StringUtil::isNotBlank($searchData['buy_times_end'])) {
180 40
            $qb
181
                ->andWhere('c.buy_times <= :buy_times_end')
182 1
                ->setParameter('buy_times_end', $searchData['buy_times_end']);
183 1
        }
184
185
        // create_date
186
        if (!empty($searchData['create_datetime_start']) && $searchData['create_datetime_start']) {
187 40
            $date = $searchData['create_datetime_start'];
188
            $qb
189 1
                ->andWhere('c.create_date >= :create_date_start')
190 1
                ->setParameter('create_date_start', $date);
191
        } elseif (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
192 40
            $qb
193 1
                ->andWhere('c.create_date >= :create_date_start')
194 1
                ->setParameter('create_date_start', $searchData['create_date_start']);
195
        }
196 1
197 1 View Code Duplication
        if (!empty($searchData['create_datetime_end']) && $searchData['create_datetime_end']) {
198
            $date = $searchData['create_datetime_end'];
199
            $qb
200
                ->andWhere('c.create_date < :create_date_end')
201 40
                ->setParameter('create_date_end', $date);
202
        } elseif (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
203 1
            $date = clone $searchData['create_date_end'];
204 1
            $date->modify('+1 days');
205
            $qb
206 40
                ->andWhere('c.create_date < :create_date_end')
207 1
                ->setParameter('create_date_end', $date);
208 1
        }
209
210 1
        // update_date
211 1
        if (!empty($searchData['update_datetime_start']) && $searchData['update_datetime_start']) {
212
            $date = $searchData['update_datetime_start'];
213
            $qb
214
                ->andWhere('c.update_date >= :update_date_start')
215 40
                ->setParameter('update_date_start', $date);
216
        } elseif (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
217 1
            $qb
218 1
                ->andWhere('c.update_date >= :update_date_start')
219
                ->setParameter('update_date_start', $searchData['update_date_start']);
220 40
        }
221 1
222 1 View Code Duplication
        if (!empty($searchData['update_datetime_end']) && $searchData['update_datetime_end']) {
223
            $date = $searchData['update_datetime_end'];
224 1
            $qb
225 1
                ->andWhere('c.update_date < :update_date_end')
226
                ->setParameter('update_date_end', $date);
227
        } elseif (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
228
            $date = clone $searchData['update_date_end'];
229 40
            $date->modify('+1 days');
230
            $qb
231 4
                ->andWhere('c.update_date < :update_date_end')
232 4
                ->setParameter('update_date_end', $date);
233
        }
234
235
        // last_buy
236 40
        if (!empty($searchData['last_buy_datetime_start']) && $searchData['last_buy_datetime_start']) {
237
            $date = $searchData['last_buy_datetime_start'];
238 2
            $qb
239 2
                ->andWhere('c.last_buy_date >= :last_buy_start')
240 2
                ->setParameter('last_buy_start', $date);
241 2
        } elseif (!empty($searchData['last_buy_start']) && $searchData['last_buy_start']) {
242
            $qb
243
                ->andWhere('c.last_buy_date >= :last_buy_start')
244
                ->setParameter('last_buy_start', $searchData['last_buy_start']);
245 40
        }
246
247 40 View Code Duplication
        if (!empty($searchData['last_buy_datetime_end']) && $searchData['last_buy_datetime_end']) {
248
            $date = $searchData['last_buy_datetime_end'];
249
            $qb
250
                ->andWhere('c.last_buy_date < :last_buy_end')
251
                ->setParameter('last_buy_end', $date);
252
        } elseif (!empty($searchData['last_buy_end']) && $searchData['last_buy_end']) {
253
            $date = clone $searchData['last_buy_end'];
254
            $date->modify('+1 days');
255 300
            $qb
256
                ->andWhere('c.last_buy_date < :last_buy_end')
257
                ->setParameter('last_buy_end', $date);
258 300
        }
259 300
260 300
        // status
261
        if (!empty($searchData['customer_status']) && count($searchData['customer_status']) > 0) {
262 300
            $qb
263
                ->andWhere($qb->expr()->in('c.Status', ':statuses'))
264
                ->setParameter('statuses', $searchData['customer_status']);
265
        }
266
267
        // buy_product_name
268
        if (isset($searchData['buy_product_name']) && StringUtil::isNotBlank($searchData['buy_product_name'])) {
269
            $qb
270
                ->leftJoin('c.Orders', 'o')
271
                ->leftJoin('o.OrderItems', 'oi')
272
                ->andWhere('oi.product_name LIKE :buy_product_name')
273
                ->setParameter('buy_product_name', '%'.$searchData['buy_product_name'].'%');
274
        }
275
276
        // Order By
277
        $qb->addOrderBy('c.update_date', 'DESC');
278
279
        return $this->queries->customize(QueryKey::CUSTOMER_SEARCH, $qb, $searchData);
280
    }
281
282
    /**
283
     * ユニークなシークレットキーを返す.
284
     *
285
     * @return string
286
     */
287 4 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...
288
    {
289 4
        do {
290 4
            $key = StringUtil::random(32);
291 4
            $Customer = $this->findOneBy(['secret_key' => $key]);
292
        } while ($Customer);
293
294
        return $key;
295
    }
296
297
    /**
298
     * ユニークなパスワードリセットキーを返す
299
     *
300
     * @return string
301
     */
302 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...
303
    {
304 1
        do {
305 1
            $key = StringUtil::random(32);
306 1
            $Customer = $this->findOneBy(['reset_key' => $key]);
307
        } while ($Customer);
308
309
        return $key;
310
    }
311
312
    /**
313
     * 仮会員をシークレットキーで検索する.
314
     *
315
     * @param $secretKey
316
     *
317 3
     * @return null|Customer 見つからない場合はnullを返す.
318
     */
319 3
    public function getProvisionalCustomerBySecretKey($secretKey)
320 3
    {
321 3
        return $this->findOneBy([
322 3
            'secret_key' => $secretKey,
323 3
            'Status' => CustomerStatus::PROVISIONAL,
324 3
        ]);
325 3
    }
326 3
327
    /**
328
     * 本会員をemailで検索する.
329
     *
330
     * @param $email
331
     *
332
     * @return null|Customer 見つからない場合はnullを返す.
333
     */
334 2
    public function getRegularCustomerByEmail($email)
335
    {
336 2
        return $this->findOneBy([
337
            'email' => $email,
338
            'Status' => CustomerStatus::REGULAR,
339
        ]);
340
    }
341
342
    /**
343
     * 本会員をリセットキー、またはリセットキーとメールアドレスで検索する.
344
     *
345 1
     * @param $resetKey
346
     * @param $email
347
     *
348
     * @return null|Customer 見つからない場合はnullを返す.
349
     */
350 1
    public function getRegularCustomerByResetKey($resetKey, $email = null)
351
    {
352
        $qb = $this->createQueryBuilder('c')
353
            ->where('c.reset_key = :reset_key AND c.Status = :status AND c.reset_expire >= :reset_expire')
354
            ->setParameter('reset_key', $resetKey)
355
            ->setParameter('status', CustomerStatus::REGULAR)
356
            ->setParameter('reset_expire', new \DateTime());
357 1
358
        if ($email) {
359 1
            $qb
360 1
                ->andWhere('c.email = :email')
361
                ->setParameter('email', $email);
362 1
        }
363
364 1
        return $qb->setMaxResults(1)
365 1
            ->getQuery()
366 1
            ->getOneOrNullResult();
367
    }
368
369 1
    /**
370 1
     * リセット用パスワードを生成する.
371
     *
372
     * @return string
373
     */
374
    public function getResetPassword()
375
    {
376
        return StringUtil::random(8);
377
    }
378 1
379 1
    /**
380
     * 仮会員, 本会員の会員を返す.
381
     * Eccube\Entity\CustomerのUniqueEntityバリデーションで使用しています.
382 1
     *
383 1
     * @param array $criteria
384 1
     *
385 1
     * @return Customer[]
386
     */
387
    public function getNonWithdrawingCustomers(array $criteria = [])
388 1
    {
389 1
        $criteria['Status'] = [
390
            CustomerStatus::PROVISIONAL,
391
            CustomerStatus::REGULAR,
392
        ];
393
394
        return $this->findBy($criteria);
395
    }
396
}
397