Complex classes like OrderConfirmationPage_Controller 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 OrderConfirmationPage_Controller, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
362 | $orderConfirmationPage->publish('Stage', 'Live'); |
||
363 | } |
||
364 | } |
||
365 | } |
||
366 | } |
||
367 | |||
368 | class OrderConfirmationPage_Controller extends CartPage_Controller |
||
369 | { |
||
370 | /** |
||
371 | * @static array |
||
372 | * standard SS variable |
||
373 | * it is important that we list all the options here |
||
374 | */ |
||
375 | private static $allowed_actions = array( |
||
376 | 'saveorder', |
||
377 | 'sendreceipt', |
||
378 | 'CreateAccountForm', |
||
379 | 'retrieveorder', |
||
380 | 'loadorder', |
||
381 | 'startneworder', |
||
382 | 'showorder', |
||
383 | 'copyorder', |
||
384 | 'sendemail', |
||
385 | 'CancelForm', |
||
386 | 'PaymentForm', |
||
387 | ); |
||
388 | |||
389 | /** |
||
390 | * standard controller function. |
||
391 | **/ |
||
392 | public function init() |
||
393 | { |
||
394 | //we retrieve the order in the parent page |
||
395 | //the parent page also takes care of the security |
||
396 | if ($sessionOrderID = Session::get('CheckoutPageCurrentOrderID')) { |
||
397 | $this->currentOrder = Order::get()->byID($sessionOrderID); |
||
398 | if ($this->currentOrder) { |
||
399 | $this->overrideCanView = true; |
||
400 | //more than an hour has passed... |
||
401 | if (strtotime($this->currentOrder->LastEdited) < (strtotime('Now') - 60 * 60)) { |
||
402 | Session::clear('CheckoutPageCurrentOrderID'); |
||
403 | Session::clear('CheckoutPageCurrentOrderID'); |
||
404 | Session::set('CheckoutPageCurrentOrderID', 0); |
||
405 | Session::save(); |
||
406 | $this->overrideCanView = false; |
||
407 | $this->currentOrder = null; |
||
408 | } |
||
409 | } |
||
410 | } |
||
411 | parent::init(); |
||
412 | Requirements::themedCSS('Order', 'ecommerce'); |
||
413 | Requirements::themedCSS('Order_Print', 'ecommerce', 'print'); |
||
414 | Requirements::themedCSS('CheckoutPage', 'ecommerce'); |
||
415 | Requirements::javascript('ecommerce/javascript/EcomPayment.js'); |
||
416 | Requirements::javascript('ecommerce/javascript/EcomPrintAndMail.js'); |
||
417 | $this->includeGoogleAnalyticsCode(); |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * This method exists just so that template |
||
422 | * sets CurrentOrder variable. |
||
423 | * |
||
424 | * @param HTTPRequest |
||
425 | * |
||
426 | * @return array |
||
427 | **/ |
||
428 | public function showorder(SS_HTTPRequest $request) |
||
429 | { |
||
430 | isset($project) ? $themeBaseFolder = $project : $themeBaseFolder = 'mysite'; |
||
431 | if (isset($_REQUEST['print'])) { |
||
432 | Requirements::clear(); |
||
433 | Requirements::themedCSS('typography', $themeBaseFolder); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
434 | Requirements::themedCSS('OrderReport', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
435 | Requirements::themedCSS('Order_Invoice', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
436 | Requirements::themedCSS('Order_Invoice_Print_Only', 'ecommerce', 'print'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
437 | Config::nest(); |
||
438 | Config::inst()->update('SSViewer', 'theme_enabled', true); |
||
439 | $html = $this->renderWith('Invoice'); |
||
440 | Config::unnest(); |
||
441 | |||
442 | return $html; |
||
443 | } elseif (isset($_REQUEST['packingslip'])) { |
||
444 | Requirements::clear(); |
||
445 | Requirements::themedCSS('typography', $themeBaseFolder); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
446 | Requirements::themedCSS('OrderReport', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
447 | Requirements::themedCSS('Order_PackingSlip', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
448 | Config::nest(); |
||
449 | Config::inst()->update('SSViewer', 'theme_enabled', true); |
||
450 | $html = $this->renderWith('PackingSlip'); |
||
451 | Config::unnest(); |
||
452 | |||
453 | return $html; |
||
454 | } |
||
455 | |||
456 | return array(); |
||
457 | } |
||
458 | |||
459 | /** |
||
460 | * This is an additional way to look at an order. |
||
461 | * The order is already retrieved from the init function. |
||
462 | * |
||
463 | * @return array |
||
464 | **/ |
||
465 | public function retrieveorder(SS_HTTPRequest $request) |
||
466 | { |
||
467 | return array(); |
||
468 | } |
||
469 | |||
470 | /** |
||
471 | * copies either the current order into the shopping cart. |
||
472 | * |
||
473 | * TO DO: untested |
||
474 | * TO DO: what to do with old order |
||
475 | * |
||
476 | * @param SS_HTTPRequest |
||
477 | * |
||
478 | * @return array |
||
479 | */ |
||
480 | public function copyorder(SS_HTTPRequest $request) |
||
481 | { |
||
482 | self::set_message(_t('CartPage.ORDERLOADED', 'Order has been loaded.')); |
||
483 | ShoppingCart::singleton()->copyOrder($this->currentOrder->ID); |
||
484 | |||
485 | return $this->redirect(CheckoutPage::find_link()); |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * @param HTTPRequest |
||
490 | * |
||
491 | * @return array - just so the template is still displayed |
||
492 | **/ |
||
493 | public function sendreceipt(SS_HTTPRequest $request) |
||
494 | { |
||
495 | if ($o = $this->currentOrder) { |
||
496 | if ($m = $o->Member()) { |
||
497 | if ($m->Email) { |
||
498 | $subject = _t('Account.COPYONLY', '--- COPY ONLY ---'); |
||
499 | $message = _t('Account.COPYONLY', '--- COPY ONLY ---'); |
||
500 | $o->sendReceipt($subject, $message, true); |
||
501 | $this->message = _t('OrderConfirmationPage.RECEIPTSENT', 'An order receipt has been sent to: ').$m->Email.'.'; |
||
502 | } else { |
||
503 | $this->message = _t('OrderConfirmationPage.RECEIPTNOTSENTNOTSENDING', 'Email could NOT be sent.'); |
||
504 | } |
||
505 | } else { |
||
506 | $this->message = _t('OrderConfirmationPage.RECEIPTNOTSENTNOEMAIL', 'No email could be found for sending this receipt.'); |
||
507 | } |
||
508 | } else { |
||
509 | $this->message = _t('OrderConfirmationPage.RECEIPTNOTSENTNOORDER', 'Order could not be found.'); |
||
510 | } |
||
511 | $baseFolder = Director::baseFolder(); |
||
512 | if (!class_exists('\Pelago\Emogrifier')) { |
||
513 | require_once Director::baseFolder().'/ecommerce/thirdparty/Emogrifier.php'; |
||
514 | } |
||
515 | Requirements::clear(); |
||
516 | isset($project) ? $themeBaseFolder = $project : $themeBaseFolder = 'mysite'; |
||
517 | Requirements::themedCSS('typography', $themeBaseFolder); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
518 | Requirements::themedCSS('OrderReport', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
519 | Requirements::themedCSS('Order_Invoice', 'ecommerce', 'print'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE |
||
520 | Config::nest(); |
||
521 | Config::inst()->update('SSViewer', 'theme_enabled', true); |
||
522 | $html = $this->renderWith('Order_ReceiptEmail'); |
||
523 | Config::unnest(); |
||
524 | // if it's an html email, filter it through emogrifier |
||
525 | $cssFileLocation = $baseFolder.'/'.EcommerceConfig::get('Order_Email', 'css_file_location'); |
||
526 | $html .= "\r\n\r\n<!-- CSS can be found here: $cssFileLocation -->"; |
||
527 | $cssFileHandler = fopen($cssFileLocation, 'r'); |
||
528 | $css = fread($cssFileHandler, filesize($cssFileLocation)); |
||
529 | fclose($cssFileHandler); |
||
530 | $emog = new \Pelago\Emogrifier($html, $css); |
||
531 | $html = $emog->emogrify(); |
||
532 | |||
533 | return $html; |
||
534 | } |
||
535 | |||
536 | /** |
||
537 | * Returns a dataobject set of the checkout steps if |
||
538 | * the OrderConfirmationPage is shown as part of the checkout process |
||
539 | * We repeat these here so that you can show the user that (s)he has reached the last step. |
||
540 | * |
||
541 | * @param int $number - if set, it returns that one step. |
||
542 | */ |
||
543 | public function CheckoutSteps($number = 0) |
||
544 | { |
||
545 | $where = ''; |
||
546 | if ($number) { |
||
547 | $where = "\"CheckoutPage_StepDescription\".\"ID\" = $number"; |
||
548 | } |
||
549 | if (EcommerceConfig::get('OrderConfirmationPage_Controller', 'include_as_checkout_step')) { |
||
550 | if ($this->currentOrder->IsInSession()) { |
||
551 | $dos = CheckoutPage_StepDescription::get()->where($where)->sort('ID', 'ASC'); |
||
552 | if ($number) { |
||
553 | if ($dos && $dos->count()) { |
||
554 | return $dos->First(); |
||
555 | } |
||
556 | } |
||
557 | $arrayList = new ArrayList(array()); |
||
558 | foreach ($dos as $do) { |
||
559 | $do->LinkingMode = 'link completed'; |
||
560 | $do->Completed = 1; |
||
561 | $do->Link = ''; |
||
562 | $arrayList->push($do); |
||
563 | } |
||
564 | $do = $this->CurrentCheckoutStep(true); |
||
565 | if ($do) { |
||
566 | $arrayList->push($do); |
||
567 | } |
||
568 | |||
569 | return $arrayList; |
||
570 | } |
||
571 | } |
||
572 | } |
||
573 | |||
574 | /** |
||
575 | * returns the percentage of checkout steps done (0 - 100). |
||
576 | * |
||
577 | * @return int |
||
578 | */ |
||
579 | public function PercentageDone() |
||
580 | { |
||
581 | return 100; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * @return string |
||
586 | */ |
||
587 | public function PaymentHeader() |
||
588 | { |
||
589 | if ($order = $this->Order()) { |
||
590 | if ($this->OrderIsCancelled()) { |
||
591 | return $this->OrderCancelledHeader; |
||
592 | } elseif ($this->PaymentIsPending()) { |
||
593 | return $this->PaymentPendingHeader; |
||
594 | } elseif ($this->IsPaid()) { |
||
595 | return $this->PaymentSuccessfulHeader; |
||
596 | } else { |
||
597 | return $this->PaymentNotSuccessfulHeader; |
||
598 | } |
||
599 | } |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * @return string | null |
||
604 | */ |
||
605 | public function PaymentMessage() |
||
606 | { |
||
607 | if ($order = $this->Order()) { |
||
608 | if ($this->OrderIsCancelled()) { |
||
609 | return $this->OrderCancelledMessage; |
||
610 | } elseif ($this->PaymentIsPending()) { |
||
611 | return $this->PaymentPendingMessage; |
||
612 | } elseif ($this->IsPaid()) { |
||
613 | return $this->PaymentSuccessfulMessage; |
||
614 | } else { |
||
615 | return $this->PaymentNotSuccessfulMessage; |
||
616 | } |
||
617 | } |
||
618 | } |
||
619 | |||
620 | /** |
||
621 | * @return string | null |
||
622 | */ |
||
623 | public function PaymentMessageType() |
||
624 | { |
||
625 | if ($order = $this->Order()) { |
||
626 | if ($this->OrderIsCancelled()) { |
||
627 | return "bad"; |
||
628 | } elseif ($this->PaymentIsPending()) { |
||
629 | return "warning"; |
||
630 | } elseif ($this->IsPaid()) { |
||
631 | return "good"; |
||
632 | } else { |
||
633 | return "bad"; |
||
634 | } |
||
635 | } |
||
636 | } |
||
637 | |||
638 | /** |
||
639 | * @return bool |
||
640 | */ |
||
641 | public function OrderIsCancelled() |
||
642 | { |
||
643 | if ($order = $this->Order()) { |
||
644 | return $order->getIsCancelled(); |
||
645 | } |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * Is the Order paid? |
||
650 | * This can be useful for choosing what header to show. |
||
651 | * |
||
652 | * @return bool |
||
653 | */ |
||
654 | public function IsPaid() |
||
655 | { |
||
656 | if ($order = $this->Order()) { |
||
657 | return $order->IsPaid(); |
||
658 | } |
||
659 | } |
||
660 | |||
661 | /** |
||
662 | * Are there any order Payments Pending |
||
663 | * This can be useful for choosing what header to show. |
||
664 | * |
||
665 | * @return bool |
||
666 | */ |
||
667 | public function PaymentIsPending() |
||
668 | { |
||
669 | if ($order = $this->Order()) { |
||
670 | return $order->PaymentIsPending(); |
||
671 | } |
||
672 | } |
||
673 | |||
674 | /** |
||
675 | * Returns the form to cancel the current order, |
||
676 | * checking to see if they can cancel their order |
||
677 | * first of all. |
||
678 | * |
||
679 | * @return OrderForm_Cancel |
||
680 | */ |
||
681 | public function CancelForm() |
||
682 | { |
||
683 | if ($this->Order()) { |
||
684 | if ($this->currentOrder->canCancel()) { |
||
685 | return OrderForm_Cancel::create($this, 'CancelForm', $this->currentOrder); |
||
686 | } |
||
687 | } |
||
688 | //once cancelled, you will be redirected to main page - hence we need this... |
||
689 | if ($this->orderID) { |
||
690 | return array(); |
||
691 | } |
||
692 | } |
||
693 | |||
694 | /** |
||
695 | * show the payment form. |
||
696 | * |
||
697 | * @return Form (OrderForm_Payment) or Null |
||
698 | **/ |
||
699 | public function PaymentForm() |
||
700 | { |
||
701 | if ($this->currentOrder) { |
||
702 | if ($this->currentOrder->canPay()) { |
||
703 | Requirements::javascript('ecommerce/javascript/EcomPayment.js'); |
||
704 | |||
705 | return OrderForm_Payment::create($this, 'PaymentForm', $this->currentOrder); |
||
706 | } |
||
707 | } |
||
708 | |||
709 | return array(); |
||
710 | } |
||
711 | |||
712 | /** |
||
713 | * Can this page only show Submitted Orders (e.g. OrderConfirmationPage) ? |
||
714 | * |
||
715 | * @return bool |
||
716 | */ |
||
717 | protected function onlyShowSubmittedOrders() |
||
718 | { |
||
719 | return true; |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * Can this page only show Unsubmitted Orders (e.g. CartPage) ? |
||
724 | * |
||
725 | * @return bool |
||
726 | */ |
||
727 | protected function onlyShowUnsubmittedOrders() |
||
728 | { |
||
729 | return false; |
||
730 | } |
||
731 | |||
732 | /** |
||
733 | * sends an order email, which can be specified in the URL |
||
734 | * and displays a sample email |
||
735 | * typically this link is opened in a new window. |
||
736 | * |
||
737 | * @param SS_HTTPRequest $request |
||
738 | * |
||
739 | * @return HTML |
||
740 | **/ |
||
741 | public function sendemail(SS_HTTPRequest $request) |
||
742 | { |
||
743 | if ($this->currentOrder) { |
||
744 | $emailClassName = 'Order_ReceiptEmail'; |
||
745 | if (class_exists($request->param('OtherID'))) { |
||
746 | if (is_a(singleton($request->param('OtherID')), Object::getCustomClass('Order_Email'))) { |
||
747 | $emailClassName = $request->param('OtherID'); |
||
748 | } |
||
749 | } |
||
750 | if ($statusID = intval($request->getVar('use'))) { |
||
751 | $subject = _t('Account.TEST_ONLY', '--- TEST ONLY ---'); |
||
752 | $step = OrderStep::get()->byID($statusID); |
||
753 | if ($step) { |
||
754 | $emailClassName = $step->getEmailClassName(); |
||
755 | } |
||
756 | if ($request->getVar('send')) { |
||
757 | $email = filter_var($request->getVar('send'), FILTER_SANITIZE_EMAIL); |
||
758 | if(! $email) { |
||
759 | $email = true; |
||
760 | } |
||
761 | $this->currentOrder->sendEmail( |
||
762 | $subject, |
||
763 | $message, |
||
764 | $resend = true, |
||
765 | $adminOnlyOrToEmail = $email, |
||
766 | $emailClassName |
||
767 | ); |
||
768 | } |
||
769 | } |
||
770 | if ($request->getVar('send')) { |
||
771 | if ($email = $this->currentOrder->getOrderEmail()) { |
||
772 | $subject = _t('Account.COPYONLY', '--- COPY ONLY ---'); |
||
773 | $message = _t('Account.COPYONLY', '--- COPY ONLY ---'); |
||
774 | if ( |
||
775 | $this->currentOrder->sendEmail( |
||
776 | $subject, |
||
777 | $message, |
||
778 | $resend = true, |
||
779 | $adminOnlyOrToEmail = false, |
||
780 | $emailClassName |
||
781 | ) |
||
782 | ) { |
||
783 | $this->message = _t('OrderConfirmationPage.RECEIPTSENT', 'An email has been sent to: ').$email.'.'; |
||
784 | } else { |
||
785 | $this->message = _t('OrderConfirmationPage.RECEIPT_NOT_SENT', 'Email sent unsuccesfully to: ').$email.'. EMAIL NOT SENT.'; |
||
786 | } |
||
787 | } else { |
||
788 | $this->message = _t('OrderConfirmationPage.RECEIPTNOTSENTNOEMAIL', 'No customer details found. EMAIL NOT SENT.'); |
||
789 | } |
||
790 | } |
||
791 | //display same data... |
||
792 | Requirements::clear(); |
||
793 | |||
794 | return $this->currentOrder->renderOrderInEmailFormat($this->message, $emailClassName); |
||
795 | } else { |
||
796 | return _t('OrderConfirmationPage.RECEIPTNOTSENTNOORDER', 'Order could not be found.'); |
||
797 | } |
||
798 | } |
||
799 | |||
800 | protected function includeGoogleAnalyticsCode() |
||
801 | { |
||
802 | if ($this->EnableGoogleAnalytics && $this->currentOrder && Director::isLive()) { |
||
803 | $currencyUsedObject = $this->currentOrder->CurrencyUsed(); |
||
804 | if ($currencyUsedObject) { |
||
805 | $currencyUsedString = $currencyUsedObject->Code; |
||
806 | } |
||
807 | if (empty($currencyUsedString)) { |
||
808 | $currencyUsedString = EcommerceCurrency::default_currency_code(); |
||
809 | } |
||
830 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.