framework/js/lib/Store.js

165 lines
4.1 KiB
JavaScript
Raw Normal View History

/**
* The `Store` class defines a local data store, and provides methods to
* retrieve data from the API.
*/
2015-04-25 20:58:39 +08:00
export default class Store {
constructor(models) {
/**
* The local data store. A tree of resource types to IDs, such that
* accessing data[type][id] will return the model for that type/ID.
*
* @type {Object}
* @protected
*/
this.data = {};
/**
* The model registry. A map of resource types to the model class that
* should be used to represent resources of that type.
*
* @type {Object}
* @public
*/
this.models = models;
2015-04-25 20:58:39 +08:00
}
/**
* Push resources contained within an API payload into the store.
*
* @param {Object} payload
* @return {Model|Model[]} The model(s) representing the resource(s) contained
* within the 'data' key of the payload.
* @public
*/
2015-04-25 20:58:39 +08:00
pushPayload(payload) {
if (payload.included) payload.included.map(this.pushObject.bind(this));
const result = payload.data instanceof Array
? payload.data.map(this.pushObject.bind(this))
: this.pushObject(payload.data);
// Attach the original payload to the model that we give back. This is
// useful to consumers as it allows them to access meta information
// associated with their request.
2015-04-25 20:58:39 +08:00
result.payload = payload;
2015-04-25 20:58:39 +08:00
return result;
}
/**
* Create a model to represent a resource object (or update an existing one),
* and push it into the store.
*
* @param {Object} data The resource object
* @return {Model|null} The model, or null if no model class has been
* registered for this resource type.
* @public
*/
2015-04-25 20:58:39 +08:00
pushObject(data) {
if (!this.models[data.type]) return null;
const type = this.data[data.type] = this.data[data.type] || {};
2015-04-25 20:58:39 +08:00
if (type[data.id]) {
type[data.id].pushData(data);
} else {
type[data.id] = this.createRecord(data.type, data);
}
2015-04-25 20:58:39 +08:00
type[data.id].exists = true;
2015-04-25 20:58:39 +08:00
return type[data.id];
}
/**
* Make a request to the API to find record(s) of a specific type.
*
* @param {String} type The resource type.
* @param {Integer|Integer[]|Object} [id] The ID(s) of the model(s) to retreive.
* Alternatively, if an object is passed, it will be handled as the
* `query` parameter.
* @param {Object} [query]
* @return {Promise}
* @public
*/
find(type, id, query = {}) {
let data = query;
let url = app.forum.attribute('apiUrl') + '/' + type;
2015-04-25 20:58:39 +08:00
if (id instanceof Array) {
url += '?ids[]=' + id.join('&ids[]=');
2015-04-25 20:58:39 +08:00
} else if (typeof id === 'object') {
data = id;
2015-04-25 20:58:39 +08:00
} else if (id) {
url += '/' + id;
2015-04-25 20:58:39 +08:00
}
2015-05-26 10:13:20 +08:00
return app.request({
2015-04-25 20:58:39 +08:00
method: 'GET',
url,
data
2015-04-25 20:58:39 +08:00
}).then(this.pushPayload.bind(this));
}
/**
* Get a record from the store by ID.
*
* @param {String} type The resource type.
* @param {Integer} id The resource ID.
* @return {Model}
* @public
*/
2015-04-25 20:58:39 +08:00
getById(type, id) {
return this.data[type] && this.data[type][id];
}
/**
* Get a record from the store by the value of a model attribute.
*
* @param {String} type The resource type.
* @param {String} key The name of the method on the model.
* @param {*} value The value of the model attribute.
* @return {Model}
* @public
*/
getBy(type, key, value) {
return this.all(type).filter(model => model[key]() === value)[0];
}
/**
* Get all loaded records of a specific type.
*
* @param {String} type
* @return {Model[]}
* @public
*/
2015-04-25 20:58:39 +08:00
all(type) {
const records = this.data[type];
return records ? Object.keys(records).map(id => records[id]) : [];
2015-04-25 20:58:39 +08:00
}
/**
* Remove the given model from the store.
*
* @param {Model} model
*/
remove(model) {
delete this.data[model.data.type][model.id()];
}
/**
* Create a new record of the given type.
*
* @param {String} type The resource type
* @param {Object} [data] Any data to initialize the model with
* @return {Model}
* @public
*/
createRecord(type, data = {}) {
2015-04-25 20:58:39 +08:00
data.type = data.type || type;
return new (this.models[type])(data, this);
}
}