How I Created a Many-to-Many Model Relationship in Sencha Touch

5 min read

Originally published August 12, 2014

Outside of the basic implementation of a Sencha Touch model, there's some more advanced stuff you an do (check out how to use the convert function on fields for another example). Model associations in Sencha Touch are a convenient way to create relationships between models. You may be familiar with relationships such as the following:

One-to-One one object belongs exclusively to another (i.e. one house to one owner)

One-to-Many one object belongs to many other objects (i.e. one product to many stores)

Many-to-Many a number of objects belongs to a number of other objects (i.e many people to many groups)

Fortunately, Sencha gives us some out of the box functionality to represent these relationships in our models. If you take a look at the Ext.data.association.Association class you will notice several different relationships that are available, namely:

  • Has Many
  • Belongs To
  • Has One

Has Many takes care of our one-to-many relationship and could be created as shown in the documentation as follows:

Ext.define('Product', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      { name: 'id', type: 'int' },
      { name: 'user_id', type: 'int' },
      { name: 'name', type: 'string' },
    ],
  },
});

Ext.define('User', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      { name: 'id', type: 'int' },
      { name: 'name', type: 'string' },
    ],
    // we can use the hasMany shortcut on the model to create a hasMany association
    hasMany: { model: 'Product', name: 'products' },
  },
});

While nothing special is happening in the Product model, in the User model we are defining a hasMany relationship. We are declaring that the relationship is on Users to Products and will take the name of products. With this relationship defined, a function is created on User instances for us that we can use to grab the products related to that user. It can be retrieved in the following way:

var products = user.products;

Where user is an instance of the User model.

Similarly belongsTo takes care of the many-to-one association and hasOne takes care of the one-to-one association. Looking good so far but there's one relationship missing… there's no many-to-many association in Sencha Touch!

This was an exercise in frustration for me, surely thousands of people have had the need to implement a many-to-many relationship and a solution would exist out there. I had no luck finding any kind of 'proper' or 'official' solution but there were certainly discussions around how to implement the relationship.

After quite a bit of research it seemed that the best option was to create an additional new model to represent the relationship between the two other models – like a junction table in a relational database. Rather than try to explain this in abstract terms, I'll walk you through the specific example I created.

In my case I had Users and Groups. This was a many-to-many relationship – I needed multiple users to belong to multiple groups. Naturally I had a User model and a Group model, but I needed to create an additional UserGroup model.

My UserGroup model looked like this:

Ext.define('MyApp.model.UserGroup', {
  extend: 'Ext.data.Model',
  requires: ['Ext.data.identifier.Uuid'],
  config: {
    identifier: {
      type: 'uuid',
    },

    fields: [
      { name: 'userId', type: 'int' },
      { name: 'groupId', type: 'int' },
    ],

    proxy: {
      type: 'ajax',
      api: {
        //api links
      },
      reader: {
        rootProperty: 'usergroups',
      },
    },
  },
});

and in my User model I added the following hasMany relationship:

hasMany: {
     model: 'MyApp.model.UserGroup',
     name: 'userGroups',
     foreignKey: 'userId'
},

Now I could run the following code to load a store containing records of every group a user belonged to (where currentUser is an instance of the User model):

currentUser.userGroups().load({
  scope: this,
  callback: function (records, operation, success) {
    Ext.each(
      records,
      function (association) {
        console.log(association.getData().groupId);
      },
      this
    );
  },
});

Problem solved! This isn't a perfect solution by any means (in fact it's really only half implemented – I still need to add the relationship the other way from Groups to Users!) but it serves the purpose I need it to for now. If you have a better way of implementing the many-to-many relationship please post it in the comments below!