Category: 小小草

IT 技术领域学海无涯。其实任何领域都学海无涯,无非 IT 发展太快了,让我有更多嘘唏。希望我掌握的技术有如小小草,虽然渺小,却有旺盛的生命力。

  • Magento code snippet

    Magento provide a beautiful GUI, which let you setup or change nearly everything. However, occasionally you prefer do it with php code. Here are a collection of codes for various functionalities. I will keep editing this post to add new methods.

    Read if a product attribute is “Use Default Value” in a Store View:

    Assume the storeview ID is $storeId, the product attribute code is $attributeCode;

    $result = Mage::getModel(‘catalog/product’)->setStoreId($storeId)->getAttributeDefaultValue($attributeCode);

    $result === false when the attribute IS “Use Default Value”;

    $result === Mage::getModel(‘catalog/product’)->setStoreId(0)->getAttributeDefaultValue($attributeCode) when the attribute HAS storeview specific value.

    Please note even if the attribute is “Yes/No” choice type, the attribute value is 1 or 0. So there is no conflict – when getAttributeDefaultValue returns false means “Use Default Value”; when it returns 0 means “default value is No”.

    Read storeview specific value to a product attribute:

    public function getAttributeOptionValue($optionId, $storeId = 0, $attributeId = null)
    {
    	$valuesCollection = Mage::getResourceModel('eav/entity_attribute_option_collection')
    		->setStoreFilter($storeId, false)
    		->addFieldToFilter('main_table.option_id', $optionId)
    		->setAttributeFilter($attributeId);
            return $valuesCollection->getFirstItem()->getValue();
    }
    

    CRUD manipulation on entity attributes, such as product attribute

    All need to do is construct an array in the right structure, add to the attribute, and save. Do not operate on ‘eav/entity_attribute_option’ directly. It won’t work because relationship between table eav_attribute_option and eav_attribute_option_value is not set in this model.

    Add options to attribute

    $attribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_product', $attributeCode);
    $data = array(
    	'option' => array(
    		'value' => array(
    			$optionId => array(
    				$storeId => $value
    			)
    		)
    	)
    );
    $attribute->addData($data)->save();
    

    Bear in mind, as this is to add a new option, you do not need to specify an actual $optionId. It can be any string which, if convert to number, must be 0. If $optionId’s numeric value is positive, and if that option does not exist or does not belong to the $attribute, it will throw exception like:

    SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails.

    You can construct an array with more than one option ID’s and store ID’s inside to add multiple options or give option values to multiple stores at a time.

    Read options

    $attribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_product', $attributeCode);
    $optionCollection = Mage::getResourceModel('eav/entity_attribute_option_collection')
    	->setStoreFilter($storeId, false)
    	->setAttributeFilter($attribute->getId());
    foreach ($optionCollection as $option) {
    	echo $option->getValue();
    }
    

    The code is straight forward. I did not find another way retrieving options. I tried
    $attribute->getOptions() and $attribute->getOption(). Neither worked.

    Delete options

    $attribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_product', $attributeCode);
    $data = array(
    	'option' => array(
    		'value' => array(
    			$optionId => ''
    		),
    		'delete' => array(
    			$optionId => 1
    		)
    	)
    );
    $attribute->addData($data)->save();
    

    The tricky thing is: the $optionId to delete must exist in both ‘value’ and ‘delete’, although the value itself is not critical (because it will be deleted).

    Update options

    $attribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_product', $attributeCode);
    $data = array(
    	'option' => array(
    		'value' => array(
    			$optionId => array(
    				$storeId => $value
    			)
    		)
    	)
    );
    $attribute->addData($data)->save();
    

    It is the same code as adding options. The difference is in $optionId. You need work out the existing option ID.

    CRUD manipulation does not need to construct $data with the complete list of existing options. Any options not in $data is untouched.

  • Magento extension: Root Category As Homepage

    It is my first time publish an Magento extension – Qian_Rcah. By default, Magento prevent root category from being shown at frontend, not to say use it as homepage. I do not see significant security leaks if showing root category. There are some benefits using root category as homepage:

    • Layered navigation starts with a complete product collection on site.
    • Selective products show on homepage without prerequisite knowledge of cms block.

    Download RootCategoryAsHomepage.tar.gz

    How to install:
    Copy the whole “app” folder to magento/app. That’s it.

    How to use:
    Log in Magento backend, go to System -> Configuration -> General -> Web -> Default Pages, change “Default web URL” to “rcah” (default is cms). Besides of root category, this module can load an arbitrary category page as homepage. In this case, change “Default web URL” to “rcah/index/index/id/ARBITRARY_CATEGORY_ID”.

    How is it created:
    This module is created by koukou1985’s module creator (http://www.magentocommerce.com/wiki/custom_module_with_custom_database_table). Thanks to koukou1985.

    How good is it:
    This module does not override any Mage classes. It is safe to use with other extensions as long as namespace and module name do not conflict.

  • How can Magento generate sitemap.xml belongs to root?

    今天看到 magento 目录下的 sitemap.xml 的属主和属组分别是 root:root,属性是644,而这一台的 webserver 是以 apache:apache 身份运行的,所以我觉得很奇怪:

    • apache 怎么能建立一个 root:root 的文件?
    • 如果这个文件不是 apache 建的,apache 怎么有权去更新它(最近更新就在今天)?

    想了好一会明白过来,sitemap.xml 是 cron job 建的,crontab 这么写

    */10 * * * * /usr/bin/php -f /path/to/magento/cron.php

    那么 sitemap.xml 归 root:root 所有就不奇怪了。

    我想这样写会好些:

    */10 * * * * sudo -u apache -g apache /usr/bin/php -f /path/to/magento/cron.php

    2010年6月29日更新:上行命令不对,设想在 cron job 里以 apache 身份运行 cron.php,但出错。正确的写法应该是:

    */10 * * * * su -c '/usr/bin/php -f /path/to/magento/cron.php' -s /bin/bash apache

  • Google do not need windows. Neither do I

    Magento grid is flexible to customise. A lot of options can be controlled by addColumn(). I knew the parameters could be header, width, type, index, but I just found a new one sortable. I am thinking where I can get a complete list of parameters for addColumn(). The best way I can imagine is searching magento source and find the file which contains all words of “header width type index sortable”. File search in eclipse can not do this kind search, so I am thinking google desktop search.

    I have not used google desktop search for a while. Last time I used it, it only has windows version. Today I am glad find google desktop search linux version is available. Up to now, my favourite google programmes, chrome, picasa, and google desktop, all embrace linux users.

    It reminds me a piece of news that google stop offering staff windows as operation system. I regard this is competition between google and microsoft, none of my business. However, google makes programmes independent of windows, which really benefits me.

    What else left in windows which has to be in windows?

    • IE: it is shame our Chinese banks’ website only support IE;
    • MS Office: how many advanced users are using Office features which are exclusive to MS Office? Not many.
    • Photoshop: it’s a pity Gimp still can not beat photoshop at moment.
  • Best place to put module installation scripts in Magento

    Magento module 中,在哪里放置安装脚本比较好?

    当然,把它放在 sql/$resourceName 目录下 $resModel.’-‘.$actionType.’-(.*)\.(sql|php) 文件里是不错的。安装脚本通常是修改数据库结构,所以放在.sql文件理所当然。但如果安装脚本跟数据库无关,放在.sql略显勉强,为此,我找了一个更好的地方。

    首先在 config.xml 声明一个 setup 类:

    
    <config>
    
    <resources>
    <mymodule_setup>
    <setup>
    <module>Mynamespace_Mymodule</module>
    <class>Mynamespace_Mymodule_Model_Setup</class>
    </setup>
    </mymodule_setup>
    </resources>
    </config>
    
    

    这个类只要 extends Mage_Core_Model_Abstract,不一定得 extends Mage_Core_Model_Resource_Setup。

    然后在该类里放置 applyUpdates() 方法,把安装脚本都写到这个方法里就可以了。模块在安装时就是执行这个方法,而且是在 .sql 执行之前。

  • What if I change the EAV attribute backend type in Magento?

    The question is raised because in my catalog_category, I have a user attribute originally 255 length varchar. Now I am told by users it was not long enough for block text. I want to change it to text type in mysql to solve the problem.

    I know EAV attributes use different tables for different type. My worry is moving attribute value from varchar table to text table when changing the EAV attribute backend type. Anyway, I have to try before I know. Here is my steps:

    1. go to table magento_eav_attribute, find the attribute, change backend_type from varchar to text
    2. (what’s next?) I am about to change something else, but surprisingly, nothing else! All done after step 1.

    I do not need to move old values from magento_catalog_category_entity_varchar to magento_catalog_category_entity_text. After change attribute’s backend_type, Magento can still read existing value from magento_catalog_category_entity_varchar, but if I save the category, the value will save to magento_catalog_category_entity_text. Old value is not deleted from magento_catalog_category_entity_varchar, but it does no harm.

    I am curious at how Magento did it – Does Magento go through 5 tables (datetime, decimal, int, text, varchar) to get a value? Or Magento cache attributes’ backend type (so even I make changes in magento_eav_attribute, Magento still have records of old type)?

  • Discovery: PhProxy & CHtml

    我认为突破访问限制的终级方案是 VPN,可是设置 VPN 的技术含量有点高,到现在我还搞不掂 OpenVPN,只能徘徊于 PPTP。今天发现可爱的 php 也有了 proxy(或许早就有了,我不知道罢了),虽然我用不着,但对国内的朋友们确实是天大的福音。简单易行,一分钟就能搭一个!

    我只发现 phproxy 三个不大的缺陷:

    1. 无法代理流文件(比如无法看 youtube 上的 video,但访问 youtube 没问题)
    2. 因为 phproxy 工具栏在网页顶部,如果代理网页在相同位置绝对定位,则会重叠在一起(比如 wikipedia,我想修改一下 style.css 就好了,或者做一个自动隐藏的工具栏)
    3. 有些被代理的网页自检而跳出代理,重定向到原来的 url(比如 google spreadsheet)

    牛人真不少,还有一个 CHtml,竟然用 recaptcha 去实现突破,构思非常巧妙。

  • AmazonPayments is removed from Magento 1.4

    最近留意到一件奇怪的事:Magento 1.3 里还在的 AmazonPayments module 整个儿从 1.4 版拿掉了。

    虽然 AmazonPayments 跟大部分英国公司没关系,但它还是造成一点小麻烦。因为它在 Magento 1.3 出现过,所以数据库里 system config 里留有 AmazonPayments 的一些配置信息;但是 Magento 1.3 升级到 1.4 时,为了减少麻烦,我把整个 1.3 源码目录删掉后再替换入 1.4 源码(而不是通常的 merge and replace),这样一来的现状是:数据库里有 AmazonPayments config data,但源码里没有 AmazonPayment module。这时,如果要新建一条基于 payment method 的 promotion rule (比如想鼓励大家使用 GoogleCheckout,给 GoogleCheckout 用户额外 10% 折扣),一点选择 payment method 就出错。

    定位了一下出错的语句,原来是 getAllMethods() 不妥,因为它会根据 config data 去或取 payment methods,结果连源码都不存在的 AmazonPayments 成了返回数组里的一员,不出错都难。(我还用过一个 payment module ProtxDirect,后来有更好的 module 以后,把 ProtxDirect源码也给删了,这也是出错的原因。)

    
    class Mage_Adminhtml_Model_System_Config_Source_Payment_Allmethods
    {
    protected function _getPaymentMethods()
    {
    return Mage::getSingleton('payment/config')->getActiveMethods();
    }
    }
    
    

    我尝试地把 getAllMethods() 改成 getActiveMethods() 就不再有错误。但是我不喜欢因为这么点事去 override class——这个方法治标,我更喜欢在数据库中找出跟 AmazonPayments 和 ProtxDirect 有关的垃圾信息(既然用不到就是垃圾)直接删除——治本。

    同时我挺奇怪 Magento 1.4 为什么不考虑一下向下兼容,即使 AmazonPayments 失效了也应该在发布时保留源码目录啊?或者,在升级之际执行一遍 config data clean up?

  • Magento 1.4.1.0 is released

    经历了好几个月沉寂之后,今天终于看到 Magento 一个新版 1.4.1.0。虽然我没去试用新版,但还是在第一时间关心了一下 Release Notes。我第一次逐行读完了 Release Notes,以前最多 scam it,这就是亲身体验者和旁观者的区别。

    Improvements

    * Added ability to use static URLs for media in WYSIWYG or image browser. Added ability to enable parsing store, skin and media directives on catalog frontend. (这个功能我已经实现了,就是在 description output 上再套一个cms filter,我称之为 text engine。当然 magento core 实现得肯定比我好,以后我就删了我的 text engine)
    * URL rewrites history: ability to auto-generate custom rewrite with redirect from old to new URL when changing product URL-key (这个问题我也想过,我没 magento core 那么能干,能做 auto-generated redirect,不过我觉得即使有 auto-generated redirect,把 url key 改来改去也不是回事,所以我建议取名需谨慎,定下的 url key不要改)
    * Added ability to exclude a category from navigation menu (这个功能我也实现了,不光能控制 exclude from top navigation,还能控制 exclude from sitemap)
    * Various improvements in system configuration UI. Introduced ability to make actual configuration structure to be independent on the system.xml structure
    * Added “Only X left” inventory feature
    * Implemented Centinel functionality in iphone theme
    * Upgraded TinyMCE to v.3.3.2 (不知是否有了 auto-save?)
    * Improved PayPal integration, includes:
    * Recurring payment profiles entity and purchasing of “subscription” products. In checkout they are treated as “nominal” items.
    * Billing agreements
    * “Pending Review” order state, “Pending Review” and “Suspected Fraud” order statuses and logic related with this. Triggers by PayPal IPR, FMF and Unilateral payments.
    * Functionality of transactions listing/searching and fetching from gateway
    * Fetching PayPal settlement reports via SFTP
    * Various improvements of PayPal integration on the frontend and admin UI
    * Added notice about creating offline creditmemo from order view page.
    * Added getter for recurring payment methods in payment helper.
    * Implemented abstract block ability to have arbitrary groups of child blocks and a method that returns such a group. Implemented a method that fetches data from child block by specified alias.
    * Implemented ability to specify “sort_order” instead of “before” and “after” in the totals declaration. All nominal totals declaration utilize “sort_order”
    * Added getter for specified object data by key in core/template block: method getObjectData()
    * Defined payment methods grouping, implemented getter for payment method list (with option to group them). Added ability to render grouped options in the “select” grid filter.
    * Moneybookers: utilized the payment methods grouping for all its methods; removed redundant “translate” attribute in the methods definition in config.
    * Utilized payment methods grouping for PayPal peyment methods.
    * Added more verbosity to PayPal NVP and IPN debugging

    Changes

    * Sales module database structure changed from EAV into flat tables (没觉得 eav 有多慢,不过 magento 这么改肯定有它道理)

    * Product custom URL rewrites redirect to SEF URL, rather than to catalog/product/view/id/ (这也是我考虑得比较多的问题,不过如果一个 product 归属多个 category,此改进后的 SEF URL 时能否正确显示 main category breadcrumb,能否配合 rel=”canonical” 使用?)
    * Reimplemented frontend top menu rendering logic (cross-browser and usability improvements)
    * Reviewed logic of the sales reports: Orders, Tax, Invoiced, Shipping, Refunds, Coupons
    * Reimplemented bestsellers report
    * Implemented 3D Secure validation for Maestro International and JCB cards
    * Changed Shopping cart methods output (top + bottom): removed

  • -dependance from templates in other modules \-> moved to Checkout module
    * Moved the Payflow Pro payment method to PayPal module
    * Refactored sales/service_quote model: instead of submit() method the submitAll() should be used. It can submit nominal items separately from the order. The methods for submitting nominal items and order separately – are available as well.
    * Refactored onepage checkout model to use the new method sales/service_quote::submitAll(), added recurring profiles and billing agreements passing through the process until the success page
    * Utilized child blocks grouping to improve flexibility of rendering additional information in catalog/product/view template. The group is called “detailed_info”; any block from layout may insert itself to this group.
    * Removed redundant wrappers hard-coded into some catalog templates. Template of parent block is responsible for wrapping.
    * Refactored onepage checkout success block and template: simplified fetching related checkout information, added ability to render billing agreement and recurring profiles that might be created with the payment placement.

    Fixes

    * Fixed read/write connection issue during new customer order place. See http://www.magentocommerce.com/boards/viewthread/19363/P0/
    * Fixed tax rule ZIP range matching (numeric values were matched as strings)
    * Various tax fixes
    * Optimized search reindex process

    Known Issues

    * PayPal and Payflow pro modules are officially backwards incompatible.
    * The `sales_order_aggregated_created` table was re-created with new columns, it is not backwards compatible.

    Upgrade Path

    * Once upgraded, go to system configuration section and re-enter all PayPal-related settings carefully. Make sure to specify the proper merchant country and email.
    * To avoid fatal errors with Payflow Pro and Amazonpayments modules, before upgrading cleanup their settings from database:
    DELETE FROM `core_config_data` WHERE `path` LIKE ‘payment/verisign%’;
    DELETE FROM `core_config_data` WHERE `path` LIKE ‘payment/amazonpayments%’;

    (之前我已经亲自动手把 Amazonpayments 删出了数据库,倒没发现 Payflow Pro 的问题)

  • How to improve Magento speed

    How to improve Magento speed 作标题,似乎合适又不合适。因为大凡讨论 Magento 速度优化,总是谈怎么配置数据库、apc、缓存、负载之类,今天我想谈点别的。

    目前我碰到的实际情况是:启用各种缓存并尽可能地将缓存数据保存在内存中,访问cms page / category page / product page 的速度都很理想,但 shopping cart page 是瓶颈,cart item 越多越慢,生成时间超过 2 秒都是不可接受的。所以我一直在想怎么让 shopping cart 生成得快一些。

    上周无意中发现,如果 after add product to cart, redirect to shopping cart,那么 shopping cart 其实在 add to cart 时已经生成了一次,然后 redirect to checkout/cart/index 又生成了一次。生成 shopping cart,最耗时间的是针对每个 cart item,去检验每条 sale rule,每个 sale rule 再来若干个 conditions and actions,不慢才怪,何况又执行了两遍!

    目前我还没有找到一个干净利落的让 shopping cart 只生成一遍的办法。不过另有一个服务器计划(跟 shopping cart 速度无关)已经在进行中,希望光靠服务器升级就能让 shopping cart 在两秒内生成。