Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / data / proxy / Proxy.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @author Ed Spencer
17  * @class Ext.data.proxy.Proxy
18  * 
19  * <p>Proxies are used by {@link Ext.data.Store Stores} to handle the loading and saving of {@link Ext.data.Model Model} data.
20  * Usually developers will not need to create or interact with proxies directly.</p>
21  * <p><u>Types of Proxy</u></p>
22  * 
23  * <p>There are two main types of Proxy - {@link Ext.data.proxy.Client Client} and {@link Ext.data.proxy.Server Server}. The Client proxies
24  * save their data locally and include the following subclasses:</p>
25  * 
26  * <ul style="list-style-type: disc; padding-left: 25px">
27  * <li>{@link Ext.data.proxy.LocalStorage LocalStorageProxy} - saves its data to localStorage if the browser supports it</li>
28  * <li>{@link Ext.data.proxy.SessionStorage SessionStorageProxy} - saves its data to sessionStorage if the browsers supports it</li>
29  * <li>{@link Ext.data.proxy.Memory MemoryProxy} - holds data in memory only, any data is lost when the page is refreshed</li>
30  * </ul>
31  * 
32  * <p>The Server proxies save their data by sending requests to some remote server. These proxies include:</p>
33  * 
34  * <ul style="list-style-type: disc; padding-left: 25px">
35  * <li>{@link Ext.data.proxy.Ajax Ajax} - sends requests to a server on the same domain</li>
36  * <li>{@link Ext.data.proxy.JsonP JsonP} - uses JSON-P to send requests to a server on a different domain</li>
37  * <li>{@link Ext.data.proxy.Direct Direct} - uses {@link Ext.direct} to send requests</li>
38  * </ul>
39  * 
40  * <p>Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four operations 
41  * are mapped to the methods {@link #create}, {@link #read}, {@link #update} and {@link #destroy} respectively. Each Proxy subclass 
42  * implements these functions.</p>
43  * 
44  * <p>The CRUD methods each expect an {@link Ext.data.Operation Operation} object as the sole argument. The Operation encapsulates 
45  * information about the action the Store wishes to perform, the {@link Ext.data.Model model} instances that are to be modified, etc.
46  * See the {@link Ext.data.Operation Operation} documentation for more details. Each CRUD method also accepts a callback function to be 
47  * called asynchronously on completion.</p>
48  * 
49  * <p>Proxies also support batching of Operations via a {@link Ext.data.Batch batch} object, invoked by the {@link #batch} method.</p>
50  * 
51  */
52 Ext.define('Ext.data.proxy.Proxy', {
53     alias: 'proxy.proxy',
54     alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],
55     requires: [
56         'Ext.data.reader.Json',
57         'Ext.data.writer.Json'
58     ],
59     uses: [
60         'Ext.data.Batch', 
61         'Ext.data.Operation', 
62         'Ext.data.Model'
63     ],
64     mixins: {
65         observable: 'Ext.util.Observable'
66     },
67     
68     /**
69      * @cfg {String} batchOrder
70      * Comma-separated ordering 'create', 'update' and 'destroy' actions when batching. Override this
71      * to set a different order for the batched CRUD actions to be executed in. Defaults to 'create,update,destroy'
72      */
73     batchOrder: 'create,update,destroy',
74     
75     /**
76      * @cfg {Boolean} batchActions True to batch actions of a particular type when synchronizing the store.
77      * Defaults to <tt>true</tt>.
78      */
79     batchActions: true,
80     
81     /**
82      * @cfg {String} defaultReaderType The default registered reader type. Defaults to 'json'
83      * @private
84      */
85     defaultReaderType: 'json',
86     
87     /**
88      * @cfg {String} defaultWriterType The default registered writer type. Defaults to 'json'
89      * @private
90      */
91     defaultWriterType: 'json',
92     
93     /**
94      * @cfg {String/Ext.data.Model} model The name of the Model to tie to this Proxy. Can be either the string name of
95      * the Model, or a reference to the Model constructor. Required.
96      */
97     
98     isProxy: true,
99     
100     /**
101      * Creates the Proxy
102      * @param {Object} config (optional) Config object.
103      */
104     constructor: function(config) {
105         config = config || {};
106         
107         if (config.model === undefined) {
108             delete config.model;
109         }
110
111         this.mixins.observable.constructor.call(this, config);
112         
113         if (this.model !== undefined && !(this.model instanceof Ext.data.Model)) {
114             this.setModel(this.model);
115         }
116     },
117     
118     /**
119      * Sets the model associated with this proxy. This will only usually be called by a Store
120      * @param {String|Ext.data.Model} model The new model. Can be either the model name string,
121      * or a reference to the model's constructor
122      * @param {Boolean} setOnStore Sets the new model on the associated Store, if one is present
123      */
124     setModel: function(model, setOnStore) {
125         this.model = Ext.ModelManager.getModel(model);
126         
127         var reader = this.reader,
128             writer = this.writer;
129         
130         this.setReader(reader);
131         this.setWriter(writer);
132         
133         if (setOnStore && this.store) {
134             this.store.setModel(this.model);
135         }
136     },
137     
138     /**
139      * Returns the model attached to this Proxy
140      * @return {Ext.data.Model} The model
141      */
142     getModel: function() {
143         return this.model;
144     },
145     
146     /**
147      * Sets the Proxy's Reader by string, config object or Reader instance
148      * @param {String|Object|Ext.data.reader.Reader} reader The new Reader, which can be either a type string, a configuration object
149      * or an Ext.data.reader.Reader instance
150      * @return {Ext.data.reader.Reader} The attached Reader object
151      */
152     setReader: function(reader) {
153         var me = this;
154         
155         if (reader === undefined || typeof reader == 'string') {
156             reader = {
157                 type: reader
158             };
159         }
160
161         if (reader.isReader) {
162             reader.setModel(me.model);
163         } else {
164             Ext.applyIf(reader, {
165                 proxy: me,
166                 model: me.model,
167                 type : me.defaultReaderType
168             });
169
170             reader = Ext.createByAlias('reader.' + reader.type, reader);
171         }
172         
173         me.reader = reader;
174         return me.reader;
175     },
176     
177     /**
178      * Returns the reader currently attached to this proxy instance
179      * @return {Ext.data.reader.Reader} The Reader instance
180      */
181     getReader: function() {
182         return this.reader;
183     },
184     
185     /**
186      * Sets the Proxy's Writer by string, config object or Writer instance
187      * @param {String|Object|Ext.data.writer.Writer} writer The new Writer, which can be either a type string, a configuration object
188      * or an Ext.data.writer.Writer instance
189      * @return {Ext.data.writer.Writer} The attached Writer object
190      */
191     setWriter: function(writer) {
192         if (writer === undefined || typeof writer == 'string') {
193             writer = {
194                 type: writer
195             };
196         }
197
198         if (!(writer instanceof Ext.data.writer.Writer)) {
199             Ext.applyIf(writer, {
200                 model: this.model,
201                 type : this.defaultWriterType
202             });
203
204             writer = Ext.createByAlias('writer.' + writer.type, writer);
205         }
206         
207         this.writer = writer;
208         
209         return this.writer;
210     },
211     
212     /**
213      * Returns the writer currently attached to this proxy instance
214      * @return {Ext.data.writer.Writer} The Writer instance
215      */
216     getWriter: function() {
217         return this.writer;
218     },
219     
220     /**
221      * Performs the given create operation.
222      * @param {Ext.data.Operation} operation The Operation to perform
223      * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
224      * @param {Object} scope Scope to execute the callback function in
225      * @method
226      */
227     create: Ext.emptyFn,
228     
229     /**
230      * Performs the given read operation.
231      * @param {Ext.data.Operation} operation The Operation to perform
232      * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
233      * @param {Object} scope Scope to execute the callback function in
234      * @method
235      */
236     read: Ext.emptyFn,
237     
238     /**
239      * Performs the given update operation.
240      * @param {Ext.data.Operation} operation The Operation to perform
241      * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
242      * @param {Object} scope Scope to execute the callback function in
243      * @method
244      */
245     update: Ext.emptyFn,
246     
247     /**
248      * Performs the given destroy operation.
249      * @param {Ext.data.Operation} operation The Operation to perform
250      * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
251      * @param {Object} scope Scope to execute the callback function in
252      * @method
253      */
254     destroy: Ext.emptyFn,
255     
256     /**
257      * Performs a batch of {@link Ext.data.Operation Operations}, in the order specified by {@link #batchOrder}. Used internally by
258      * {@link Ext.data.Store}'s {@link Ext.data.Store#sync sync} method. Example usage:
259      * <pre><code>
260      * myProxy.batch({
261      *     create : [myModel1, myModel2],
262      *     update : [myModel3],
263      *     destroy: [myModel4, myModel5]
264      * });
265      * </code></pre>
266      * Where the myModel* above are {@link Ext.data.Model Model} instances - in this case 1 and 2 are new instances and have not been 
267      * saved before, 3 has been saved previously but needs to be updated, and 4 and 5 have already been saved but should now be destroyed.
268      * @param {Object} operations Object containing the Model instances to act upon, keyed by action name
269      * @param {Object} listeners Optional listeners object passed straight through to the Batch - see {@link Ext.data.Batch}
270      * @return {Ext.data.Batch} The newly created Ext.data.Batch object
271      */
272     batch: function(operations, listeners) {
273         var me = this,
274             batch = Ext.create('Ext.data.Batch', {
275                 proxy: me,
276                 listeners: listeners || {}
277             }),
278             useBatch = me.batchActions, 
279             records;
280         
281         Ext.each(me.batchOrder.split(','), function(action) {
282             records = operations[action];
283             if (records) {
284                 if (useBatch) {
285                     batch.add(Ext.create('Ext.data.Operation', {
286                         action: action,
287                         records: records
288                     }));
289                 } else {
290                     Ext.each(records, function(record){
291                         batch.add(Ext.create('Ext.data.Operation', {
292                             action : action, 
293                             records: [record]
294                         }));
295                     });
296                 }
297             }
298         }, me);
299         
300         batch.start();
301         return batch;
302     }
303 }, function() {
304     // Ext.data.proxy.ProxyMgr.registerType('proxy', this);
305     
306     //backwards compatibility
307     Ext.data.DataProxy = this;
308     // Ext.deprecate('platform', '2.0', function() {
309     //     Ext.data.DataProxy = this;
310     // }, this);
311 });
312