Complex classes like OrderModifier 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 OrderModifier, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 31 | class OrderModifier extends OrderAttribute |
||
| 32 | { |
||
| 33 | /** |
||
| 34 | * what variables are accessible through http://mysite.com/api/ecommerce/v1/OrderModifier/. |
||
| 35 | * |
||
| 36 | * @var array |
||
| 37 | */ |
||
| 38 | private static $api_access = array( |
||
|
|
|||
| 39 | 'view' => array( |
||
| 40 | 'CalculatedTotal', |
||
| 41 | 'Sort', |
||
| 42 | 'GroupSort', |
||
| 43 | 'TableTitle', |
||
| 44 | 'TableSubTitle', |
||
| 45 | 'CartTitle', |
||
| 46 | 'CartSubTitle', |
||
| 47 | 'Name', |
||
| 48 | 'TableValue', |
||
| 49 | 'HasBeenRemoved', |
||
| 50 | 'Order', |
||
| 51 | ), |
||
| 52 | ); |
||
| 53 | |||
| 54 | // ######################################## *** 1. model defining static variables (e.g. $db, $has_one) |
||
| 55 | |||
| 56 | /** |
||
| 57 | * @var array |
||
| 58 | * stardard SS definition |
||
| 59 | */ |
||
| 60 | private static $db = array( |
||
| 61 | 'Name' => 'HTMLText', // we use this to create the TableTitle, CartTitle and TableSubTitle |
||
| 62 | 'TableValue' => 'Currency', //the $$ shown in the checkout table |
||
| 63 | 'HasBeenRemoved' => 'Boolean', // we add this so that we can see what modifiers have been removed |
||
| 64 | ); |
||
| 65 | |||
| 66 | /** |
||
| 67 | * make sure to choose the right Type and Name for this. |
||
| 68 | * stardard SS variable. |
||
| 69 | * |
||
| 70 | * @var array |
||
| 71 | */ |
||
| 72 | private static $defaults = array( |
||
| 73 | 'Name' => 'Modifier', //making sure that you choose a different name for any class extensions. |
||
| 74 | ); |
||
| 75 | |||
| 76 | // ######################################## *** 2. cms variables + functions (e.g. getCMSFields, $searchableFields) |
||
| 77 | |||
| 78 | /** |
||
| 79 | * stardard SS variable. |
||
| 80 | * |
||
| 81 | * @var array |
||
| 82 | */ |
||
| 83 | private static $searchable_fields = array( |
||
| 84 | 'OrderID' => array( |
||
| 85 | 'field' => 'NumericField', |
||
| 86 | 'title' => 'Order Number', |
||
| 87 | ), |
||
| 88 | //"TableTitle" => "PartialMatchFilter", |
||
| 89 | 'TableValue', |
||
| 90 | 'HasBeenRemoved', |
||
| 91 | ); |
||
| 92 | |||
| 93 | /** |
||
| 94 | * stardard SS definition. |
||
| 95 | * |
||
| 96 | * @var array |
||
| 97 | */ |
||
| 98 | private static $summary_fields = array( |
||
| 99 | 'OrderID' => 'Order ID', |
||
| 100 | 'TableTitle' => 'Table Title', |
||
| 101 | 'TableSubTitle' => 'More ...', |
||
| 102 | 'TableValue' => 'Value Shown', |
||
| 103 | 'CalculatedTotal' => 'Calculation Total', |
||
| 104 | ); |
||
| 105 | |||
| 106 | /** |
||
| 107 | * stardard SS definition. |
||
| 108 | * |
||
| 109 | * @var array |
||
| 110 | */ |
||
| 111 | private static $casting = array( |
||
| 112 | 'TableValueAsMoney' => 'Money', |
||
| 113 | ); |
||
| 114 | |||
| 115 | /** |
||
| 116 | * stardard SS variable. |
||
| 117 | * |
||
| 118 | * @var string |
||
| 119 | */ |
||
| 120 | private static $singular_name = 'Order Modifier'; |
||
| 121 | public function i18n_singular_name() |
||
| 125 | |||
| 126 | /** |
||
| 127 | * stardard SS variable. |
||
| 128 | * |
||
| 129 | * @var string |
||
| 130 | */ |
||
| 131 | private static $plural_name = 'Order Modifiers'; |
||
| 132 | public function i18n_plural_name() |
||
| 136 | |||
| 137 | /** |
||
| 138 | * Standard SS variable. |
||
| 139 | * |
||
| 140 | * @var string |
||
| 141 | */ |
||
| 142 | private static $description = 'An addition to the order that sits between the sub-total and the total (e.g. tax, delivery, etc...).'; |
||
| 143 | |||
| 144 | /** |
||
| 145 | * stardard SS metbod. |
||
| 146 | * |
||
| 147 | * @return FieldList |
||
| 148 | */ |
||
| 149 | public function getCMSFields() |
||
| 193 | |||
| 194 | /** |
||
| 195 | * Determine which properties on the DataObject are |
||
| 196 | * searchable, and map them to their default {@link FormField} |
||
| 197 | * representations. Used for scaffolding a searchform for {@link ModelAdmin}. |
||
| 198 | * |
||
| 199 | * Some additional logic is included for switching field labels, based on |
||
| 200 | * how generic or specific the field type is. |
||
| 201 | * |
||
| 202 | * Used by {@link SearchContext}. |
||
| 203 | * |
||
| 204 | * @param array $_params |
||
| 205 | * 'fieldClasses': Associative array of field names as keys and FormField classes as values |
||
| 206 | * 'restrictFields': Numeric array of a field name whitelist |
||
| 207 | * |
||
| 208 | * @return FieldList |
||
| 209 | */ |
||
| 210 | public function scaffoldSearchFields($_params = null) |
||
| 217 | |||
| 218 | // ######################################## *** 3. other static variables (e.g. special_name_for_something) |
||
| 219 | |||
| 220 | /** |
||
| 221 | * $doNotAddAutomatically Identifies whether a modifier is NOT automatically added |
||
| 222 | * Most modifiers, such as delivery and GST would be added automatically. |
||
| 223 | * However, there are also ones that are not added automatically. |
||
| 224 | * |
||
| 225 | * @var bool |
||
| 226 | **/ |
||
| 227 | protected $doNotAddAutomatically = false; |
||
| 228 | |||
| 229 | /** |
||
| 230 | * $can_be_removed Identifies whether a modifier can be removed by the user. |
||
| 231 | * |
||
| 232 | * @var bool |
||
| 233 | **/ |
||
| 234 | protected $canBeRemoved = false; |
||
| 235 | |||
| 236 | /** |
||
| 237 | * This is a flag for running an update. |
||
| 238 | * Running an update means that all fields are (re)set, using the Live{FieldName} methods. |
||
| 239 | * |
||
| 240 | * @var bool |
||
| 241 | **/ |
||
| 242 | protected $mustUpdate = false; |
||
| 243 | |||
| 244 | /** |
||
| 245 | * When recalculating all the modifiers, this private variable is added to as a running total |
||
| 246 | * other modifiers can then tap into this to work out their own values. |
||
| 247 | * For example, a tax modifier needs to know the value of the other modifiers before calculating |
||
| 248 | * its own value (i.e. tax is also paid over handling and shipping). |
||
| 249 | * Always consider the "order" (which one first) of the order modifiers when using this variable. |
||
| 250 | * |
||
| 251 | * @var float |
||
| 252 | **/ |
||
| 253 | private $runningTotal = 0; |
||
| 254 | |||
| 255 | // ######################################## *** 4. CRUD functions (e.g. canEdit) |
||
| 256 | |||
| 257 | // ######################################## *** 5. init and update functions |
||
| 258 | |||
| 259 | /** |
||
| 260 | * |
||
| 261 | */ |
||
| 262 | public static function init_for_order($className) |
||
| 268 | |||
| 269 | /** |
||
| 270 | * This method runs when the OrderModifier is first added to the order. |
||
| 271 | **/ |
||
| 272 | public function init() |
||
| 281 | |||
| 282 | /* |
||
| 283 | * all classes extending OrderModifier must have this method if it has more fields |
||
| 284 | * @param boolean $recalculate - run it, even if it has run already |
||
| 285 | **/ |
||
| 286 | public function runUpdate($recalculate = false) |
||
| 299 | |||
| 300 | /** |
||
| 301 | * You can overload this method as canEdit might not be the right indicator. |
||
| 302 | * |
||
| 303 | * @return bool |
||
| 304 | **/ |
||
| 305 | protected function canBeUpdated() |
||
| 309 | |||
| 310 | /** |
||
| 311 | * standard SS Method. |
||
| 312 | * |
||
| 313 | * @return bool |
||
| 314 | **/ |
||
| 315 | public function canCreate($member = null) |
||
| 319 | |||
| 320 | /** |
||
| 321 | * standard SS Method. |
||
| 322 | * |
||
| 323 | * @return bool |
||
| 324 | **/ |
||
| 325 | public function canDelete($member = null) |
||
| 329 | |||
| 330 | /** |
||
| 331 | * This method simply checks if a fields has changed and if it has changed it updates the field. |
||
| 332 | * |
||
| 333 | * @param string $fieldName |
||
| 334 | **/ |
||
| 335 | protected function checkField($fieldName) |
||
| 347 | |||
| 348 | /** |
||
| 349 | * Provides a modifier total that is positive or negative, depending on whether the modifier is chargable or not. |
||
| 350 | * This number is used to work out the order Grand Total..... |
||
| 351 | * It is important to note that this can be positive or negative, while the amount is always positive. |
||
| 352 | * |
||
| 353 | * @return float / double |
||
| 354 | */ |
||
| 355 | public function CalculationTotal() |
||
| 363 | |||
| 364 | // ######################################## *** 6. form functions (Showform and getform) |
||
| 365 | |||
| 366 | /** |
||
| 367 | * This determines whether the OrderModifierForm is shown or not. {@link OrderModifier::get_form()}. |
||
| 368 | * OrderModifierForms are forms that are added to check out to facilitate the use of the modifier. |
||
| 369 | * An example would be a form allowing the user to select the delivery option. |
||
| 370 | * |
||
| 371 | * @return bool |
||
| 372 | */ |
||
| 373 | public function ShowForm() |
||
| 377 | |||
| 378 | /** |
||
| 379 | * Should the form be included in the EDITABLE form |
||
| 380 | * on the checkout page? |
||
| 381 | * |
||
| 382 | * @return bool |
||
| 383 | */ |
||
| 384 | public function ShowFormInEditableOrderTable() |
||
| 389 | |||
| 390 | /** |
||
| 391 | * Should the form be shown outside of editable table |
||
| 392 | * on the checkout page (opposite of ShowFormInEditableOrderTable)? |
||
| 393 | * |
||
| 394 | * @return bool |
||
| 395 | */ |
||
| 396 | public function ShowFormOutsideEditableOrderTable() |
||
| 401 | |||
| 402 | /** |
||
| 403 | * This function returns a form that allows a user |
||
| 404 | * to change the modifier to the order. |
||
| 405 | * |
||
| 406 | * We have mainly added this function as an example! |
||
| 407 | * |
||
| 408 | * @param Controller $optionalController - optional custom controller class |
||
| 409 | * @param Validator $optionalValidator - optional custom validator class |
||
| 410 | * |
||
| 411 | * @return OrderModifierForm or subclass |
||
| 412 | */ |
||
| 413 | public function getModifierForm(Controller $optionalController = null, Validator $optionalValidator = null) |
||
| 423 | |||
| 424 | /** |
||
| 425 | * @return object (HeadingField) |
||
| 426 | */ |
||
| 427 | protected function headingField() |
||
| 436 | |||
| 437 | /** |
||
| 438 | * @return object (LiteralField) |
||
| 439 | */ |
||
| 440 | protected function descriptionField() |
||
| 449 | |||
| 450 | // ######################################## *** 7. template functions (e.g. ShowInTable, TableTitle, etc...) |
||
| 451 | |||
| 452 | /** |
||
| 453 | * Casted variable, returns the table title. |
||
| 454 | * |
||
| 455 | * @return string |
||
| 456 | */ |
||
| 457 | public function TableTitle() |
||
| 465 | |||
| 466 | /** |
||
| 467 | * caching of relevant OrderModifier_Descriptor. |
||
| 468 | * |
||
| 469 | * @var OrderModifier_Descriptor |
||
| 470 | */ |
||
| 471 | private $orderModifier_Descriptor = null; |
||
| 472 | |||
| 473 | /** |
||
| 474 | * returns the relevant orderModifier_Descriptor. |
||
| 475 | * |
||
| 476 | * @return OrderModifier_Descriptor | Null |
||
| 477 | */ |
||
| 478 | protected function getOrderModifier_Descriptor() |
||
| 489 | |||
| 490 | /** |
||
| 491 | * returns a heading if there is one. |
||
| 492 | * |
||
| 493 | * @return string |
||
| 494 | **/ |
||
| 495 | public function Heading() |
||
| 503 | |||
| 504 | /** |
||
| 505 | * returns a description if there is one. |
||
| 506 | * |
||
| 507 | * @return string (html) |
||
| 508 | **/ |
||
| 509 | public function Description() |
||
| 517 | |||
| 518 | /** |
||
| 519 | * returns a page for a more info link... (if there is one). |
||
| 520 | * |
||
| 521 | * @return object (SiteTree) |
||
| 522 | **/ |
||
| 523 | public function MoreInfoPage() |
||
| 531 | |||
| 532 | /** |
||
| 533 | * tells you whether the modifier shows up on the checkout / cart form. |
||
| 534 | * this is also the place where we check if the modifier has been updated. |
||
| 535 | * |
||
| 536 | * @return bool |
||
| 537 | */ |
||
| 538 | public function ShowInTable() |
||
| 548 | |||
| 549 | /** |
||
| 550 | * Returns the Money object of the Table Value. |
||
| 551 | * |
||
| 552 | * @return Money |
||
| 553 | **/ |
||
| 554 | public function TableValueAsMoney() |
||
| 562 | |||
| 563 | /** |
||
| 564 | * some modifiers can be hidden after an ajax update (e.g. if someone enters a discount coupon and it does not exist). |
||
| 565 | * There might be instances where ShowInTable (the starting point) is TRUE and HideInAjaxUpdate return false. |
||
| 566 | * |
||
| 567 | *@return bool |
||
| 568 | **/ |
||
| 569 | public function HideInAjaxUpdate() |
||
| 580 | |||
| 581 | /** |
||
| 582 | * Checks if the modifier can be removed. |
||
| 583 | * |
||
| 584 | * @return bool |
||
| 585 | **/ |
||
| 586 | public function CanBeRemoved() |
||
| 590 | |||
| 591 | /** |
||
| 592 | * Checks if the modifier can be added manually. |
||
| 593 | * |
||
| 594 | * @return bool |
||
| 595 | **/ |
||
| 596 | public function CanAdd() |
||
| 600 | |||
| 601 | /** |
||
| 602 | *Identifier whether a modifier will be added automatically for all new orders. |
||
| 603 | * |
||
| 604 | * @return bool |
||
| 605 | */ |
||
| 606 | public function DoNotAddAutomatically() |
||
| 610 | |||
| 611 | /** |
||
| 612 | * Actual calculation used. |
||
| 613 | * |
||
| 614 | * @return float / Double |
||
| 615 | **/ |
||
| 616 | public function CalculatedTotal() |
||
| 620 | |||
| 621 | /** |
||
| 622 | * This link is for modifiers that have been removed and are being put "back". |
||
| 623 | * |
||
| 624 | * @return string |
||
| 625 | **/ |
||
| 626 | public function AddLink() |
||
| 638 | |||
| 639 | /** |
||
| 640 | * Link that can be used to remove the modifier. |
||
| 641 | * |
||
| 642 | * @return string |
||
| 643 | **/ |
||
| 644 | public function RemoveLink() |
||
| 656 | |||
| 657 | /** |
||
| 658 | * retursn and array like this: array(Title => "bla", Link => "/doit/now/");. |
||
| 659 | * |
||
| 660 | * @return array |
||
| 661 | */ |
||
| 662 | public function PostSubmitAction() |
||
| 666 | |||
| 667 | // ######################################## *** 8. inner calculations.... |
||
| 668 | |||
| 669 | /** |
||
| 670 | * returns the running total variable. |
||
| 671 | * |
||
| 672 | * @see variable definition for more information |
||
| 673 | * |
||
| 674 | * @return float |
||
| 675 | */ |
||
| 676 | public function getRunningTotal() |
||
| 680 | |||
| 681 | // ######################################## *** 9. calculate database fields ( = protected function Live[field name]() { ....} |
||
| 682 | |||
| 683 | protected function LiveName() |
||
| 690 | |||
| 691 | protected function LiveTableValue() |
||
| 695 | |||
| 696 | /** |
||
| 697 | * This function is always called to determine the |
||
| 698 | * amount this modifier needs to charge or deduct - if any. |
||
| 699 | * |
||
| 700 | * |
||
| 701 | * @return Currency |
||
| 702 | */ |
||
| 703 | protected function LiveCalculatedTotal() |
||
| 707 | |||
| 708 | // ######################################## *** 10. Type Functions (IsChargeable, IsDeductable, IsNoChange, IsRemoved) |
||
| 709 | |||
| 710 | /** |
||
| 711 | * should be extended if it is true in child class. |
||
| 712 | * |
||
| 713 | * @return bool |
||
| 714 | */ |
||
| 715 | public function IsChargeable() |
||
| 719 | /** |
||
| 720 | * should be extended if it is true in child class. |
||
| 721 | * |
||
| 722 | * @return bool |
||
| 723 | */ |
||
| 724 | public function IsDeductable() |
||
| 728 | |||
| 729 | /** |
||
| 730 | * should be extended if it is true in child class. |
||
| 731 | * |
||
| 732 | * @return bool |
||
| 733 | */ |
||
| 734 | public function IsNoChange() |
||
| 738 | |||
| 739 | /** |
||
| 740 | * should be extended if it is true in child class |
||
| 741 | * Needs to be a public class. |
||
| 742 | * |
||
| 743 | * @return bool |
||
| 744 | */ |
||
| 745 | public function IsRemoved() |
||
| 749 | |||
| 750 | // ######################################## *** 11. standard database related functions (e.g. onBeforeWrite, onAfterWrite, etc...) |
||
| 751 | |||
| 752 | /** |
||
| 753 | * standard SS method. |
||
| 754 | */ |
||
| 755 | public function onBeforeWrite() |
||
| 759 | |||
| 760 | /** |
||
| 761 | * removing the Order Modifier does not delete it |
||
| 762 | * rather, it ignores it (e.g. remove discount coupon) |
||
| 763 | * We cant delete it, because we need to have a positive record |
||
| 764 | * of it being removed. |
||
| 765 | * Extend on Child Classes. |
||
| 766 | */ |
||
| 767 | public function onBeforeRemove() |
||
| 771 | |||
| 772 | /** |
||
| 773 | * removing the Order Modifier does not delete it |
||
| 774 | * rather, it ignores it (e.g. remove discount coupon) |
||
| 775 | * We cant delete it, because we need to have a positive record |
||
| 776 | * of it being removed. |
||
| 777 | * Extend on Child Classes. |
||
| 778 | */ |
||
| 779 | public function onAfterRemove() |
||
| 783 | |||
| 784 | // ######################################## *** 11. AJAX related functions |
||
| 785 | |||
| 786 | /** |
||
| 787 | * @param array $js javascript array |
||
| 788 | * |
||
| 789 | * @return array for AJAX JSON |
||
| 790 | **/ |
||
| 791 | public function updateForAjax(array $js) |
||
| 852 | |||
| 853 | // ######################################## *** 12. debug functions |
||
| 854 | |||
| 855 | /** |
||
| 856 | * Debug helper method. |
||
| 857 | * Access through : /shoppingcart/debug/. |
||
| 858 | */ |
||
| 859 | public function debug() |
||
| 865 | } |
||
| 866 |