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 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 |
||
49 | class CustomerRepository extends AbstractRepository implements UserProviderInterface |
||
50 | { |
||
51 | /** |
||
52 | * @Inject("eccube.queries") |
||
53 | * @var Queries |
||
54 | */ |
||
55 | protected $queries; |
||
56 | |||
57 | /** |
||
58 | * @Inject("orm.em") |
||
59 | * @var EntityManager |
||
60 | */ |
||
61 | protected $entityManager; |
||
62 | |||
63 | /** |
||
64 | * @Inject(OrderRepository::class) |
||
65 | * @var OrderRepository |
||
66 | */ |
||
67 | protected $orderRepository; |
||
68 | |||
69 | /** |
||
70 | * @Inject("config") |
||
71 | * @var array |
||
72 | */ |
||
73 | protected $appConfig; |
||
74 | |||
75 | /** |
||
76 | * @Inject("security.encoder_factory") |
||
77 | * @var EncoderFactory |
||
78 | */ |
||
79 | protected $encoderFactory; |
||
80 | |||
81 | 8 | public function newCustomer() |
|
|
|||
82 | { |
||
83 | 8 | $Customer = new \Eccube\Entity\Customer(); |
|
84 | 8 | $Status = $this->getEntityManager() |
|
85 | 8 | ->getRepository('Eccube\Entity\Master\CustomerStatus') |
|
86 | 8 | ->find(1); |
|
87 | |||
88 | $Customer |
||
89 | 8 | ->setStatus($Status); |
|
90 | |||
91 | 8 | return $Customer; |
|
92 | } |
||
93 | |||
94 | /** |
||
95 | * Loads the user for the given username. |
||
96 | * |
||
97 | * This method must throw UsernameNotFoundException if the user is not |
||
98 | * found. |
||
99 | * |
||
100 | * @param string $username The username |
||
101 | * |
||
102 | * @return UserInterface |
||
103 | * |
||
104 | * @see UsernameNotFoundException |
||
105 | * |
||
106 | * @throws UsernameNotFoundException if the user is not found |
||
107 | */ |
||
108 | 38 | public function loadUserByUsername($username) |
|
109 | { |
||
110 | // 本会員ステータスの会員のみ有効. |
||
111 | 38 | $query = $this->createQueryBuilder('c') |
|
112 | 38 | ->where('c.email = :email') |
|
113 | 38 | ->leftJoin('c.Status', 's') |
|
114 | 38 | ->andWhere('s.id = :status') |
|
115 | 38 | ->setParameters(array( |
|
116 | 38 | 'email' => $username, |
|
117 | 'status' => CustomerStatus::REGULAR, |
||
118 | )) |
||
119 | 38 | ->setMaxResults(1) |
|
120 | 38 | ->getQuery(); |
|
121 | 38 | $Customer = $query->getOneOrNullResult(); |
|
122 | 38 | if (!$Customer) { |
|
123 | 1 | throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username)); |
|
124 | } |
||
125 | |||
126 | 37 | return $Customer; |
|
127 | } |
||
128 | |||
129 | /** |
||
130 | * Refreshes the user for the account interface. |
||
131 | * |
||
132 | * It is up to the implementation to decide if the user data should be |
||
133 | * totally reloaded (e.g. from the database), or if the UserInterface |
||
134 | * object can just be merged into some internal array of users / identity |
||
135 | * map. |
||
136 | * |
||
137 | * @param UserInterface $user |
||
138 | * |
||
139 | * @return UserInterface |
||
140 | * |
||
141 | * @throws UnsupportedUserException if the account is not supported |
||
142 | */ |
||
143 | 35 | View Code Duplication | public function refreshUser(UserInterface $user) |
144 | { |
||
145 | 35 | if (!$user instanceof Customer) { |
|
146 | 1 | throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); |
|
147 | } |
||
148 | |||
149 | 34 | return $this->loadUserByUsername($user->getUsername()); |
|
150 | } |
||
151 | |||
152 | /** |
||
153 | * Whether this provider supports the given user class. |
||
154 | * |
||
155 | * @param string $class |
||
156 | * |
||
157 | * @return bool |
||
158 | */ |
||
159 | 1 | public function supportsClass($class) |
|
160 | { |
||
161 | 1 | return $class === 'Eccube\Entity\Customer'; |
|
162 | } |
||
163 | |||
164 | 39 | public function getQueryBuilderBySearchData($searchData) |
|
165 | { |
||
166 | 39 | $qb = $this->createQueryBuilder('c') |
|
167 | 39 | ->select('c'); |
|
168 | |||
169 | 39 | if (isset($searchData['multi']) && Str::isNotBlank($searchData['multi'])) { |
|
170 | //スペース除去 |
||
171 | 16 | $clean_key_multi = preg_replace('/\s+|[ ]+/u', '', $searchData['multi']); |
|
172 | 16 | $id = preg_match('/^\d+$/', $clean_key_multi) ? $clean_key_multi : null; |
|
173 | $qb |
||
174 | 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') |
|
175 | 16 | ->setParameter('customer_id', $id) |
|
176 | 16 | ->setParameter('name', '%' . $clean_key_multi . '%') |
|
177 | 16 | ->setParameter('kana', '%' . $clean_key_multi . '%') |
|
178 | 16 | ->setParameter('email', '%' . $clean_key_multi . '%'); |
|
179 | } |
||
180 | |||
181 | // Pref |
||
182 | 39 | View Code Duplication | if (!empty($searchData['pref']) && $searchData['pref']) { |
183 | $qb |
||
184 | 1 | ->andWhere('c.Pref = :pref') |
|
185 | 1 | ->setParameter('pref', $searchData['pref']->getId()); |
|
186 | } |
||
187 | |||
188 | // sex |
||
189 | 39 | if (!empty($searchData['sex']) && count($searchData['sex']) > 0) { |
|
190 | 2 | $sexs = array(); |
|
191 | 2 | foreach ($searchData['sex'] as $sex) { |
|
192 | 2 | $sexs[] = $sex->getId(); |
|
193 | } |
||
194 | |||
195 | $qb |
||
196 | 2 | ->andWhere($qb->expr()->in('c.Sex', ':sexs')) |
|
197 | 2 | ->setParameter('sexs', $sexs); |
|
198 | } |
||
199 | |||
200 | 39 | if (!empty($searchData['birth_month']) && $searchData['birth_month']) { |
|
201 | $qb |
||
202 | 1 | ->andWhere('EXTRACT(MONTH FROM c.birth) = :birth_month') |
|
203 | 1 | ->setParameter('birth_month', $searchData['birth_month']); |
|
204 | } |
||
205 | |||
206 | // birth |
||
207 | 39 | if (!empty($searchData['birth_start']) && $searchData['birth_start']) { |
|
208 | $qb |
||
209 | 2 | ->andWhere('c.birth >= :birth_start') |
|
210 | 2 | ->setParameter('birth_start', $searchData['birth_start']); |
|
211 | } |
||
212 | 39 | View Code Duplication | if (!empty($searchData['birth_end']) && $searchData['birth_end']) { |
213 | 2 | $date = clone $searchData['birth_end']; |
|
214 | 2 | $date->modify('+1 days'); |
|
215 | $qb |
||
216 | 2 | ->andWhere('c.birth < :birth_end') |
|
217 | 2 | ->setParameter('birth_end', $date); |
|
218 | } |
||
219 | |||
220 | // tel |
||
221 | 39 | View Code Duplication | if (isset($searchData['tel']) && Str::isNotBlank($searchData['tel'])) { |
222 | $qb |
||
223 | 1 | ->andWhere('CONCAT(c.tel01, c.tel02, c.tel03) LIKE :tel') |
|
224 | 1 | ->setParameter('tel', '%' . $searchData['tel'] . '%'); |
|
225 | } |
||
226 | |||
227 | // buy_total |
||
228 | 39 | View Code Duplication | if (isset($searchData['buy_total_start']) && Str::isNotBlank($searchData['buy_total_start'])) { |
229 | $qb |
||
230 | 1 | ->andWhere('c.buy_total >= :buy_total_start') |
|
231 | 1 | ->setParameter('buy_total_start', $searchData['buy_total_start']); |
|
232 | } |
||
233 | 39 | View Code Duplication | if (isset($searchData['buy_total_end']) && Str::isNotBlank($searchData['buy_total_end'])) { |
234 | $qb |
||
235 | 1 | ->andWhere('c.buy_total <= :buy_total_end') |
|
236 | 1 | ->setParameter('buy_total_end', $searchData['buy_total_end']); |
|
237 | } |
||
238 | |||
239 | // buy_times |
||
240 | 39 | if (!empty($searchData['buy_times_start']) && $searchData['buy_times_start']) { |
|
241 | $qb |
||
242 | 1 | ->andWhere('c.buy_times >= :buy_times_start') |
|
243 | 1 | ->setParameter('buy_times_start', $searchData['buy_times_start']); |
|
244 | } |
||
245 | 39 | if (!empty($searchData['buy_times_end']) && $searchData['buy_times_end']) { |
|
246 | $qb |
||
247 | 1 | ->andWhere('c.buy_times <= :buy_times_end') |
|
248 | 1 | ->setParameter('buy_times_end', $searchData['buy_times_end']); |
|
249 | } |
||
250 | |||
251 | // create_date |
||
252 | 39 | if (!empty($searchData['create_date_start']) && $searchData['create_date_start']) { |
|
253 | $qb |
||
254 | 1 | ->andWhere('c.create_date >= :create_date_start') |
|
255 | 1 | ->setParameter('create_date_start', $searchData['create_date_start']); |
|
256 | } |
||
257 | 39 | View Code Duplication | if (!empty($searchData['create_date_end']) && $searchData['create_date_end']) { |
258 | 1 | $date = clone $searchData['create_date_end']; |
|
259 | 1 | $date->modify('+1 days'); |
|
260 | $qb |
||
261 | 1 | ->andWhere('c.create_date < :create_date_end') |
|
262 | 1 | ->setParameter('create_date_end', $date); |
|
263 | } |
||
264 | |||
265 | // update_date |
||
266 | 39 | if (!empty($searchData['update_date_start']) && $searchData['update_date_start']) { |
|
267 | $qb |
||
268 | 1 | ->andWhere('c.update_date >= :update_date_start') |
|
269 | 1 | ->setParameter('update_date_start', $searchData['update_date_start']); |
|
270 | } |
||
271 | 39 | View Code Duplication | if (!empty($searchData['update_date_end']) && $searchData['update_date_end']) { |
272 | 1 | $date = clone $searchData['update_date_end']; |
|
273 | 1 | $date->modify('+1 days'); |
|
274 | $qb |
||
275 | 1 | ->andWhere('c.update_date < :update_date_end') |
|
276 | 1 | ->setParameter('update_date_end', $date); |
|
277 | } |
||
278 | |||
279 | // last_buy |
||
280 | 39 | View Code Duplication | if (!empty($searchData['last_buy_start']) && $searchData['last_buy_start']) { |
281 | $qb |
||
282 | 1 | ->andWhere('c.last_buy_date >= :last_buy_start') |
|
283 | 1 | ->setParameter('last_buy_start', $searchData['last_buy_start']); |
|
284 | } |
||
285 | 39 | View Code Duplication | if (!empty($searchData['last_buy_end']) && $searchData['last_buy_end']) { |
286 | 1 | $date = clone $searchData['last_buy_end']; |
|
287 | 1 | $date->modify('+1 days'); |
|
288 | $qb |
||
289 | 1 | ->andWhere('c.last_buy_date < :last_buy_end') |
|
290 | 1 | ->setParameter('last_buy_end', $date); |
|
291 | } |
||
292 | |||
293 | // status |
||
294 | 39 | if (!empty($searchData['customer_status']) && count($searchData['customer_status']) > 0) { |
|
295 | $qb |
||
296 | 2 | ->andWhere($qb->expr()->in('c.Status', ':statuses')) |
|
297 | 2 | ->setParameter('statuses', $searchData['customer_status']); |
|
298 | } |
||
299 | |||
300 | // buy_product_name、buy_product_code |
||
301 | 39 | View Code Duplication | if (isset($searchData['buy_product_code']) && Str::isNotBlank($searchData['buy_product_code'])) { |
302 | $qb |
||
303 | 1 | ->leftJoin('c.Orders', 'o') |
|
304 | 1 | ->leftJoin('o.OrderDetails', 'od') |
|
305 | 1 | ->andWhere('od.product_name LIKE :buy_product_name OR od.product_code LIKE :buy_product_name') |
|
306 | 1 | ->setParameter('buy_product_name', '%' . $searchData['buy_product_code'] . '%'); |
|
307 | } |
||
308 | |||
309 | // Order By |
||
310 | 39 | $qb->addOrderBy('c.update_date', 'DESC'); |
|
311 | |||
312 | 39 | return $this->queries->customize(QueryKey::CUSTOMER_SEARCH, $qb, $searchData); |
|
313 | } |
||
314 | |||
315 | /** |
||
316 | * ユニークなシークレットキーを返す |
||
317 | * @param $app |
||
318 | * @return string |
||
319 | */ |
||
320 | 237 | View Code Duplication | public function getUniqueSecretKey($app) |
332 | |||
333 | /** |
||
334 | * ユニークなパスワードリセットキーを返す |
||
335 | * @param $app |
||
336 | * @return string |
||
337 | */ |
||
338 | 1 | View Code Duplication | public function getUniqueResetKey($app) |
350 | |||
351 | /** |
||
352 | * saltを生成する |
||
353 | * |
||
354 | * @param $byte |
||
355 | * @return string |
||
356 | */ |
||
357 | 237 | public function createSalt($byte) |
|
358 | { |
||
359 | 237 | return bin2hex(openssl_random_pseudo_bytes($byte)); |
|
360 | } |
||
361 | |||
362 | /** |
||
363 | * 入力されたパスワードをSaltと暗号化する |
||
364 | * |
||
365 | * @param $app |
||
366 | * @param Customer $Customer |
||
367 | * @return mixed |
||
368 | */ |
||
369 | 237 | public function encryptPassword($app, \Eccube\Entity\Customer $Customer) |
|
375 | |||
376 | 4 | public function getProvisionalCustomerBySecretKey($secret_key) |
|
377 | { |
||
378 | 4 | $qb = $this->createQueryBuilder('c') |
|
379 | 4 | ->where('c.secret_key = :secret_key') |
|
380 | 4 | ->leftJoin('c.Status', 's') |
|
381 | 4 | ->andWhere('s.id = :status') |
|
382 | 4 | ->setParameter('secret_key', $secret_key) |
|
383 | 4 | ->setParameter('status', CustomerStatus::PROVISIONAL); |
|
384 | 4 | $query = $qb->getQuery(); |
|
385 | |||
386 | 4 | return $query->getSingleResult(); |
|
387 | } |
||
388 | |||
389 | 2 | public function getRegularCustomerByEmail($email) |
|
390 | { |
||
391 | 2 | $query = $this->createQueryBuilder('c') |
|
392 | 2 | ->where('c.email = :email AND c.Status = :status') |
|
393 | 2 | ->setParameter('email', $email) |
|
394 | 2 | ->setParameter('status', CustomerStatus::REGULAR) |
|
395 | 2 | ->setMaxResults(1) |
|
396 | 2 | ->getQuery(); |
|
397 | |||
398 | 2 | $Customer = $query->getOneOrNullResult(); |
|
399 | |||
400 | 2 | return $Customer; |
|
401 | } |
||
402 | |||
403 | 4 | public function getRegularCustomerByResetKey($reset_key) |
|
404 | { |
||
405 | 4 | $query = $this->createQueryBuilder('c') |
|
406 | 4 | ->where('c.reset_key = :reset_key AND c.Status = :status AND c.reset_expire >= :reset_expire') |
|
407 | 4 | ->setParameter('reset_key', $reset_key) |
|
408 | 4 | ->setParameter('status', CustomerStatus::REGULAR) |
|
409 | 4 | ->setParameter('reset_expire', new \DateTime()) |
|
410 | 4 | ->getQuery(); |
|
411 | |||
412 | 4 | $Customer = $query->getSingleResult(); |
|
413 | |||
414 | 2 | return $Customer; |
|
415 | } |
||
416 | |||
417 | 3 | public function getResetPassword() |
|
421 | |||
422 | /** |
||
423 | * 会員の初回購入時間、購入時間、購入回数、購入金額を更新する |
||
424 | * |
||
425 | * @param $app |
||
426 | * @param Customer $Customer |
||
427 | * @param $orderStatusId |
||
428 | */ |
||
429 | 2 | public function updateBuyData($app, Customer $Customer, $orderStatusId) |
|
430 | { |
||
474 | |||
475 | /** |
||
476 | * 仮会員, 本会員の会員を返す. |
||
477 | * Eccube\Entity\CustomerのUniqueEntityバリデーションで使用しています. |
||
478 | * |
||
479 | * @param array $criteria |
||
480 | * @return Customer[] |
||
481 | */ |
||
482 | 46 | public function getNonWithdrawingCustomers(array $criteria = []) |
|
491 | } |
||
492 |