public
Description: JavaScript Application Framework - JS library only
Home | Edit | New

Many-to-Many Relationship: Relationship Object Method

by Evin Grano (etgryphon)

NOTE: Erich Ocean has informed me that there is a better way of doing this. This method still works, but you get better performance using the other method that ties the objects directly to each other. I am researching this and hopefully I will have a tutorial using the other method soon.

In this tutorial we will be making a simple application focusing mainly on the models and such for creating a many-to-many relationship. The example that we will make is products that is a part of many category groups. Let’s get started!

Create the Project:

One of the first things we will need to do is create a model for our products. Let’s construct a simple SproutCore application to show how this could work in practice. First, generate the app:

$ sc-init myApp

Create the Product Model

Now create a new model class:

$ cd myApp $ sc-gen model myApp/product

Running this command will create a fixtures directory which will contain a file we can put our test data in. Since we will not be connecting to an external data source lets open up the fixtures/product.js file and fill it up with some test data.


// ==========================================================================
// myApp.Product Fixtures
// ==========================================================================

require('core') ;

myApp.FIXTURES = myApp.FIXTURES.concat([

  // All fixture records must have a unique guid and a type matching the
  // name of your model.  See the example below.

	{
		guid: 1,
		type: 'Product',
		name: 'Sample Product'
	},
	
	{
		guid: 2,
		type: 'Product',
		name: 'Sample Electronic Product'
	},
	
	{
		guid: 3,
		type: 'Product',
		name: 'Sample Electronic Product 2'
	}
]);

We will also need to modify our model and set the properties that are going to be available. This is also where we are going to make our first connection to our relationship object:

// ==========================================================================
// myApp.Product
// ==========================================================================

require('core');

/** @class

  (Document your class here)

  @extends SC.Record
  @author AuthorName
  @version 0.1
*/
App.Products = SC.Record.extend(
/** @scope App.Products.prototype */ {

  dataSource: myApp.server,
  properties: ['name']
  categories: SC.Record.hasMany('myApp.CategoryProduct', 'product')

}) ;

Create the Category-Product Relationship Object:

Now, we have this reference to a

 myApp.CategoryProduct 
. This is our relationship object that we need to create:

Now create a new Category-Product relationship model class:

$ sc-gen model myApp/categoryProduct

Now, we will create some connections to the Category that we haven’t created yet but we will…


// ==========================================================================
// myApp.CategoryProduct Fixtures
// ==========================================================================

require('core') ;

myApp.FIXTURES = myApp.FIXTURES.concat([

  // All fixture records must have a unique guid and a type matching the
  // name of your model.  See the example below.

	{
		guid: 1,
		type: 'CategoryProduct',
		category: 1,
                product: 1,
	},
	
	{
		guid: 2,
		type: 'CategoryProduct',
		category: 1,
                product: 2,
	},
	
	{
		guid: 3,
		type: 'CategoryProduct',
		category: 1,
                product: 3,
	},

	{
		guid: 4,
		type: 'CategoryProduct',
		category: 2,
                product: 2,
	},

	{
		guid: 5,
		type: 'CategoryProduct',
		category: 2,
                product: 3,
	}
]);

We also have to change the model object to do what we want it to do…


// ==========================================================================
// myApp.CategoryProduct
// ==========================================================================

require('core');

/** @class

  (Document your class here)

  @extends SC.Record
  @author Evin Grano
  @version 0.1
*/
myApp.CategoryProduct= SC.Record.extend(
/** @scope myApp.CategoryProduct.prototype */ {

  // TODO: Add your own code here.
  properties: ['guid', 'product', 'category'],
  productType: 'myApp.Product',
  categoryType: 'myApp.Category'

}) ;

I also added a couple of proxy computed properties that allow you to get the name of the category and the name of product from the relationship. This opens great avenues to use the relationship object for general information presentation and then get to the category or product object when you need detailed information

Create the Category Model

Now, we have this reference to a

 myApp.Category 
. This is our last object of the many-to-many relationship we need to create:

Now create a new Category-Product relationship model class:

$ sc-gen model myApp/category

Now, we will create some fixtures…

// ==========================================================================
// myApp.Category Fixtures
// ==========================================================================

require('core') ;

myApp.FIXTURES = myApp.FIXTURES.concat([

  // All fixture records must have a unique guid and a type matching the
  // name of your model.  See the example below.

	{
		guid: 1,
		type: 'Category',
		name: 'All Products'
	},
	
	{
		guid: 2,
		type: 'Category',
		name: 'All Electronic Products'
	}
]);

Now we will make the change in the model code:

// ==========================================================================
// myApp.Category
// ==========================================================================

require('core');

/** @class

  (Document your class here)

  @extends SC.Record
  @author AuthorName
  @version 0.1
*/
myApp.Category= SC.Record.extend(
/** @scope myApp.Category.prototype */ {

  dataSource: myApp.server,
  properties: ['guid', 'name', 'products'],
  products: SC.Record.hasMany('myApp.CategoryProduct', 'category')

}) ;

Conclusion:

This is the basic way of creating the many-to-many relationship using the relationship object method.

Comments

Credit needs to go to jdiaz, onitunes, and endash for some of the code reuse. – Evin (etgryphon)


Anyway, this is great. How would you do a One-To-Many Relationship? Say you wanted a category to be able to have many products, but you wanted each product to only have (or belong to) one category? Would all you have to do is take this line of code categories: SC.Record.hasMany('myApp.CategoryProduct', 'product') out of the myApp.Product model file (so that way the same product cannot have many categories)?

Also, I wasn’t really sure what these two properties are for in the myApp.CategoryProduct model file:


campaignType: ‘myApp.Product’,
categoryType: ‘myApp.Category’,

First, are campaignType and categoryType names you made up (i.e. can I use different property names for my application) or are these names defined by sproutcore and that have more meaning than you give them above?
I think these properties are what you refer to as the “proxy computed properties that allow you to get the name of the category and the name of product from the relationship,” but I thought that was what the functions categoryName and productName are for. So, how would you use these two properties and why do you need them if you already have the functions?
-MattDiPasquale (MattCollegeWikis)


Matt:

campaignType is a typo from my code it has been fixed

If you wanted to do the one-to-many you don’t need the relationship object all you need is myApp.Category and myApp.Product. You would move the categoryType: 'myApp.Category', to the myApp.Product model and remove the categories: SC.Record.hasMany('myApp.CategoryProduct', 'product') from the myApp.Product. The categoyType and the productType are made up names, but they have to match the name in the Fixture or what you call it in the code. – Evin (etgryphon)


Removed unnecessary computed getters, which in any case would fail at runtime with errors and also would fail to update when their underlying properties changed due to an improper observer specification. – Erich Ocean (onitunes).


Erich:

Really? I use this code and it doesn’t fail at run time. – Evin (etgryphon)


Evin, there’s actually four separate, serious problems with the code. If you’re up for it, it might be instructive for me to break it down on a separate page so everyone can benefit. But I’d like to get permission before I dissect your code in public ;-). — Erich Ocean (onitunes)


Erich: Most certainly do! :) -Evin


See discussion of computed properties here – Erich Ocean (onitunes)

Last edited by erichocean, Tue Sep 16 05:45:08 -0700 2008
Home | Edit | New
Versions: