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 : ' ') ?>
</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方法;
今天写的有点多,休息下

浙公网安备 33010602011771号