Jorge is the author of three software development books: "Building a Sencha Touch Application", "How to Build a jQuery Mobile Application", and the "Ext JS 3.0 Cookbook". He runs a software development and developer education shop that focuses on mobile, web and desktop technologies. Jorge is a DZone MVB and is not an employee of DZone and has posted 52 posts at DZone. You can read more from them at their website. View Full User Profile

Sencha Touch 2 Models – Loading And Saving Model Data Using a Proxy, PHP Example

08.31.2012
| 9903 views |
  • submit to reddit

Sencha Touch models have the ability to work with a proxy. This feature allows you to save and retrieve model data from the server, memory or local storage, without depending on a Sencha Touch data store. Let’s try it with a very simple scenario where the server side is a PHP page.

In this example you will create a simple Sencha Touch application with the following files:

In the model/Hotel.js file, you will define the Hotel model like so:

Ext.define('App.model.Hotel', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: 'id', type: 'int' },
            { name: 'name', type: 'string' },
            { name: 'address', type: 'string' },
            { name: 'status', type: 'int' }
        ],
        proxy: {
            type: 'ajax',
            api: {
                create: '../../services/hotels.php?act=createhotel',
                read: '../../services/hotels.php?act=loadhotel',
                update: '../../services/hotels.php?act=updatehotel',
                destroy: '../../services/hotels.php?act=erasehotel'
            },
            reader: {
                rootProperty:'hotels'
            }
        }
    }
});

Then, you need to create the hotels.php file, which will be the server-side handler for the model’s proxy. For this example we will use PHP as the server-side language. You can check out the C# example if you work with .NET.

class Hotel {
	function __construct($id, $name, $address, $status) {
		$this->id = $id;
		$this->name = $name;
		$this->address = $address;
		$this->status = $status;
	}
}

$action = $_GET["act"];

$result = "{'success':false}";

switch($action) {

	case "loadhotel":

		$hotelId = $_GET["id"];
		$result = "{'success':true,'hotels':[{'id': 1, 'name': 'Siesta by the Ocean', 'address': '1 Ocean Front, Happy Island', 'status': 1}]}";
		break;

	case "createhotel":

		$hotelJson = file_get_contents('php://input');
		$hotel = json_decode($hotelJson);

		// Here you would save the hotel to the database...

        // Returning success for demo purposes.
		$result = "{'success':true,'hotels':[" . json_encode(new Hotel($hotel->id, $hotel->name, $hotel->address, $hotel->status)) . "]}";
		break;

	case "updatehotel":

		$hotelJson = file_get_contents('php://input');
		$hotel = json_decode($hotelJson);

		// Here you would save the hotel to the database...

		// Returning success for demo purposes.
		$result = "{'success':true,'hotels':[" . json_encode(new Hotel($hotel->id, $hotel->name, $hotel->address, $hotel->status)) . "]}";

		break;

	case "erasehotel":

		$hotelJson = file_get_contents('php://input');
		$hotel = json_decode($hotelJson);

		// Here you would delete the hotel from the database...

		// Returning success for demo purposes.
		$result = "{'success':true}";

		break;

}

header('Cache-Control: no-cache, must-revalidate');
header("content-type:application/json");
echo($result);

Once the handler is ready, you can try the proxy by placing the following code in the app.js file:

Ext.application({
    name: 'App',
    requires: ['Ext.util.DelayedTask'],
    models: ['App.model.Hotel'],
    launch: function () {

        App.model.Hotel.load(1, {
            scope: this,
            success: this.proxySuccess,
            failure: this.proxyFailure,
            callback: this.proxyCallback
        });

        var hotel = Ext.create('App.model.Hotel', {
            name: 'La Playa',
            address: '100 Ocean Front, Happy Island',
            status: 1
        });

        hotel.save({
            scope: this,
            success: this.proxySuccess,
            failure: this.proxyFailure,
            callback: this.proxyCallback
        });

        var updateTask = Ext.create('Ext.util.DelayedTask', function () {

            // Saving inside a delayed task, just to wait for the previous operations
            // to execute on the server and invoke the callbacks.
            hotel.set('name', 'Playa Hermosa');
            hotel.save({
                scope: this,
                success: this.proxySuccess,
                failure: this.proxyFailure,
                callback: this.proxyCallback
            });
        }, this);

        updateTask.delay(3000);

        var deleteTask = Ext.create('Ext.util.DelayedTask', function () {

            // Erasing inside a delayed task, just to wait for the previous operations
            // to execute on the server and invoke the callbacks.
            hotel.erase({
                scope: this,
                success: this.proxySuccess,
                failure: this.proxyFailure,
                callback: this.proxyCallback
            });
        }, this);

        deleteTask.delay(6000);

    },

    proxySuccess: function (record, operation) {
        switch (operation.getAction()) {
            case 'create':
                console.log('From the server: Created the ' + record.get('name') + ' hotel.');
                break;
            case 'read':
                console.log('From the server: Loaded the ' + record.get('name') + ' hotel.');
                break;
            case 'update':
                console.log('From the server: Updated the ' + record.get('name') + ' hotel.');
                break;
            case 'destroy':
                console.log('From the server: Erased the ' + record.get('name') + ' hotel.');
                break;
        }
    },

    proxyFailure: function (record, operation) {
        switch (operation.getAction()) {
            case 'create':
                console.log('From the server: Failed to create hotel.');
                break;
            case 'read':
                console.log('From the server: Failed to read hotel.');
                break;
            case 'update':
                console.log('From the server: Failed to update hotel.');
                break;
            case 'destroy':
                console.log('From the server: Failed to erase hotel.');
                break;
        }
    },

    proxyCallback: function (record, operation) {
        console.log('This function is always invoked, regardless of success or failure');
    }
});

In Google Chrome, when you open the index.html, you will see the following output in the JavaScript console:

How It Works

You connect the Hotel model to a proxy through the model’s proxy config:

 proxy: {
    type: 'ajax',
    api: {
        create: '../../services/hotels.ashx?act=createhotel',
        read: '../../services/hotels.ashx?act=loadhotel',
        update: '../../services/hotels.ashx?act=updatehotel',
        destroy: '../../services/hotels.ashx?act=erasehotel'
    },
    reader: {
        rootProperty:'hotels'
    }
}

The proxy we are using is of type ajax. Its api config allows you to specify which server-side end points will handle the create, read, update and destroy (CRUD) operations that can occur on the data.

The rootProperty config of the proxy’s reader defines which property of the JSON-encoded server response contains the data for the model.

On the server side, the Hotels.php handler inspects that act (short for action) query string parameter, defined in the proxy’s api config, to determine what operation will be performed.

Instead of executing database access code, for this example the handler will return canned responses that demonstrate the results of each operation:

switch($action) {

	case "loadhotel":

		$hotelId = $_GET["id"];
		$result = "{'success':true,'hotels':[{'id': 1, 'name': 'Siesta by the Ocean', 'address': '1 Ocean Front, Happy Island', 'status': 1}]}";
		break;

	case "createhotel":

		$hotelJson = file_get_contents('php://input');
		$hotel = json_decode($hotelJson);

		// Here you would save the hotel to the database...

        // Returning success for demo purposes.
		$result = "{'success':true,'hotels':[" . json_encode(new Hotel($hotel->id, $hotel->name, $hotel->address, $hotel->status)) . "]}";
		break;

	case "updatehotel":

		$hotelJson = file_get_contents('php://input');
		$hotel = json_decode($hotelJson);

		// Here you would save the hotel to the database...

		// Returning success for demo purposes.
		$result = "{'success':true,'hotels':[" . json_encode(new Hotel($hotel->id, $hotel->name, $hotel->address, $hotel->status)) . "]}";

		break;

	case "erasehotel":

		$hotelJson = file_get_contents('php://input');
		$hotel = json_decode($hotelJson);

		// Here you would delete the hotel from the database...

		// Returning success for demo purposes.
		$result = "{'success':true}";

		break;

}

header('Cache-Control: no-cache, must-revalidate');
header("content-type:application/json");
echo($result);

Switching back to the client-side, in the app.js file you will place code that executes each of the CRUD operations. First, a read:

App.model.Hotel.load(1, {
    scope: this,
    success: this.proxySuccess,
    failure: this.proxyFailure,
    callback: this.proxyCallback
});

Second, a create:

var hotel = Ext.create('App.model.Hotel', {
    name: 'La Playa',
    address: '100 Ocean Front, Happy Island',
    status: 1
});

hotel.save({
    scope: this,
    success: this.proxySuccess,
    failure: this.proxyFailure,
    callback: this.proxyCallback
});

Third, an update:

var updateTask = Ext.create('Ext.util.DelayedTask', function () {

    // Saving inside a delayed task, just to wait for the previous operations
    // to execute on the server and invoke the callbacks.
    hotel.set('name', 'Playa Hermosa');
    hotel.save({
        scope: this,
        success: this.proxySuccess,
        failure: this.proxyFailure,
        callback: this.proxyCallback
    });
}, this);

updateTask.delay(3000);

 And fourth, an erase:

var deleteTask = Ext.create('Ext.util.DelayedTask', function () {

    // Erasing inside a delayed task, just to wait for the previous operations
    // to execute on the server and invoke the callbacks.
    hotel.erase({
        scope: this,
        success: this.proxySuccess,
        failure: this.proxyFailure,
        callback: this.proxyCallback
    });
}, this);

deleteTask.delay(6000);

As the CRUD executes asynchronously, we are placing some of the operations inside a DelayedTask instance so we give the prior operation time to complete on the server. This is fine for demo purposes, but in your applications you don’t have to do this.

You can pass callback functions to the the load, save and erase methods of the model. In the example, you use the proxySuccess, proxyFailure and proxyCallback methods defined inside of the application function.

You can use the record and operation arguments of these callbacks to take action based on the results returned by the server:

proxySuccess: function (record, operation) {
    switch (operation.getAction()) {
        case 'create':
            console.log('From the server: Created the ' + record.get('name') + ' hotel.');
            break;
        case 'read':
            console.log('From the server: Loaded the ' + record.get('name') + ' hotel.');
            break;
        case 'update':
            console.log('From the server: Updated the ' + record.get('name') + ' hotel.');
            break;
        case 'destroy':
            console.log('From the server: Erased the ' + record.get('name') + ' hotel.');
            break;
    }
},

Want To Learn More?

My Sencha Touch books will teach you how to create an application from scratch. Check them out!

 

 

 

 

 

 

 

 

 

 

 

 

 

Published at DZone with permission of Jorge Ramon, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)