betajs/betajs-data

View on GitHub
src/modelling/associations/has_many_through_array_associations.js

Summary

Maintainability
A
40 mins
Test Coverage
Scoped.define("module:Modelling.Associations.HasManyThroughArrayAssociation", [
    "module:Modelling.Associations.HasManyAssociation",
    "base:Objs",
    "base:Types",
    "base:Functions"
], function(HasManyAssociation, Objs, Types, Functions, scoped) {
    return HasManyAssociation.extend({
        scoped: scoped
    }, function(inherited) {
        return {

            __foreignKeyArray: function() {
                return Types.is_array(this._foreign_key) ? this._foreign_key : [this._foreign_key];
            },

            __arrayMapToKeys: function(a) {
                if (!this._options.sub_key)
                    return a;
                return a.map(function(i) {
                    return i[this._options.sub_key];
                }, this);
            },

            __readForeignKey: function() {
                var result = [];
                this.__foreignKeyArray().forEach(function(fk) {
                    result = result.concat(this._model.get(fk) || []);
                }, this);
                return this.__arrayMapToKeys(result);
            },

            constructor: function() {
                inherited.constructor.apply(this, arguments);
                this._options.collectionOptions = Objs.extend({
                    secondary_ident: Functions.as_method(this._mapItemValue, this)
                }, this._options.collectionOptions);
                this.__foreignKeyArray().forEach(function(fk) {
                    this._model.on("change:" + fk, this._queryChanged, this);
                }, this);
            },

            _buildQuery: function(query, options) {
                var arr = this.__readForeignKey();
                if (this._options.map)
                    arr = arr.map(this._options.map, this._options.mapctx || this);
                return {
                    "query": Objs.extend(Objs.objectBy(
                        this._options.foreign_attr || this._foreignTable().primary_key(), Objs.objectBy(
                            "$in",
                            arr
                        )), query)
                };
            },

            _queryCollectionUpdated: function(coll) {
                if (this._options.create_virtual) {
                    this.__readForeignKey().forEach(function(key) {
                        var models = [];
                        var realModels = 0;
                        coll.iterate(function(item) {
                            if (!this._matchItem(item, key))
                                return;
                            if (item.hasId())
                                realModels = 1;
                            else
                                models.push(item);
                        }, this);
                        while (models.length + realModels > 1)
                            coll.remove(models.shift());
                        if (models.length + realModels === 0)
                            coll.add(this._options.create_virtual.call(this._options.create_virtual_ctx || this, key));
                    }, this);
                }
            },

            _mapValue: function(value) {
                if (this._options.map)
                    value = this._options.map.call(this._options.mapctx || this, value);
                return value;
            },

            _mapItemValue: function(item) {
                return this._mapValue(item.get(this._options.foreign_attr || this._foreignTable().primary_key()));
            },

            _matchItem: function(item, key) {
                return this._mapItemValue(item) === this._mapValue(key);
            },

            _remove: function(item) {
                this.__foreignKeyArray().forEach(function(fk) {
                    this._model.set(fk, this._model.get(fk).filter(function(key) {
                        return !this._matchItem(item, this._options.sub_key ? key[this._options.sub_key] : key);
                    }, this));
                }, this);
                if (this._options.create_virtual && this.collection.value() && !item.destroyed())
                    this.collection.value().remove(item);
            },

            _add: function(item) {
                if (!this.__readForeignKey().some(function(key) {
                        return this._matchItem(item, key);
                    }, this)) {
                    var fk = Types.is_array(this._foreign_key) ? this._foreign_key[0] : this._foreign_key;
                    var current = Objs.clone(this._model.get(fk) || [], 1);
                    var add = item.get(this._options.foreign_attr || this._foreignTable().primary_key());
                    current.push(this._options.sub_key ? Objs.objectBy(this._options.sub_key, add) : add);
                    if (this._options.create_virtual && this.collection.value())
                        this.collection.value().add(item);
                    this._model.set(fk, current);
                }
            }

        };
    });
});