diff --git a/README.md b/README.md index 53746bc..a5cfdcc 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ Getting related models is easy, just use a query parameter `include`. ```js // returns all teams with their related City model -// GET /teams?include=City +// GET /teams?include=city // results in a Sequelize query: Team.findAll({include: City}) @@ -118,12 +118,21 @@ Team.findAll({include: City}) If you want to get multiple related models, just pass multiple `include` parameters. ```js // returns all teams with their related City and Uniform models -// GET /teams?include=City&include=Uniform +// GET /teams?include[]=city&include[]=uniform // results in a Sequelize query: Team.findAll({include: [City, Uniform]}) ``` +For models that have a many-to-many relationship, you can also pass the plural version of the association. +```js +// returns all teams with their related City and Uniform models +// GET /teams?include=players + +// results in a Sequelize query: +Team.findAll({include: [Player]}) +``` + ## `limit` and `offset` queries Restricting list (`GET`) and scope queries to a restricted count can be done by passing `limit=` and/or `offset=`. diff --git a/src/crud.js b/src/crud.js index 7ac31fc..6a9211d 100644 --- a/src/crud.js +++ b/src/crud.js @@ -55,15 +55,24 @@ models: { export default (server, model, { prefix, defaultConfig: config, models: permissions }) => { const modelName = model._singular; const modelAttributes = Object.keys(model.attributes); - const modelAssociations = Object.keys(model.associations); + const associatedModelNames = Object.keys(model.associations); + const modelAssociations = [ + ...associatedModelNames, + ..._.flatMap(associatedModelNames, (associationName) => { + const { target } = model.associations[associationName]; + const { _singular, _plural, _Singular, _Plural } = target; + return [_singular, _plural, _Singular, _Plural]; + }), + ]; const attributeValidation = modelAttributes.reduce((params, attribute) => { params[attribute] = joi.any(); return params; }, {}); + const validAssociations = joi.string().valid(...modelAssociations); const associationValidation = { - include: joi.array().items(joi.string().valid(...modelAssociations)), + include: [joi.array().items(validAssociations), validAssociations], }; const scopes = Object.keys(model.options.scopes); diff --git a/src/index.js b/src/index.js index 48ddb4c..7580a15 100644 --- a/src/index.js +++ b/src/index.js @@ -32,11 +32,12 @@ const register = (server, options = {}, next) => { const { plural, singular } = model.options.name; model._plural = plural.toLowerCase(); model._singular = singular.toLowerCase(); + model._Plural = plural; + model._Singular = singular; // Join tables if (model.options.name.singular !== model.name) continue; - crud(server, model, options); for (const key of Object.keys(model.associations)) { const association = model.associations[key]; @@ -92,6 +93,13 @@ const register = (server, options = {}, next) => { } } + // build the methods for each model now that we've defined all the + // associations + Object.keys(models).forEach((modelName) => { + const model = models[modelName]; + crud(server, model, options); + }); + next(); }; diff --git a/src/utils.js b/src/utils.js index 7ca006b..bc19051 100644 --- a/src/utils.js +++ b/src/utils.js @@ -20,6 +20,13 @@ export const parseInclude = request => { const { models } = noGetDb ? request : request.getDb(); return include.map(a => { + const singluarOrPluralMatch = Object.keys(models).find((modelName) => { + const { _singular, _plural } = models[modelName]; + return _singular === a || _plural === a; + }); + + if (singluarOrPluralMatch) return models[singluarOrPluralMatch]; + if (typeof a === 'string') return models[a]; if (a && typeof a.model === 'string' && a.model.length) {