From 84c8bcc45733445c523ff8373f81cf2757c3d8cc Mon Sep 17 00:00:00 2001 From: Nazar Gargol Date: Tue, 8 Sep 2020 16:15:47 +1200 Subject: [PATCH] Extracted ordering into separate bookshelf plugin refs #11729 - Having a plugin allows to reason about ordering better and gives way to further extraction away form the core. - Just like with filtering, ordering falls into similar category of having effect on knex'es query builder object extension through additional statements - ORDER BY in this case. - Because there were bugs associated with use of permittedAttributes inside of `parseOrderOption` method, introduced separate `orderAttributes` function which returns only those fields which are orderable (https://github.com/TryGhost/Ghost/issues/11729#issuecomment-685740836) --- core/server/models/base/index.js | 47 ++++++----------------------- core/server/models/plugins/index.js | 1 + core/server/models/plugins/order.js | 46 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 core/server/models/plugins/order.js diff --git a/core/server/models/base/index.js b/core/server/models/base/index.js index c8ef41b9fe..4871e07a87 100644 --- a/core/server/models/base/index.js +++ b/core/server/models/base/index.js @@ -43,6 +43,9 @@ ghostBookshelf.plugin(plugins.customQuery); // Load the Ghost filter plugin, which handles applying a 'filter' to findPage requests ghostBookshelf.plugin(plugins.filter); +// Load the Ghost filter plugin, which handles applying a 'order' to findPage requests +ghostBookshelf.plugin(plugins.order); + // Load the Ghost search plugin, which handles applying a search query to findPage requests ghostBookshelf.plugin(plugins.search); @@ -170,6 +173,11 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ return _.keys(schema.tables[this.tableName]); }, + // Ghost ordering handling, allows to order by permitted attributes by default and can be overriden on specific model level + orderAttributes: function orderAttributes() { + return this.permittedAttributes(); + }, + // When loading an instance, subclasses can specify default to fetch defaultColumnsToFetch: function defaultColumnsToFetch() { return []; @@ -912,7 +920,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ } if (options.order) { - options.order = this.parseOrderOption(options.order, options.withRelated); + options.order = itemCollection.parseOrderOption(options.order, options.withRelated); } else if (options.autoOrder) { options.orderRaw = options.autoOrder; } else if (this.orderDefaultRaw) { @@ -1165,43 +1173,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ return checkIfSlugExists(slug); }, - parseOrderOption: function (order, withRelated) { - let permittedAttributes; - let result; - let rules; - - permittedAttributes = this.prototype.permittedAttributes(); - if (withRelated && withRelated.indexOf('count.posts') > -1) { - permittedAttributes.push('count.posts'); - } - result = {}; - rules = order.split(','); - - _.each(rules, function (rule) { - let match; - let field; - let direction; - - match = /^([a-z0-9_.]+)\s+(asc|desc)$/i.exec(rule.trim()); - - // invalid order syntax - if (!match) { - return; - } - - field = match[1].toLowerCase(); - direction = match[2].toUpperCase(); - - if (permittedAttributes.indexOf(field) === -1) { - return; - } - - result[field] = direction; - }); - - return result; - }, - /** * If you want to fetch all data fast, i recommend using this function. * Bookshelf is just too slow, too much ORM overhead. diff --git a/core/server/models/plugins/index.js b/core/server/models/plugins/index.js index 565240e957..f7a12d9acc 100644 --- a/core/server/models/plugins/index.js +++ b/core/server/models/plugins/index.js @@ -1,5 +1,6 @@ module.exports = { filter: require('./filter'), + order: require('./order'), customQuery: require('./custom-query'), search: require('./search'), includeCount: require('./include-count'), diff --git a/core/server/models/plugins/order.js b/core/server/models/plugins/order.js new file mode 100644 index 0000000000..dff7da49cc --- /dev/null +++ b/core/server/models/plugins/order.js @@ -0,0 +1,46 @@ +const _ = require('lodash'); + +const order = function order(Bookshelf) { + Bookshelf.Model = Bookshelf.Model.extend({ + orderAttributes() {}, + + parseOrderOption: function (orderQueryString, withRelated) { + let orderAttributes; + let result; + let rules; + + orderAttributes = this.orderAttributes(); + if (withRelated && withRelated.indexOf('count.posts') > -1) { + orderAttributes.push('count.posts'); + } + result = {}; + rules = orderQueryString.split(','); + + _.each(rules, function (rule) { + let match; + let field; + let direction; + + match = /^([a-z0-9_.]+)\s+(asc|desc)$/i.exec(rule.trim()); + + // invalid order syntax + if (!match) { + return; + } + + field = match[1].toLowerCase(); + direction = match[2].toUpperCase(); + + if (orderAttributes.indexOf(field) === -1) { + return; + } + + result[field] = direction; + }); + + return result; + } + }); +}; + +module.exports = order;