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 EbayEnterprise_PayPal_CheckoutController 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 EbayEnterprise_PayPal_CheckoutController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class EbayEnterprise_PayPal_CheckoutController extends Mage_Core_Controller_Front_Action |
||
20 | { |
||
21 | /** @var EbayEnterprise_Paypal_Model_Express_Checkout */ |
||
22 | protected $_checkout = null; |
||
23 | |||
24 | /** @var EbayEnterprise_PayPal_Helper_Data */ |
||
25 | protected $_helper = null; |
||
26 | /** @var EbayEnterprise_PayPal_Model_Config */ |
||
27 | protected $_config = null; |
||
28 | /** @var Mage_Sales_Model_Quote */ |
||
29 | protected $_quote = false; |
||
30 | /** @var Mage_Checkout_Helper_Data */ |
||
31 | protected $_checkoutHelper; |
||
32 | /** @var EbayEnterprise_MageLog_Helper_Data */ |
||
33 | protected $_logger; |
||
34 | /** @var EbayEnterprise_MageLog_Helper_Context */ |
||
35 | protected $_context; |
||
36 | |||
37 | /** |
||
38 | * prepare instances of the config model and the helper. |
||
39 | */ |
||
40 | protected function _construct() |
||
41 | { |
||
42 | parent::_construct(); |
||
43 | $this->_helper = Mage::helper('ebayenterprise_paypal'); |
||
44 | $this->_config = $this->_helper->getConfigModel(); |
||
45 | $this->_logger = Mage::helper('ebayenterprise_magelog'); |
||
46 | $this->_context = Mage::helper('ebayenterprise_magelog/context'); |
||
47 | $this->_checkoutHelper = Mage::helper('checkout'); |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * prevent dispatching controller actions when not enabled. |
||
52 | * |
||
53 | * @return self |
||
54 | */ |
||
55 | View Code Duplication | public function preDispatch() |
|
|
|||
56 | { |
||
57 | parent::preDispatch(); |
||
58 | if (!$this->_config->isEnabledFlag) { |
||
59 | // when the payment method is disabled, prevent dispatching actions from |
||
60 | // this controller. |
||
61 | $this->setFlag('', static::FLAG_NO_DISPATCH, true)->_forward( |
||
62 | '/noroute' |
||
63 | ); |
||
64 | } |
||
65 | return $this; |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * return true if a guest is allowed to checkout without registering |
||
70 | * @return boolean |
||
71 | */ |
||
72 | protected function _isGuestAllowedWithoutRegistering($quoteCheckoutMethod, Mage_Sales_Model_Quote $quote) |
||
73 | { |
||
74 | return (!$quoteCheckoutMethod || $quoteCheckoutMethod != Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER) && |
||
75 | !$this->_checkoutHelper->isAllowedGuestCheckout($quote, $quote->getStoreId()); |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * Start Express Checkout by requesting initial token and dispatching customer to PayPal |
||
80 | */ |
||
81 | public function startAction() |
||
82 | { |
||
83 | try { |
||
84 | $this->_initCheckout(); |
||
85 | $customer = Mage::getSingleton('customer/session')->getCustomer(); |
||
86 | $quoteCheckoutMethod = $this->_getQuote()->getCheckoutMethod(); |
||
87 | if ($customer && $customer->getId()) { |
||
88 | $this->_checkout->setCustomerWithAddressChange( |
||
89 | $customer, |
||
90 | $this->_getQuote()->getBillingAddress(), |
||
91 | $this->_getQuote()->getShippingAddress() |
||
92 | ); |
||
93 | } elseif ($this->_isGuestAllowedWithoutRegistering($quoteCheckoutMethod, $this->_getQuote())) { |
||
94 | Mage::getSingleton('core/session')->addNotice( |
||
95 | $this->_helper->__( |
||
96 | 'To proceed to Checkout, please log in using your email address.' |
||
97 | ) |
||
98 | ); |
||
99 | $this->redirectLogin(); |
||
100 | Mage::getSingleton('customer/session') |
||
101 | ->setBeforeAuthUrl( |
||
102 | Mage::getUrl('*/*/*', array('_current' => true)) |
||
103 | ); |
||
104 | return; |
||
105 | } |
||
106 | |||
107 | try { |
||
108 | $buttonKey = EbayEnterprise_Paypal_Model_Express_Checkout::PAYMENT_INFO_BUTTON; |
||
109 | $startReply = $this->_checkout->start( |
||
110 | Mage::getUrl('*/*/return'), |
||
111 | Mage::getUrl('*/*/cancel'), |
||
112 | $this->getRequest()->getParam($buttonKey) |
||
113 | ); |
||
114 | $this->_initToken($startReply['token']); |
||
115 | $this->_redirectToPayPalSite($startReply); |
||
116 | return; |
||
117 | } catch (EbayEnterprise_PayPal_Exception $e) { |
||
118 | $this->_getCheckoutSession()->addError($e->getMessage()); |
||
119 | $this->_redirect('checkout/cart'); |
||
120 | } |
||
121 | } catch (Mage_Core_Exception $e) { |
||
122 | $this->_getCheckoutSession()->addError($e->getMessage()); |
||
123 | } catch (Exception $e) { |
||
124 | $this->_getCheckoutSession()->addError( |
||
125 | $this->__('Unable to start Express Checkout.') |
||
126 | ); |
||
127 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
128 | } |
||
129 | |||
130 | $this->_redirect('checkout/cart'); |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Return shipping options items for shipping address from request |
||
135 | */ |
||
136 | public function shippingOptionsCallbackAction() |
||
137 | { |
||
138 | try { |
||
139 | $quoteId = $this->getRequest()->getParam('quote_id'); |
||
140 | $this->_quote = Mage::getModel('sales/quote')->load($quoteId); |
||
141 | $this->_initCheckout(); |
||
142 | $response = $this->_checkout->getShippingOptionsCallbackResponse( |
||
143 | $this->getRequest()->getParams() |
||
144 | ); |
||
145 | $this->getResponse()->setBody($response); |
||
146 | } catch (Exception $e) { |
||
147 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
148 | } |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Cancel Express Checkout |
||
153 | */ |
||
154 | public function cancelAction() |
||
155 | { |
||
156 | try { |
||
157 | $this->_initToken(false); |
||
158 | // TODO verify if this logic of order cancelation is deprecated |
||
159 | // if there is an order - cancel it |
||
160 | $orderId = $this->_getCheckoutSession()->getLastOrderId(); |
||
161 | $order = ($orderId) ? Mage::getModel('sales/order')->load($orderId) |
||
162 | : false; |
||
163 | if ($order && $order->getId() |
||
164 | && $order->getQuoteId() == $this->_getCheckoutSession() |
||
165 | ->getQuoteId() |
||
166 | ) { |
||
167 | $order->cancel()->save(); |
||
168 | $this->_getCheckoutSession() |
||
169 | ->unsLastQuoteId() |
||
170 | ->unsLastSuccessQuoteId() |
||
171 | ->unsLastOrderId() |
||
172 | ->unsLastRealOrderId() |
||
173 | ->addSuccess( |
||
174 | $this->__( |
||
175 | 'Express Checkout and Order have been canceled.' |
||
176 | ) |
||
177 | ); |
||
178 | } else { |
||
179 | $this->_getCheckoutSession()->addSuccess( |
||
180 | $this->__('Express Checkout has been canceled.') |
||
181 | ); |
||
182 | } |
||
183 | } catch (Mage_Core_Exception $e) { |
||
184 | $this->_getCheckoutSession()->addError($e->getMessage()); |
||
185 | } catch (Exception $e) { |
||
186 | $this->_getCheckoutSession()->addError( |
||
187 | $this->__('Unable to cancel Express Checkout.') |
||
188 | ); |
||
189 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
190 | } |
||
191 | |||
192 | $this->_redirect('checkout/cart'); |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Return from PayPal and dispatch customer to order review page |
||
197 | */ |
||
198 | public function returnAction() |
||
199 | { |
||
200 | if ($this->getRequest()->getParam('retry_authorization') == 'true' |
||
201 | && is_array( |
||
202 | $this->_getCheckoutSession()->getPaypalTransactionData() |
||
203 | ) |
||
204 | ) { |
||
205 | $this->_forward('placeOrder'); |
||
206 | return; |
||
207 | } |
||
208 | try { |
||
209 | $this->_getCheckoutSession()->unsPaypalTransactionData(); |
||
210 | $this->_checkout = $this->_initCheckout(); |
||
211 | $this->_checkout->returnFromPaypal($this->_initToken()); |
||
212 | $this->_redirect('*/*/review'); |
||
213 | return; |
||
214 | } catch (Mage_Core_Exception $e) { |
||
215 | Mage::getSingleton('checkout/session')->addError($e->getMessage()); |
||
216 | } catch (Exception $e) { |
||
217 | Mage::getSingleton('checkout/session')->addError( |
||
218 | $this->__('Unable to process Express Checkout approval.') |
||
219 | ); |
||
220 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
221 | } |
||
222 | $this->_redirect('checkout/cart'); |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Review order after returning from PayPal |
||
227 | */ |
||
228 | public function reviewAction() |
||
229 | { |
||
230 | /** @var Mage_Checkout_Model_Session */ |
||
231 | $checkoutSession = $this->_getCheckoutSession(); |
||
232 | if ($checkoutSession->getIsUseMultiShippingCheckout()) { |
||
233 | /** @var array */ |
||
234 | $paymentData = (array) $checkoutSession->getMultiShippingPaymentData(); |
||
235 | $paymentData['is_return_from_paypal'] = true; |
||
236 | $checkoutSession->setMultiShippingPaymentData($paymentData); |
||
237 | $this->_redirect('checkout/multishipping/overview'); |
||
238 | } |
||
239 | try { |
||
240 | $this->_initCheckout(); |
||
241 | $this->_checkout->prepareOrderReview($this->_initToken()); |
||
242 | $this->loadLayout(); |
||
243 | $this->_initLayoutMessages('ebayenterprise_paypal/session'); |
||
244 | $this->renderLayout(); |
||
245 | return; |
||
246 | } catch (Mage_Core_Exception $e) { |
||
247 | $checkoutSession->addError($e->getMessage()); |
||
248 | } catch (Exception $e) { |
||
249 | $checkoutSession->addError( |
||
250 | $this->__('Unable to initialize Express Checkout review.') |
||
251 | ); |
||
252 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
253 | } |
||
254 | $this->_redirect('checkout/cart'); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Redirect back to PayPal to all editing payment information |
||
259 | */ |
||
260 | public function editAction() |
||
261 | { |
||
262 | try { |
||
263 | $this->_redirectToPayPalSite( |
||
264 | array( |
||
265 | 'useraction' => 'continue', |
||
266 | 'token' => $this->_initToken(), |
||
267 | ) |
||
268 | ); |
||
269 | } catch (Mage_Core_Exception $e) { |
||
270 | $this->_getSession()->addError($e->getMessage()); |
||
271 | $this->_redirect('*/*/review'); |
||
272 | } |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Update shipping method (combined action for ajax and regular request) |
||
277 | */ |
||
278 | public function saveShippingMethodAction() |
||
279 | { |
||
280 | try { |
||
281 | $isAjax = $this->getRequest()->getParam('isAjax'); |
||
282 | $this->_initCheckout(); |
||
283 | $this->_checkout->updateShippingMethod( |
||
284 | $this->getRequest()->getParam('shipping_method') |
||
285 | ); |
||
286 | if ($isAjax) { |
||
287 | $this->loadLayout('paypal_express_review_details'); |
||
288 | $this->getResponse()->setBody( |
||
289 | $this->getLayout()->getBlock('root') |
||
290 | ->setQuote($this->_getQuote()) |
||
291 | ->toHtml() |
||
292 | ); |
||
293 | return; |
||
294 | } |
||
295 | } catch (Mage_Core_Exception $e) { |
||
296 | $this->_getSession()->addError($e->getMessage()); |
||
297 | } catch (Exception $e) { |
||
298 | $this->_getSession()->addError( |
||
299 | $this->__('Unable to update shipping method.') |
||
300 | ); |
||
301 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
302 | } |
||
303 | if ($isAjax) { |
||
304 | $this->getResponse()->setBody( |
||
305 | '<script type="text/javascript">window.location.href = ' |
||
306 | . Mage::getUrl('*/*/review') . ';</script>' |
||
307 | ); |
||
308 | } else { |
||
309 | $this->_redirect('*/*/review'); |
||
310 | } |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * Submit the order |
||
315 | */ |
||
316 | public function placeOrderAction() |
||
317 | { |
||
318 | try { |
||
319 | $requiredAgreements = $this->_checkoutHelper |
||
320 | ->getRequiredAgreementIds(); |
||
321 | if ($requiredAgreements) { |
||
322 | $postedAgreements = array_keys( |
||
323 | $this->getRequest()->getPost('agreement', array()) |
||
324 | ); |
||
325 | if (array_diff($requiredAgreements, $postedAgreements)) { |
||
326 | Mage::throwException( |
||
327 | $this->_helper->__( |
||
328 | 'Please agree to all the terms and conditions before placing the order.' |
||
329 | ) |
||
330 | ); |
||
331 | } |
||
332 | } |
||
333 | |||
334 | $this->_initCheckout(); |
||
335 | $this->_checkout->place($this->_initToken()); |
||
336 | |||
337 | // prepare session to success or cancellation page |
||
338 | $session = $this->_getCheckoutSession(); |
||
339 | $session->clearHelperData(); |
||
340 | |||
341 | // last successful quote |
||
342 | $quoteId = $this->_getQuote()->getId(); |
||
343 | $session->setLastQuoteId($quoteId)->setLastSuccessQuoteId($quoteId); |
||
344 | |||
345 | // an order may be created |
||
346 | $order = $this->_checkout->getOrder(); |
||
347 | if ($order) { |
||
348 | $session->setLastOrderId($order->getId()) |
||
349 | ->setLastRealOrderId($order->getIncrementId()); |
||
350 | } |
||
351 | $this->_initToken(false); // no need in token anymore |
||
352 | $this->_redirect('checkout/onepage/success'); |
||
353 | return; |
||
354 | } catch (EbayEnterprise_PayPal_Exception $e) { |
||
355 | $this->_checkoutHelper->sendPaymentFailedEmail( |
||
356 | $this->_getQuote(), |
||
357 | $e->getMessage() |
||
358 | ); |
||
359 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
360 | // If a PayPal exception is thrown while trying to place the order, |
||
361 | // the PayPal transaction failed or was voided and customer needs to |
||
362 | // begin PayPal flow again. Place error message in checkout session |
||
363 | // so it will be displayed in the cart and redirect back to the cart |
||
364 | // for the checkout flow to begin again. |
||
365 | $this->_getCheckoutSession()->addError($e->getMessage()); |
||
366 | $this->_redirect('checkout/cart'); |
||
367 | } catch (Mage_Core_Exception $e) { |
||
368 | $this->_checkoutHelper->sendPaymentFailedEmail( |
||
369 | $this->_getQuote(), |
||
370 | $e->getMessage() |
||
371 | ); |
||
372 | $this->_getSession()->addError($e->getMessage()); |
||
373 | $this->_redirect('*/*/review'); |
||
374 | } catch (Exception $e) { |
||
375 | $this->_checkoutHelper->sendPaymentFailedEmail( |
||
376 | $this->_getQuote(), |
||
377 | $this->__('Unable to place the order.') |
||
378 | ); |
||
379 | $this->_getSession()->addError( |
||
380 | $this->__('Unable to place the order.') |
||
381 | ); |
||
382 | $this->_logger->logException($e, $this->_context->getMetaData(__CLASS__, [], $e)); |
||
383 | $this->_redirect('*/*/review'); |
||
384 | } |
||
385 | } |
||
386 | |||
387 | /** |
||
388 | * Redirect customer back to PayPal with the same token |
||
389 | */ |
||
390 | protected function _redirectSameToken() |
||
391 | { |
||
392 | $token = $this->_initToken(); |
||
393 | $this->getResponse()->setRedirect( |
||
394 | $this->_config->getExpressCheckoutStartUrl($token) |
||
395 | ); |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * Redirect customer to shopping cart and show error message |
||
400 | * |
||
401 | * @param string $errorMessage |
||
402 | */ |
||
403 | protected function _redirectToCartAndShowError($errorMessage) |
||
404 | { |
||
405 | $cart = Mage::getSingleton('checkout/cart'); |
||
406 | $cart->getCheckoutSession()->addError($errorMessage); |
||
407 | $this->_redirect('checkout/cart'); |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * Instantiate quote and checkout |
||
412 | * |
||
413 | * @return EbayEnterprise_PayPal_CheckoutController |
||
414 | * @throws Mage_Core_Exception |
||
415 | */ |
||
416 | protected function _initCheckout() |
||
417 | { |
||
418 | $quote = $this->_getQuote(); |
||
419 | if (!$quote->hasItems() || $quote->getHasError()) { |
||
420 | $this->getResponse()->setHeader('HTTP/1.1', '403 Forbidden'); |
||
421 | Mage::throwException( |
||
422 | $this->_helper->__('Unable to initialize Express Checkout.') |
||
423 | ); |
||
424 | } |
||
425 | $this->_checkout = Mage::getSingleton( |
||
426 | 'ebayenterprise_paypal/express_checkout', |
||
427 | array( |
||
428 | 'helper' => $this->_helper, |
||
429 | 'logger' => null, |
||
430 | 'config' => $this->_config, |
||
431 | 'quote' => $quote |
||
432 | ) |
||
433 | ); |
||
434 | $this->_checkout->setCustomerSession( |
||
435 | Mage::getSingleton('customer/session') |
||
436 | ); |
||
437 | return $this->_checkout; |
||
438 | } |
||
439 | |||
440 | /** |
||
441 | * Search for proper checkout token in request or session or (un)set specified one |
||
442 | * |
||
443 | * @param string $setToken |
||
444 | * |
||
445 | * @return EbayEnterprise_PayPal_CheckoutController |string |
||
446 | */ |
||
447 | protected function _initToken($setToken = null) |
||
448 | { |
||
449 | if (null !== $setToken) { |
||
450 | if (false === $setToken) { |
||
451 | // security measure for avoid unsetting token twice |
||
452 | if (!$this->_getSession()->getExpressCheckoutToken()) { |
||
453 | Mage::throwException( |
||
454 | $this->_helper->__( |
||
455 | 'PayPal Express Checkout Token does not exist.' |
||
456 | ) |
||
457 | ); |
||
458 | } |
||
459 | $this->_getSession()->unsExpressCheckoutToken(); |
||
460 | } else { |
||
461 | $this->_getSession()->setExpressCheckoutToken($setToken); |
||
462 | } |
||
463 | return $this; |
||
464 | } |
||
465 | if ($setToken = $this->getRequest()->getParam('token')) { |
||
466 | if ($setToken !== $this->_getSession()->getExpressCheckoutToken()) { |
||
467 | Mage::throwException( |
||
468 | $this->_helper->__( |
||
469 | 'Wrong PayPal Express Checkout Token specified.' |
||
470 | ) |
||
471 | ); |
||
472 | } |
||
473 | } else { |
||
474 | $setToken = $this->_getSession()->getExpressCheckoutToken(); |
||
475 | } |
||
476 | return $setToken; |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * PayPal session instance getter |
||
481 | * |
||
482 | * @return EbayEnterprise_PayPal_Model_Session |
||
483 | */ |
||
484 | private function _getSession() |
||
488 | |||
489 | /** |
||
490 | * Return checkout session object |
||
491 | * |
||
492 | * @return Mage_Checkout_Model_Session |
||
493 | */ |
||
494 | protected function _getCheckoutSession() |
||
498 | |||
499 | /** |
||
500 | * Return checkout quote object |
||
501 | * |
||
502 | * @return Mage_Sales_Model_Quote |
||
503 | */ |
||
504 | private function _getQuote() |
||
505 | { |
||
506 | if (!$this->_quote) { |
||
507 | return Mage::helper('ebayenterprise_paypal')->getQuote(); |
||
508 | } |
||
509 | return $this->_quote; |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Redirect to login page |
||
514 | * |
||
515 | */ |
||
516 | public function redirectLogin() |
||
526 | |||
527 | /** |
||
528 | * redirect to the paypal site so the user can login. |
||
529 | * |
||
530 | * @param array $data |
||
531 | * - requires 'token' => token string from the set express reply |
||
532 | * |
||
533 | * @return self |
||
534 | */ |
||
535 | protected function _redirectToPayPalSite(array $data) |
||
547 | } |
||
548 |
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.