After a couple of weeks I finally found some time to write about Symfony and ExtJS. This time i want to show how to put together to work Symfony 1.4, ExtJS 3.2 and Grid Filter Plugin - for me it’s the one of most powerfull filtering solutions i have ever seen.
Since my last entry about displaying data in ExtJS Grid Panel i have updated Ext library to 3.2 version - they have added some nice features (more information you can find in changelog 3.1 and changelog 3.2) and changed some ExtJs components (more details you will find later).
Configuration and project setup
Information about how to setup new Symfony project and configure it to work well with ExtJS - you can find in this entry - i’m not gonna repeat all steps again. Notice that you should download latest ExtJS version: 3.2 and set up view.yml file to attach neccessary files. You should also load sample data - we need it for our example.
After setting up the environment we can start coding.
ExtJS components
This time i’m going to put all JavaScript code in separate files - it is really good practice for building bigger ExtJS applications than in this example. First i will create grid.js file and put there grid component class and other configuration - i will create my own custom component class: App.country.Grid which inherits from Ext.grid.GridPanel - it’s not really needed now but it will save us time next time. Commnets should help you understand the concept:
// Typical data Store automatically configured with a Ext.data.JsonReader.
var store = new Ext.data.JsonStore({
// store configs
url: 'default/list', // url for retreiving data
autoLoad: true,
// reader configs
totalProperty: 'total',
successProperty: 'success', // response status name
idProperty: 'id',
root: 'data',
fields: ['id', 'name', 'description', 'population', 'created_at']
});
// Columns configuration
var columns = [
{header: "ID", width: 50, fixed: true, sortable: true, dataIndex: 'id'},
{header: "Country", width: 50, sortable: true, dataIndex: 'name' },
{header: "Description", width: 100, sortable: true, dataIndex: 'description' },
{header: "Population", width: 50, sortable: true, dataIndex: 'population' },
{header: "Created at", width: 150, fixed:true, sortable: true, dataIndex: 'created_at' }
];
Ext.ns('App', 'App.country');
// my custom grid component
App.country.Grid = Ext.extend(Ext.grid.GridPanel, {
initComponent : function() {
// typical viewConfig
this.viewConfig = {
forceFit: true,
emptyText: 'No data found.'
};
App.country.Grid.superclass.initComponent.call(this);
}
});
I think this code do not need additional explanations.
Next step is to create main app.js file - place where i’m going to put my components logic (components initialization) - nothing special so far - simple retrieving data and displaying it in grid. In this example i will place grid into simple window component - for better usability- it’s another good example of using ExtJS components. Code responsible for that:
// when document is ready
Ext.onReady(function() {
Ext.QuickTips.init();
// create my custom grid component
var grid = new App.country.Grid({
store: store, // data store for grid
columns : columns, //columns configuration
plugins: [filters], // additional plugin - Grid Filter Plugin
bbar: new Ext.PagingToolbar({ //bottom bar configuration
pageSize: 15, // records per page
store: store,
displayInfo: true
})
});
// simple window for grid
var win = new Ext.Window({
title: 'Advanced data filtering with Grid Filter Plugin',
height: 410,
width: 800,
layout: 'fit',
items: grid // just one item
});
win.show(); // display window with grid
});
On the server side
There is one more thing to get it to work - we need PHP code on the server side. We can use code from the latest entry or use the following:
public function executeList(sfWebRequest $request) {
# data retrieved from the request
$limit = $request->getParameter('limit', 15);
$page = ($request->getParameter('start', 0)/$limit)+1;
$dir = $request->getParameter('dir', 'ASC');
$column = strtolower($request->getParameter('sort', 'name'));
# create query object
$query = Doctrine_Query::create()
->from('Country c');
# conditions for sorting
if (Doctrine::getTable('Country')->hasColumn($column)) {
$query->orderBy(sprintf('c.%s %s', Doctrine::getTable('Country')->getFieldName($column), $dir));
}
# object responsible for paging
$pager = new sfDoctrinePager('Country', $limit);
$pager->setQuery($query);
$pager->setPage($page);
$pager->init();
$result = array();
# format result array
foreach($pager->getResults() as $country) {
$result[] = $country->toArray();
}
# formatted data are returned to the grid
return $this->renderText(json_encode(array(
'success' => true,
'total' => $pager->getNbResults(),
'data' => $result
)));
}
Calling default action of our application we should achieved the following:
Grid Filter plugin
Next step is to apply Grid Filter plugin to our example.
I just only copied Filter plugin folder from Ext/examples/ux to Ext/ux for better code organisation. To add all neccessary JS and CSS files we need to edit view.yml file - final version should looks similar to this:
stylesheets:
- ../js/ext-3.2.0/resources/css/ext-all.css
- ../js/ext-3.2.0/ux/gridfilters/css/GridFilters.css
- ../js/ext-3.2.0/ux/gridfilters/css/RangeMenu.css
javascripts:
- ext-3.2.0/adapter/ext/ext-base-debug.js
- ext-3.2.0/ext-all-debug.js
- ext-3.2.0/ux/gridfilters/menu/RangeMenu.js
- ext-3.2.0/ux/gridfilters/menu/ListMenu.js
- ext-3.2.0/ux/gridfilters/GridFilters.js
- ext-3.2.0/ux/gridfilters/filter/Filter.js
- ext-3.2.0/ux/gridfilters/filter/StringFilter.js
- ext-3.2.0/ux/gridfilters/filter/DateFilter.js
- ext-3.2.0/ux/gridfilters/filter/ListFilter.js
- ext-3.2.0/ux/gridfilters/filter/NumericFilter.js
- ext-3.2.0/ux/gridfilters/filter/BooleanFilter.js
- grid.js
- app.js
Now we can configure filter component:
var filters = new Ext.ux.grid.GridFilters({
local: false, // enable remote filtering
filters: [{
type: 'numeric', // filter type
dataIndex: 'id' // column name
}, {
type: 'string',
dataIndex: 'name'
}, {
type: 'string',
dataIndex: 'description'
}, {
type: 'numeric',
dataIndex: 'population'
}, {
type: 'date',
dataIndex: 'created_at'
}]
});
And enable it in grid:
var grid = new App.country.Grid({
store: store, // data store for grid
columns : columns, //columns configuration
plugins: [filters], // additional plugin - Grid Filter Plugin
bbar: new Ext.PagingToolbar({ //bottom bar configuration
pageSize: 15, // records per page
store: store,
displayInfo: true
})
});
So filter component could work properly we need to write additional PHP code responsible for filtering data. Using symfony and Doctrine Query Language it is really easy. The fallowing code of action.class.php presents complete code:
public function executeList(sfWebRequest $request) {
# data retrieved from the request
$limit = $request->getParameter('limit', 15);
$page = ($request->getParameter('start', 0)/$limit)+1;
$dir = $request->getParameter('dir', 'ASC');
$column = strtolower($request->getParameter('sort', 'name'));
# create query object
$query = Doctrine_Query::create()
->from('Country c');
# conditions for sorting
if (Doctrine::getTable('Country')->hasColumn($column)) {
$query->orderBy(sprintf('c.%s %s', Doctrine::getTable('Country')->getFieldName($column), $dir));
}
# code resposible for filtering data
foreach($request->getParameter('filter') as $filter) {
# comparison condition
if (isset($filter['data']['comparison'])) {
switch($filter['data']['comparison']) {
case 'eq':
$comparison = '=';
break;
case 'lt':
$comparison = '<';
break;
case 'gt':
$comparison = '>';
break;
}
}
# switch 5 filter types
switch($filter['data']['type']) {
case 'boolean':
$query->addWhere(sprintf('c.%s = ?', $filter['field']), $filter['data']['value']);
break;
case 'string':
$query->addWhere(sprintf('c.%s LIKE ?', $filter['field']), '%'.$filter['data']['value'].'%');
break;
case 'numeric':
$query->addWhere(sprintf('c.%s %s ?', $filter['field'], $comparison), $filter['data']['value']);
break;
case 'list':
$query->whereIn(sprintf('c.%s', $filter['field']), explode(',', $filter['data']['value']));
break;
case 'date':
$query->addWhere(sprintf('c.%s %s ?', $filter['field'], $comparison), date('Y-m-d', strtotime($filter['data']['value'])));
break;
default:
break;
}
}
# object responsible for paging
$pager = new sfDoctrinePager('Country', $limit);
$pager->setQuery($query);
$pager->setPage($page);
$pager->init();
$result = array();
# format result array
foreach($pager->getResults() as $country) {
$result[] = $country->toArray();
}
# formatted data are returned to the grid
return $this->renderText(json_encode(array(
'success' => true,
'total' => $pager->getNbResults(),
'data' => $result
)));
}
}
Thats all. As a result we should get:
Enyoj!


Great post, thanks.
Nicely done!, i can’t wait to see the create and update new records functionality