Tag: magento

  • Lightbox not working in Magento 1.4 solved

    My case is special –

    很久以前为 Magento 1.3 加装 Lightbox 2 时,我听从某人的建议,去掉了 Magento 自带的 scriptaculous effects.js,安装了当时最新的 scriptaculous 1.8.3 effects.js。Lightbox 调试成功后一直没去动它。

    Magento 升级到 1.4 后,默认启用 Merge JavaScript Files,这时 Lightbox 就不工作了。如果禁用 Merge JavaScript Files,Lightbox 恢复正常。我调试不出 Lightbox 失效的原因,大概是 js library 版本不兼容。再看了一下 Magento 如今自带 scriptaculous 1.8.2,估计和 1.8.3 相差不大,为了兼容性,干脆就用 Magento 原装的 js。

    于是去掉我自装的 scriptaculous 1.8.3 effects.js,启用 Merge JavaScript Files,如我所愿 Lightbox 恢复正常。

  • Magento theme improvements from 1.3 to 1.4

    There are loads of improvements in the newest version of Magento themes. I just list some from my point of view. I observed these changes when I started using Magento 1.4 three days ago, but they may exist in the late version of 1.3.x.x already for long.

    Everyone knows we should separate presentation from content. Magento does it very well since its first public version. Magento 1.4 just makes separation even better. A theme is about presentation, usually made of two parts, app/design and skin. Compared with skin, app/design part is somehow closer to content. Magento 1.4 themes remove layouts and templates from app/design, which means they are fully rely on css to present different themes. Base/default theme is an exception, but it is a fall-back theme.

    In 2-column or 3-column page templates, main column code comes before left/right column. I think it hugely helps SEO. I first saw this kind layout in a Zen-Cart template. Maybe Magento was inspired by it. Under template/page, one-column.phtml was deleted, which saves confusion with 1column.phtml.

    The header logo is no longer displayed as a background image, which is a minor change but makes big improvement.

    I spent 3 days to re-write my theme to be compliant with Magento 1.4 but I love these changes.

  • How to call a Block multi times in Magento

    我觉得我对 Magento 的理解与日俱增(哈,这不是废话)。

    今天更新以前写的一段代码,有关在 block 里多次调用同一个 child block,但 child block 在每次调用时出现不同的内容,比如 product list block 去调用每个产品的 product block。Magento default theme 对此的处理方式跟我设想的不同,它把整个 product list block 就写成一个 block,没有 child block。在产品表现比较复杂的时候,特别是不同类的产品使用不同表现的时候,混写在一个 block 里代码就很乱,我想用 product list block 套用 product block 的方法去理清代码。

    这里涉及四个要点:

    1. 如果用 getChildHtml(‘product_block_name’),Magento 默认 block 只被调用一次,在 product list 循环里多次调用 getChildHtml(‘product_block_name’),结果所有产品显示的都是第一个产品。这是因为 Magento 缓存了 getChildHtml 返回值。如果要求每次都不用缓存值,必须使用 $useCache = false。$useCache 是 getChildHtml 的第二个可选参数,默认为 true,所以 getChildHtml(‘product_block_name’) 每次都得到了相同的结果;getChildHtml(‘product_block_name’, false) 就可以得到想要的不同结果。

    2. 既然说到缓存 $useCache,请分清这不是 block html cache。前者只是一个变量 cache,存储在 block class 的属性里;后者是 php code cache,存储在 files/apc/memcached, etc.

    3. Block 和 child block 之间怎么传递变量?亦即 product block 需要知道当前计算的哪个产品。很久以前我只会用 Mage::registry,于是每次在 product list block 里注册一个当前产品,然后调用 product block,在 product block 再取出注册值。我当时就不满意这种方案,我总觉得没有必要在全局注册一个变量来解决此类问题,但当时水平有限,想不到其他办法。现在有了:在 product list block 里使用

    getChild('product_block_name’)->setProduct($product)->toHtml();
    

    在 product block 写一个 setProduct() 方法:

    public function setProduct($product) {
    $this->_product = $product;
    return $this;
    }
    

    请注意 block 不同于 model,没有 magic method 可以用,所以必须老老实实写一个对应的setProduct() 方法。

    4. 如果用 getChild,就不用担心返回值被缓存,因为 getChild 不同于 getChildHtml,根本就不缓存返回值。

  • Editable Order extension for Magento is excellent

    Magento 要求太严,后台想修改 Order billing address or delivery address,必须 cancel the order and raise a new one。为了 traceability?我们觉得没有必要,怎么才能直接修改?

    找到两个 extension,一个 commercial,收 $89;一个 community,叫 Editable Order。

    当然先试试 Editable Order,一试很满意,虽然是德文,不影响使用。就用它了,有空再看看怎么把界面改成英文。

  • Cleanup after upgrade Magento to 1.4

    升级总有这样那样的问题,我不想节外生枝,也就不想在项目中途升级。我把一个正在开发中的 Magento 商店开始开发时版本是 1.3.2.4,但今天一不小心,敲入了

    ./pear install magento-core/Mage_All_Latest

    就把 Magento 升级到了 1.4。

    (要怪 Magento 升级太方便了?其实,今天的不小心是有原因的:我发现 Magento Connect 启动不了。尝试进入 Magento Connect 时通常要求再次输入管理员密码,但今天这个页面只有 Magento Connect logo,看上去页面意外中止。而我急着想试用一下某个 community extension,就用了 pear 命令。在敲键盘的过程中,脑筋没多想,竟然敲入 ./pear install magento-core/Mage_All_Latest,就坏事了。)

    升级以后发现后台 System/Configuration 保存时经常出错,错误提示:

    Error while saving this configuration: Invalid mode for clean() method

    CMS/Pages 也无法保存页面,错误提示:

    Error while saving Page. Please try again later

    Catalog/Products 也无法保存修改…

    我猜想所有这些错误都来自一个地方。错误多了反而好办——肯定他人也会碰到。果然,google 一下不怎么费力就找到答案:删除 app/code/core/Zend/Cache 目录就可以了。

    这证明了我的一个保证升级后不出错的土办法:先保持原有文件不动进行升级,然后把新老版本混合在一起的文件夹删除,然后再上传一份干净的新版本(被新版本抛弃的老文件就被删除了)。当然也可以用版本比较工具来找不同,但土办法更省事。

  • Dull_Addressfields Magento extension

    I could not edit customer order delivery address at backend using Magento 1.3.2.4. Checkbox “Same As Billing Address” is always ticked. When I untick it, it automatically tick itself. I was able to edit it some while ago. What happened since then?

    Half an hour later I found the problem was caused by installation of Dull_Addressfields 1.0. This extension was installed because I wanted to “Same As Billing Address” as a default choice at checkout. But even if I set “Default delivery address to billing address” to “No” without uninstall Dull_Addressfields. I upgrade Magento to 1.4. Problem remains.

    So I have to uninstall Dull_Addressfields. Actually, with the skills I acquired nowadays, I can make “Same As Billing Address” as a default choice without 3rd party extension. But my boss don’t want to make “Same As Billing Address” as a default choice. Situation changed since I installed Dull_Addressfields.

    Btw, Dull_Addressfields rewrites two classes: Mage_Customer_Model_Address and Mage_Sales_Model_Quote_Address. As Magento can’t safely handle two extensions rewrite a same class, I don’t want Mage_Customer_Model_Address and Mage_Sales_Model_Quote_Address to be rewritten by Dull_Addressfields (just to achieve a simple goal?). I’d like these important classes be untouched for future rewriting for important functionalities.

  • Magento 1.4 is released

    说来也巧,当我想用 Magento 1.4 时,正好 1.4 stable 也刚发布。Magento 1.4 的测试版一直诱惑着我,就是太忙没去测试它,再说我也不太爱测试版,所以我一直抵制着诱惑等待正式版。

    今天在一台机子上搭建了一个全新的服务器环境,同时也有用到 Magento,既然是全新的,我就想装个 Magento 1.4 吧。在 Magento download 页面,找了半天,没发现 Magento 1.4 rc 版。好久才回过神,原来 1.4 在 full release 里,那就是说已经有正式版啦。我喜欢正式版不喜欢测试版,因为我不喜欢和一大堆的 bugs 打交道。既然 Magento 1.4 已经有了正式版,那还犹豫什么,赶紧装一个。

    Magento 1.4 stable is now available
    Magento 1.4 stable is now available

    安装碰到一点小问题,不清楚算不算 bug: 这次使用的是 zend server,默认 AllowOverride None,安装时却选择了使用 rewrite,安装完成后才设置 AllowOverride All,重启了 zend server,前台正常,却进不了后台,即使清空了 cache 和 session 仍无济于事。错误提示为:

    There has been an error processing your request

    我删了 Magento 数据库和 local.xml,重新安装一次,终于正常。

  • How to override a Magento controller

    Say I want to override Magento Catalog Product controller, say my namespace is Magex and my module name is Powercat.

    Firstly, add the following to Powercat/etc/config.xml. What it does is adding another route “prowercat” in addition to “catalog” to frontend. When http://mydomain/catalog/somecontroller/someaction/ is routered, powercat/somecontroller/someaction will be loaded before (see the attribute “before”?) catalog/somecontroller/someaction. If powercat/somecontroller/someaction is available, people get some page when visiting http://mydomain/catalog/somecontroller/someaction/ or http://mydomain/powercat/somecontroller/someaction/. I will not publish the latter url because the whole point of overriding a controller is reusing the Magento original url.

    <?xml version="1.0"?>
    <config>
    <frontend>
    <routers>
    <catalog>
    <args>
    <modules>
    <anyname before="Mage_Catalog">Magex_Powercat</anyname><!-- the tag name can be any name -->
    </modules>
    </args>
    </catalog>
    </frontend>
    </config>
    
    

    Secondly, create a subclass in the following format. “require_once” is essential because controllers are not auto-loaded.

    <?php
    require_once 'Mage/Catalog/controllers/SomeController.php';
    class Magex_Powercat_SomeController extends Mage_Catalog_SomeController
    {
    //function someAction here
    }
    ?>
    
    

    Last but not the least, put the overriding controllers into a dedicated module. Otherwise it is very easy to break Magento fallback mechanism. For example, suppose Magex_Powercat has two controllers.

    1. Magex_Powercat_IndexController does its own logic (not extending Mage_Catalog_IndexController, and
    2. Magex_Powercat_ProductController extends Mage_Catalog_ProductController

    The scenarios are:

    • When people visit http://mydomain/catalog/product/someaction/, powercat/somecontroller/someaction will be loaded – OK.
    • When people visit http://mydomain/catalog/category/someaction/, catalog/category/someaction will be loaded (because Powercat does not have CategoryController, Magento will fallback to Mage’s CategoryController) – OK.
    • When people visit http://mydomain/catalog/index/someaction/, if Powercat’s IndexController does not have someAction (only Mage’s IndexController has) – Error happens!
    • When people visit http://mydomain/catalog/index/someaction/, if Powercat’s IndexController does have someAction, page will load without error. However, remember Magex_Powercat_IndexController does its own logic? No router to the original logic!
  • Magent Events

    Magento wiki 上有篇文章列出了所有的 events dispatched by Magento。但 wiki as a CMS 自作主张地转化了某些字符,比如把

    ->转化成了→

    =>转化成了⇒

    这让我觉得有必要自己来格式化这篇文档。虽然曾想用 OpenOffice 来存储,但它竟然连一张简单的表格都格式得很费劲,最后只好用了 Ms Word。

    Download magento-events

  • Understand how tax is calculated in Magento

    彻底理解 Magento tax 是如何计算的不是一件容易的事。我做了无数次组合测试,算是了解 Magento tax 是如何计算的,但我未及去读代码,不敢说彻底摸透它的来龙去脉,写下这段文字权作日后参考。

    首先,零税率不用设置,因为如果什么设置都没有的话税率就是零,所以大多数时候只要针对非零税率进行设置。稍后会讲到什么时候需要进行零税率设置。

    其次,要分清以下名词:

    • tax rate 税率
    • tax zone 税区域
    • tax rule 税规则
    • customer tax class 顾客税种
    • product tax class 产品税种
    • shipping tax class 运费税种
    • customer group 顾客属组
    • customer address 顾客地址

    它们的关系是:

    1. 税计算的起点是税规则。换言之,如果后台设置了若干税率、税区域、顾客税种、产品税种、运费税种,却没有设置税规则,那么没有任何税生效。
    2. 只有税规则可以设置优先级,税区域无法设置优先级(除非去改数据库,id 越小的税区域优先)。
    3. 税规则是产品税种(或运费税种)、顾客税种、税区域在三维空间决定的。
    4. 后台界面设置税率和税区域在一个表单里,似乎税率由税区域直接决定,但并不一定能生效,原因见第3点。
    5. 税区域与顾客税种无关。这点曾是我困惑的地方,因为地址是顾客的一个属性,所以我很自然地认为顾客地址就对应税区域。如果尝试着放弃“顾客地址就对应税区域”思维,一切都容易理解了。
    6. 税规则的三维空间较难描述,可以这么来简化:先拿产品税种(或运费税种)和顾客税种交叉排列组合出多个税规则,再拿税区域去套,套中的税规则就生效,套不中就不生效。
    7. 顾客属组和顾客税种是多对一的关系。
    8. 在前台 checkout 时,Magento 会根据顾客地址启用一条或多条税规则,但后台 create order 时,Magento 的税规则却无视顾客地址。这或许是 Magento 的一个 bug。

    我以一个实例说明如何在 Magento 中设置 UK VAT。以我目前所知,within the UK, postcode JE 和 GY9 开头的区域可以免 VAT,其他区域需加收 VAT,英国以外免 VAT。因为我没有免 VAT 区域的完整列表,必须照顾到今后能随时添加更多的 postcode 进免 VAT 区域。我是这么设置的:

    • 因为产品比较单一,运费税率与产品税率相同,目前都是17.5%,所以只设一个产品税种,不搞运费税种,运费就沿用产品税种,便于理解。
    • 顾客税种分两种,普通和VAT FREE。顾客属组也分两种,普通和VAT FREE,一一对应便于理解。请注意,普通类不是指一定要收 VAT,而是按税规则的第三维——税区域智能决定税率;但免 VAT 类下的顾客一定是零税率,这么做是为了照顾后台 create order 时的 bug(姑且当是)。
    • 建若干个税区域, UK postcode JE* 税率为0、UK postcode GY9* 税率为0、UK postcode * 税率为17.%。其他国家的税率不需要一个建,因为没有税规则,那就是零税率。

    现在开始创建税规则,按优先级别排序:

    1. 产品税种全选(反正只有一个),顾客税种选普通,税区域选 UK postcode JE*和UK postcode GY9*
    2. 产品税种全选(反正只有一个),顾客税种选普通,税区域选 UK postcode *
    3. 产品税种全选(反正只有一个),顾客税种选VAT FREE,税区域选 UK postcode JE*

    前两条规则是为了前台 checkout,第三条税规则是为了照顾后台 create order 时的 bug。Magento 目前版本 1.4.0.1,后台 create order 若把顾客归类普通,bug 无视税区域的存在,也不管税规则的优先级,似乎只会挑一个最高税率。普通顾客税种有0和17.5%两种可能,后台却总是用17.5%去收税。为避免激发 bug,干脆,顾客税种选VAT FREE,VAT FREE税种只有一个可能,永远是0,只在后台使用。

    如果今后得知 UK postcode XYZ* 也免 VAT,那就再建一个税区域 UK postcode XYZ* 税率为0;然后进入已有的税规则1,税区域增选 UK postcode XYZ* 即可。虽然税区域 UK postcode * 创建在先,UK postcode XYZ* 创建在后,但税规则的优先级别保证了 UK postcode XYZ* 先于 UK postcode *。

    再说一个极端的例子,若 UK 免 VAT 的区域只有 JE* 和 GY9*,今后也不会增加了,那么先创建税区域 UK postcode JE*和UK postcode GY9*,后创建 UK postcode *,然后只需要第1条税规则里同时选中UK postcode JE*、UK postcode GY9* 和 UK postcode *,删除税规则2也可以保证 UK postcode JE*和UK postcode GY9* 的优先性。但这么做有悖 Magento tax 的设计思路,不可取。