magento 后台订单grid表获取shipping_description

有时候后台的grid 表我们想要查看到当前订单选择了什么样的运送方式。

例如:之前添加过上门自提的方式,那么当有很多订单的时候就需要过滤掉上门自提的订单,先处理那些需要发快递的。

解决办法:

(最好重写相应模块,不要修改 core文件。下面就不再提醒这个了。)

1. 修改 grid.php 中的 _getCollectionClass函数,将

return 'sales/order_grid_collection';

修改为

return 'sales/order_collection';

但是仅仅这么做显然是不合理的,因为你会发现 shipping name 等都为空了。

当然你可以修改$collection join sales_flat_order_grid (我没测试过)。

2.大多数网上 搜到的资料是

为$collection 添加 join  例如:

protected function _prepareCollection(){
	$collection = Mage::getResourceModel($this->_getCollectionClass());
	$collection->getSelect()->join('sales_flat_order', 'main_table.entity_id = sales_flat_order.entity_id',array('shipping_description'));
	$this->setCollection($collection);
	return parent::_prepareCollection();
}

然后在 grid.php 的 _prepareColumns() 函数中addColumn()

$this->addColumnAfter('shipping_description', array(
            'header' => Mage::helper('sales')->__('Shipping Method'),
            'index' => 'shipping_description',
            'width' => '100px',
            'filter_index' => 'sales_flat_order.shipping_description',
        ),'method');

但是仅仅这么做 你会发现许多过滤(搜索)功能都不好使了,例如 搜索某个订单号。

其实解决办法很简单就是把在你重写的 grid.php 中将所有的 要取出来的值都重写一遍并且要带上参数 filter_index

例如:increment_id

 $this->addColumn('real_order_id', array(
            'header'=> Mage::helper('sales')->__('Order #'),
            'width' => '80px',
            'type'  => 'text',
            'index' => 'increment_id',
            'filter_index'=>'main_table.increment_id',
        ));

此文章通过 python 爬虫创建,原文是自己的csdn 地址: magento 后台订单grid表获取shipping_description

magento 后台取产品自定义的select(下拉菜单)字段

例如我在后台 添加自定义的下拉菜单 假设是 opt
重写 core 文件的
MageAdminhtmlBlockCatalogProductGrid.php

// 错误的方法

protected function _prepareCollection()
    {
        $store = $this->_getStore();
        $collection = Mage::getModel('catalog/product')->getCollection()
            ->addAttributeToSelect('sku')
            ->addAttributeToSelect('name')
            ->addAttributeToSelect('opt') 
            ->addAttributeToSelect('attribute_set_id')
            ->addAttributeToSelect('type_id');
            ...

之后 在 _prepareColumns 方法中
添加addColumn

第一种:

$this->addColumn('opt',
            array(
                'header'=> Mage::helper('catalog')->__('Options'),
                'width' => '80px',
                'index' => 'opt',
            ));
这种显然是不正确的,这么写你会发现 在grid 中显示的只是 下拉菜单里的 value 值,且搜索处是个text 类型 不是一个select

第二种:

先定义方法:

protected function _getAttributeOptions($attribute_code)
    {
        $attribute = Mage::getModel('eav/config')->getAttribute('catalog_product', $attribute_code);
        $options = array();
        foreach( $attribute->getSource()->getAllOptions(true, true) as $option ) {
            if($option['value'] == ''){
                continue;
            }
            $options[$option['value']] = $option['label'];
        }
        return $options;
    }

然后是

$this->addColumn('opt',
            array(
                'header'=> Mage::helper('catalog')->__('Options'),
                'width' => '80px',
                'index' => 'opt',
                'type'  => 'options',
                'options' => $this->_getAttributeOptions('opt'),
            ));

这时候 去后台管理产品的地方就可以看到相应产品的 opt 属性取出来了,但是 试一下过滤(搜索)功能,

过滤status type  等等都好用唯有过滤 name时候 这个选项的值都没了。。。。

于是去网上查资料 发现另一种定义 _getAttributeOptions 函数的方法

protected function _getProductAttributeOptions($attributeName) {
    $attribute = Mage::getModel('eav/config')->getAttribute('catalog_product',$attributeName);
    /* @var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */       
    $attributeOptions = $attribute->getSource()->getAllOptions();
    $options = array();
    // options in key => value Format bringen
    foreach ($attributeOptions as $option) {
        $options[number_format($option['value'], 4, '.', '')] = $option['label'];
    }       
    return $options;       
}

结果 过滤name的时候好好的 其他的时候都不正常了。 具体 你可以打印出来这两种取法的
sql 语句 就可以大概发现这里面的差别。 (其实我没大明白为什么会这样 - -!)

解决办法 只需要我们在_prepareCollection函数应该使用下面的方法取出 opt 属性

$collection->joinAttribute('vendor_code', 'catalog_product/vendor_code', 'entity_id', null, 'left');

注意 使用的是left 使用inner 只会取出 opt 属性有值的产品,除非你的这个选项是必填项。

此文章通过 python 爬虫创建,原文是自己的csdn 地址: magento 后台取产品自定义的select(下拉菜单)字段

解决magento 订单0元的情况

magento 有着强大的促销规则,再加上自己开发或者安装 积分插件等功能,总会出现用户结账后订单为 0元的情况,这个时候

第三方支付平台往往是不支持这个价格的。

(测试了 支付宝 和快钱 是这样的)

这个时候怎么解决?对于magento 当然有现成解决办法

(可是我是自己开发完才注意到的。。。。所以我的解决方法就放后面吧,可以当作进一步理解magento用的 大笑,其实对于有些用户就选择支付宝等支付方式的话,还是需要做我这种类似的开发的)

1.

在magento 后台 System -> Configuration -> Payment Methods  中有个支付方式是

Zero Subtotal Checkout (中文翻译的应该是 “无小计结账”)

启用这个支付方式,当订单为 0元的时候就会显示这个 支付方式 。

我这里设置 New Order Status 为 Processing 就表明用户订单为 0元的时候选择这个支付方式。

这会直接将订单状态置为 processing。 如果你选择了 new oreder status 是 processing 会同时出现选项

Automatically Invoice All Items 选择no 表明 不同时创建发票 。

2.

我的解决办法是在确认订单,跳转链接的时候判断 订单金额 是0元的直接 跳转到success 页面并且 做订单状态 创建发票和发邮件等操作。

例如支付宝

在支付宝插件的model中有 方法getOrderPlaceRedirectUrl() 这个决定了确认订单后跳转的链接。

 public function getOrderPlaceRedirectUrl()
    {
        return Mage::getUrl('alipay/payment/redirect');
    }

我是在 controller 的 redirectAction() 中做相应操作的

在合适位置加入如下代码:

$order = $this->getOrder();

        $orderGrandTotal = (float)$order->getGrandTotal();
        if($orderGrandTotal == 0){

            if($order->getStatus() == "pending"){
                $order->addStatusToHistory(
                    'processing',
                    Mage::helper('customer')->__('总计为 0 元的订单直接置为processing'));
            }
            try{
                $order->sendNewOrderEmail();
                if($this->saveInvoice($order)){
                    $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true);
                }
                $order->save();
            } catch(Exception $e){

            }
            $this->_redirectUrl(Mage::getUrl('checkout/onepage/success'));
            return;
        }

你可以将 第三方支付插件的redirectAction 中都加入这个判断来解决问题。

这时当用户选择 支付宝支付 但是 订单又是0元的时候 并不会跳转到支付宝也就不会提示价格参数 不合法。

当然 你也可以采用 订单价格为0元的时候

只显示 无小计结账的支付方式。

根据订单价格屏蔽其它支付方式 可以在

template/checkout/onepage/payment/methods.phtml 中修改。(这里的修改,用户是可以修改 post数据来更改的)

此文章通过 python 爬虫创建,原文是自己的csdn 地址: 解决magento 订单0元的情况

HandlerSocket (mysql + nosql)

HandlerSocket的原理

HandlerSocket的应用场景:

MySQL自身的局限性,很多站点都采用了MySQL+Memcached的经典架构,甚至一些网站放弃MySQL而采用NoSQL产品,比如Redis/MongoDB等。不可否认,在做一些简单查询(尤其是PK查询)的时候,很多NoSQL产品比MySQL要快很多,而且前台网站上的80%以上查询都是简洁的查询业务。

MySQL通过HandlerSocket插件提供了API访问接口,在我们的基准测试中,普通的R510服务器单实例Percona/XtraDB达到了72W+QPS(纯读),如果采用更强劲的CPU增加更多的网卡,理论上可以获得更高的性能。而同等条件下Memcached仅有40W+QPS(纯读),并且在R510上Memcached单实例已经无法提升性能,因为Memcached对内存的一把大锁限制了它的并发能力。

HandlerSocket原理:

MySQL的架构是“数据库管理”和“数据管理”分离,即MySQL Server+Storage Engine的模式。MySQL Server是直接与Client交互的一层,它负责管理连接线程,解析SQL生成执行计划,管理和实现视图、触发器、存储过程等这些与具体数据操作管理无关的事情,通过调用Handler API让存储引擎去操作具体的数据。Storage Engine通过继承实现Handler API的函数,负责直接与数据交互,数据存取实现(必须实现),事务实现(可选),索引实现(可选),数据缓存实现(可选)。

(图1-1 MySQL架构)

HandlerSocket是在MySQL的内部组件,以MySQL Daemon Plugin的形式提供类似NoSQL的网络服务,它并不直接处理数据,只是侦听配置好的某个端口方式,接收采用NoSQL/API的通讯协议,然后通过MySQL内部的Handler API来调用存储引擎(例如InnoDB)处理数据。理论上,HanderSocket可以处理各种MySQL存储引擎,但是用MyISAM时,会出现插入的数据查不出来,这个实际上是构造行时第一字节没有初始化为0xff,初始化以后就没有问题,MyISAM也一样可以支持,但是为了更好地利用内存,用HandlerSocket都会搭配InnoDB存储引擎一起使用。

图1-2描述HandlerSocket具体做了哪些事情:

(图1-2 HandlerSocket原理)

因为HandlerSocket是以MySQL Daemon Plugin形式存在,所以在应用中,可把MySQL当NoSQL使用。它最大的功能是实现了与存储引擎交互,比如InnoDB,而这不需要任何SQL方面的初始化开销。访问MySQL的TABLE时,当然也是需要open/close table的,但是它并不是每次都去open/close table,因为它会将以前访问过的table cache保存下来以重复使用,而opening/closing tables是最耗资源的,而且很容易引起互斥量的争夺,这样一来,对于提高性能非常有效。在流量变小时,HandlerSocket会close
tables,所以它一般不会阻塞DDL。

HandlerSocket与MySQL+Memcached的区别在哪呢?对比图1-2和图1-3,可从中看出其不同点,图1-3展示了典型的MySQL+Memecached的应用架构。因为Memcached的get操作比MySQL的内存中或磁盘上的主键查询要快很多,所以Memcached用于缓存数据库记录。若是HandlerSocket的查询速度和相应时间能与Memcached媲美,我们就可以考虑替换Memcached缓存记录的架构层。

(图1-3 典型MySQL+Memcached架构)

HandlerSocket的优势和缺陷阐述

HandlerSocket的优势和特点:

1) 支持多种查询模式

HandlerSocket目前支持索引查询(主键索引和非主键的普通索引均可),索引范围扫描,LIMIT子句,也即支持增加、删除、修改、查询完整功能,但还不支持无法使用任何索引的操作。另外支持execute_multi() 一次网络传输多个Query请求,节省网络传输时间。

2) 处理大量并发连接

HandlerSocket的连接是轻量级的,因为HandlerSocket采用epoll() 和worker-thread/thread-pooling架构,而MySQL内部线程的数量是有限的(可以由my.cnf中的handlersocket_threads/handlersocket_threads_wr参数控制),所以即使建立上千万的网络连接到HandlerSocket,也不会消耗很多内存,它的稳定性不会受到任何影响(消耗太多的内存,会造成巨大的互斥竞争等其他问题,如bug#26590,bug#33948,bug#49169)。

3) 优秀的性能

HandlerSocket的性能见文章HandlerSocket的性能测试报告描述,相对于其它NoSQL产品,性能表现一点也不逊色,它不仅没有调用与SQL相关的函数,还优化了网络/并发相关的问题:

(1). 更小的网络数据包:和传统 MySQL 协议相比,HandlerSocket 协议更简短,因此整个网络的流量更小。

(2). 运行有限的MySQL内部线程数:参考上面的内容。

(3). 将客户端请求分组:当大量的并发请求到达HandlerSocket时,每个工作线程尽可能多地聚集请求,然后同时执行聚集起来的请求和返回结果。这样,通过牺牲一点响应时间,而大大地提高性能。例如,可以减少fsync()调用的次数,减少复制延迟。

4) 无重复缓存

当使用Memcached缓存MySQL/InnoDB记录时,在Memcached和InnoDB Buffer Pool中均缓存了这些记录,因此效率非常低(实际上有两份数据,Memcached本身可能还需要做HA支持),而采用 HandlerSocket插件, 它直接访问 InnoDB 存储引擎,记录缓存在InnoDB Buffer Pool,于是其它SQL语句还可以重复使用缓存的数据。

5) 无数据不一致的现象

由于数据只存储在一个地方(InnoDB存储引擎缓存区内),不像使用Memcached时,需要在Memcached和MySQL之间维护数据一致性。

6) 崩溃安全

后端存储是InnoDB引擎,支持事务的ACID特性,能确保事务的安全性,即使设置innodb_flush_log_at_trx_commit=2,若数据库服务器崩溃时,也只会丢掉<= 1s的数据。

7) SQL/NOSQL并存

在许多情况下,我们仍然希望使用SQL(例如复杂的报表查询),而大多数NoSQL产品都不支持SQL接口,HandlerSocket仅仅是一个 MySQL 插件,我们依然可以通过MySQL客户端发送SQL语句,但当需要高吞吐量和快速响应时,则使用 HandlerSocket。

8) 继承MySQL的功能

因为HandlerSocket运行于MySQL,因此所有MySQL的功能依然被支持,例如:SQL、在线备份、复制、HA、监控等等。

9) 不需要修改/重建MySQL

因为HandlerSocket是一个插件并且开源,所以它支持从任何MySQL源码、甚至是第三方版本(例如Percona)构建,而无需对MySQL做出任何修改。

10) 独立于存储引擎

虽然我们只测试了MySQL-EnterpriseInnoDB和Percona XtraDB插件,但HandlerSocket理论上可以和任何存储引擎交互。MyISAM通过简单的修改也是可以被支持的,但是从数据缓存而利用内存的角度看这个意义不大。

HandlerSocket的缺陷和注意事项

1) 协议不兼容

HandlerSocket API与Memcached API并不兼容,尽管它很容易使用,但仍然需要一点学习来学会如何与HandlerSocket交互。不过我们可以通过重载Memecached函数来翻译到HandlerSocket API。

2) 没有安全功能

与其它NoSQL数据库类似,HandlerSocket不支持安全功能,HandlerSocket的工作线程以系统用户权限运行,因此应用程序可以通过HandlerSocket协议访问所有的表对象,但是可以通过简单的修改协议,在my.cnf中增加一个配置项为密码,连接时通过这个配置的密码验证,当然也可以通过网络防火墙来过滤数据包。

3) 对于磁盘IO密集的场景没有优势

对于IO密集的应用场景,数据库每秒无法执行数千次查询,通常只有1-10%的CPU利用率,在这种情况下,SQL解析不会成为性能瓶颈,因此使用HandlerSocket没有什么优势,应当只在数据完全装载到内存的服务器上使用 HandlerSocket。但是对于PCI-E SSD(例如Fusion-IO)设备,每秒可以提供4w+ IOPS,并且IO设备本身消耗CPU比较大,使用HandlerSocket依然具有优势。

HandlerSocket的性能测试

HandlerSocket Oprofile测试报告

(MySQL通过SQL执行K/V查询的Oprofile信息)

MySQL执行SQL语句,首先要经过SQL解析阶段,调用MYSQLparse() 和MYSQLlex() 进行语法和词法解析;然后进入查询优化阶段,调用make_join_statistics() 和JOIN::optimize() 获得统计信息和生成执行计划,可以清洗第发现,主要耗资源的是SQL解析和优化层,而不是InnoDB存储层,row_search_for_mysql只消耗了很少的时间。

因此我们对比Memcached/NoSQL,知道MySQL除了数据操作,还要很多额外的步骤需要完成:

1 Parsing SQL statements【解析SQL】

2 Opening, locking tables【打开并锁定表】

3 Making SQL execution plans SQL【解析SQL并生成执行计划】

4 Unlocking, closing tables【解锁并关闭表】

另外,MySQL 还必须要做大量的并发控制,比如在发送/接收网络数据包的时候,fcntl() 就要被调用很多次;Global mutexes比如LOCK_open,LOCK_thread_count也被频繁地取得/释放。所以在Oprofile的输出中,排在第二位的是my_pthread_fastmutex_lock()。并且Mutex的竞争带来的上下文切换,导致%system占用CPU使用比例相当高(>20%)。

其实, MySQL 开发团队和外围的开发团体早已意识到大量并发控制对性能的影响,MySQL 5.5中已经解决了一些问题,Percona也对Mutex做了一些拆分处理,未来的MySQL版本中,也应该会越来越好。

在完全内存操作的情况时,CPU的效率非常重要。如果只有一小部分数据进入内存,那么SQL语句带来的消耗可以忽略不计。很简单,因为机械磁盘IO操作的时间消耗远比CPU解析SQL语句的时间消耗多,这种情况下,就不需要过分考虑SQL语句所带来的消耗。但是对于SSD盘,尤其是PCI-E SSD盘,响应时间在微秒级(Fusion I/O为30us左右),就必须考虑SQL带来的消耗了。

在大多数的MySQL 服务器中,大部分的热点数据都缓存在内存中,因而访问变得只受CPU的限制。Profiling 的结果就类似上所述的情况:SQL 层消耗了大量的资源。假设需要做大量的PK查询(例如:SELECT x FROM t WHERE id=?)或者是做LIMIT的范围查询,即使有70-80%都是在同一张表中做PK查询(仅仅只是查询条件中给定的值不同,即value不同而已), MySQL 还是每次需要去做 parse/open/lock/unlock/close,
这对我们来说是非常影响效率的事情。

(MySQL通过HandlerSocket执行K/V查询的Oprofile信息)

HandlerSocket性能测试报告:

【测试主机】

机型:R510

CPU:Intel(R) Xeon(R) CPU E5520 @ 2.27GHz

内存:4G*6

磁盘:146G*2(OS) + 300G*12 RAID10(data)

1) 完全随机测试

测试场景描述:

单实例MySQL 5.1.48 InnoDB Plugin

测试SQL:INSERT INTO table (key, value) VALUES(#key#, #value#) / SELECT value FROM table WHERE key=#key#

HS API:execute_single

2) 重复获取同一条数据

测试场景描述:

1 单实例Percona 5.1.57-12.8 XtraDB

2 测试SQL:SELECT value FROM table WHERE key=#key#

3 HS API:execute_single

4 MC API:get

转自:http://www.uml.org.cn/sjjm/201211093.asp

此文章通过 python 爬虫创建,原文是自己的csdn 地址: HandlerSocket (mysql + nosql)

php 随机生成数字字母组合

直接上代码:

function getRandomString($len, $chars=null)
{
    if (is_null($chars)) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    }
    mt_srand(10000000*(double)microtime());
    for ($i = 0, $str = '', $lc = strlen($chars)-1; $i < $len; $i++) {
        $str .= $chars[mt_rand(0, $lc)];
    }
    return $str;
}

例如随机生成 2 位 字母和数字组合

只需调用函数 并传参2即可。

echo getRandomString(2);

如果仅仅是生成小写字母你可以使用类似方法

 echo chr(mt_rand(65, 90);

大写字母

 echo chr(mt_rand(97, 122));

此文章通过 python 爬虫创建,原文是自己的csdn 地址: php 随机生成数字字母组合