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