大宝在哪里呀,大宝在哪里(升调)
Blog
-
Full page caching preparer module for Magento
I am planning a project called Fpcp (Full Page Caching Preparer) to improve Magento performance. Yeah, it is called Fpcp and very similar to another module Cpfp (Cms Page Foot Print).
I have a bit research on Magento Enterprise only features, and pick full page caching as my next interested project. I do not know how Magento Enterprise implements full page caching, but my idea is rewriting all blocks containing session related content with an ajax get method.
-
MB200 week calculation bug
一个月前,我的 MB200 显示有系统更新可用,我就升级了一下。升级了以后很失望,firmware version 仍是 1.5,看来 motorola 不打算让 MB200 用上 android 1.6 以上版本了,有空了我还得自行升级。
用了一个月,我发现了 MB200 现有的版本 (Blur version 1.3.39) 居然有个日期计算上的 bug。比如今天是星期天,它会认为是星期一。这样,我设定的闹钟,闹周一到周五,结果它闹的是周日到周四;10月的最后一个星期天的凌晨从夏令时切换到冬令时,结果它提前一天星期六就让我用上冬令时了,搞得我周六一早起来看时间后总觉得怪怪的。
如果我来升级,我就上 android 2.2。
-
Double rainbow is nothing special
Double Rainbow over Devon This week I read a news on dailymail After seven years and 2,000 photographs… a double delight as rainbow lights up Devon
By Daily Mail Reporter
Last updated at 9:26 AM on 27th October 2010A photographer who has taken pictures of the view from his office window every day for seven years has finally struck gold by snapping this stunning double rainbow.
Andy Kyle, 53, captured the weather phenomenon as it appeared to create a bridge over the river at Dartmouth in Devon.
Andy, who runs a website called The View From The Dartmouth Office, has been posting pictures of the vista each day for the past seven years.
He said he had taken more than 2,000 images of the view before he snapped the beautiful double rainbow.
Howzat? Sometimes a dim secondary rainbow is seen outside the primary rainbow. Secondary rainbows are caused by a double reflection of sunlight inside the raindropsHowzat? The ‘bridge’ over the river at Dartmouth in Devon. Sometimes a dim secondary rainbow is seen outside the primary rainbow. They are caused by a double reflection of sunlight inside the raindrops
Sometimes a fainter ‘secondary rainbow’ can be seen outside the main rainbow.
Secondary rainbows are caused by a double reflection of sunlight inside the raindrops, and appear at an angle of 50–53°.
If you look closely you can see the colours of the secondary rainbow are inverted, with blue on the outside and red on the inside.
The secondary rainbow is fainter than the primary because more light escapes from two reflections than one and because the rainbow itself is spread over a greater area of the sky.
The effect is quite ghostly, and makes for a stunning photograph, although it makes looking for the pot of gold slightly more tricky.
I did not think about it too much. But today when I was taking my friend Erin to Heathrow Terminal 3, I saw double rainbow just above the airport. It was a pity I had no hands free to take a photo to compete because I was busy driving my car.
When I am back home, I search for “double rainbow” photos, I find it is nothing special but reported many times in many places. If I head up a bit often, I may see double rainbow again very soon.
-
Magento promotional free shipping does not apply to tablerate
Magento 自有的 shipping calculation 已足够强大,如果谁还说不够用,我就会说是经营上的问题。
最近我被要求做这么一件事,在一个 storeview 下,对部分商品不论金额大小,一律 free shipping。一听之下我觉得可以用 cart salesrule free shipping action 来实现,实际尝试后却发现,cart salesrule free shipping 不能对 tablerate 起作用。而 tablerate 是 frontend 唯一一个 shipping method,被设置强制选择,顾客不会被问到 choose shipping method。在这个逻辑下,如果我启用 free shipping method,checkout 就多问一个问题,老板说这不好。而 flatrate 另有他用(backend 接单时对特殊地区额外征收运费),也无法拿它来实现部分商品 free shipping。
这件事最后用一个普通 discount 抵消 shipping charge 的方式迂回解决,但解决得很别扭。我认为
- shipping rates 是一种全局设置,就算能,也不应该为某些商品创建例外的;
- 如果想为某些商品创建例外,那么再想一下为什么要创建例外,比如这些商品针对是一群特殊用户,如果 cross selling 对特殊用户不起作用,那就专门为他们建一个 store 或 website;
- 新建 store 或 website 的成本与销售预期收益相比,是否值得?如果不值得,再反思一下是否值得为这些商品创建例外;
- 自己写一个 shipping module 肯定可以实现任何稀奇古怪的 shipping rules,但写程序不是为了照顾 business 复杂化的需要。
-
Place Magento model, resource, collection class at your will
Most Magento model, resource, collection are placed this way:
- Model is located at Model folder
- Resource is located at Model/Mysql4
- Collection is located at Model/Mysql4/(ClassName)
What if you want organise them in a different way? Say how place all of 3 classes under one folder?
I enclose a complete a working sample.
1. Model class
class Qian_Msdk_Model_MediaGallery_Mg extends Mage_Core_Model_Abstract { public function _construct() { parent::_construct(); $this->_init('msdk_mg/resource'); } }
2. Resource class
class Qian_Msdk_Model_MediaGallery_Resource extends Mage_Core_Model_Mysql4_Abstract { public function _construct() { $this->_init('msdk_mg/product_attribute_media_gallery', 'value_id'); } }
3. Collection class
class Qian_Msdk_Model_MediaGallery_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract { public function _construct() { parent::_construct(); $this->_init('msdk_mg/mg'); } }
4. config.xml
<?xml version="1.0" encoding="UTF-8"?> <config> <global> <models> <msdk_mg> <class>Qian_Msdk_Model_MediaGallery</class> <resourceModel>msdk_mg_mysql4</resourceModel> </msdk_mg> <msdk_mg_mysql4> <class>Qian_Msdk_Model_MediaGallery</class> <entities> <product_attribute_media_gallery> <table>catalog_product_entity_media_gallery</table> </product_attribute_media_gallery> <product_attribute_media_gallery_value> <table>catalog_product_entity_media_gallery_value</table> </product_attribute_media_gallery_value> </entities> </msdk_mg_mysql4> </models> </global> </config>
-
this vs $(this) in jQuery
I don’t know why, but this is how it happens.
When I make a jQuery plugin, the skeleton is
(function($) { $.fn.myplugin = function() { return this; }; })(jQuery);
And if I need an each operation inside plugin, the skeleton can be
(function($) { $.fn.myplugin = function() { //outside each, this.width() can work return this.each(function() { //inside each, only $(this).width() works }); }; })(jQuery);
The interesting thing is inside each, I must use “$(this)” to run jQuery functions, but outside each, I can also use “this”.
I don’t know why now, but I think I will know some day.
-
How to read Magento product custom options correctly?
Magento product custom options 的存储结构比较“绕”,我绕了好长时间才解开。
假设 $product 有两个 custom options,一个是 textarea input type,另一个是 multiple select input type:
先用 $product->getOptions(),得到是包含两个 custom options 的集合;
然后 foreach 一下,得到每个 $option。对第一个 textarea 类型的,处理起来相对简单,用 $option->getData(‘title’) 查看 title,以此类推。对第二个 multiple select 类型的,用 $option->getData() 只能查看概况,对于 select 里的每个 option (对应 <select><option>…</option></select>),还得用 $option->getValues(),然后再次 foreach 才能得到 select option。够复杂吧。
-
How to pager array items in Magento
The Mage class Mage_Page_Block_Html_Pager is handy to pager collection items, but it is designed to work with a collection derived from Mage_Core_Model_Mysql4_Collection_Abstract.
Now I run into a situation to pager arbitrary array items. The array items are pre-built and not loaded via a resource model, so the pager lost a clue how to control which items to display. (Which makes me think – Magento is powerful, but not so powerful without the help of mysql – Magento collection functionality rely heavily on mysql operation – it is a bit off topic.)
So I need to tell the pager how to load the array after array items are loaded already. The following is my solution class.
<?php class Qian_Msdk_Model_PagerableCollection extends Varien_Data_Collection { /*** * load only current page items */ public function load($printQuery = false, $logQuery = false) { if ($this->isLoaded()) { return $this; } $this->loadForPager(); $this->_setIsLoaded(); return $this; } public function loadForPager() { if (!$this->getPageSize()) { //not pagerized return $this; } $items = array(); $currentPage = $this->getCurPage(); $i = 0; foreach ($this->_items as $item) { if ($i < ($currentPage-1) * $this->getPageSize()) { $i++; } elseif ($i >= $currentPage * $this->getPageSize()) { break; } else { $items[] = $item; $i++; } } $this->_items = $items; return $this; } /*** * get the size of collection before pagerize */ public function getSize() { if (is_null($this->_totalRecords)) { $this->_totalRecords = count($this->_items); } return intval($this->_totalRecords); } }
-
A universal recursion method for Magento
Magento 历遍 category 时用的 Tree/Node 模型,我看了一知半解。当我把 CMS page 整理成树状,想对所有 pages 如同 category 历遍操作时,我抛开 Tree/Node,就写了两个简单的方法来套用(感谢 php 5.3 让这方法成为可能)。
public function recurseSubpages($function, &$params = null) { $this->_recurse($this, $function, 0, $params); } protected function _recurse($page, $function, $level, &$params = null) { $function($page, $level, $params); foreach ($page->getSubpageCollection() as $subpage) { $this->_recurse($subpage, $function, $level + 1, $params); } }
若要 CMS page 历遍 someOperation(),则只要临时定义一个 $function,然后调用 $page->recurseSubpages(),具体代码是
$function = function($page, $level, &$params) { $page->someOperation(); }; Mage::getModel('cms/page')->recurseSubpages($function);
$params 是用来回传参数的,如果不需要回传,可以省略。
我认为这样让复杂的递归变得很简单,可是在想做一张 html 格式的 CMS page sitemap (用<ul><li>…</li></ul>层次嵌套体现 CMS page 的层次,如同 Magento top navigation 那段代码。顺便说一下,我认为 Magento category sitemap 的代码不够好,它把树形结构线性化了,不够 semantic)时,遇到困难。当时我觉得要在 _recurse() 里再次调用 _recurse() 之前和之后有条件地插入一些操作,不是简单定义一个 $function 可以做到的。
所以第一版的 CMS page sitemap 绕开了我自己的 page recurse 模型独自写了很长一段代码,大意如下:
public function prepareSitemap() { if ($id = Mage::getSingleton('cms/page')->getId()) { $page = Mage::getModel('cpfp/page')->load($id); } else { $page = Mage::getModel('cpfp/page'); } $this->_output = self::_recurseSitemap($page, 0); return $this; } protected function _recurseSitemap($page, $level) { $output = ''; $hasId = ($page->getId())?true:false; $title = Mage::helper('cpfp')->htmlEscape($page->getTitle()); foreach ($page->getSubpageCollection() as $subpage) { $output .= self::_recurseSitemap($subpage, $level+1); } if ($hasId) { if ('xhtml' == $this->_outputFormat) { $pageContent = "<a href='{$this->getUrl($page->getIdentifier())}'>" . $title . '</a>'; if ($output) { $output = "<li>$pageContent<ul>$output</ul></li>"; } else { $output = "<li>$pageContent</li>"; } } elseif ('text' == $this->_outputFormat) { $pageContent = str_repeat("\t", $level) . $title . "\n"; $output = $pageContent . $output; } else { //do nothing } } else { //!$hasId //do nothing } if ($level == 0 && $output && 'xhtml' == $this->_outputFormat) { //final wrap here $output = '<ul>' . $output . '</ul>'; } return $output; }
以上代码是放在一个 Block 类里,该 Block 没有使用 template 直接输出了 html。Magento top navigation 也是由 Mage_Catalog_Block_Navigation 直接输出了 html,所以当时我想,输出递归结果也只能是这样了。
过了一段时间,突然有了灵感,其实还有更好的方法输出递归结果!具体步骤是:
- 定义这么一个 $function,把当前 $level 信息存储到当前 $page 里,把 $page 压入 collection 通过 $params 返回;
- 调用 recurseSubpages($function, $params) 历遍 CMS pages,就得到了树状顺序、包含层次信息的 collection;
- 在 template 里 foreach $collection 的元素时,比对前一个元素和后一个元素和当前元素的 level
- 如果 $levelFormer = $levelCurrent,而且 $levelCurrent = $levelLatter,输出<li>当前元素</li>
- 如果 $levelFormer = $levelCurrent,而且 $levelCurrent < $levelLatter,输出<li>当前元素
- 如果 $levelFormer = $levelCurrent,而且 $levelCurrent > $levelLatter,输出<li>当前元素</li></ul></li>
- 如果 $levelFormer < $levelCurrent,而且 $levelCurrent = $levelLatter,输出<ul><li>当前元素</li>
- 如果 $levelFormer < $levelCurrent,而且 $levelCurrent < $levelLatter,输出<ul><li>当前元素
- 如果 $levelFormer < $levelCurrent,而且 $levelCurrent > $levelLatter,输出<ul><li>当前元素</li></ul></li>
- 如果 $levelFormer > $levelCurrent,而且 $levelCurrent = $levelLatter,输出<li>当前元素</li>
- 如果 $levelFormer > $levelCurrent,而且 $levelCurrent < $levelLatter,输出<li>当前元素
- 如果 $levelFormer > $levelCurrent,而且 $levelCurrent > $levelLatter,输出<li>当前元素</li></ul></li>
一一列出 template 的 9 种情况只为理清思路,实际代码并不需要分 9 种情况处理。不管怎样,我把历遍和表现相分离了,相比 Mage_Catalog_Block_Navigation 的代码更体现 MVC 的精髓。
讲了这么多,如果不摆完整的例子出来,估计谁也看不明白。那就请关注一下 Cpfp (Cms Page Foot Print)这个模块吧,在 Cpfp 模块中,我实现了 CMS page 的层次化,附送 CMS Page Sitemap,并修改了 CMS page breadcrumb 以充分体现页面的层次。有人用 category page 去构筑需要层次的内容,有人用 blog 模块去管理层次内容,有人干脆整合 wordpress 去管理内容。我的 Cpfp 只是用另一种方法去实现内容的层次,出发点是在内容表现方式上,CMS page 比 category 要强大,我在不修改 Magento 原有 cms_page 数据表的前提下实现层次化,是非常“绿色”、非常“轻量级”的模块。