Операции ExtJS и комплексного сохранения
ExtJS 4.1.0
Обновление 6/6/13:
Я разместил этот же вопрос на форумах Sencha, где не было много действий. Сообщение более или менее одно и то же, но я решил, что добавлю его здесь только для справки. Я все еще хочу услышать мнение других членов сообщества о том, что должно быть очень распространенным сценарием в приложении ExtJS!
http://www.sencha.com/forum/showthread.php?265358-Complex-Model-Save-Decoupling-Data-and-Updating-Related-Stores
Обновление 7/16/13 (вывод?)
Сообщение Sencha собрало очень мало дискуссий. Я решил поставить большую часть загрузки сложных операций сохранения на моем сервере приложений и лениво обновлять клиентские магазины, где это необходимо. Таким образом, я могу использовать свою собственную оболочку базы данных, чтобы охватить все транзакции, связанные с одним сложным сохранением объекта домена, чтобы гарантировать атомарность. Если сохранение нового Order
состоит из сохранения метаданных заказа, десяти новых экземпляров OrderContents
и потенциально другой информации (адреса, которые находятся в других таблицах, новый клиент, определенный во время создания заказа и т.д.), Я бы предпочел отправьте полезную нагрузку на сервер приложений, а не установите вульгарный веб-обратный вызов в клиентском коде приложения. Данные, связанные с принципом "один-к-одному" (например, Order
hasOne Address
), обновляются в обратном вызове success
операции Order.save()
. Более сложные данные, такие как содержимое Order
, лениво обрабатываются путем простого вызова contentStore.sync()
. Я чувствую, что это средство гарантировать атомарность без подавляющего числа обратных вызовов клиентов
Исходный исходный текст
Учитывая общую неудовлетворительную функциональность спасительных моделей с объединением, у меня есть все, кроме отброшенных ассоциаций моделей в моем приложении, и я полагаюсь на получение связанных данных самостоятельно. Все это хорошо и хорошо, но, к сожалению, не решает проблему фактического сохранения данных и обновления магазинов ExtJS, чтобы отразить изменения на сервере.
Возьмите, например, сохранение объекта Order
, который состоит из метаданных, а также OrderContents
, т.е. частей в порядке. Метаданные заканчиваются таблицей Order_Data
в базе данных, тогда как содержимое все заканчивается в таблице Order_Contents
, где каждая строка связана с родительским порядком через столбец order_id
.
На клиенте получение содержимого для заказа довольно легко обойтись без необходимости в ассоциациях: var contents = this.getContentsStore().query('order_id', 10).getRange()
. Однако основным недостатком является то, что это привязка записей контента, доступных в OrderContents
ExtJS Store, которые будут применяться, если бы я использовал ассоциации, не возвращенные сервером данных с "основным", объект.
При сохранении заказа я отправляю один запрос, который содержит метаданные заказа (например, дату, номер заказа, информацию поставщика и т.д.), а также массив содержимого. Эти фрагменты данных отбираются отдельно и сохраняются в соответствующих таблицах. Это имеет для меня достаточный смысл и хорошо работает.
Все будет хорошо, пока не произойдет возврат сохраненных/обновленных записей с сервера приложений. Поскольку запрос вызывается вызовом OrderObject.save()
, ничего не сообщается в хранилище OrderContents
, что новые записи доступны. Это было бы обработано автоматически, если бы я вместо этого добавлял записи в хранилище и вызывал .sync()
, но я чувствую, что это усложняет процесс сохранения, и я бы просто скорее обработал эту развязку на сервере приложений, не говоря уже о сохранении целого запроса тоже неплохо.
Есть ли лучший способ решить эту проблему? Мое текущее решение выглядит следующим образом.
var orderContentsStore = this.getOrderContentsStore();
MyOrderObject.save({
success: function(rec, op){
// New Content Records need to be added to the contents store!
orderContentsStore.add(rec.get('contents')); // Array of OrderContent Records
orderContentsStore.commitChanges(); // This is very important
}
});
При вызове commitChanges()
записи, добавленные в хранилище, считаются чистыми (не phantom, не загрязненными) и, следовательно, больше не возвращаются методом store getModifiedRecords()
; правильно, так как записи не должны передаваться серверу приложений в случае store.sync()
.
Этот подход кажется мне неаккуратным/хриплым, но я не понял лучшего решения...
Любые ввод/мысли очень ценятся!
Ответы
Ответ 1
Обновление 8/26/13. Я обнаружил, что связанные данные действительно обрабатываются Ext в обратном вызове create/update на прокси-сервере модели, но поиск этих данных был нелегким... См. мой здесь: ExtJS 4.1 - Возврат связанных данных в Model.Save() Response
Хорошо, прошло несколько месяцев с открытым вопросом, и я чувствую, что нет волшебного, удивительного решения этой проблемы.
Мое решение таково:
При сохранении сложной модели (например, модели, которая имела бы или имела бы несколько ассоциаций hasMany
), я сохраняю родительскую модель, которая включает все связанные данные (как свойство/поле модели!). а затем добавьте (сохраненные) связанные данные в обратном вызове afterSave/afterUpdate.
Возьмем, например, мою модель PurchaseOrder
, которая hasMany
Items
и hasOne
Address
. Обратите внимание, что связанные данные включены в свойства модели, так как они не будут переданы серверу, если он существует только в хранилище ассоциаций моделей.
console.log(PurchaseOrder.getData());
---
id: 0
order_num: "PO12345"
order_total: 100.95
customer_id: 1
order_address: Object
id: 0
ship_address_1: "123 Awesome Street"
ship_address_2: "Suite B"
ship_city: "Gnarlyville"
ship_state: "Vermont"
ship_zip: "05401"
...etc...
contents: Array[2]
0: Object
id: 0
sku: "BR10831"
name: "Super Cool Shiny Thing"
quantity: 5
sold_price: 84.23
1: Object
id: 0
sku: "BR10311"
name: "Moderately Fun Paddle Ball"
quantity: 1
sold_price: 1.39
Я установил Models
для PurchaseOrder.Content
и PurchaseOrder.Address
, но данные в PurchaseOrder
не являются экземпляром этих моделей, а просто данными. Опять же, это необходимо для правильной передачи сервера приложений.
Как только у меня есть объект, как описано выше, я отправляю его на сервер приложений через .save()
следующим образом:
PurchaseOrder.save({
scope: me,
success: me.afterOrderSave,
failure: function(rec,op){
console.error('Error saving Purchase Order', op);
}
});
afterOrderSave: function(record, operation){
var me = this;
switch(operation.action){
case 'create':
/**
* Add the records to the appropriate stores.
* Since these records (from the server) have an id,
* they will not be marked as dirty nor as phantoms
*/
var savedRecord = operation.getResultSet().records[0]; // has associated!
me.getOrderStore().add(savedRecord);
me.getOrderContentStore().add(savedRecord.getContents()); //association!
me.getOrderAddressStore().add(savedRecord.getAddress()); // association!
break;
case 'update':
// Locate and update records with response from server
break;
}
}
Мой сервер приложений получает PurchaseOrder
и обрабатывает данные соответственно. Я не буду вдаваться в детали, поскольку этот процесс во многом зависит от вашей собственной реализации. Моя прикладная структура свободно основана на Zend 1.11 (в основном используется Zend_Db
).
Я считаю, что это лучший подход по следующим причинам:
- Никакая беспорядочная строка различных обратных вызовов model.save() на клиенте
- Только один запрос, который очень прост в управлении
- Атоматичность легко обрабатывается на сервере приложений
- Меньше круговых поездок = меньше потенциальных проблем, о которых можно беспокоиться
- Если вы действительно чувствуете себя ленивым, метод обратного вызова
success
может просто хранить reload
.
Я дам этому ответу немного поучаствовать в обсуждении.
Спасибо за чтение!