Helma Logo
main list history

Version 4 by hannes on 17. September 2009, 15:48

Version 3 by maks on 18. February 2009, 23:31

94channel = <i>channel_id</i> &gt; Channel.idobject(Channel)
95channel.local = channel_id
96channel.foreign = id

Version 2 by hannes on 18. February 2009, 00:07

9&nbsp;  +-userObject2        +-userObject3
10&nbsp;  +-userObject3        +-userObject5
11&nbsp;  +-...                +-...
12&nbsp;  +-userObject[n]      +-userObject[n]
12&nbsp;  +-HopObject1        +-HopObject1
13&nbsp;  +-HopObject2        +-...
14&nbsp;  +-...                +-HopObject[m]
15&nbsp;  +-HopObject[m]
15We cannot simply map this special hierarchy between two HopObjects the way we are used to do with setting the appropriate type.properties for them. We are restricted to either map the user objects to one HopObject or the HopObjects to one user object (ie. building a 1:1one-to-one, resp. 1:n one-to-many relationship). That is due to the limitation of the relational table structure:<pre>+---------+-----------+structure:
16
17<pre>+---------+-----------+
30However, we are on the right track and this table leads directly to the solution of the problem if we use it as "manager" for the other two tables (which are in consequences the user objects and HopObjects inside Helma). To do that, we simply add another column containing the missing unique key to the table above:<pre>+----+---------+-----------+above:
31
32<pre>+----+---------+-----------+

Version 1 by hannes on 17. February 2009, 23:38

1This page shows how to implement many-to-many using the Helma 1 *Object-Relational Mapping* layer.
2
3Let's assume the following scenario: each user should be able to subscribe to (ie. adopt) one or more custom HopObjects (e.g. news channels, weblogs and the like).
4
5Ie. it must be possible to attach one or more custom HopObjects to each user object and, vice versa, to attach one ore more user objects to any custom HopObject.
6
7<pre><b>HopObject1</b>          <b>HopObject2</b>
8&nbsp; +-userObject1        +-userObject1
9&nbsp;  +-userObject2        +-userObject3
10&nbsp;  +-userObject3        +-userObject5
11&nbsp;  +-...                +-...
12&nbsp;  +-userObject[n]      +-userObject[n]
13
14<b>userObject1</b>          <b>userObject2</b>
15&nbsp;  +-HopObject1        +-HopObject1
16&nbsp;  +-HopObject2        +-...
17&nbsp;  +-...                +-HopObject[m]
18&nbsp;  +-HopObject[m]
19</pre>
20
21We cannot simply map this special hierarchy between two HopObjects the way we are used to do with setting the appropriate type.properties for them. We are restricted to either map the user objects to one HopObject or the HopObjects to one user object (ie. building a 1:1, resp. 1:n relationship). That is due to the limitation of the relational table structure:<pre>+---------+-----------+
22| user_id | hopobj_id |
23+---------+-----------+
24|  1      |  1        |
25+---------+-----------+
26|  2      |  1        |
27+---------+-----------+
28|  3      |  1        |
29+---------+-----------+
30|  1      |  2        |
31+---------+-----------+
32</pre>
33
34Here the trouble starts: it is not possible to assign the same <tt>user_id</tt> to another <tt>hopobj_id</tt> inside the same table because either <tt>user_id</tt> or <tt>hopobj_id</tt> has to be a unique key.
35
36However, we are on the right track and this table leads directly to the solution of the problem if we use it as "manager" for the other two tables (which are in consequences the user objects and HopObjects inside Helma). To do that, we simply add another column containing the missing unique key to the table above:<pre>+----+---------+-----------+
37| id | user_id | hopobj_id |
38+----+---------+-----------+
39| 1  |  1      |  1        |
40+----+---------+-----------+
41| 2  |  2      |  1        |
42+----+---------+-----------+
43| 3  |  3      |  1        |
44+----+---------+-----------+
45| 4  |  1      |  2        |
46+----+---------+-----------+
47| 5  |  3      |  2        |
48+----+---------+-----------+
49| 6  |  5      |  2        |
50+----+---------+-----------+
51</pre>
52
53In summary, we now have to deal with three tables: one containing the user objects, the second containing the custom HopObjects (which certainly have to be defined more precisely) and the third one above, mapping user objects and HopObjects to each other.
54
55The steps to turn the relational tables into object-oriented representations accesible by Helma are quite easy. However, yet the general type-mapping syntax appears rather tricky (but better type-mapping is just around the corner).
56
57Let's assume at this point that the HopObjects we deal with are constructed from a prototype i will refer to as <tt>Channel</tt> from now on. Ie. there is a directory called "Channel" in the application's directory as well as a database table called "db_channel" (certainly both could use the same name, but I make a difference here for transparency reasons).
58
59The user objects are derived from the standard prototype <tt>user</tt> and reside in a database table called "db_user".
60
61The "managing" object I will call <tt>Subscription</tt> from now on. Also here, we then assume that there is a prototype called "Subscription" and a db table called "db_subscription".
62
63We start off with the <tt>user</tt>'s type.properties:
64
65<blockquote><tt>_db = <i>dbconnex</i>
66_table = db_user
67_children = collection(Subscription)
68_children.local = id
69_children.foreign = <i>user_id</i>
70_id = <i>id</i>
71_name = <i>name</i>
72name = <i>name</i>
73password = <i>password</i>
74email = <i>email</i></tt></blockquote>
75
76Please note that you probably have to arrange the datasource and column names according to those in your tables (I have written them in italics).
77
78As you might have noticed, this user prototype comes along with some more properties (<tt>name</tt>, <tt>password</tt> etc.), which is certainly no necessity, however very realistic.
79
80For <tt>Channel</tt> we set-up a very basic type mapping:
81
82<blockquote><tt>_db = <i>dbconnex</i>
83_table = db_channel
84_id = <i>id</i></tt></blockquote>
85Finally, we turn to the type.properties file for <tt>Subscription</tt>:
86
87<blockquote><tt>_db = <i>dbconnex</i>
88_table = db_subscription
89_id = <i>id</i>
90channel = <i>channel_id</i> &gt; Channel.id
91user = object(user)
92user.local = <i>user_id</i>
93user.foreign = id</tt></blockquote>
94
95Et voilà, with these settings <i>n:m</i> relationships should not be a big deal anymore.
96
97Just be aware that when you want to execute a user subscription (ie. to add a <tt>Channel</tt> object to a <tt>user</tt> object in this case), you have to use the <tt>Subscription</tt> object:
98
99 function subscribe(channel) {
100  var subscr = new Subscription();
101  subscr.user = user;
102  subscr.channel = channel;
103  user.add(subscr);
104 }