ExtJS 4: 模型与关联和存储器

32

介绍

我在使用ExtJS的Ext.data.Model类时遇到了一个应用程序设计问题。我将尝试在这里开发一个非常常见的在线商店场景,以便您可以跟随我的思路。我真的很感谢任何关于我的想法和结论的评论!

模型

假设您想要将“每个客户都可以订购多个产品”映射到ExtJS中。从这些单词中可以确定涉及以下三个模型:CustomerOrderProduct。在这种情况下,Order是连接CustomerProduct的东西。

关联

我发现ExtJS实际上允许您使用Ext.data.HasManyAssociationExt.data.BelongsToAssociation类来指定此(Customer)1-n(Order)1-n(Product)关系。但这是人们想要的吗?您是否希望Product始终属于Order?如果您想要没有与Order有任何连接的Product列表呢?

存储

这就是它变得更加ExtJS特定的地方。在ExtJS中,您有Ext.data.Store来保存所有数据。对我来说,组织我的数据的一种自然方式是为每个模型都有一个Ext.data.Store

  1. CustomerStore
  2. OrderStore
  3. ProductStore

考虑将三个Ext.grid.Panel并排放置;每个存储一个。在第一个网格中选择客户时,在第二个网格中自动显示他的订单。在第二个网格中选择订单时,相关产品会出现在第三个网格中。

这听起来自然吗?如果不是,请发表评论!

将所有内容汇总

现在我们有三件需要汇总的事情:

  1. 模型及其
  2. 关联(hasManybelongsTo)和
  3. 数据(Store

是否可能仅从一个模型-模型关系的一侧定义关联?例如,我可以指定一个OrderhasManyProduct,但省略ProductbelongsToOrder吗?因为一个Product实际上可以属于多个Order。因此,我在下面指定了Product模型hasManyOrder

以下是ExtJS的模型:

客户

Ext.define('Customer', {
    extend   : 'Ext.data.Model',
    requires : [
        'Order',
    ],
    fields   : [
           {name : 'id',          type : 'int'},
           {name : 'lastname',    type : 'string'}
           {name : 'firstname',   type : 'string'}
    ],
    hasMany: 'Order' /* Generates a orders() method on every Customer instance */
});

订单

Ext.define('Order', {
    extend   : 'Ext.data.Model',
    fields   : [
            {name : 'id',          type : 'int'},
            {name : 'customer_id', type : 'int'}, /* refers to the customer that this order belongs to*/
            {name : 'date',        type : 'date'}
    ],
    belongsTo: 'Customer', /* Generates a getCustomer method on every Order instance */
    hasMany: 'Product' /* Generates a products() method on every Order instance */
});

产品

Ext.define('Product', {
    extend   : 'Ext.data.Model',
    fields   : [
            {name : 'id',          type : 'int'},
            {name : 'name',        type : 'string'},
            {name : 'description', type : 'string'},
            {name : 'price',       type : 'float'}
    ],
    /*
        I don't specify the relation to the "Order" model here
        because it simply doesn't belong here.

        Will it still work?
    */
    hasMany: 'Order'
});

以下是商店列表:

客户店铺

Ext.define('CustomerStore', {
    extend      : 'Ext.data.Store',
    storeId     : 'CustomerStore',
    model       : 'Customer',
    proxy   : {
        type   : 'ajax',
        url    : 'data/customers.json',
        reader : {
            type           : 'json',
            root           : 'items',
            totalProperty  : 'total'
        }
    }
});

订单存储

Ext.define('OrderStore', {
    extend      : 'Ext.data.Store',
    storeId     : 'OrderStore',
    model       : 'Order',
    proxy   : {
        type   : 'ajax',
        url    : 'data/orders.json',
        reader : {
            type           : 'json',
            root           : 'items',
            totalProperty  : 'total'
        }
    }
});

产品商店

Ext.define('ProductStore', {
    extend      : 'Ext.data.Store',
    storeId     : 'ProductStore',
    model       : 'Product',
    proxy   : {
        type   : 'ajax',
        url    : 'data/products.json',
        reader : {
            type           : 'json',
            root           : 'items',
            totalProperty  : 'total'
        }
    }
});

这里有一个与公司和其产品相关的示例(不是我写的)http://superdit.com/2011/05/23/extjs-load-grid-from-another-grid/。它使用了两个模型和两个存储,但没有定义任何关联。
提前感谢您。
-Konrad

6
你不需要指定关联,我个人也不这样做。 - JamesHalsall
4个回答

15

我最近遇到了处理Models+Associations+Stores的问题,这不是一个非常愉快的经历。这是我学到的:

假设我们有Store1、Store2、Model1、Model2、Grid1和Grid2。Grid1使用Store1,Store1使用Model1,类似地,Grid2使用Store2,Store2使用Model2。

到目前为止,一切都很顺利,数据正在加载等等。

但现在我们想向Model1添加hasMany关联。好的,我们要在Model1的配置中添加:

hasMany: {model: 'Model2', name: 'model2'}

接下来你认为可以通过以下方式,在Grid1的itemclick事件中填充Store2的数据:

Grid1.on('itemclick', function(view, item_which_uses_model1){
  //item_which_uses_model1.model2() supposed to return Model2's store
  item_which_uses_model1.model2().load();
}
你期望在Grid1的itemclick事件中填充Grid2的数据,但是没有任何反应。事实上,请求已经被发送并得到了响应,但Grid2没有被填充数据。
过了一段时间,你意识到item_which_uses_model1.model2() 不是Store2
当Model2发现它与Model1有关联时,它会创建Model1自己的存储,这个存储与Store2不相等,这并不好,因为Grid2正在使用Store2。
实际上,你可以通过一些方法来解决这个问题,例如向Grid2的配置添加:
store: new Model1().model2(),

但是如果你正在使用ExtJS mvc模式呢。Grid2不需要知道Model1的任何信息。

不建议在现实项目中使用associations,至少现在不要这样做。而是使用你的示例中展示的方法:Model+Store+Filtering。


嗨,Molecule,感谢你的回答。我已经创建了一个Github仓库来存放我的源代码。这是一个完整的工作示例,展示了我上面描述的问题,虽然过滤只在存储器的第二个加载时才起作用。这有点让人不舒服,但大致上给出了想法。这里是GitHub Repo的链接 https://github.com/kwk/ExtJS-examples - Konrad Kleine
为什么不直接使用 Grid2.reconfigure(item_which_uses_model1.model2()) ? - Neil McGuigan
@Neil,因为我的store2不是一个简单的存储,它是扩展的。如果我执行(item_which_uses_model1.model2()),它会用新的普通存储替换我的调整过的存储。 - Molecular Man
我遇到的问题完全一样。 - bgs

9
在ExtJS 4中,所有内容都能够正常工作,只是第一次很难做对,前几次也是如此。
是的,您可以避免指定子级的belongsTo关联,并仍然在Has Many关系中使用它。
在我的项目中,我们有数十个模型,其中许多具有深层次的关系,这非常好用。
关于Many-Many关系,您应该要么有:
Customer -< CustomerOrder >- Order 这是三个模型,
或者您可以在服务器上进行大量工作来假装客户HasMany订单。嵌套的json可以帮助实现。

感谢您的博客,现在加载嵌套模型不再是问题了。如果我有一个具有hasMany关联的模型,是否可以保存它呢?据我所见,当我保存父模型时,嵌套的子数据并没有被提交。请为我的回答点赞。 - bhovhannes
1
可以实现。您需要覆盖JsonWriter.getRecordData()方法,并在其中调用Ext.apply(record.data, record.getAssociatedData()); 然后返回record.data。 - Neil McGuigan
感谢@Neil。虽然覆盖也是一种选择,但我最终决定在提交之前直接向我的模型添加所需的字段(我对Ext JS开发持乐观态度:)并希望他们能在不久的将来实现嵌套模型保存)。 - bhovhannes
@bhovhannes ... 博客的链接是什么?我很想看看使用JsonWriter.getRecordData()的实现。 - Entree
1
@MacGyver,这是你需要的链接:http://extjs-tutorials.blogspot.ca/2012/05/extjs-hasmany-relationships-rules.html - Neil McGuigan
@MacGyver 我不认为我在我的博客中提到了 getRecordData()。我想我最初是在 Sencha 论坛上看到它的(大约几年前)。 - Neil McGuigan

3

我完全同意Molecular Man的观点。通过阅读ExtJS文档,它似乎更像是证明了这是一种在将树集结构分布到不同存储中时的好方法。但老实说,只有在这样的关联中有任何方式的存储引用才可以成立。据我所测试的情况来看并不是这种情况。模型关联没有这样的关系。过滤是保持与依赖于其他模型的其他网格顺序更新的最佳方法。


0
请注意您的假设:

(客户)1-n(订单)1-n(产品)

是错误的。您实际上的模型应该是:

(客户)1-n(订单)n-n(产品)

这就是为什么您在使用hasMany-belongsTo进行建模时遇到困难的原因。

对于一个客户,它有n个订单,每个订单中有n个产品。总概览中会出现n-n的关系,其中会显示每个客户n-n订单n-n产品。无论如何,每个开发人员都在等待表记录之间的交叉链接,而不是创建类型为MyRecord的嵌套对象。 - Eluny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接