From faace33d75e9779869a7d4cd5d4d753ddfb33738 Mon Sep 17 00:00:00 2001 From: Dan Ribbens Date: Tue, 22 Oct 2019 18:55:08 -0400 Subject: [PATCH] first pass at flexible content fields --- demo/collections/Page.js | 7 ++++ demo/content-blocks/Quote.js | 28 +++++++++++++++ demo/content-blocks/index.js | 5 +++ demo/payload.config.js | 2 ++ src/index.js | 62 +++++++++++++++++++++++--------- src/mongoose/fieldToSchemaMap.js | 3 ++ 6 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 demo/content-blocks/Quote.js create mode 100644 demo/content-blocks/index.js diff --git a/demo/collections/Page.js b/demo/collections/Page.js index 82cdbee691..335364349a 100644 --- a/demo/collections/Page.js +++ b/demo/collections/Page.js @@ -73,6 +73,13 @@ module.exports = { } ] }, + { + name: 'flexible', + label: 'Flexible Content Blocks', + type: 'flexible', + blocks: ['quote'], + hasMany: true, + }, { label: 'Meta Information', type: 'group', diff --git a/demo/content-blocks/Quote.js b/demo/content-blocks/Quote.js new file mode 100644 index 0000000000..520a0c1c91 --- /dev/null +++ b/demo/content-blocks/Quote.js @@ -0,0 +1,28 @@ +module.exports = { + slug: 'quote', + label: 'Quote', + fields: [ + { + name: 'author', + label: 'Author', + type: 'input', + maxLength: 100, + required: true + }, + { + name: 'quote', + label: 'Description', + type: 'textarea', + height: 100, + required: true + }, + { + name: 'color', + label: 'Color', + type: 'input', + maxLength: 7, + required: true + }, + ], + timestamps: true +}; diff --git a/demo/content-blocks/index.js b/demo/content-blocks/index.js new file mode 100644 index 0000000000..bacf92c012 --- /dev/null +++ b/demo/content-blocks/index.js @@ -0,0 +1,5 @@ +const Quote = require('./Quote'); + +module.exports = { + Quote, +}; diff --git a/demo/payload.config.js b/demo/payload.config.js index 921323b578..553a72231d 100644 --- a/demo/payload.config.js +++ b/demo/payload.config.js @@ -1,5 +1,6 @@ const collections = require('./collections'); const globals = require('./globals'); +const contentBlocks = require('./content-blocks'); module.exports = { port: 3000, @@ -13,6 +14,7 @@ module.exports = { mongoURL: 'mongodb://localhost/payload', collections: collections, globals: globals, + contentBlocks: contentBlocks, localization: { locales: [ 'en', diff --git a/src/index.js b/src/index.js index daf4553e5a..3333d220a0 100644 --- a/src/index.js +++ b/src/index.js @@ -14,9 +14,9 @@ import buildQueryPlugin from './mongoose/buildQuery.plugin'; import localizationPlugin from './localization/localization.plugin'; import bindModelMiddleware from './mongoose/bindModel.middleware'; import localizationMiddleware from './localization/localization.middleware'; -import { query, create, findOne, destroy, update } from './mongoose/requestHandlers'; -import { upsert, fetch } from './mongoose/requestHandlers/globals'; -import { schemaBaseFields } from './mongoose/schemaBaseFields'; +import {query, create, findOne, destroy, update} from './mongoose/requestHandlers'; +import {upsert, fetch} from './mongoose/requestHandlers/globals'; +import {schemaBaseFields} from './mongoose/schemaBaseFields'; import fieldToSchemaMap from './mongoose/fieldToSchemaMap'; import passwordResetConfig from './auth/passwordResets/passwordReset.config'; import validateCollection from './utilities/validateCollection'; @@ -27,6 +27,7 @@ import authRoutes from './routes/auth.routes'; class Payload { collections = {}; + contentBlocks = {}; globals = {}; constructor(options) { @@ -71,12 +72,29 @@ class Payload { options.app.use(localizationMiddleware(options.config.localization)); options.app.use(options.router); + // build schema for content-blocks + const blockSchema = new mongoose.Schema({}, + {discriminatorKey: 'blockType', _id: false}); + + options.config.contentBlocks && Object.values(options.config.contentBlocks).forEach(config => { + // TODO: any kind of validation for blocks? + this.contentBlocks[config.slug] = config; + const fields = {}; + + config.fields.forEach(field => { + const fieldSchema = fieldToSchemaMap[field.type]; + if (fieldSchema) fields[field.name] = fieldSchema(field); + }); + + this.contentBlocks[config.slug] = new mongoose.Schema(fields); + }); + // TODO: Build safe config before initializing models and routes options.config.collections && Object.values(options.config.collections).forEach(config => { validateCollection(config, this.collections); this.collections[config.labels.singular] = config; - const fields = { ...schemaBaseFields }; + const fields = {...schemaBaseFields}; // authentication if (config.auth && config.auth.passwordResets) { @@ -88,12 +106,22 @@ class Payload { config.fields.push(...mediaConfig.fields); } + const flexibleSchema = {}; config.fields.forEach(field => { const fieldSchema = fieldToSchemaMap[field.type]; - if (fieldSchema) fields[field.name] = fieldSchema(field); + if (fieldSchema) fields[field.name] = fieldSchema(field, blockSchema); + if (field.type === 'flexible') { + flexibleSchema[field.name] = field; + } }); - const Schema = new mongoose.Schema(fields, { timestamps: config.timestamps }); + const Schema = new mongoose.Schema(fields, {timestamps: config.timestamps}); + + Object.values(flexibleSchema).forEach(flexible => { + flexible.blocks.forEach(blockType => { + Schema.path(flexible.name).discriminator(blockType.slug, this.contentBlocks[blockType]) + }); + }); Schema.plugin(paginate); Schema.plugin(buildQueryPlugin); @@ -120,8 +148,10 @@ class Payload { options.router.use('', authRoutes(config, model)); options.router.use('/config', - passport.authenticate(config.auth.strategy, { session: false }), - (req, res) => { res.json(options.config) }); + passport.authenticate(config.auth.strategy, {session: false}), + (req, res) => { + res.json(options.config) + }); } options.router.all(`/${config.slug}*`, @@ -158,16 +188,16 @@ class Payload { const fieldSchema = fieldToSchemaMap[field.type]; if (fieldSchema) globalFields[config.slug][field.name] = fieldSchema(field); }); - globalSchemaGroups[config.slug] = new mongoose.Schema(globalFields[config.slug], { _id : false }); - }); + globalSchemaGroups[config.slug] = new mongoose.Schema(globalFields[config.slug], {_id: false}); + }); if (options.config.globals) { - globalModel = mongoose.model( - 'global', - new mongoose.Schema({...globalSchemaGroups, timestamps: false}) - .plugin(localizationPlugin, options.config.localization) - .plugin(autopopulate) - ); + globalModel = mongoose.model( + 'global', + new mongoose.Schema({...globalSchemaGroups, timestamps: false}) + .plugin(localizationPlugin, options.config.localization) + .plugin(autopopulate) + ); } options.router.all('/globals*', diff --git a/src/mongoose/fieldToSchemaMap.js b/src/mongoose/fieldToSchemaMap.js index 4a9c55ea16..20c30ab4f9 100644 --- a/src/mongoose/fieldToSchemaMap.js +++ b/src/mongoose/fieldToSchemaMap.js @@ -49,6 +49,9 @@ const fieldToSchemaMap = { enum: field.enum, }; }, + flexible: (field, flexibleSchema) => { + return field.hasMany ? [flexibleSchema] : flexibleSchema; + }, }; export default fieldToSchemaMap;