Операции 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.

Я дам этому ответу немного поучаствовать в обсуждении.

Спасибо за чтение!