Skip to content

Commit

Permalink
fix(slugifyWithCount): persist slug count (#82)
Browse files Browse the repository at this point in the history
* fix(slugification): persist slug count

* fix(slugification): ensure correct count used for old plugin versions and projects with existing slugs

* fix(slug CT): do not show plugin CT in content manager

* fix(syncSlugCount): ensure we have slugs to sync for createMany

* refactor(buildSlug): use early return
  • Loading branch information
ComfortablyCoding authored Dec 8, 2022
1 parent 9c336eb commit 2d53829
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 26 deletions.
11 changes: 9 additions & 2 deletions server/bootstrap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

const { buildSettings } = require('./buildSettings');
const { setupLifecycles } = require('./setupLifecycles');
const { syncSlugCount } = require('./syncSlugCount');

module.exports = async () => {
const settings = await buildSettings();

if (settings.slugifyWithCount) {
// Ensure correct count used for old plugin versions and projects with existing slugs.
await syncSlugCount(settings);
}

module.exports = async ({ strapi }) => {
const settings = await buildSettings(strapi);
setupLifecycles(settings);
};
75 changes: 75 additions & 0 deletions server/bootstrap/syncSlugCount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const syncSlugCount = async (settings) => {
const entries = await strapi.entityService.findMany('plugin::slugify.slug', {
filters: { createdAt: { $gt: 1 } },
});

// if entries aready present we can skip sync
if (entries && entries.length) {
return;
}

strapi.log.info('[slugify] syncing slug count for registered content types');

const slugs = new Map();

// chec slugs in each reigistered model
for (const uid in settings.modelsByUID) {
if (!Object.hasOwnProperty.call(settings.modelsByUID, uid)) {
continue;
}

const model = settings.modelsByUID[uid];

// using db query to avoid the need to check if CT has draftAndPublish enabled
const modelEntries = await strapi.db.query(model.uid).findMany({
filters: { createdAt: { $gt: 1 } },
});

strapi.log.info(`[slugify] syncing slug count for ${model.uid}`);
for (const entry of modelEntries) {
const slug = entry[model.field];
if (!slug) {
continue;
}

const record = slugs.get(getNonAppendedSlug(slug));
if (!record) {
slugs.set(slug, { slug, count: 1 });
continue;
}

slugs.set(record.slug, { slug: record.slug, count: record.count + 1 });
}
strapi.log.info(`[slugify] sync for ${model.uid} completed`);
}

if (slugs.size) {
// create all required records
const createResponse = await strapi.db.query('plugin::slugify.slug').createMany({
data: [...slugs.values()],
});

strapi.log.info(
`[slugify] ${createResponse.count} out of ${slugs.size} slugs synced successfully`
);
} else {
strapi.log.info('[slugify] No syncable slugs found');
}
};

// removes any appended number from a slug/string if found
const getNonAppendedSlug = (slug) => {
const match = slug.match('[\\-]{1}[\\d]+$');

if (!match) {
return slug;
}

return slug.replace(match[0], '');
};

module.exports = {
syncSlugCount,
};
9 changes: 9 additions & 0 deletions server/content-types/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

const slugSchema = require('./slug/schema.json');

module.exports = {
slug: {
schema: slugSchema,
},
};
29 changes: 29 additions & 0 deletions server/content-types/slug/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"kind": "collectionType",
"collectionName": "slugs",
"info": {
"singularName": "slug",
"pluralName": "slugs",
"displayName": "slug"
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"pluginOptions": {
"content-manager": {
"visible": false
},
"content-type-builder": {
"visible": false
}
},
"attributes": {
"slug": {
"type": "text"
},
"count": {
"type": "integer"
}
}
}
2 changes: 2 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const bootstrap = require('./bootstrap');
const config = require('./config');
const contentTypes = require('./content-types');
const controllers = require('./controllers');
const register = require('./register');
const routes = require('./routes');
Expand All @@ -10,6 +11,7 @@ const services = require('./services');
module.exports = {
bootstrap,
config,
contentTypes,
controllers,
register,
routes,
Expand Down
40 changes: 40 additions & 0 deletions server/services/slug-service/buildSlug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const slugify = require('@sindresorhus/slugify');

const buildSlug = async (string, settings) => {
let slug = slugify(string, settings.slugifyOptions);

// slugify with count
if (!settings.slugifyWithCount) {
return slug;
}

const slugEntry = await strapi.db.query('plugin::slugify.slug').findOne({
select: ['id', 'count'],
where: { slug },
});

// if no result then count is 1 and base slug is returned
if (!slugEntry) {
await strapi.entityService.create('plugin::slugify.slug', {
data: {
slug,
count: 1,
},
});

return slug;
}

const count = slugEntry.count + 1;
await strapi.entityService.update('plugin::slugify.slug', slugEntry.id, {
data: {
count,
},
});

return `${slug}-${count}`;
};

module.exports = {
buildSlug,
};
9 changes: 3 additions & 6 deletions server/services/slug-service/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const _ = require('lodash');
const { toSlug } = require('../../utils/slugification');
const { getPluginService } = require('../../utils/getPluginService');
const { shouldUpdateSlug } = require('./shoudUpdateSlug');
const { getReferenceFieldValues } = require('./getReferenceFieldValues');
const { buildSlug } = require('./buildSlug');

module.exports = ({ strapi }) => ({
async slugify(ctx) {
Expand Down Expand Up @@ -40,13 +40,10 @@ module.exports = ({ strapi }) => ({
}

referenceFieldValues = referenceFieldValues.join(' ');
const toSlugOptions = settings.slugifyOptions;
if (settings.slugifyWithCount) {
toSlugOptions.slugifyWithCount = settings.slugifyWithCount;
}

// update slug field based on action type
const slug = toSlug(referenceFieldValues, toSlugOptions);
const slug = await buildSlug(referenceFieldValues, settings);

if (ctx.action === 'beforeCreate' || ctx.action === 'beforeUpdate') {
data[field] = slug;
} else if (ctx.action === 'afterCreate') {
Expand Down
18 changes: 0 additions & 18 deletions server/utils/slugification.js

This file was deleted.

0 comments on commit 2d53829

Please sign in to comment.