HasMany.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-data-association-HasMany'>/**
  19. </span> * @author Ed Spencer
  20. * @class Ext.data.association.HasMany
  21. *
  22. * &lt;p&gt;Represents a one-to-many relationship between two models. Usually created indirectly via a model definition:&lt;/p&gt;
  23. *
  24. &lt;pre&gt;&lt;code&gt;
  25. Ext.define('Product', {
  26. extend: 'Ext.data.Model',
  27. fields: [
  28. {name: 'id', type: 'int'},
  29. {name: 'user_id', type: 'int'},
  30. {name: 'name', type: 'string'}
  31. ]
  32. });
  33. Ext.define('User', {
  34. extend: 'Ext.data.Model',
  35. fields: [
  36. {name: 'id', type: 'int'},
  37. {name: 'name', type: 'string'}
  38. ],
  39. // we can use the hasMany shortcut on the model to create a hasMany association
  40. hasMany: {model: 'Product', name: 'products'}
  41. });
  42. &lt;/pre&gt;&lt;/code&gt;
  43. *
  44. * &lt;p&gt;Above we created Product and User models, and linked them by saying that a User hasMany Products. This gives
  45. * us a new function on every User instance, in this case the function is called 'products' because that is the name
  46. * we specified in the association configuration above.&lt;/p&gt;
  47. *
  48. * &lt;p&gt;This new function returns a specialized {@link Ext.data.Store Store} which is automatically filtered to load
  49. * only Products for the given model instance:&lt;/p&gt;
  50. *
  51. &lt;pre&gt;&lt;code&gt;
  52. //first, we load up a User with id of 1
  53. var user = Ext.create('User', {id: 1, name: 'Ed'});
  54. //the user.products function was created automatically by the association and returns a {@link Ext.data.Store Store}
  55. //the created store is automatically scoped to the set of Products for the User with id of 1
  56. var products = user.products();
  57. //we still have all of the usual Store functions, for example it's easy to add a Product for this User
  58. products.add({
  59. name: 'Another Product'
  60. });
  61. //saves the changes to the store - this automatically sets the new Product's user_id to 1 before saving
  62. products.sync();
  63. &lt;/code&gt;&lt;/pre&gt;
  64. *
  65. * &lt;p&gt;The new Store is only instantiated the first time you call products() to conserve memory and processing time,
  66. * though calling products() a second time returns the same store instance.&lt;/p&gt;
  67. *
  68. * &lt;p&gt;&lt;u&gt;Custom filtering&lt;/u&gt;&lt;/p&gt;
  69. *
  70. * &lt;p&gt;The Store is automatically furnished with a filter - by default this filter tells the store to only return
  71. * records where the associated model's foreign key matches the owner model's primary key. For example, if a User
  72. * with ID = 100 hasMany Products, the filter loads only Products with user_id == 100.&lt;/p&gt;
  73. *
  74. * &lt;p&gt;Sometimes we want to filter by another field - for example in the case of a Twitter search application we may
  75. * have models for Search and Tweet:&lt;/p&gt;
  76. *
  77. &lt;pre&gt;&lt;code&gt;
  78. Ext.define('Search', {
  79. extend: 'Ext.data.Model',
  80. fields: [
  81. 'id', 'query'
  82. ],
  83. hasMany: {
  84. model: 'Tweet',
  85. name : 'tweets',
  86. filterProperty: 'query'
  87. }
  88. });
  89. Ext.define('Tweet', {
  90. extend: 'Ext.data.Model',
  91. fields: [
  92. 'id', 'text', 'from_user'
  93. ]
  94. });
  95. //returns a Store filtered by the filterProperty
  96. var store = new Search({query: 'Sencha Touch'}).tweets();
  97. &lt;/code&gt;&lt;/pre&gt;
  98. *
  99. * &lt;p&gt;The tweets association above is filtered by the query property by setting the {@link #filterProperty}, and is
  100. * equivalent to this:&lt;/p&gt;
  101. *
  102. &lt;pre&gt;&lt;code&gt;
  103. var store = Ext.create('Ext.data.Store', {
  104. model: 'Tweet',
  105. filters: [
  106. {
  107. property: 'query',
  108. value : 'Sencha Touch'
  109. }
  110. ]
  111. });
  112. &lt;/code&gt;&lt;/pre&gt;
  113. */
  114. Ext.define('Ext.data.association.HasMany', {
  115. extend: 'Ext.data.association.Association',
  116. alternateClassName: 'Ext.data.HasManyAssociation',
  117. requires: ['Ext.util.Inflector'],
  118. alias: 'association.hasmany',
  119. <span id='Ext-data-association-HasMany-cfg-foreignKey'> /**
  120. </span> * @cfg {String} foreignKey The name of the foreign key on the associated model that links it to the owner
  121. * model. Defaults to the lowercased name of the owner model plus &quot;_id&quot;, e.g. an association with a where a
  122. * model called Group hasMany Users would create 'group_id' as the foreign key. When the remote store is loaded,
  123. * the store is automatically filtered so that only records with a matching foreign key are included in the
  124. * resulting child store. This can be overridden by specifying the {@link #filterProperty}.
  125. * &lt;pre&gt;&lt;code&gt;
  126. Ext.define('Group', {
  127. extend: 'Ext.data.Model',
  128. fields: ['id', 'name'],
  129. hasMany: 'User'
  130. });
  131. Ext.define('User', {
  132. extend: 'Ext.data.Model',
  133. fields: ['id', 'name', 'group_id'], // refers to the id of the group that this user belongs to
  134. belongsTo: 'Group'
  135. });
  136. * &lt;/code&gt;&lt;/pre&gt;
  137. */
  138. <span id='Ext-data-association-HasMany-cfg-name'> /**
  139. </span> * @cfg {String} name The name of the function to create on the owner model to retrieve the child store.
  140. * If not specified, the pluralized name of the child model is used.
  141. * &lt;pre&gt;&lt;code&gt;
  142. // This will create a users() method on any Group model instance
  143. Ext.define('Group', {
  144. extend: 'Ext.data.Model',
  145. fields: ['id', 'name'],
  146. hasMany: 'User'
  147. });
  148. var group = new Group();
  149. console.log(group.users());
  150. // The method to retrieve the users will now be getUserList
  151. Ext.define('Group', {
  152. extend: 'Ext.data.Model',
  153. fields: ['id', 'name'],
  154. hasMany: {model: 'User', name: 'getUserList'}
  155. });
  156. var group = new Group();
  157. console.log(group.getUserList());
  158. * &lt;/code&gt;&lt;/pre&gt;
  159. */
  160. <span id='Ext-data-association-HasMany-cfg-storeConfig'> /**
  161. </span> * @cfg {Object} storeConfig Optional configuration object that will be passed to the generated Store. Defaults to
  162. * undefined.
  163. */
  164. <span id='Ext-data-association-HasMany-cfg-filterProperty'> /**
  165. </span> * @cfg {String} filterProperty Optionally overrides the default filter that is set up on the associated Store. If
  166. * this is not set, a filter is automatically created which filters the association based on the configured
  167. * {@link #foreignKey}. See intro docs for more details. Defaults to undefined
  168. */
  169. <span id='Ext-data-association-HasMany-cfg-autoLoad'> /**
  170. </span> * @cfg {Boolean} autoLoad True to automatically load the related store from a remote source when instantiated.
  171. * Defaults to &lt;tt&gt;false&lt;/tt&gt;.
  172. */
  173. <span id='Ext-data-association-HasMany-cfg-type'> /**
  174. </span> * @cfg {String} type The type configuration can be used when creating associations using a configuration object.
  175. * Use 'hasMany' to create a HasMany association
  176. * &lt;pre&gt;&lt;code&gt;
  177. associations: [{
  178. type: 'hasMany',
  179. model: 'User'
  180. }]
  181. * &lt;/code&gt;&lt;/pre&gt;
  182. */
  183. constructor: function(config) {
  184. var me = this,
  185. ownerProto,
  186. name;
  187. me.callParent(arguments);
  188. me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase());
  189. ownerProto = me.ownerModel.prototype;
  190. name = me.name;
  191. Ext.applyIf(me, {
  192. storeName : name + &quot;Store&quot;,
  193. foreignKey: me.ownerName.toLowerCase() + &quot;_id&quot;
  194. });
  195. ownerProto[name] = me.createStore();
  196. },
  197. <span id='Ext-data-association-HasMany-method-createStore'> /**
  198. </span> * @private
  199. * Creates a function that returns an Ext.data.Store which is configured to load a set of data filtered
  200. * by the owner model's primary key - e.g. in a hasMany association where Group hasMany Users, this function
  201. * returns a Store configured to return the filtered set of a single Group's Users.
  202. * @return {Function} The store-generating function
  203. */
  204. createStore: function() {
  205. var that = this,
  206. associatedModel = that.associatedModel,
  207. storeName = that.storeName,
  208. foreignKey = that.foreignKey,
  209. primaryKey = that.primaryKey,
  210. filterProperty = that.filterProperty,
  211. autoLoad = that.autoLoad,
  212. storeConfig = that.storeConfig || {};
  213. return function() {
  214. var me = this,
  215. config, filter,
  216. modelDefaults = {};
  217. if (me[storeName] === undefined) {
  218. if (filterProperty) {
  219. filter = {
  220. property : filterProperty,
  221. value : me.get(filterProperty),
  222. exactMatch: true
  223. };
  224. } else {
  225. filter = {
  226. property : foreignKey,
  227. value : me.get(primaryKey),
  228. exactMatch: true
  229. };
  230. }
  231. modelDefaults[foreignKey] = me.get(primaryKey);
  232. config = Ext.apply({}, storeConfig, {
  233. model : associatedModel,
  234. filters : [filter],
  235. remoteFilter : false,
  236. modelDefaults: modelDefaults
  237. });
  238. me[storeName] = Ext.data.AbstractStore.create(config);
  239. if (autoLoad) {
  240. me[storeName].load();
  241. }
  242. }
  243. return me[storeName];
  244. };
  245. },
  246. <span id='Ext-data-association-HasMany-method-read'> /**
  247. </span> * Read associated data
  248. * @private
  249. * @param {Ext.data.Model} record The record we're writing to
  250. * @param {Ext.data.reader.Reader} reader The reader for the associated model
  251. * @param {Object} associationData The raw associated data
  252. */
  253. read: function(record, reader, associationData){
  254. var store = record[this.name](),
  255. inverse,
  256. items, iLen, i;
  257. store.add(reader.read(associationData).records);
  258. //now that we've added the related records to the hasMany association, set the inverse belongsTo
  259. //association on each of them if it exists
  260. inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
  261. return assoc.type === 'belongsTo' &amp;&amp; assoc.associatedName === record.$className;
  262. });
  263. //if the inverse association was found, set it now on each record we've just created
  264. if (inverse) {
  265. items = store.data.items;
  266. iLen = items.length;
  267. for (i = 0; i &lt; iLen; i++) {
  268. items[i][inverse.instanceName] = record;
  269. }
  270. }
  271. }
  272. });</pre>
  273. </body>
  274. </html>