Tag: magento

  • Paypal vs PaypalUk in Magento

    Magento 里有两个有关 Paypal 支付的模块:Paypal and PaypalUk。我尚未查到文档有关它们的区别,只知道 PaypalUK 依赖于 Paypal,在同时启用 Paypal and PaypalUk 时,后台可以看到

    Configuration Paypal section when PaypalUk is enabled
    Configuration Paypal section when PaypalUk is enabled

    Configuration Payment Methods section when PaypalUk is enabled
    Configuration Payment Methods section when PaypalUk is enabled

    若不启用 PaypalUk 模块,后台变成:

    Configuration Paypal section when PaypalUk is disabled
    Configuration Paypal section when PaypalUk is disabled

    Configuration Payment Methods section when PaypalUk is disabled
    Configuration Payment Methods section when PaypalUk is disabled

    对比可见后台多了 Payflow Edition,估计 PaypalUk 是面向开发者的称呼,Payflow 是面向普通用户的称呼。更多区别还有待摸索。

  • Dispatched events on a simple Magento homepage

    每访问一次我一个并不复杂的 Magento 站点主页,将会触发多少个事件?我记了下来:


    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    controller_front_init_before

    controller_front_init_routers

    resource_get_tablename

    resource_get_tablename

    model_load_after

    core_abstract_load_after

    resource_get_tablename

    core_locale_set_locale

    controller_action_predispatch

    controller_action_predispatch_cms

    controller_action_predispatch_cms_index_index

    resource_get_tablename

    resource_get_tablename

    model_load_after

    cms_page_load_after

    resource_get_tablename

    model_load_after

    core_abstract_load_after

    cms_page_render

    controller_action_layout_load_before

    customer_session_init

    resource_get_tablename

    model_load_after

    core_abstract_load_after

    resource_get_tablename

    controller_action_layout_generate_xml_before

    controller_action_layout_generate_blocks_before

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    resource_get_tablename

    resource_get_tablename

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    controller_action_layout_generate_blocks_after

    controller_action_layout_render_before

    controller_action_layout_render_before_cms_index_index

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_to_html_before

    model_load_after

    core_abstract_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    resource_get_tablename

    model_load_after

    catalog_entity_attribute_load_after

    model_load_after

    catalog_entity_attribute_load_after

    model_load_after

    catalog_entity_attribute_load_after

    model_load_after

    catalog_entity_attribute_load_after

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    model_load_after

    customer_load_after

    resource_get_tablename

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    model_load_after

    core_abstract_load_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    resource_get_tablename

    resource_get_tablename

    model_load_after

    core_abstract_load_after

    core_block_abstract_prepare_layout_before

    core_block_abstract_prepare_layout_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    model_load_after

    core_abstract_load_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    model_load_after

    core_abstract_load_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    resource_get_tablename

    model_load_after

    sales_quote_load_after

    resource_get_tablename

    model_load_after

    customer_group_load_after

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    resource_get_tablename

    sales_quote_item_collection_products_after_load

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    eav_collection_abstract_load_before

    catalog_product_collection_load_before

    catalog_product_collection_load_after

    resource_get_tablename

    sales_quote_item_set_product

    sales_quote_item_qty_set_after

    resource_get_tablename

    core_collection_abstract_load_before

    core_collection_abstract_load_after

    resource_get_tablename

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    model_load_after

    core_abstract_load_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_after

    core_block_abstract_to_html_before

    core_block_abstract_to_html_after

    controller_action_postdispatch_cms_index_index

    controller_action_postdispatch_cms

    controller_action_postdispatch

    model_save_before

    core_abstract_save_before

    resource_get_tablename

    resource_get_tablename

    resource_get_tablename

    model_save_after

    core_abstract_save_after

    model_save_commit_after

    core_abstract_save_commit_after

    http_response_send_before

    controller_front_send_response_after

  • Manage magento catalog images on the local server

    Magento 的 Image / Media / Gallery 模型非常好用,可以迅速在全站范围内调整输出图片的尺寸。如果用户习惯于用上传图片的方式管理图片库,他会觉得 Magento 很方便。Magento 自动把上传的图片保存到 media/catalog/product/(文件名首字母)/(文件名第二字母)/(文件名或拖尾_1, _2, … 以防重名)。

    在产品库初建时,要输入大量的图片路径。虽然 product import dataflow 也很好用,但图片的保存位置规则跟上传时一样,我觉得不够好。因为:

    1. 原始图片是静态文件,可以独立于 Magento,存放在服务器的某个本地路径,也方便共享给其他程序使用;
    2. 如果 Magento 自行保存图片,浪费了服务器空间(对我来说,这倒没什么,服务器上空了好几百 G),而且按 media/catalog/product/(文件名首字母)/(文件名第二字母)/(文件名或拖尾_1, _2, … 以防重名) 去查找图片非常不方便,产品图片理应按 category 归入各个子目录。

    所以,我想把 Magento 的 Image / Media / Gallery 模型改进一下,做到:

    1. 原始图片存放于 static_file_server/catalog/product/category_1/subcategory_1/image_name。
    2. Magento 不保存原始图片,只要记牢原始图片在服务器上的某个位置。
    3. 喜欢 upload 的用户仍可以使用 upload,但为喜欢 ftp / ssh 的用户增加指定 local path 的功能。
    4. import product 接受 local_path。
    5. Magento 缓存图片的机制不变。
  • How magento deducts stock level

    我心里一直有个疑问,收到订单后,Magento 是怎么更新库存数量的?是直接用一个新库存数字去更新,还是用 upadte magento_cataloginventory_stock_item set qty=qty-qty_ordered 之类的办法。

    通过跟踪 sql 语句,我感觉前者的可能性很大。

    UPDATE `magento_cataloginventory_stock_item` SET `item_id` = '125', `product_id` = '125', `stock_id` = '1', `qty` = '93.000000', `min_qty` = '0.000000', `use_config_min_qty` = '1', `is_qty_decimal` = '0', `backorders` = '1', `use_config_backorders` = '0', `min_sale_qty` = '1.000000', `use_config_min_sale_qty` = '1', `max_sale_qty` = '0.000000', `use_config_max_sale_qty` = '1', `is_in_stock` = '1', `low_stock_date` = NULL, `notify_stock_qty` = '0.000000', `use_config_notify_stock_qty` = '1', `manage_stock` = '1', `use_config_manage_stock` = '0', `stock_status_changed_automatically` = '0', `shelf` = '' WHERE (item_id='125')
    

    这样很不好,如果几个人同时买同一个产品,会搞乱库存数。不知道被破坏的几率有多大,有空要搞一个并发下单的测试。

  • If you missed anything after customising all email templates in Magento

    To double check if you missed anything after customising all email templates in Magento, you can do a File Search on Selected resources (app/locale/(locale_code)/template/email in eclipse with these keywords:

    • (555) 555-0123
    • magento
    • demo

    By the way, eclipse can do Replace on Selected resources, which is an equivalent tool of File & Replace in Folder using Notepad++. I am sure a single linux command can also do the job but I can’t remember the syntax. Let’s just use eclipse, straight forward.

  • Add a section in Magento system configuration

    Want to add a section in Magento system configuration? It seems very easy as mentioned by others. No coding but editing xml files. Magento takes care of the rest. However, it still took me two hours to find the way out. So I think I need tell the world the caveats.

    • A section will not be shown if it is empty (no fields inside). Ditto tab or group.
    • acl must be defined per section in config.xml, otherwise you get Access Denied message.
    • Re-login is required, otherwise you still get Access Denied message.
  • Wysiwyg on individual page basis

    Html wysiwyg editing is good, but the html wysiwyg editors do not always format text as I wish. Sometimes they transforms some characters which I want to keep them intact.

    For example, I wanted to rewrite some of my posts on this blog by hand coding the post. But I must turn off wysiwyg editing in WordPress globally before I can save my post. Another “but” is I do not want to turn it off globally because it is a handy tool for writing some other posts. So I want wysiwyg on and off on individual page basis.

    For another example, Magento 1.4 now has wysiwyg editor built-in. However, it can’t recognise Magento’s mark-up tags for CMS block or CMS page. It wasted me an hour’s work by stripping these tags in a second after I clicked “Save”.

    In short, for people like me enjoying hand coding, I think a wysiwyg on-off attribute for each page basis would give me the best of both worlds.

  • Magento order fulfilment templates

    I said Magento was week at backend, that might be wrong. Of course compared with any accounting or ERP software, Magento is lack of some facilities to control the work flow of order fulfilment. When I dive into Magento backend, I think it should be sufficient for most of business. Most of business do not need an extension to fulfil orders, only need to customise the templates involved.

    Supprisingly, the templates are separated. I mean, Magento does not offer an overall template to generate different but related documents. For example, I must go through all the areas in the following table to change what to show up as order items detail.

      Frontend Backend
      Screen Print Email Screen Pdf
    Order thead          
    tbody          
    tfoot          
    totals          
    Invoice thead          
    tbody          
    tfoot          
    totals          
    Credit Memo thead          
    tbody          
    tfoot          
    totals          
    Shipment thead          
    tbody          
    tfoot          
    totals          

    Separating those templates make the programming less hassle, and the template changer can understand them easily. However, if I am that template changer, I hate copy & paste. As a programmer, I do my best to avoid delivering a program which require people to copy & paste.

    More importantly, if a template changer forgets to go through the above checklist to make a simple change in the order items detail, admin and customer get different view of the same order on screen, in email, or print-out when ordered, invoiced, credited, or shipped. I do not think this approach is acceptable. It is on my agenda to write an extension to make these documents under control.

  • Eclipse XML formatter

    Magento 有个我早就知道的 bug:读入 module config.xml 时,class name 必须在和 xml opening tag & closing tag 写在一行上,而且中间不能有 white space。这个 bug 直到最近的 Magento 1.4.0.1 都还有,可能 Magento 认为这不重要,重要的是如何书写 config.xml。所以我写 config.xml 一直都很小心。

    <catalog>
    	<rewrite>
    		<convert_adapter_category>MyNamespace_MyModule_Model_Eav_Convert_Adapter_Category</convert_adapter_category>
    	</rewrite>
    </catalog>
    

    但最近一次写完了 config.xml 下意识地按了 Ctrl + Shift + F 格式化了 config.xml,而这一行很长,eclipse XML formatter 自动把它换行书写了,造成 Magento 无法正确读入 class name。页面出不来了,什么原因?!我花了差不多一天时间才找到原因,一看到是这个原因,我又非常地懊恼——都是因为我懒,没有好好设置 eclipse 为我所用。

    Preferences for Eclipse XML formatter
    Preferences for Eclipse XML formatter

    于是亡羊补牢,我花了几分钟找到设置所在,把 Line width 设定一个足够大的数字就不怕它换行了。Aptana plugin 也带一个 Aptana XML Editor,另有地方设置,这里不多说了。

  • Better to custom Magento interface from base/default

    Magento 官方推荐以 blank interface 为基础开发个性化的 interface。这说法没错,但有局限性。如果不熟悉 Magento interface 的构成,blank interface 确实一个很好的出发点,如”blank“的名称所言,它没有花里胡哨的背景影响你的构思,如果你是设计师,blank interface 给了你极大的方便。

    但世上难有两全其美的事情——blank interface 方便了设计师,但不方便最终用户(我指后台用户)。设计是一时的,用户是一世的,所以我认为两者有冲突时应该更多地照顾用户。Magento interface 有三层 fall-back 机制,在 System > Configuration > Design 可以设置 theme 和 default theme,系统还有最底层的 fall-back to base/default。

    设想一下,如果基于 blank interface 开发 my_wonderful_theme,那在后台只能设置 theme 为 “my_wonderful_theme”,设置 default theme 为 “blank”。同时,严格按照 Magento 的规范,只把 “blank” 中需要修改的文件拷贝到 “my_wonderful_theme” 中进行修改。最终 “my_wonderful_theme” 保存了一些修改文件。如果全站页面统一使用 “my_wonderful_theme”,则没有问题;可是,Magento 允许个别 cms, category 或 product page 使用不同的 theme,通常会是 “my_wonderful_theme” 局部的变种,这样就有问题了。假设需要对某个 category 下的页面使用 “my_fantastic_theme”,而 “my_fantastic_theme” 只是在“my_wonderful_theme” 基础上修改了 header 部分,但 “my_fantastic_theme” 无法就它所缺的文件 fall-back to “my_wonderful_theme”——很可惜,Magento 只提供了三层 fall-back 机制,假如它就多给一层 fall-back,那就没有我这篇文章了。

    简言之,如果基于 blank interface 开发,那么 cms, category 或 product page 里的 custom design 功能就成了鸡肋。如果设计师/开发者交付一个基于 blank interface 的 theme 和若干变种,用户无法方便地完成切换,这就是个败笔。以后还是以 base/default interface 为起点开发吧,其实也很方便。