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