Tutorial hero
Lesson icon

How to Run a Server Side Search Query in Sencha Touch

Originally published November 05, 2014 Time 6 mins

Sencha Touch provides some handy functions for sorting and filtering data. Take the following operations on data stores as an example:

var store = Ext.getStore('MyStore');

//Only display records that have the value 'male' for 'gender'
store.filter('gender', 'male');
var store = Ext.getStore('MyStore');

//Order records from smallest height to biggest height
store.sort('height', 'ASC');

A filter excludes data if it does not meet a certain condition, and sorting controls the order in which the data is displayed. This is quite easy to accomplish for locally stored data – but when we’re loading in our data from a remote database our application can’t possibly know how to sort or filter data that it doesn’t even know about yet.

This is where it gets a little trickier. Fortunately though, we have the option to defer the “heavy lifting” of sorting and filtering to the server, rather than doing it in the application. We can define either or both of the remoteSort and remoteFilter options on our store to achieve this:

Ext.define('MyApp.store.MyStore', {
  extend: 'Ext.data.Store',
  requires: ['MyApp.model.MyStore'],
  config: {
    autoLoad: true,
    remoteFilter: true,
    remoteSort: true,
    model: 'MyApp.model.MyStore',
    storeId: 'MyStore',
  },
});

Now when we call a sort or filter, rather that doing it immediately on the data that is currently loaded, the store sends a bit of extra information along with our read request.

Filter Parameters

The above is a request sent from an application I created that maps out playgrounds in the area. Different filters can be applied to determine which playgrounds to show – in this case I’ve set a filter on “icon_toilets” and “icon_bbq”. You can see that these filters are added in a JSON format to the query string. We can utilise this information at the server level to only return the desired records back from our database. So rather than filtering within Sencha Touch, we are filtering using a MySQL query (or the server side technologies of your choosing).

To give you an example of exactly how you might implement this, let’s go through a simple example of running a search query against a database and returning a result set.

1. Add the remote filtering configuration to your store

  • First we are going to add the ‘remoteFilter’ option to our store. We won’t require the sort option for this example.
Ext.define('MyApp.store.MyStore', {
  extend: 'Ext.data.Store',
  requires: ['MyApp.model.MyStore'],
  config: {
    autoLoad: true,
    remoteFilter: true,
    model: 'MyApp.model.MyStore',
    storeId: 'MyStore',
  },
});

2. Add a search field

  • We want to accept user defined input for the search query, so let’s add a field to do that:
Ext.define('MyApp.view.MyContainer', {
  extend: 'Ext.Container',
  requires: ['MyApp.view.MyList', 'Ext.field.Search'],
  xtype: 'mycontainer',

  config: {
    layout: {
      type: 'fit',
    },

    listeners: [
      {
        fn: 'onSearchAction',
        event: 'action',
        delegate: '#search',
      },
      {
        fn: 'onSearchReset',
        event: 'clearicontap',
        delegate: '#search',
      },
    ],

    items: [
      {
        xtype: 'searchfield',
        itemId: 'search',
        docked: 'top',
      },
      {
        xtype: 'mylist',
      },
    ],
  },

  onSearchAction: function (field, e, eOpts) {
    var store = Ext.getStore('MyStore'),
      value = field.getValue();

    store.clearFilter();
    store.currentPage = 1;

    if (value) {
      store.filter('name', value);
    }

    //Server side filtering requires a load call
    store.load();
  },

  onSearchReset: function (field) {
    var store = Ext.getStore('MyStore');
    store.clearFilter();
    store.currentPage = 1;
    store.load();
  },
});

In the code above we’ve added a ’searchfield’ to accept user input, and have set up a listener to do something with that input, and also to clear the filter if they tap the reset button in the field.

In the ’onSearchAction’ listener we take the user input and apply it as a filter on the ’name’ column. So if the user enters the value ‘bob’ then only records containing ‘bob’ will be returned.

With remote sorting and filtering you will also need to re-load the store each time a filter or sorter is added, so after we have added the filter we are triggering a store load. We also set the current page to 1 so that if the user runs a new search query they don’t miss the first few pages of results.

3. Implement the server side code in PHP and MySQL

  • The store load will trigger a request to be sent to the URL defined as the proxy. Now we need to make use of those extra parameters (page, start, limit and filter) by modifying our MySQL query, making sure to only return the desired rows.
$start = $mysqli->real_escape_string($_GET['start']);
$limit = $mysqli->real_escape_string($_GET['limit']);

$query = "SELECT * FROM table";

if(isset($_GET['filter'])){

	$filters = json_decode($_GET['filter']);

	if(!empty($filters)){

		$query .= " WHERE ";

		for($i = 0; $i < count($filters); $i++){

			$col = $mysqli->real_escape_string($filters[$i]->property);
			$value = $mysqli->real_escape_string($filters[$i]->value);

			if($col == 'name'){
				//Return similar results
				$query .= $col." LIKE '%".$value."%'";
			}
			else
			{
				//Return exact results
				$query .= $col." = '".$value."'";
			}

			if ($i != (count($filters) - 1)){
				//Still more filters, so add AND
				$query .= " AND ";
			}
		}

	}
}

$query .= " LIMIT ".$limit." OFFSET ".$start;

$dbresult = $mysqli->query($query);
$resultArray = array();

while($row = $dbresult->fetch_array(MYSQLI_ASSOC)){

	$resultArray[] = array(
		'id' => $row['id'],
		'name' => $row['name']
	);
}

if($dbresult){

	//Get the total number of records, this is required
	//for list paging
	$query = "SELECT * FROM table";
	$dbresult = $mysqli->query($query);
	$total = $dbresult->num_rows;

	$result = "{'success':true, 'results':" . json_encode($resultArray) . ", 'total':".$total."}";
}
else
{
	$result = "{'success':false}";
}

The code above returns the desired records from the database by looping through the filter parameters and adding WHERE clauses for each of them. If we were sorting instead of filtering then we would use the sort parameters to define the ORDER BY clause or perhaps grouping. There’s some additional code in here that relates to paging – how to implement the list itself and paging has been left out of this tutorial but if you’re interested in seeing the rest you can view How to Create an Infinite Scrolling List in Sencha Touch.

Learn to build modern Angular apps with my course