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 53 posts at DZone. You can read more from them at their website. View Full User Profile

Sencha Touch 2 Models – hasMany Associations, PHP Example

09.13.2012
| 4744 views |
  • submit to reddit

In this article you will learn how to use the hasMany association, a feature of Sencha Touch models that allows you to connect two models in a one-to-many relationship.

Let’s create a simple Sencha Touch application with the following files:

In the model/Hotel.js file, you will define a 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' }
        ],
        hasMany: {
            model: 'App.model.Room',
            name:'rooms'
        }
    }
});

The interesting part of this model definition is the hasMany config, consisting of the model and name properties, which reference a Room model. You will create this model in the model/Room.js file:

Ext.define('App.model.Room', {
    extend: 'Ext.data.Model',
    requires: ['Ext.data.Store'],
    config: {
        fields: [
            { name: 'id', type: 'int' },
            { name: 'floor', type: 'string' },
            { name: 'type', type: 'string' },
            { name: 'standardRate', type: 'float' },
            { name: 'smoking', type: 'string' },
            { name: 'status', type: 'int' },
            { name: 'hotelId', type: 'int'}         // This works with belongsTo property below.
        ],
        belongsTo: {
            model: 'App.model.Hotel',
            foreignKey: 'hotelId'
        },
        proxy: {
            type: 'ajax',
            api: {
                create: '../../services/rooms.php?act=createrooms',
                read: '../../services/rooms.php?act=loadrooms',
                update: '../../services/rooms.php?act=updaterooms',
                destroy: '../../services/rooms.php?act=eraserooms'
            },
            reader: {
                rootProperty: 'hotels'
            }
        }
    }
});

This model has a belongsTo config, with the model and foreignKey properties, which reference the Hotel model. It also has a proxy config, which references the services/rooms.php page.

This is the code for the rooms.php page:

// Class to represent a hotel.
class Room {
      function __construct($id, $floor, $type, $standardRate, $smoking, $status, $hotelId) {
 		$this->id = $id;
		$this->floor = $floor;
		$this->type = $type;
		$this->standardRate = $standardRate;
		$this->smoking = $smoking;
		$this->status = $status;
		$this->hotelId = $hotelId;
	}
}

$action = $_GET["act"];

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

switch($action) {

	case "updaterooms":

		$roomJson = file_get_contents('php://input');
		$rooms = json_decode($roomJson);

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

		// Returning success for demo purposes.
		$result = "{'success':true,'rooms':" . json_encode($rooms) . "}";

		break;

	case "loadrooms":

		$filterJson = $_GET["filter"];
		$filter = json_decode($filterJson);

		// The value of $filter[0]->property should be "hotelId" for rooms associated to a hotel.
		// The value of $filter[0]->value should be the id of the hotel to which the rooms are associated.
                // Here you would load the rooms for the given hotelId from the database.
                // We are returning hard-coded results for demo purposes.
		$result = "{'success':'true','rooms':[{'id': 1, 'floor': '3', 'type': 'Royal Suite', 'standardRate': '2', 'smoking': 'false', 'status': '1', 'hotelId': '1'}]}";

		break;

	case "createrooms":

		$roomJson = file_get_contents('php://input');
		$rooms = json_decode($roomJson);

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

        // Returning success for demo purposes.
		$result = "{'success':true,'rooms':" . json_encode($rooms) . "}";

		break;

	case "eraserooms":

		$roomJson = file_get_contents('php://input');
		$rooms = json_decode($roomJson);

		// Here you would delete the rooms 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);

Now you can check out the behaviors that the related models have. In the app.js file, you can try the following code:

Ext.application({
    name: 'App',
    models: ['Room', 'Hotel'],
    launch: function () {

        var myHotel = Ext.create('App.model.Hotel', {
            id: 1,
            name: 'Siesta by the Ocean',
            address: '1 Ocean Front, Happy Island',
            status: 1
        });

        var rooms = myHotel.rooms();

        rooms.each(function (item, index, length) {
            console.log('Room Id: ' + item.id + ', room type: ' + item.type);
        });

        rooms.load(); // Should see request with: act:loadrooms, page: 1, start: 0, limit: 25, filter: [{ "property": "hotelId", "value": 1}]

    }
});

Using Google Chrome, you can browse to the index.html file, open the developer tools and inspect the request to the rooms.php file. The console’s output should look similar to this:

The query string parameters clearly show a filter property that contains the id of the hotel the rooms belong to. This is one of the behaviors that the hasMany association provides for us.

How It Works

As you can see in the code, you can obtain the rooms associated with a hotel by invoking the rooms() method of the Hotel model, which returns an instance of a data store. The method is automatically created based on the hasMany config:

hasMany: {
    model: 'App.model.Room',
    name:'rooms'
}

In the app.js file, you will now add a couple of rooms to the current hotel:

Ext.application({
    name: 'App',
    models: ['Room', 'Hotel'],
    launch: function () {

        var myHotel = Ext.create('App.model.Hotel', {
            id: 1,
            name: 'Siesta by the Ocean',
            address: '1 Ocean Front, Happy Island',
            status: 1
        });

        var rooms = myHotel.rooms();

        rooms.each(function (item, index, length) {
            console.log('Room Id: ' + item.id + ', room type: ' + item.type);
        });

        rooms.load(); // Should see request with: act:loadrooms, page: 1, start: 0, limit: 25, filter: [{ "property": "hotelId", "value": 1}]

        var room1 = Ext.create('App.model.Room', {
            id: 1,
            standardRate: 5,
            floor: 3,
            type: 'Royal Suite',
            status: 1,
            smoking: false
        });

        var room2 = Ext.create('App.model.Room', {
            id: 2,
            standardRate: 2,
            floor: 1,
            type: 'Single',
            status: 1,
            smoking: false
        });

        rooms.add(room1);
        rooms.add(room2);

        rooms.sync(); // Rooms have the hotelId property populated.
    }
});

The call to rooms.sync() will upload the rooms to the server. Chrome’s console should reflect the operation:

The hotelId field of these rooms is set to 1, which is the id of their parent hotel. Note that you did not have to take care of this. It happens automatically thanks to the hasMany association you defined.

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.)