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 ShoppingController 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 ShoppingController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
49 | class ShoppingController extends AbstractShoppingController |
||
50 | { |
||
51 | /** |
||
52 | * @var BaseInfo |
||
53 | */ |
||
54 | protected $BaseInfo; |
||
55 | |||
56 | /** |
||
57 | * @var OrderHelper |
||
58 | */ |
||
59 | protected $orderHelper; |
||
60 | |||
61 | /** |
||
62 | * @var CartService |
||
63 | */ |
||
64 | protected $cartService; |
||
65 | |||
66 | /** |
||
67 | * @var ShoppingService |
||
68 | */ |
||
69 | protected $shoppingService; |
||
70 | |||
71 | /** |
||
72 | * @var CustomerAddressRepository |
||
73 | */ |
||
74 | protected $customerAddressRepository; |
||
75 | |||
76 | /** |
||
77 | * @var ParameterBag |
||
78 | */ |
||
79 | protected $parameterBag; |
||
80 | |||
81 | /** |
||
82 | * ShoppingController constructor. |
||
83 | * |
||
84 | * @param BaseInfoRepository $baseInfoRepository |
||
85 | * @param OrderHelper $orderHelper |
||
86 | * @param CartService $cartService |
||
87 | * @param ShoppingService $shoppingService |
||
88 | * @param CustomerAddressRepository $customerAddressRepository |
||
89 | * @param ParameterBag $parameterBag |
||
90 | */ |
||
91 | 59 | View Code Duplication | public function __construct( |
|
|||
92 | BaseInfoRepository $baseInfoRepository, |
||
93 | OrderHelper $orderHelper, |
||
94 | CartService $cartService, |
||
95 | ShoppingService $shoppingService, |
||
96 | CustomerAddressRepository $customerAddressRepository, |
||
97 | OrderRepository $orderRepository, |
||
98 | ParameterBag $parameterBag |
||
99 | ) { |
||
100 | 59 | $this->BaseInfo = $baseInfoRepository->get(); |
|
101 | 59 | $this->orderHelper = $orderHelper; |
|
102 | 59 | $this->cartService = $cartService; |
|
103 | 59 | $this->shoppingService = $shoppingService; |
|
104 | 59 | $this->customerAddressRepository = $customerAddressRepository; |
|
105 | 59 | $this->orderRepository = $orderRepository; |
|
106 | 59 | $this->parameterBag = $parameterBag; |
|
107 | } |
||
108 | |||
109 | /** |
||
110 | * 購入画面表示 |
||
111 | * |
||
112 | * @Route("/shopping", name="shopping") |
||
113 | * @Template("Shopping/index.twig") |
||
114 | */ |
||
115 | 51 | public function index(Request $request) |
|
116 | { |
||
117 | // カートチェック |
||
118 | 51 | $response = $this->forwardToRoute('shopping_check_to_cart'); |
|
119 | 51 | if ($response->isRedirection() || $response->getContent()) { |
|
120 | 1 | return $response; |
|
121 | } |
||
122 | |||
123 | // 受注情報を初期化 |
||
124 | 50 | $response = $this->forwardToRoute('shopping_initialize_order'); |
|
125 | 50 | if ($response->isRedirection() || $response->getContent()) { |
|
126 | return $response; |
||
127 | } |
||
128 | |||
129 | /** @var Order $Order */ |
||
130 | 50 | $Order = $this->parameterBag->get('Order'); |
|
131 | |||
132 | // 単価集計 |
||
133 | 50 | $flowResult = $this->validatePurchaseFlow($Order); |
|
134 | |||
135 | // 明細が丸められる場合に, カートから注文画面へ遷移できなくなるため, 集計の結果を保存する |
||
136 | 50 | $this->entityManager->flush(); |
|
137 | |||
138 | // フォームを生成する |
||
139 | 50 | $this->forwardToRoute('shopping_create_form'); |
|
140 | |||
141 | 50 | if ($flowResult->hasWarning() || $flowResult->hasError()) { |
|
142 | 10 | return $this->redirectToRoute('cart'); |
|
143 | } |
||
144 | |||
145 | 45 | $form = $this->parameterBag->get(OrderType::class); |
|
146 | |||
147 | return [ |
||
148 | 45 | 'form' => $form->createView(), |
|
149 | 45 | 'Order' => $Order, |
|
150 | ]; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * 購入確認画面から, 他の画面へのリダイレクト. |
||
155 | * 配送業者や支払方法、お問い合わせ情報をDBに保持してから遷移する. |
||
156 | * |
||
157 | * @Route("/shopping/redirect", name="shopping_redirect_to") |
||
158 | * @Template("Shopping/index.twig") |
||
159 | */ |
||
160 | 20 | public function redirectTo(Request $request) |
|
161 | { |
||
162 | // カートチェック |
||
163 | 20 | $response = $this->forwardToRoute('shopping_check_to_cart'); |
|
164 | 20 | if ($response->isRedirection() || $response->getContent()) { |
|
165 | return $response; |
||
166 | } |
||
167 | |||
168 | // 受注の存在チェック |
||
169 | 20 | $response = $this->forwardToRoute('shopping_exists_order'); |
|
170 | 20 | if ($response->isRedirection() || $response->getContent()) { |
|
171 | return $response; |
||
172 | } |
||
173 | |||
174 | // フォームの生成 |
||
175 | 20 | $this->forwardToRoute('shopping_create_form'); |
|
176 | 20 | $form = $this->parameterBag->get(OrderType::class); |
|
177 | 20 | $form->handleRequest($request); |
|
178 | |||
179 | // 各種変更ページへリダイレクトする |
||
180 | 20 | $response = $this->forwardToRoute('shopping_redirect_to_change'); |
|
181 | 20 | if ($response->isRedirection() || $response->getContent()) { |
|
182 | 8 | return $response; |
|
183 | } |
||
184 | 12 | $form = $this->parameterBag->get(OrderType::class); |
|
185 | 12 | $Order = $this->parameterBag->get('Order'); |
|
186 | |||
187 | return [ |
||
188 | 12 | 'form' => $form->createView(), |
|
189 | 12 | 'Order' => $Order, |
|
190 | ]; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * 購入処理 |
||
195 | * |
||
196 | * @Route("/shopping/confirm", name="shopping_confirm") |
||
197 | * @Method("POST") |
||
198 | * @Template("Shopping/confirm.twig") |
||
199 | */ |
||
200 | 5 | public function confirm(Request $request) |
|
201 | { |
||
202 | // カートチェック |
||
203 | 5 | $response = $this->forwardToRoute('shopping_check_to_cart'); |
|
204 | 5 | if ($response->isRedirection() || $response->getContent()) { |
|
205 | return $response; |
||
206 | } |
||
207 | |||
208 | // 受注の存在チェック |
||
209 | 5 | $response = $this->forwardToRoute('shopping_exists_order'); |
|
210 | 5 | if ($response->isRedirection() || $response->getContent()) { |
|
211 | return $response; |
||
212 | } |
||
213 | |||
214 | // フォームの生成 |
||
215 | 5 | $this->forwardToRoute('shopping_create_form'); |
|
216 | 5 | $form = $this->parameterBag->get(OrderType::class); |
|
217 | 5 | $form->handleRequest($request); |
|
218 | |||
219 | 5 | $form = $this->parameterBag->get(OrderType::class); |
|
220 | 5 | $Order = $this->parameterBag->get('Order'); |
|
221 | |||
222 | 5 | $flowResult = $this->validatePurchaseFlow($Order); |
|
223 | 5 | if ($flowResult->hasWarning() || $flowResult->hasError()) { |
|
224 | return $this->redirectToRoute('shopping_error'); |
||
225 | } |
||
226 | |||
227 | 5 | $paymentMethod = $this->createPaymentMethod($Order, $form); |
|
228 | |||
229 | 5 | $PaymentResult = $paymentMethod->verify(); |
|
230 | // エラーの場合は注文入力画面に戻す? |
||
231 | 5 | if ($PaymentResult instanceof PaymentResult) { |
|
232 | if (!$PaymentResult->isSuccess()) { |
||
233 | $this->entityManager->getConnection()->rollback(); |
||
234 | |||
235 | $this->addError($PaymentResult->getErrors()); |
||
236 | } |
||
237 | |||
238 | $response = $PaymentResult->getResponse(); |
||
239 | if ($response && ($response->isRedirection() || $response->getContent())) { |
||
240 | $this->entityManager->flush(); |
||
241 | |||
242 | return $response; |
||
243 | } |
||
244 | } |
||
245 | 5 | $this->entityManager->flush(); |
|
246 | |||
247 | return [ |
||
248 | 5 | 'form' => $form->createView(), |
|
249 | 5 | 'Order' => $Order, |
|
250 | ]; |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * 購入処理 |
||
255 | * |
||
256 | * @Route("/shopping/order", name="shopping_order") |
||
257 | * @Method("POST") |
||
258 | * @Template("Shopping/index.twig") |
||
259 | */ |
||
260 | 6 | public function order(Request $request) |
|
298 | |||
299 | /** |
||
300 | * 支払方法バーリデト |
||
301 | */ |
||
302 | private function isValidPayment(Application $app, $form) |
||
327 | |||
328 | /** |
||
329 | * 購入完了画面表示 |
||
330 | * |
||
331 | * @Route("/shopping/complete", name="shopping_complete") |
||
332 | * @Template("Shopping/complete.twig") |
||
333 | */ |
||
334 | 1 | public function complete(Request $request) |
|
371 | |||
372 | /** |
||
373 | * お届け先の設定一覧からの選択 |
||
374 | * |
||
375 | * @Route("/shopping/shipping/{id}", name="shopping_shipping", requirements={"id" = "\d+"}) |
||
376 | * @Template("Shopping/shipping.twig") |
||
377 | */ |
||
378 | public function shipping(Request $request, Shipping $Shipping) |
||
445 | |||
446 | /** |
||
447 | * お届け先の設定(非会員でも使用する) |
||
448 | * |
||
449 | * @Route("/shopping/shipping_edit/{id}", name="shopping_shipping_edit", requirements={"id" = "\d+"}) |
||
450 | * @Template("Shopping/shipping_edit.twig") |
||
451 | */ |
||
452 | public function shippingEdit(Request $request, $id) |
||
561 | |||
562 | /** |
||
563 | * ログイン |
||
564 | * |
||
565 | * @Route("/shopping/login", name="shopping_login") |
||
566 | * @Template("Shopping/login.twig") |
||
567 | */ |
||
568 | 2 | public function login(Request $request, AuthenticationUtils $authenticationUtils) |
|
569 | { |
||
570 | 2 | if ($this->isGranted('IS_AUTHENTICATED_FULLY')) { |
|
571 | return $this->redirectToRoute('shopping'); |
||
572 | } |
||
573 | |||
574 | /* @var $form \Symfony\Component\Form\FormInterface */ |
||
575 | 2 | $builder = $this->formFactory->createNamedBuilder('', CustomerLoginType::class); |
|
576 | |||
577 | 2 | if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) { |
|
578 | $Customer = $this->getUser(); |
||
579 | if ($Customer) { |
||
580 | $builder->get('login_email')->setData($Customer->getEmail()); |
||
581 | } |
||
582 | } |
||
583 | |||
584 | 2 | $event = new EventArgs( |
|
585 | [ |
||
586 | 2 | 'builder' => $builder, |
|
587 | ], |
||
588 | 2 | $request |
|
589 | ); |
||
590 | 2 | $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_LOGIN_INITIALIZE, $event); |
|
591 | |||
592 | 2 | $form = $builder->getForm(); |
|
593 | |||
594 | return [ |
||
595 | 2 | 'error' => $authenticationUtils->getLastAuthenticationError(), |
|
596 | 2 | 'form' => $form->createView(), |
|
597 | ]; |
||
598 | } |
||
599 | |||
600 | /** |
||
601 | * 購入エラー画面表示 |
||
602 | * |
||
603 | * @Route("/shopping/error", name="shopping_error") |
||
604 | * @Template("Shopping/shopping_error.twig") |
||
605 | */ |
||
606 | 1 | public function shoppingError(Request $request) |
|
620 | |||
621 | /** |
||
622 | * カート画面のチェック |
||
623 | * |
||
624 | * @ForwardOnly |
||
625 | * @Route("/shopping/check_to_cart", name="shopping_check_to_cart") |
||
626 | */ |
||
627 | 55 | public function checkToCart(Request $request) |
|
645 | |||
646 | /** |
||
647 | * 受注情報を初期化する. |
||
648 | * |
||
649 | * @ForwardOnly |
||
650 | * @Route("/shopping/initialize_order", name="shopping_initialize_order") |
||
651 | */ |
||
652 | 50 | public function initializeOrder(Request $request) |
|
700 | |||
701 | /** |
||
702 | * フォームを作成し, イベントハンドラを設定する |
||
703 | * |
||
704 | * @ForwardOnly |
||
705 | * @Route("/shopping/create_form", name="shopping_create_form") |
||
706 | */ |
||
707 | 50 | public function createShoppingForm(Request $request) |
|
728 | |||
729 | /** |
||
730 | * mode に応じて各変更ページへリダイレクトする. |
||
731 | * |
||
732 | * @ForwardOnly |
||
733 | * @Route("/shopping/redirect_to_change", name="shopping_redirect_to_change") |
||
734 | */ |
||
735 | 20 | public function redirectToChange(Request $request) |
|
771 | |||
772 | /** |
||
773 | * 受注の存在チェック |
||
774 | * |
||
775 | * @ForwardOnly |
||
776 | * @Route("/shopping/exists_order", name="shopping_exists_order") |
||
777 | */ |
||
778 | 26 | public function existsOrder(Request $request) |
|
791 | |||
792 | /** |
||
793 | * 受注完了処理 |
||
794 | * |
||
795 | * @ForwardOnly |
||
796 | * @Route("/shopping/complete_order", name="shopping_complete_order") |
||
797 | */ |
||
798 | 6 | public function completeOrder(Request $request) |
|
877 | |||
878 | /** |
||
879 | * 決済完了処理 |
||
880 | * |
||
881 | * @ForwardOnly |
||
882 | * @Route("/shopping/do_checkout_order", name="shopping_do_checkout_order") |
||
883 | */ |
||
884 | 6 | public function doCheckoutOrder(Request $request) |
|
906 | |||
907 | /** |
||
908 | * 受注完了の後処理 |
||
909 | * |
||
910 | * @ForwardOnly |
||
911 | * @Route("/shopping/after_complete", name="shopping_after_complete") |
||
912 | */ |
||
913 | 6 | public function afterComplete(Request $request) |
|
961 | |||
962 | 6 | private function createPaymentMethod(Order $Order, FormInterface $form) |
|
970 | } |
||
971 |
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.