Day: October 21, 2010

  • Counter down for Magento CMS blocks

    我做了一个 Magento CMS static blocks 里用的倒计时 block。虽然功能比较简单,但还是比较智能的,所以还是想自我表彰一下。这不是独立的模块,我也没打算就拿这么单一的功能去生成一个模块,类似的功能我都会合并在一个 Msdk (Magento SDK) 模块里。

    
    <?php
    /***
    * Usage:
    *
    * Example 1:
    * {{block type="msdk/date_countdown" deadline="2010-10-31" before="Halloween in %d days!!" after="Happy Halloween!"}}
    *
    * Example 2:
    * {{block type="msdk/date_countdown" deadline="2010-10-31 19:35:00" before="Lucky draw in %i minute!!" after="" smart_plural="true"}}
    *
    * Obviously, "deadline" value is required. And it should be fed in ISO 8601 format.
    *
    * "before" and "after" are the message strings to be used before and after deadline respectively.
    * If any of them is not set, it uses "%h" as default.
    * If you want the message string be empty, you must set it to "", explicitly. (Not setting it will result in "%h")
    * You can use "%d" for days, "%m" for months, ... in the message strings. For a complete list of usable symbols, visit
    * http://www.php.net/manual/en/dateinterval.format.php
    *
    * "smart_plural" is a switch you can turn it on using smart_plural="true" (It is default to false).
    * When it is on, it looks for singular words in the message strings, and turn them into plurals when appropriate.
    * This feature is in experiment stage, so it may not be so smart.
    */
    class Qian_Msdk_Block_Date_Countdown extends Mage_Core_Block_Template {
    
    //unix timestamp
    protected $_deadline;
    
    protected $_before = '%h'; //default to hour diff
    
    protected $_after = '%h'; //default to hour diff
    
    protected $_smartPlural = false; //default to false;
    
    //DateInterval
    protected $_interval; //to store value used by different methods, avoid passing value
    
    protected function _toHtml() {
    $now = Mage::getModel('core/date')->date();
    $dateNow = date_create($now);
    $dateDeadline = date_create($this->_deadline);
    $interval = $dateNow->diff($dateDeadline);
    if ($interval->format('%R%s') > 0) {  //before deadline
    $display = $this->_before;
    }
    else { //after deadline
    $display = $this->_after;
    }
    
    $this->_interval = $interval;
    $display = $this->_pluralize($display);
    return $interval->format($display);
    }
    
    public function setDeadline($strDeadline) {
    $this->_deadline = Mage::getModel('core/date')->date($strDeadline);
    return $this;
    }
    
    public function setBefore($before) {
    $this->_before = $before;
    return $this;
    }
    
    public function setAfter($after) {
    $this->_after = $after;
    return $this;
    }
    
    public function setSmartPlural($smartPlural) {
    if ($smartPlural == 'true' || $smartPlural == 'yes' || $smartPlural == 1) {
    $this->_smartPlural = true;
    }
    else {
    $this->_smartPlural = false;
    }
    return $this;
    }
    
    protected function _pluralize($str) {
    if (!$this->_smartPlural) {  //smart plural not turned on, no further processing
    return $str;
    }
    $pattern = '/(.*)(%[YyMmDdaHhIiSs])(\s+)([a-zA-Z]+)([^a-zA-Z]|$)(.*)/Ui';
    return preg_replace_callback($pattern, "self::_pluralizeMatches", $str);
    }
    
    protected function _pluralizeMatches($matches) {
    return $matches[1] .
    Mage::helper('msdk/plural')->english($this->_interval->format($matches[2]), $matches[4]) .
    $matches[5] . $matches[6];
    }
    }
    
    
  • Use collection model without creating resource model in Magento

    问题的起因是为 Magento CMS pages 增加一个 html sitemap。我新建了一个类 Qian_Cpfp_Model_Page extends Mage_Cms_Model_Page,但我不想在 config.xml 让 Qian_Cpfp_Model_Page rewrite Mage_Cms_Model_Page。在 Qian_Cpfp_Model_Page 里,有一个方法 getSubpagesCollection(),返回某种规则下的子页集。

    我想递归调用 Qian_Cpfp_Model_Page::getSubpagesCollection() 得到子页树。调试时发现,getSubpagesCollection() 返回的是 collection of Mage_Cms_Model_Page instead of collection of Qian_Cpfp_Model_Page,而 Mage_Cms_Model_Page 里没有 getSubpagesCollection(),所以递归嵌套一层就进行不下去了。

    于是我又建了第2个类 Qian_Cpfp_Model_Mysql4_Page_Collection extends Mage_Cms_Model_Mysql4_Page_Collection。我想让 Qian_Cpfp_Model_Page::getSubpagesCollection() 返回 Qian_Cpfp_Model_Mysql4_Page_Collection, which is a collection of Qian_Cpfp_Model_Page。通常一个 model 和它的 collection 是通过一个 resource model 来沟通的,这样需要再建第3个类 Qian_Cpfp_Model_Mysql4_Page extends Mage_Cms_Model_Mysql4_Collection。但我实在不想再多建一个文件,因为:

    • Qian_Cpfp_Model_Mysql4_Page 即使建了也没有提供额外的功能。
    • 为了让 Qian_Cpfp_Model_Mysql4_Page 正常运转,config.xml 还得多几行关于 <resourceModel> 的定义及其 <entities> 的定义,而相同的定义已经在 Mage_Cms module 里已经做过了。
    • 我实在不喜欢用拷贝代码的方式去解决问题。

    到底能不能跳过 resource model 让 model 和 collection 互访呢?准确地说,我的问题是,能不能不建子类的 resource model 而让子类的 model 和 collection 互访?

    答案是能!具体实施步骤是:

    1. 在 Qian_Cpfp_Model_Page 以特殊方式指定 collection

    
    public function getResourceCollection() {
    return Mage::getModel('cpfp/mysql4_page_collection');
    }
    
    

    2. 在 Qian_Cpfp_Model_Mysql4_Page_Collection 以特殊方式指定 model 和 resouce model (通常方式不明示 resource model,根据 model 自行寻找,一找不存在就出错了)

    
    public function _construct()
    {
    parent::_construct();
    $this->_init('cpfp/page', 'cms/page'); //the second param is vital here
    }