javascript - Implementar una relación de muchos a muchos con Backbone-Relational
backbone.js knockback.js (2)
Tengo una aplicación simple que define dos clases, una Person
y un PersonGroup
, en donde hay una relación de muchos a muchos. Una Persona no puede tener ningún grupo, ni ser asignada a todos los grupos, ni nada entre ellos.
El ejemplo de backbonerelational.org sugiere usar un modelo intermedio para las relaciones de muchos a muchos, sin embargo, no puedo lograr que este patrón funcione con la extracción (deserialización) y el almacenamiento (serialización).
Lo que quiero hacer es utilizar Backbone para deserializar un JSON similar al siguiente:
{
People:
[
{
"ID": 1,
"Name": "John"
},
{
"ID": 2,
"Name": "Bob"
},
{
"ID": 3,
"Name": "Tim"
},
],
PeopleGroups:
[
{
"ID": 1,
"Name": "Owners",
"People":
[
1,
2
],
},
{
"ID": 2,
"Name": "Everyone",
"People":
[
1,
2,
3
],
},
]
}
Estoy usando Knockback / Knockout para el enlace de datos, así que el problema es que necesito poder acceder a las relaciones por referencia. Una serie de ID no me sirve de nada, a menos que pueda crear Knockback.CollectionObservable para envolver la colección y resolver los ID a las referencias.
Esta pregunta tiene alrededor de dos meses, sin embargo, me gustaría compartir que me encontré con problemas similares relacionados con las relaciones de muchos a muchos al trabajar con Backbone-relational. Estos problemas llevaron a la escritura de una implementación propia de relaciones Backbone que admiten relaciones de muchos a muchos sin un modelo de vinculación entre: Backbone-JJRelational
Estoy respondiendo como uno de los autores, pero podría valer la pena revisarlo.
El JSON que publicó en su pregunta, por ejemplo, se deserializaría automáticamente de la siguiente manera:
Cada PeopleGroups
tiene un atributo People
que es un Backbone.Collection que contiene los People
mode y, por supuesto, viceversa, por lo que se puede acceder fácilmente a la relación desde ambos lados (sin un modelo de enlace).
Algunos conceptos de Backbone-JJRelational son similares a Backbone-relational (tienda de modelo global, por ejemplo).
Terminé haciendo que esto funcionara como quería. La relación Many-to-Many se mantiene en la base de datos, pero solo se puede acceder a las relaciones en mis modelos Backbone en una dirección. Decidí que los modelos Persona serían independientes y los modelos PersonGroup tendrían una colección de referencias a los modelos Persona a los que están vinculados.
Los puntos clave que hicieron que todo funcionara fueron especificar includeInJSON: "ID"
y eliminar la relación reverseRelation
. Esto todavía le permite acceder a las referencias a los modelos en JavaScript, pero se serializa y deserializa correctamente a JSON. Los modelos Persona simplemente no tienen acceso a una propiedad de navegación para los Grupos en los que se encuentran, sin embargo, pueden existir en múltiples grupos.
Simplemente asumí que el uso de una lista de ID obligaría a saltar a través de aros para resolver las referencias, pero Backbone-relational parece usar el almacén de modelo Backbone global para resolver las referencias por ID sin crear modelos duplicados. (por ej., tres grupos diferentes pueden hacer referencia a la misma Persona y solo se crea un modelo).
var Person = Backbone.RelationalModel.extend(
{
idAttribute: "ID",
});
var PersonGroup = Backbone.RelationalModel.extend(
{
idAttribute: "ID",
relations:
[
{
type: Backbone.HasMany,
key: "People",
relatedModel: "Person",
collectionType: "PersonCollection",
includeInJSON: "ID",
},
],
});
Y aquí está el resto de la plomería si ayuda a alguien. Puede definir Backbone.Collections de la siguiente manera y obtenerlos a través de solicitudes API separadas:
var PersonCollection = Backbone.Collection.extend(
{
model: Person,
url: "/api/person",
});
var PersonGroupCollection = Backbone.Collection.extend(
{
model: PersonGroup,
url: "/api/persongroup",
});
var PersonModels = new PersonCollection();
var GroupsModels = new PersonGroupCollection();
this.PersonModels.fetch();
this.GroupsModels.fetch();
this.People = kb.collectionObservable(
this.PersonModels,
{
factories:
{
"models": PersonViewModel,
},
}
);
this.PersonGroups = kb.collectionObservable(
this.GroupsModels,
{
factories:
{
"models": PersonGroupViewModel,
"models.People.models": PersonViewModel,
},
}
);
Incluí las colecciones específicas de Knockback.js para usar enlaces de Knockout.js. Solo se crea un ViewModel por modelo, por lo que el seguimiento de cambios se propaga a través de toda la aplicación.