|
379 | 379 | return type; |
380 | 380 | }, |
381 | 381 |
|
| 382 | + /** |
| 383 | + * Create a store collection to hold a certain type of model |
| 384 | + * @param type |
| 385 | + * @return {Backbone.Collection} |
| 386 | + * @private |
| 387 | + */ |
382 | 388 | _createCollection: function( type ) { |
383 | 389 | var coll; |
384 | 390 |
|
|
390 | 396 | // Type should inherit from Backbone.RelationalModel. |
391 | 397 | if ( type.prototype instanceof Backbone.RelationalModel ) { |
392 | 398 | coll = new Backbone.Collection(); |
| 399 | + coll.cid = _.uniqueId( 'c' ); |
393 | 400 | coll.model = type; |
394 | 401 |
|
395 | 402 | this._collections.push( coll ); |
|
624 | 631 | } |
625 | 632 |
|
626 | 633 | this.setKeyContents( this.instance.get( contentKey ) ); |
| 634 | + |
627 | 635 | this.relatedCollection = Backbone.Relational.store.getCollection( this.relatedModel ); |
628 | 636 |
|
629 | 637 | // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'. |
|
634 | 642 | // Add this Relation to instance._relations |
635 | 643 | this.instance._relations[ this.key ] = this; |
636 | 644 |
|
| 645 | + var reverseIndexKey = Backbone.Relational.store.getCollection( this.model ).cid + ":" + this.key; |
| 646 | + if ( this.relatedCollection._reverseIndexes == null ) { |
| 647 | + this.relatedCollection._reverseIndexes = {}; |
| 648 | + } |
| 649 | + |
| 650 | + this.reverseIndex = this.relatedCollection._reverseIndexes[ reverseIndexKey ]; |
| 651 | + |
| 652 | + if ( this.reverseIndex == null ) { |
| 653 | + this.reverseIndex = this.relatedCollection._reverseIndexes[ reverseIndexKey ] = {}; |
| 654 | + this.relatedCollection.on( 'relational:add relational:change:id', function( model, coll, opts ) { |
| 655 | + var id = model.get( model.idAttribute ); |
| 656 | + if ( id != null ) { |
| 657 | + _.each( this.reverseIndex[ id ], function( obj ) { |
| 658 | + obj._relations[ this.key ].tryAddRelated( model, coll, opts ); |
| 659 | + }, this ); |
| 660 | + } |
| 661 | + }, this ); |
| 662 | + } |
| 663 | + |
637 | 664 | this.initialize( opts ); |
638 | 665 |
|
639 | 666 | if ( this.options.autoFetch ) { |
|
642 | 669 |
|
643 | 670 | // When 'relatedModel' are created or destroyed, check if it affects this relation. |
644 | 671 | this.listenTo( this.instance, 'destroy', this.destroy ) |
645 | | - .listenTo( this.relatedCollection, 'relational:add relational:change:id', this.tryAddRelated ) |
646 | 672 | .listenTo( this.relatedCollection, 'relational:remove', this.removeRelated ); |
647 | 673 | } |
648 | 674 | }; |
|
784 | 810 | var related = this.findRelated( opts ); |
785 | 811 | this.setRelated( related ); |
786 | 812 |
|
| 813 | + if ( this.keyId !== null ) { |
| 814 | + var index = this.reverseIndex[this.keyId] || (this.reverseIndex[this.keyId] = {}); |
| 815 | + index[this.instance.cid] = this.instance; |
| 816 | + } |
| 817 | + |
787 | 818 | // Notify new 'related' object of the new relation. |
788 | 819 | _.each( this.getReverseRelations(), function( relation ) { |
789 | 820 | relation.addRelated( this.instance, opts ); |
|
847 | 878 | this.setKeyContents( attr ); |
848 | 879 | var related = this.findRelated( options ); |
849 | 880 | this.setRelated( related ); |
| 881 | + |
| 882 | + var id = this.related != null ? this.related.get(this.related.idAttribute) : null; |
| 883 | + |
| 884 | + if ( this.reverseIndex != null && this.related != null && id != null ) { |
| 885 | + var reverseIndex = this.reverseIndex[id] || (this.reverseIndex[id] = {}); |
| 886 | + reverseIndex[this.instance.cid] = this.instance; |
| 887 | + } |
850 | 888 | } |
851 | 889 |
|
852 | 890 | // Notify old 'related' object of the terminated relation |
853 | 891 | if ( oldRelated && this.related !== oldRelated ) { |
854 | 892 | _.each( this.getReverseRelations( oldRelated ), function( relation ) { |
| 893 | + var id = this.instance.get( this.instance.idAttribute ); |
| 894 | + if ( this.reverseIndex != null && id != null ) { |
| 895 | + var reverseIndex = relation.reverseIndex[id] || |
| 896 | + (relation.reverseIndex[id] = {}); |
| 897 | + delete reverseIndex[relation.instance.cid]; |
| 898 | + } |
855 | 899 | relation.removeRelated( this.instance, null, options ); |
856 | 900 | }, this ); |
857 | 901 | } |
|
860 | 904 | // that can be necessary for bi-directional relations if 'this.instance' was created after 'this.related'. |
861 | 905 | // In that case, 'this.instance' will already know 'this.related', but the reverse might not exist yet. |
862 | 906 | _.each( this.getReverseRelations(), function( relation ) { |
863 | | - relation.addRelated( this.instance, options ); |
| 907 | + var id = this.instance.get( this.instance.idAttribute ); |
| 908 | + if ( id != null && relation.reverseIndex != null ) { |
| 909 | + var reverseIndex = relation.reverseIndex[id] || (relation.reverseIndex[id] = {}); |
| 910 | + reverseIndex[relation.instance.cid] = this.instance; |
| 911 | + } |
| 912 | + relation.addRelated( this.instance, options ); |
864 | 913 | }, this ); |
865 | 914 |
|
866 | 915 | // Fire the 'change:<key>' event if 'related' was updated |
|
936 | 985 | throw new Error( '`collectionType` must inherit from Backbone.Collection' ); |
937 | 986 | } |
938 | 987 |
|
| 988 | + _.each( this.keyIds, function( id ) { |
| 989 | + var index = this.reverseIndex[id] || (this.reverseIndex[id] = {}); |
| 990 | + index[this.instance.cid] = this.instance; |
| 991 | + }, this ); |
| 992 | + |
939 | 993 | var related = this.findRelated( opts ); |
940 | 994 | this.setRelated( related ); |
941 | 995 | }, |
|
1061 | 1115 | */ |
1062 | 1116 | onChange: function( model, attr, options ) { |
1063 | 1117 | options = options ? _.clone( options ) : {}; |
| 1118 | + |
| 1119 | + if ( this.reverseIndex !== null ) { |
| 1120 | + _.each( this.keyIds, function( keyId ) { |
| 1121 | + var index = this.reverseIndex[keyId]; |
| 1122 | + delete index[this.instance.cid]; |
| 1123 | + }, this ); |
| 1124 | + } |
| 1125 | + |
1064 | 1126 | this.setKeyContents( attr ); |
1065 | 1127 | this.changed = false; |
1066 | 1128 |
|
1067 | 1129 | var related = this.findRelated( options ); |
1068 | 1130 | this.setRelated( related ); |
1069 | 1131 |
|
| 1132 | + if ( this.reverseIndex !== null && this.keyIds !== null ) { |
| 1133 | + _.each( this.keyIds, function( keyId ) { |
| 1134 | + var index = this.reverseIndex[keyId] || (this.reverseIndex[keyId] = {}); |
| 1135 | + index[this.instance.cid] = this.instance; |
| 1136 | + }, this ); |
| 1137 | + } |
| 1138 | + |
1070 | 1139 | if ( !options.silent ) { |
1071 | 1140 | var dit = this; |
1072 | 1141 | Backbone.Relational.eventQueue.add( function() { |
|
0 commit comments