the beautiful magento module - magetitans 2014
TRANSCRIPT
Manchester, November 1st 2014
Vinai Kopp
Introduction
2 The beautiful Magento Module November, 1st 2014
Magento 1.x
Introduction
3 The beautiful Magento Module November, 1st 2014
Magento 1.x
It has contributed to the collective knowledge of the PHP community.
Both as a good and a bad example :)
Introduction
4 The beautiful Magento Module November, 1st 2014
This is not an introduction into Magento development.
Introduction
5 The beautiful Magento Module November, 1st 2014
How cana Magento modulebe "beautiful"?
Introduction
6 The beautiful Magento Module November, 1st 2014
Qualities of a beautiful module
1.It provides business value2.Magento can be upgraded without breaking the module3.It is easy to understand and modify4.It has good test coverage5.It is free of bugs
7 The beautiful Magento Module November, 1st 2014
Principles and Patterns
Principles and Patterns
8 The beautiful Magento Module November, 1st 2014
Robert C. Martin (Uncle Bob)
Principles and Patterns
9 The beautiful Magento Module November, 1st 2014
Some principles and some of their fancy acronyms
•Single Responsibility Principle (SRP) •Open Closed Principle (OCP)•Liskov Substitution Principle (LSP)•Interface Segregation Principle (ISP)•Dependency Inversion Principle (DIP)
Principles and Patterns
10 The beautiful Magento Module November, 1st 2014
Some more principles and some more fancy acronyms
•Encapsulate what varies (ECV)•Strive for loosely coupled designs between objects that interact (LC)•Favor composition over inheritance (FCoI)•Only talk to friends (LoD)•Don't call us, we'll call you (IoC)
Principles and Patterns
11 The beautiful Magento Module November, 1st 2014
What have you done for me lately, principles?
Principles and Patterns
12 The beautiful Magento Module November, 1st 2014
Like with all good things in life,there can be too much.
13 The beautiful Magento Module November, 1st 2014
Open Closed Principle
14 The beautiful Magento Module November, 1st 2014
“Classes should be open for extension but closed for modification”
The Open Closed PrincipleUncle Bob
The Open Closed Principle
15 The beautiful Magento Module November, 1st 2014
Does Magento have it?
The Open Closed Principle
16 The beautiful Magento Module November, 1st 2014
Configuration XML merging
Often the cause of conflicts
The Open Closed Principle
17 The beautiful Magento Module November, 1st 2014
Lets look deeper
The Open Closed Principle
18 The beautiful Magento Module November, 1st 2014
Self-Documenting Code
Method visibility and naming
The Open Closed Principle
19 The beautiful Magento Module November, 1st 2014
public Methods
Are an open invitation to the world
The Open Closed Principle
20 The beautiful Magento Module November, 1st 2014
public Methods
Once published should never change their signature or functionality
The Open Closed Principle
21 The beautiful Magento Module November, 1st 2014
protected Methods
Communicate “override me!”
The Open Closed Principle
22 The beautiful Magento Module November, 1st 2014
protected Methods
Change in signature or functionality risks breaking subclasses
The Open Closed Principle
23 The beautiful Magento Module November, 1st 2014
private Methods
Safe to change
The Open Closed Principle
24 The beautiful Magento Module November, 1st 2014
How is method visibility used in Magento?
Only public and protected
A too open design!
The Open Closed Principle
25 The beautiful Magento Module November, 1st 2014
A good example ofmethod visibility documentinghow to extend a class.
26 The beautiful Magento Module November, 1st 2014
abstract class Mage_Core_Block_Abstract ...{ final public function toHtml() { // ...housekeeping $html = $this->_toHtml(); // ...more housekeeping
return $html; }
protected function _toHtml() { return ''; }
A good example from the core how method visibility helps
The Open Closed Principle
27 The beautiful Magento Module November, 1st 2014
Another good example of OCP
Event Observers
The Open Closed Principle
28 The beautiful Magento Module November, 1st 2014
How can we apply this principleto our modules?
By dispatching events
29 The beautiful Magento Module November, 1st 2014
Mage::dispatchEvent('events_example_import_prepare', ['importer' => $this]);
// ...
Mage::dispatchEvent( 'events_example_import_batch_start', ['importer' => $this, 'batch' => $batch]);
// ...
Mage::dispatchEvent( 'events_example_import_batch_end', ['importer' => $this, 'batch' => $batch]);
// ...
Mage::dispatchEvent('events_example_import_complete', ['importer' => $this]);
Choosing strategic places to dispatch events.
The Open Closed Principle
30 The beautiful Magento Module November, 1st 2014
Favor domain events overinstructional events
The Open Closed Principle
31 The beautiful Magento Module November, 1st 2014
Choose the right method visibility
Use private as the default
The Open Closed Principle
32 The beautiful Magento Module November, 1st 2014
Use the Magento Factory Methodsand Class Aliases
33 The beautiful Magento Module November, 1st 2014
Rewrites are applied during class name resolution.
Using a Factory Method with a PHP class nameis no better then using new.
$notLikeThis = new Mage_Catalog_Model_Product();
$badExample = Mage::getModel('Mage_Catalog_Model_Product');
34 The beautiful Magento Module November, 1st 2014
Rewrites are applied during class name resolution.
Using class aliases allows for rewrites.
$correctlyInstantiated = Mage::getModel('catalog/product');
gist of class name resolution steps: http://vin.ai/class-name-resolution
35 The beautiful Magento Module November, 1st 2014
$model = Mage::getModel('example_module/import_parser_xml');$resource = Mage::getResourceModel('example_module/search');$helper = Mage::helper('example_module');$block = Mage::app()->getLayout()->createBlock('example_module/list');
To keep our code open for extension, we should use class aliases for our classes.
36 The beautiful Magento Module November, 1st 2014
Encapsulate what varies
37 The beautiful Magento Module November, 1st 2014
“Encapsulate the Concept that Varies, i.e. a design is better when those parts that vary are encapsulated in a separate module”On the Criteria To Be Used inDecomposing Systems into ModulesDavid Parnas
Encapsulate what varies
38 The beautiful Magento Module November, 1st 2014
Keep changes local so they don't affect other parts of the system
Encapsulate what varies
39 The beautiful Magento Module November, 1st 2014
Magento, do you ECV?
Encapsulate what varies
40 The beautiful Magento Module November, 1st 2014
First some "missed opportunities"
Encapsulate what varies
41 The beautiful Magento Module November, 1st 2014
Configuration of theTax Calculation Algorithm
42 The beautiful Magento Module November, 1st 2014
// from Mage_Tax_Model_Sales_Quote_Tax::collect()
switch ($this->_config->getAlgorithm($this->_store)) { case Mage_Tax_Model_Calculation::CALC_UNIT_BASE: $this->_unitBaseCalculation($address, $request); break; case Mage_Tax_Model_Calculation::CALC_ROW_BASE: $this->_rowBaseCalculation($address, $request); break; case Mage_Tax_Model_Calculation::CALC_TOTAL_BASE: $this->_totalBaseCalculation($address, $request); break; default: break;}
Choosing the tax calculation algorithm based on the system configuration
Encapsulate what varies
43 The beautiful Magento Module November, 1st 2014
Order State Management
44 The beautiful Magento Module November, 1st 2014
public function canCancel(){ if (!$this->_canVoidOrder()) { return false; } if ($this->canUnhold()) { // $this->isPaymentReview() return false; } // ... more of the same + comments removed for brevity...
$state = $this->getState(); if ($this->isCanceled() || $state === self::STATE_COMPLETE || $state === self::STATE_CLOSED) { return false; }
if ($this->getActionFlag(self::ACTION_FLAG_CANCEL) === false) { return false; } return true;}
Example of sales/order state behavior management
45 The beautiful Magento Module November, 1st 2014
class Mage_Sales_Model_Order_State_Complete implements OrderState{ public function canInvoice() { return false; } public function canShip() { return false; } public function canReorder() { return true; }
// ...
Theoretical example of encapsulating the stateful behavior
Encapsulate what varies
46 The beautiful Magento Module November, 1st 2014
Where does the core adhere to the principle, at least in parts?
Encapsulate what varies
47 The beautiful Magento Module November, 1st 2014
Total Models
Total calculationPreparation of display data
Encapsulate what varies
48 The beautiful Magento Module November, 1st 2014
EAV Attribute Models
Backend,Source and Frontend models
Encapsulate what varies
49 The beautiful Magento Module November, 1st 2014
How can we apply this principlein our Modules?
Encapsulate what varies
50 The beautiful Magento Module November, 1st 2014
A simple example scenario
Add informationdepending on a the attribute setto the product detail page.
Encapsulate what varies
51 The beautiful Magento Module November, 1st 2014
A simple example scenario
Kitchen Attribute Set
Chair Attribute Set
Insurance Attribute Set
52 The beautiful Magento Module November, 1st 2014
private function getTemplateForAttributeSet(){ switch ($this->getAttributeSetId()) { case $this->kitchenAttributeSetId: return 'example/attributesetinfo/kitchen-info.phtml'; case $this->chairAttributeSetId: return 'example/attributesetinfo/chair-info.phtml'; case $this->insuranceAttributeSetId: return 'example/attributesetinfo/insurance-info.phtml'; }}
First try, not adhering to the principle
53 The beautiful Magento Module November, 1st 2014
private function getTemplateForAttributeSet(){ switch ($this->getAttributeSetId()) { case $this->kitchenAttributeSetId: return 'example/attributesetinfo/kitchen-info.phtml'; case $this->chairAttributeSetId: return 'example/attributesetinfo/chair-info.phtml'; case $this->insuranceAttributeSetId: return 'example/attributesetinfo/insurance-info.phtml'; }}
public function getChairCoverWashingInstructionsPDFUrl() {...}
public function getInsuranceTermsAndConditions() {...}
public function getKitchenDesignPdfUrl() {...}
public function getServicePartnerDirectoryUrl() {...}
The block also needs to provide all the methods called from the templates
Encapsulate what varies
54 The beautiful Magento Module November, 1st 2014
Decomposed classes
Kitchen Attribute Set Info Block
Chair Attribute Set Info Block
Insurance Attribute Set Info Block
Attribute Set Info Block Locator
55 The beautiful Magento Module November, 1st 2014
class Example_AttributeSetInfo_Block_InfoBlockLocator extends Mage_Core_Block_Text_List{ // ...}
The locator extends core/text_list
56 The beautiful Magento Module November, 1st 2014
protected function _prepareLayout(){ $infoBlockAlias = $this->_getInfoBlockClassAlias(); if (null !== $infoBlockAlias) { $this->insert($this->_createChildBlock($infoBlockAlias)); }}
private function _getInfoBlockClassAlias(){ switch ($this->getAttributeSetId()) { case $this->kitchenAttributeSetId: return 'example_attributesetinfo/info_kitchen'; case $this->chairAttributeSetId: return 'example_attributesetinfo/info_chair'; case $this->insuranceAttributeSetId: return 'example_attributesetinfo/info_insurance'; }}
Select and instantiate the delegate object
57 The beautiful Magento Module November, 1st 2014
class Example_AttributeSetInfo_Block_Info_Chair extends Mage_Core_Block_Template{ protected function _prepareLayout() { $this->setTemplate('example/attributesetinfo/chair-info.phtml'); }
public function getChairCoverWashingInstructionsPDFUrl() { return 'the url of the chair cover washing instructions pdf'; }}
The attribute set dependent differences are encapsulated
Encapsulate what varies
58 The beautiful Magento Module November, 1st 2014
What would be affected by change?
What would be affected by adding an attribute set?
What would be affected if the output for one attribute set needs to change?
59 The beautiful Magento Module November, 1st 2014
More Principles
60 The beautiful Magento Module November, 1st 2014
What else makes a module beautiful?
61 The beautiful Magento Module November, 1st 2014
Readable Code
Readable Code
62 The beautiful Magento Module November, 1st 2014
What makes code readable?
Coding Standard
63 The beautiful Magento Module November, 1st 2014
private function getInfoBlockClassAliasBadExample () { if ($this->getAttributeSetId()==$this->kitchenAttributeSetId) return 'example_attributesetinfo/info_kitchen'; if($this->chairAttributeSetId == $this->getAttributeSetId()) return 'example_attributesetinfo/info_chair'; else // insurance attribute set return 'example_attributesetinfo/info_insurance';}
private function getInfoBlockClassAliasGoodExample(){ switch ($this->getAttributeSetId()) { case $this->kitchenAttributeSetId: return 'example_attributesetinfo/info_kitchen'; case $this->chairAttributeSetId: return 'example_attributesetinfo/info_chair'; case $this->insuranceAttributeSetId: return 'example_attributesetinfo/info_insurance'; }}
Obviously
Readable Code
64 The beautiful Magento Module November, 1st 2014
What else makes code readable?
Descriptive method names
Readable Code
65 The beautiful Magento Module November, 1st 2014
How can we choose good method names?
A method should do only one thing.The name should state that thing.
Readable Code
66 The beautiful Magento Module November, 1st 2014
If naming a method is hard split it into smaller ones.
Readable Code
67 The beautiful Magento Module November, 1st 2014
First some examples of method names that could be improved
68 The beautiful Magento Module November, 1st 2014
protected function _construct(){ $this->_init('example_module/foo');}
One bad example straight out of the Magento ORM
69 The beautiful Magento Module November, 1st 2014
protected function _initializeCollection(){ $this->_setModelClassAlias('example_module/foo');}
I find this would be nicer to read
70 The beautiful Magento Module November, 1st 2014
public function isNewObject() {...}
public function hasChildren() {...}
Methods returning a boolean value should start with is or has
Readable Code
71 The beautiful Magento Module November, 1st 2014
$request->setDispatched(false)
Setters should start with set
Readable Code
Readable Code
72 The beautiful Magento Module November, 1st 2014
Some more good bad examples
73 The beautiful Magento Module November, 1st 2014
// from Mage_Catalog_Model_Category
public function getChildrenCategories() {...}public function getChildrenCategoriesWithInactive() {...}public function getChildren() {...}public function getAllChildren(...) {...}public function getCategories(...) {...}
Similar methods need very descriptive names to be self documenting
74 The beautiful Magento Module November, 1st 2014
// from Mage_Core_Model_Email
public function __construct(){ // TODO: move to config $this->setFromName('Magento'); $this->setFromEmail('[email protected]'); $this->setType('text');}
Dead code should be removed without mercy
75 The beautiful Magento Module November, 1st 2014
// from Mage_Shipping_Model_Carrier_Abstract
public function getTotalNumOfBoxes($weight){ // ... return $weight;}
WTF
76 The beautiful Magento Module November, 1st 2014
Wrap up
Wrap up
77 The beautiful Magento Module November, 1st 2014
What qualifies a Magento module as beautiful?
1.It provides business value2.Magento can be upgraded without breaking the module3.It is easy to understand and modify4.It has good test coverage5.It is free of bugs
78 The beautiful Magento Module November, 1st 2014
Further reading
Further reading
79 The beautiful Magento Module November, 1st 2014
Link to Amazon
Further reading
80 The beautiful Magento Module November, 1st 2014
Link to Amazon
Further reading
81 The beautiful Magento Module November, 1st 2014
Link to Amazon
Further reading
82 The beautiful Magento Module November, 1st 2014
Link to Amazon
83 The beautiful Magento Module November, 1st 2014
Thank you
84 The beautiful Magento Module November, 1st 2014
Questions | Comments
Tweet me @VinaiKopphttp://vinaikopp.com