Tag: magento

  • Zend_Form in Magento

    我喜欢 Zend_Form 这个组件,为了在 Magento 里使用 Zend_Form,走了不少弯路,但最终方法是很简单的。

    要在 Magento 里使用 Zend_Form 最关键的一点是明白 Zend_Form 依赖于 Zend_View,而 Magento 从头到尾都没有用到 Zend_View。如果使用 Zend Framework Bootstrap,它会搭建好 MVC,在你想到去用 Zend_View 之前,它就已经就位了,所以 Zend_Form 在真正的 Zend Framework 里是取之即用的。在 Magento 里,必须自己初始化一个 Zend_View,传递给 Zend_Form 的 $_view,或者用 setView()方法,否则你会得到一个错误提示:ViewHelper decorator cannot render without a registered view object。

    我走的弯路是刻意在 Magento 里照搬 Zend Framework 里 Zend_Form 的输出。我专门建了 views 这个目录,在其下又建了scripts 目录(Magento 里最接近 views/scripts 的目录是 templates,但我不想把 Zend Framework 那套文件与 Magento 的文件混在一起),然后把初始化后的 Zend_Form 赋值给 $view->form,然后在 views/scripts/index.phtml 里

    
    echo $this->form;
    
    

    然后在 Magento 的 Block 文件里新建一个方法

    
    public function getZendView() {
    	return $this->view;
    }
    
    

    最后在 Magento template 文件里

    
    echo $this->getZendView();
    
    

    虽然能运行,但过了一天,我自己都觉得不对头,尝试着直接在 Magento 里输出 Zend_Form,也成功了。具体做法是:

    先为 Zend_Form 创建一个目录,我把它建在 {MyModuleName}/Model 下(我见 Magento 把与 Form 相关的文件归入 Block,但调用 Block 文件没有调用 Model 文件方便。既然如何在 Magento 里使用 Zend_Form 属于我的原创,在不违背 Magento 文件规范的情况下,我应该有自己的自由决定。再说把 Form 对象归于 Model 也不违背 MVC 的原则)。

    然后在其下建了一个 helpers 目录,即 {MyModuleName}/Model/Form/helpers(若不涉及自定义 helper,则跳过此步骤)。选择这个位置,我对自己都有争议,但总得为 Zend_Form 的 ViewHelper 文件找个地方。我在 Zend_Form 里初始化 Zend_View,而 Zend_View 需要知道 ViewHelper 文件的位置,我可以用很简单的dirname(__FILE__) . ‘/helpers’ 得到这个位置。如果放在 Magento 自己的 Helper 目录,则搞混了 Magento Helper 与 Zend ViewHelper,而且我用不着用 Mage::helper(‘mymodulename’) 去调用它。所以我决定在 Form 目录下放置 helpers 目录,以后若把 Form 迁移位置,helpers 也可以随之迁移,方便 refactoring。

    然后创建 Zend_Form,在 Block 里用 $form->render() 得到这个表单的 html 输出,最后在 template 下该怎么做我就不多说了。

  • Magento Block has no getters and setters

    I finally realise Block class in Magento is not like Model class – it has no getters and setters.

    It breaks MVC logic if we want to set data in Block, that’s why Block has no setters. However, when Block makes a variable which can be used in template. It is a maker, not a setter. So the function name is usually getSomething(). From the pointer view of template, it is ‘get’, but it is not a getter for Block.

    Magento could name this kind of function in Block something else for people easy to understand.

  • Magento module vs Zend Framework module

    Magento 和 Zend Framework 都有 module 这个概念,我觉得这两者在设计思路略有区别,不能拿 Zend Framework module 去套 Magento module,反之亦然。

    简单点说,Magento module 是功能模块,Zend Framework module 是物理模块。Magento 把系统分割为十几个子系统,每个子系统各司其职,相对独立,但在整个 Magento 框架下,相对独立的子系统也能非常方便地与其他子系统通信,调用其他子系统中的功能。Zend Framework module 着眼逻辑分层,从 module 到 controller 再到 action。Zend Framework 并没有为 module 之间的通信作过多的考虑,如果用户有此需求,需要自行考虑,比如借鉴 Magento 的做法,在 Zend Framework 上再套一个框架。

  • Magento: Pass values from Block to template

    There are many ways in Magento to pass values from Block to template. I think these two ways are neat:

    Method 1:

    in Block, $this->setVariableName($value);
    and in template, $this->getVariableName();

    Method 2:

    in Block, $this->assign(‘variableName’, $value);
    and in template, $variableName is ready to use.

  • Zend Framework in Magento

    The library of Zend Framework included in Magento under lib folder is not the genuine Zend Framework. Although I don’t know why Magento did that, I do know:

    • If I replace Magento lib/Zend with genuine ZF, Magento throws errors.
    • If I put geninue ZF in php include_path, Magento appends it to its own include_path, and Magento runs into fussy status. I notice email facility stops working. There may be more functionalities affected.
    • I have loads of code extending Zend_Form. I was intended to reuse them in Magento without rewriting. However, Zend_Form depends on Zend_View, which is not handy in Magento.

    As one of caveats I’ve got from Magento, is whenever possible, avoid touching Zend classes directly.

  • Magento database fetch mode

    I was used to write $modelName->column_name to get attribute value in database. It requires $resource->_getReadAdapter()->setFetchMode(Zend_Db::FETCH_OBJ);.

    However, it is not Magento’s default fetch mode (default is Zend_Db:FETCH_ASSOC). Whenever fetch mode is changed, it must be changed back to the default after using it. Otherwise, method getCollection() of ALL models will stop working. There may be other side effects if you leave the adapter’s fetch mode changed.

  • Surcharge on card payment in Magento

    I just searched for a solution to surcharge customers for a certain PSP in Magento. The only thing I could find a module which priced at USD 49. I also found a lot of argument about whether card payment surcharge is illegal or against PSP’s policy, to which I do not care. What I do care is a free and easy solution to do the job.

    Looking at Promotions -> Shopping Cart Price Rules, we can give discount for some kinds of payment method. But the discount can not be minus, which makes it work as surcharge. However, Magento only validates discount must be positive when saving a shopping cart price rule to database. It does not validate again when applying this rule. Therefore, we can make up a rule, enter discount with a positive value, then change the value to minus in database. That’s all you need to do to surcharge customers.

    By the way, I think Magento validation on discount limits its usability. Why not use the salesrule module for both promotions and surcharges? I really like to see the menu Promotions be replaced by Sales Rules. (The word salesrule is already being used as table name and in script.)

  • Half constructed Magento objects

    Magento 对象有个半构造状态。称之为半构造或许不恰当,但我一时找不到更合适的词来表达。

    先说一个我以前碰到的问题:为什么在产品列表里的产品取不到自定义属性的值?因为此时产品处于半构造状态,半构造状态默认不加载自定义属性。只有在某产品所在的那一产品页,该产品才处于全构造状态。Magento 对象半构造主要是照顾速度。

    再说另一个问题:甲产品是一个 bundled product,bundled items 里只有一个 option,该 option 里只有一个 selection,该 selection 是 20 件乙产品。我不明白 Magento 创造这么多名词干嘛,又是 option,又是 selection,暂且不管它,就按 Magento 层层 wrapper 来办。在甲产品的产品页上,要取得捆绑数量(20),可以这么办——

    $_product = $this->getProduct(); //$_product is 甲产品
    $_option = current($_product->getOptions());  //because only one option
    $_selection = current($_option->getSelections()); //because only one selection
    $_qty = $_selection->getSelectionQty(); 
    

    如果甲产品是乙产品的相关产品,在乙产品的产品页上,要取得相关产品甲产品的捆绑数量,就无法按上述思路办,这是因为此时甲产品处于半构造状态。那么换个思路,这么来——

    $_product = Mage::getModel('catalog/product')->loadByAttribute('sku',$sku); //$_product is 甲产品
    $_productType = $_product->getTypeInstance();
    $_optionCollection = $_productType->getOptionsCollection();
    $_selectionCollection = $_productType->getSelectionsCollection($_productType->getOptionsIds());
    $_option = current($_optionCollection->appendSelections($_selectionCollection));
    $_selection = current($_option->getSelections());
    

    以上代码微妙之处就在$_option = current($_optionCollection->appendSelections($_selectionCollection));,它把半构造状态的甲产品进一步prepare,于是就有了selections。

  • Magento compilation does not help if php is already optimised

    实测了一下,在已做了一些 Magento 速度优化措施以后,如 php opcode,memcache,mysql query cache,再启用 Magento 编译,速度并未提高,request per second 反而从 11 降为 8。

  • Magento defect in running multi-stores

    First I want to declare – I love Magento very much!

    但再完美的东西也是有缺陷的。正是因为 Magento 接近完美,所以改正它缺陷的愿望也强烈些。Magento website – store – storeview 3-tier 的模型逻辑分明,可是 Magento 在解释模型时用词不一致,造成最终用户和开发者如果是同一人的话思维混乱:

    最终用户:website – store – storeview

    These terms are used in documentation, and designer’s guide.

    开发者:website – group – store

    These terms are used in php code, and database table/field names.

    这还是小毛病,关键是根据访问域名(或别的条件)加载不同的 website, store, storeview 时,我能看到的所有的指导都是叫我们修改 index.php,用一个条件选择 Mage:run(‘code’) 取代 Mage:run()。这样每次升级 Magento 都要特殊照顾一下 index.php,不符合 Magento 自己提倡的安全升级的原则。

    其实如果是根据 base url 决定加载不同的 website, store, storeview,database 里已有对应的资料,就不能让 Magento 自己去选择加载,何必要用 php 再写一遍?因为 Magento overwrite 规则在 index.php 和 Mage.php 以后生效,所以我不欣赏修改 index.php 和 Mage.php 任何一部分。看来看去,只有 Mage_Core_Model_App 才是切入点,要写一个 Company_Core_Model_App 才能让 Magento 自动加载不同的 website, store, storeview。