среда, 3 августа 2011 г.

Получение в SproutCore нескольких массивов данных одним запросом к бакэнду.

Получение нескольких массивов данных одним запросом к бакэнду.

Допустим, у нас есть приложение Blog, в котором есть модели Blog.Post и Blog.Quote. Пусть они выводятся сервером по адресу '/records.json' и в json выглядят вот так:
{posts: [{post1}, {post2}, {post3}], quotes: [{quote1}, {quote2}, {quote3}]}
Что бы уменьшить количество запросов к серверу и тем самым увеличить скорость получения данных, нам нужно немного изменить datasource приложения.
Шаг 1. Создать запрос:
Blog.QUERY_POSTS_AND_QUOTES = SC.Query.local([Blog.Post, Blog.Quote]);
В SC.Query.local можно передавать не только один тип записи, но и массив типов.
Шаг 2. Создать хэш-таблицу с ссылками на данные и типами записей:
Это шаг нужен что бы избежать огромного количества if в функции fetch()

Blog.Datasource = SC.DataSource.extend({
   urls: {
     posts_and_quotes: { url: '/records.json', records: ['posts', 'quotes'] }
   },
});

Так же нам нужно поправить шан запрос, установив ему атрибум id, соответствующий имени одного из элементов urls, в данном случае posts_and_quotes:
Blog.QUERY_POSTS_AND_QUOTES = SC.Query.local([Blog.Post,Blog.Quote]).set('id','posts_and_quotes');
Шаг 3. Пишем fetch():

fetch: function(store, query) {
   //Получаем id запроса и берём из urls ссылку для запроса в соответствии с этим id
   var id = query.get('id');
   //Если id не задан, значит запрос не должен обращаться к серверу
   if(!id) return NO;
   var url = this.urls[id]['url'],
     funcs = this.get('fetchDidComplete'),
     //Если нужна особая обработка какого нибудь запроса то в fetchDidComplete
     //нужно добавить функцию, имя которой совпадает с id (ещё одна хэш-таблица).
     //Если имя не указано, то по завершении запроса будет вызвана default функция

     func = funcs[id] || funcs['default'];
   SC.Request.getUrl(url).json().notify(this, func, store, query).send();
   return YES;
}



Шаг 4. Обрабатываем полученные данные:

fetchDidComplete: {
   'default': function(response, store, query) {
     if (SC.ok(response)) {
       //В зависимости от id берём нужные записи из полученного json
       var body = this.urls[query.get('id')]['records'];
       //Получаем тип записи
       var recordType = query.get('recordType');
       //Если тип не задан, значит их несколько и они хранятся в recordsTypes
       if (!recordType) {
       var recordTypes = query.get('recordTypes'); //Получаем типы записей
       jQuery.each(body, function(i) {
       //Загружаем в store записи разных типов. Порядок выдачи записей разных типов сервером
       //и порядок этих типов в запросе должны совпадать. Т.е.:
       //SC.Query.local[Scm.Product, Scm.Taxon] и {products: [{product1}, {product2}], taxons: [{taxon1}, {taxon2}]}

       store.loadRecords(recordTypes[i], response.get('body')[body[i]]); });
     }
     else {
       store.loadRecords(recordType, response.get('body'));
     }
     store.dataSourceDidFetchQuery(query);
     } else store.dataSourceDidErrorQuery(query, response);
   }
}


Шаг 5. Используем новый запрос в приложении:
var data = Blog.store.find(Blog.QUERY_POSTS_AND_QUOTES);
Теперь, что бы получить отдельно posts, а отдельно quotes используем scoped queries:

var posts = data.find(SC.Query.local(Blog.Post);
var quotes = data.find(SC.Query.local(Blog.Quote);

Ну и теперь можно заполнить ArrayController'ы приложения полученными данными:

Blog.postsController.set('content', posts);
Blog.quotesController.set('content', quotes);

Полностью datasource будет выглядеть так:
Blog.Datasource = SC.DataSource.extend({
urls: {
   posts_and_quotes: { url: '/records.json', records: ['posts', 'quotes'] }
},
fetch: function(store, query) {
   //Получаем id запроса и берём из urls ссылку для запроса в соответствии с этим id
   var id = query.get('id');
   //Если id не задан, значит запрос не должен обращаться к серверу
   if(!id) return NO;
   var url = this.urls[id]['url'],
     funcs = this.get('fetchDidComplete'),
     //Если нужна особая обработка какого нибудь запроса то в fetchDidComplete
     //нужно добавить функцию, имя которой совпадает с id (ещё одна хэш-таблица).
     //Если имя не указано, то по завершении запроса будет вызвана default функция
     func = funcs[id] || funcs['default'];
   SC.Request.getUrl(url).json().notify(this, func, store, query).send();
   return YES;
},
fetchDidComplete: {
   'default': function(response, store, query) {
   if (SC.ok(response)) {
     //В зависимости от id берём нужные записи из полученного json
     var body = this.urls[query.get('id')]['records'];
     //Получаем тип записи
     var recordType = query.get('recordType');
     //Если тип не задан, значит их несколько и они хранятся в recordsTypes
     if (!recordType) {
       var recordTypes = query.get('recordTypes'); //Получаем типы записей
       jQuery.each(body, function(i) {
       //Загружаем в store записи разных типов. Порядок выдачи записей разных типов сервером
       //и порядок этих типов в запросе должны совпадать. Т.е.:
       //SC.Query.local[Scm.Product, Scm.Taxon] и {products: [{product1}, {product2}], taxons: [{taxon1}, {taxon2}]}
       store.loadRecords(recordTypes[i], response.get('body')[body[i]]); });
     }
     else {
       store.loadRecords(recordType, response.get('body'));
     }
     store.dataSourceDidFetchQuery(query);
     } else store.dataSourceDidErrorQuery(query, response);
     }
   },
});

Комментариев нет: