今天需要在 symfony 中使用 datagrid 型控件对一个分页+排序的表格进行展示。但是 sfDataGrid 写得实在是太糟糕了,没有找到其他的比较好的 plugin。

PEAR 中的 Structures/DataGrid 倒是不错。但是两个问题:无法和现在偶使用的 sfSmartyView 直接结合,因为在 $datagrid->fill($smarty); 的时候,symfony 还没有生成 view instance,除非直接获取生成的 HTML_TABLE 或者类似的结果,这样不利于 MVC 的分离和对具体样式的控制;二是现有的 datasource 无法和 symfony 使用的 propel 直接结合,如果采用检索全部结果再传递给 Structures/DataGrid 进行分页处理,那么效率非常低下。

于是自己写一个 datasource,并且使用了一点技巧解决和 sfSmartyView 结合的问题。

Structures_DataGrid_DataSource_Propel 类的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Structures_DataGrid_DataSource_Propel extends Structures_DataGrid_DataSource {
  protected $criteria = null;
  protected $con = null;
  protected $object = null;
 
  function __construct($object, $criteria, $con = null) {
    parent::Structures_DataGrid_DataSource();
    $this->object = new $object();
    $this->criteria = $criteria;
    $this->con = $con;
  }
 
  function count() {
    //TODO: this is not a good way, due to MySQL Bug #21787
    $this->criteria->setLimit(0);
    $this->criteria->setOffset(0);
    $result = $this->object->doCount($this->criteria);
    return $result;
  }
 
  function sort($sortSpec, $sortDir = 'ASC') {
    switch (strtolower($sortDir)) {
      case 'asc' :
        $this->criteria->addAscendingOrderByColumn($sortSpec);
        break;
      case 'desc' :
        $this->criteria->addDescendingOrderByColumn($sortSpec);
        break;
      default :
        return PEAR::raiseError("Sorting dir must be ASC or DESC!");
    }
    return true;
  }
 
  function fetch($offset = 0, $len = null) {
    $c = $this->criteria;
    $c->setOffset($offset);
    if (! is_null($len)) {
      $c->setLimit($len);
    }
    $objects = $this->object->doSelect($c);
    $result = array ();
    foreach ($objects as $object) {
      $result[] = $object->toArray(BasePeer::TYPE_COLNAME);
    }
    return $result;
  }
}

action 中的代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function execute() {
  // get data source
  $c = new Criteria();
  $datasource = new Structures_DataGrid_DataSource_Propel('SysUiaAccountPeer', $c);

  // setup datagrid
  $datagrid = & new Structures_DataGrid(10);
  $datagrid->bindDataSource($datasource);
  $datagrid->setDefaultSort(array (
    SysUiaAccountPeer::USERNAME=>"DESC"
  ));
  $datagrid->addColumn(new Structures_DataGrid_Column('用户名', SysUiaAccountPeer::USERNAME, SysUiaAccountPeer::USERNAME, array (), ' ', ''));
  // add more columns here
 
  // use a tip to get output using smarty
  $smarty = $datagrid->setRenderer('smarty');
  // set pager params
  $pager = $smarty->smartyGetPaging(array (
    'prevImg'=>"上一页", 'nextImg'=>"下一页", 'separator'=>"|", 'delta'=>"5", 'clearIfVoid'=>true, 'spacesBeforeSeparator'=>"1", 'spacesAfterSeparator'=>"1", 'firstPageText'=>'第一页', 'lastPageText'=>'最后页'
  ), $smarty);
  $this->dg = $datagrid->getOutput();
  $this->pager = $pager;
  return sfView::SUCCESS;
}

模板中的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<table>
  <!-- Build header -->
  <tr>
    {section name=col loop=$dg.columnSet}
    <th{$dg.columnSet[col].attributes}><!-- Check if the column is sortable -->
    {if $dg.columnSet[col].link != ""} <a href="{$dg.columnSet[col].link}">{$dg.columnSet[col].label}</a>
    <!-- Show the current ordering with an arrow --> {if
    $dg.columnSet[col].name == $smarty.request.orderBy} {if
    $smarty.request.direction == "ASC"}&uarr;{elseif
    $smarty.request.direction == "DESC"}&darr;{/if} {/if} {else}
    {$dg.columnSet[col].label} {/if}</th>
    {/section}
  </tr>
  <!-- Build body -->
  {section name=row loop=$dg.recordSet}
  <tr {if $smarty.section.row.iteration is even}bgcolor="#EEEEEE"{/if}>
    {section name=col loop=$dg.recordSet[row]}
    <td{$columnSet[col].attributes}>{$dg.recordSet[row][col]}</td>
    {/section}
  </tr>
  {/section}
  <tr>
    <td colspan="...">{$pager}</td>
  </tr>
</table>