Magento后台grid控件

后台grid控件到处可见,如在上篇讲到的订单列表页;
以订单中用到的grid控件为例,相关类为Mage_Adminhtml_Block_Sales_Order_Grid
类的层次如下,下面的类为上面的父类
Mage_Adminhtml_Block_Sales_Order_Grid
Mage_Adminhtml_Block_Widget_Grid
Mage_Adminhtml_Block_Widget
Mage_Adminhtml_Block_Template
Mage_Core_Block_Template
Mage_Core_Block_Abstract


要使用grid控件,一般重写以下方法
_prepareCollection:准备数据集
_prepareColumns:准备列,即从数据集中显示哪些列,列的名称
_prepareMassaction:列表上面的一些按钮:如导出,删除按钮就在这里实现
getRowUrl:每行的url,如订单列表显示明细
getGridUrl

这些方法在什么时候调用呢
Mage_Core_Block_Abstract::toHtml负责渲染模板:
$this->_beforeToHtml();
$html = $this->_toHtml();
$html = $this->_afterToHtml($html);

即父类定义了框架,先调用子类的_beforeToHtml,再调用_toHtml,最后调用_afterToHtml;

其中Mage_Adminhtml_Block_Widget_Grid实现了_beforeToHtml方法
protected function _beforeToHtml()
{
        $this->_prepareGrid();
        return parent::_beforeToHtml();
}

Mage_Adminhtml_Block_Widget_Grid::_prepareGrid会调用子类实现的_prepareColumns、_prepareMassactionBlock、_prepareCollection方法
 protected function _prepareGrid()
    {
        $this->_prepareColumns();
        $this->_prepareMassactionBlock();
        $this->_prepareCollection();
        return $this;
    }

接下来是_toHtml方法,只有类Mage_Core_Block_Template实现了此方法
 protected function _toHtml()
    {
        if (!$this->getTemplate()) {
            return '';
        }
        $html = $this->renderView();
        return $html;
    }

Mage_Adminhtml_Block_Widget_Grid在构造函数中就设置了模板
 $this->setTemplate('widget/grid.phtml');
这个模板的路径为app\design\adminhtml\default\default\template\widget\grid.phtml,有兴趣的同学可以看下;

渲染表头的关键代码
<?php foreach ($this->getColumns() as $_column): ?>
<?php if ($this->getHeadersVisibility()): ?>
                    <tr class="headings">
                    <?php foreach ($this->getColumns() as $_column): ?>
                        <th<?php echo $_column->getHeaderHtmlProperty() ?>><span class="nobr"><?php echo $_column->getHeaderHtml() ?></span></th>
                    <?php endforeach; ?>
                    </tr>
                <?php endif; ?>

                <?php if ($this->getFilterVisibility()): ?>
                    <tr class="filter">
                    <?php $i=0;foreach ($this->getColumns() as $_column): ?>
                        <th<?php echo $_column->getHeaderHtmlProperty() ?>><?php echo $_column->getFilterHtml() ?></th>
                    <?php endforeach; ?>
                    </tr>
                <?php endif ?>

渲染每行的关键代码如下:
<?php foreach ($this->getCollection() as $_index=>$_item): ?>
<?php $i=0;foreach ($this->getColumns() as $_column): ?>

                <?php if ($this->shouldRenderCell($_item, $_column)):?>
                    <?php $_rowspan = $this->getRowspan($_item, $_column);?>
                    <td <?php echo ($_rowspan ? 'rowspan="' . $_rowspan . '" ' : '') ?>class="<?php echo $_column->getCssProperty() ?> <?php echo ++$i==$numColumns?'last':'' ?>">
                        <?php echo (($_html = $_column->getRowField($_item)) != '' ? $_html : '&nbsp;') ?>
                    </td>
                    <?php if ($this->shouldRenderEmptyCell($_item, $_column)):?>
                        <td colspan="<?php echo $this->getEmptyCellColspan($_item)?>" class="last"><?php echo $this->getEmptyCellLabel()?></td>
                    <?php endif;?>
                <?php endif;?>

            <?php endforeach; ?>

再 来看下数据是如何加载进来的,上面讲到渲染的过是:_beforeToHtml, _toHtml, _afterHtml,而Mage_Adminhtml_Block_Widget_Grid在_beforeToHtml方法会调用我们自己实现的 _prepareCollection方法,我们看下订单的grid是如何实现的

$collection = Mage::getResourceModel($this->_getCollectionClass());
$this->setCollection($collection);
return parent::_prepareCollection();

接下来调用Mage_Adminhtml_Block_Widget_Grid::_prepareCollection
 if (!$this->_isExport) {
                $this->getCollection()->load();
                $this->_afterLoadCollection();
            }
这 个getCollection返回的是我们上面调用setCollection设置的,即我们自己实现_getCollectionClass方法,告诉 resource的model是哪个类,订单的是sales/order_grid_collection,即类 Mage_Sales_Model_Resource_Order_Grid_Collection

类的层次如下(不画UML图了^_^)
Mage_Sales_Model_Resource_Order_Grid_Collection
Mage_Sales_Model_Resource_Order_Collection
Mage_Sales_Model_Resource_Collection_Abstract
Mage_Core_Model_Resource_Db_Collection_Abstract
Varien_Data_Collection_Db
Varien_Data_Collection

其中Varien_Data_Collection_Db实现了load方法
//事件处理
$this->_beforeLoad();

        $this->_renderFilters()
             ->_renderOrders()
             ->_renderLimit();

        $this->printLogQuery($printQuery, $logQuery);

        //加载数据
        $data = $this->getData();
        $this->resetData();
      
        //处理数据
        if (is_array($data)) {
            foreach ($data as $row) {
                $item = $this->getNewEmptyItem();
                if ($this->getIdFieldName()) {
                    $item->setIdFieldName($this->getIdFieldName());
                }
                $item->addData($row);
                $this->addItem($item);
            }
        }
       
        //事件处理
        $this->_afterLoad();


Mage_Core_Model_Resource_Db_Collection_Abstract 类实现了_beforeLoad和_afterLoad方法,主要是事件分 发:core_collection_abstract_load_before、 core_collection_abstract_load_after,如订单列表页要加个状态列,可以实现对事件 core_collection_abstract_load_before的监听,从数据库中把所有订单状态取出来;

继续回到load方法
 $this->_renderFilters()
             ->_renderOrders()
             ->_renderLimit();
上述代码实现过滤及排序及limit

再往下看:
  $data = $this->getData();

有两个类实现了这个方法,分别是Mage_Core_Model_Resource_Db_Collection_Abstract和,Varien_Data_Collection_Db
(不明白为什么这样设计?)
看下Mage_Core_Model_Resource_Db_Collection_Abstract的实现
 $query       = $this->_prepareSelect($this->getSelect());
 $this->_data = $this->_fetchAll($query, $this->_bindParams);

_fetchAll的代码如下
$data = $this->getConnection()->fetchAll($select, $this->_bindParams);
其中getConnection返回连接对象是
Mage_Core_Model_Resource_Db_Collection_Abstract的构造函数设置的
……
$this->setConnection($this->getResource()->getReadConnection());
这里会读取config.xml配置的读连接信息了;

再回到上面的_fetchAll方法,getConnection是一个Magento_Db_Adapter_Pdo_Mysql类型的变量,这个是在app/etc/config.xml中配置的
 <default_setup>
                <connection>
                    <type>pdo_mysql</type>
                </connection>
   </default_setup>

 <resource>
            <connection>
                <types>
                    <pdo_mysql>
                        <adapter>Magento_Db_Adapter_Pdo_Mysql</adapter>
                        <class>Mage_Core_Model_Resource_Type_Db_Pdo_Mysql</class>
                        <compatibleMode>1</compatibleMode>
                    </pdo_mysql>
                </types>
            </connection>
        </resource>


Magento_Db_Adapter_Pdo_Mysql的类层次如下
Magento_Db_Adapter_Pdo_Mysql
Varien_Db_Adapter_Pdo_Mysql
Zend_Db_Adapter_Pdo_Mysql
Zend_Db_Adapter_Pdo_Abstract
Zend_Db_Adapter_Abstract

而fetchAll在Zend_Db_Adapter_Abstract中实现
$stmt = $this->query($sql, $bind);
$result = $stmt->fetchAll($fetchMode);

所有要记录Magento所有查询的sql,可以在Zend_Db_Adapter_Abstract::query中打日志
 $sql = $sql->assemble();
$stmt = $this->prepare($sql);
$stmt->execute($bind);

其中prepare返回的是Varien_Db_Statement_Pdo_Mysql类型的变量;

再看load方法中转换数据的代码
 foreach ($data as $row) {
                $item = $this->getNewEmptyItem();
                if ($this->getIdFieldName()) {
                    $item->setIdFieldName($this->getIdFieldName());
                }
                $item->addData($row);
                $this->addItem($item);
            }
这个getNewEmptyItem返回生成的实体类,我的环境是OnePlus_Jerp_Model_Order,这个在哪里设置的呢?
因为我们的某个模块重写了订单类:
在其config.xml中配置如下
<global>
<models>
 <sales>
              <rewrite>
                  <order>OnePlus_Jerp_Model_Order</order>
              </rewrite>
            </sales>
          </models>
</global>

因为所有的Model类都继承从Varien_Object,可以看下这个类的Varien_Object方法;
今天写的有点多,休息下

posted @ 2015-06-20 10:52  szphper  阅读(756)  评论(0)    收藏  举报