Skip to content
This repository was archived by the owner on Sep 16, 2025. It is now read-only.

Commit 1463e67

Browse files
feat(slugification): add slugify with count (#21)
* feat(slugification): add slugifyWithCount * chore(README): add slugifyWithCount documentation
1 parent f27ce29 commit 1463e67

File tree

9 files changed

+55
-40
lines changed

9 files changed

+55
-40
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ This will listen for any record created or updated in the article content type a
6767
| contentTypes | The Content Types to add auto slugification and search findOne by slug search utility to | Object | {} | No |
6868
| contentTypes[modelName] | The model name of the content type (it is the `singularName` in the [model schema](https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#model-schema)) | String | N/A | Yes |
6969
| contentTypes[modelName]field | The name of the field to add the slug | String | N/A | Yes |
70-
| contentTypes[modelName]references | The name of the field that is used to build the slug | String | N/A | Yes |
70+
| contentTypes[modelName]references | The name of the field that is used to build the slug | String | N/A | Yes |
71+
| SlugifyWithCount | Duplicate strings will have their occurrence appended to the end of the slug | Boolean | false | No |
7172
| slugifyOptions | The options to pass the the slugify function. All options can be found in the [slugify docs](https://github.com/sindresorhus/slugify#api) | Object | {} | No |
7273

7374
## Usage

server/bootstrap.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,15 @@ module.exports = ({ strapi }) => {
88
const settingsService = getPluginService(strapi, 'settingsService');
99
const settings = settingsService.get();
1010

11-
const { contentTypes, slugifyOptions } = settings;
12-
1311
// build settings structure
14-
const models = settingsService.build(contentTypes);
12+
const normalizedSettings = settingsService.build(settings);
1513

1614
// reset plugin settings
17-
settingsService.set({
18-
models,
19-
slugifyOptions,
20-
});
15+
settingsService.set(normalizedSettings);
2116

2217
// set up lifecycles
2318
const subscribe = {
24-
models: _.map(models, (m) => m.uid),
19+
models: _.map(normalizedSettings.models, (m) => m.uid),
2520
};
2621

2722
SUPPORTED_LIFECYCLES.forEach((lifecycle) => {

server/config/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = {
77
return {
88
contentTypes: {},
99
slugifyOptions: {},
10+
slugifyWithCount: false,
1011
};
1112
},
1213
async validator(config) {

server/config/schema.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ const _ = require('lodash');
66
const pluginConfigSchema = yup.object().shape({
77
slugifyOptions: yup.object(),
88
contentTypes: yup.lazy((obj) => {
9-
// eslint-disable-next-line no-unused-vars
109
let shape = {};
11-
_.each(obj, (_, key) => {
10+
_.each(obj, (_value, key) => {
1211
shape[key] = yup.object().shape({
1312
field: yup.string().required(),
1413
references: yup.string().required(),
1514
});
1615
});
1716
return yup.object().shape(shape);
1817
}),
18+
slugifyWithCount: yup.bool(),
1919
});
2020

2121
module.exports = {
Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const _ = require('lodash');
4+
const { isValidModelField } = require('../utils/isValidModelField');
45
const { pluginId } = require('../utils/pluginId');
56

67
module.exports = ({ strapi }) => ({
@@ -10,34 +11,36 @@ module.exports = ({ strapi }) => ({
1011
set(settings) {
1112
return strapi.config.set(`plugin.${pluginId}`, settings);
1213
},
13-
build(contentTypes) {
14-
let models = {};
15-
16-
_.filter(strapi.contentTypes, (value, uid) => {
17-
const model = contentTypes[value.modelName];
14+
build(settings) {
15+
// build models
16+
settings.models = {};
17+
_.filter(strapi.contentTypes, (contentType, uid) => {
18+
const model = settings.contentTypes[contentType.modelName];
1819
if (!model) {
1920
return;
2021
}
2122

2223
// ensure provided fields are present on the model
23-
const hasField = _.get(value, ['attributes', model.field], false);
24-
const hasReference = _.get(value, ['attributes', model.references], false);
24+
const hasField = isValidModelField(model, model.field);
25+
const hasReference = isValidModelField(model, model.references);
2526
if (!hasField || !hasReference) {
2627
strapi.log.warn(
27-
`[slugify] skipping ${value.info.singularName} registration, invalid field and/or reference provided.`
28+
`[slugify] skipping ${contentType.info.singularName} registration, invalid field and/or reference provided.`
2829
);
2930
return;
3031
}
3132

3233
const data = {
3334
uid,
34-
...contentTypes[value.modelName],
35-
contentType: value,
35+
...model,
36+
contentType,
3637
};
37-
models[uid] = data;
38-
models[value.modelName] = data;
38+
settings.models[uid] = data;
39+
settings.models[contentType.modelName] = data;
3940
});
4041

41-
return models;
42+
_.omit(settings, ['contentTypes']);
43+
44+
return settings;
4245
},
4346
});

server/services/slug-service.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
const _ = require('lodash');
44
const { getPluginService } = require('../utils/getPluginService');
5-
const { stringToSlug } = require('../utils/stringToSlug');
5+
const { toSlug, toSlugWithCount } = require('../utils/slugification');
66

77
module.exports = ({ strapi }) => ({
88
slugify(ctx) {
9-
const { models, slugifyOptions } = getPluginService(strapi, 'settingsService').get();
10-
9+
const settings = getPluginService(strapi, 'settingsService').get();
1110
const { params, model: entityModel } = ctx;
12-
const model = models[entityModel.uid];
1311
const { data } = params;
1412

13+
const model = settings.models[entityModel.uid];
14+
1515
if (!data) {
1616
return;
1717
}
@@ -24,7 +24,12 @@ module.exports = ({ strapi }) => ({
2424
return;
2525
}
2626

27-
data[field] = stringToSlug(referenceFieldValue, slugifyOptions);
27+
if (settings.slugifyWithCount) {
28+
data[field] = toSlugWithCount(referenceFieldValue, settings.slugifyOptions);
29+
return;
30+
}
31+
32+
data[field] = toSlug(referenceFieldValue, settings.slugifyOptions);
2833
},
2934

3035
async findOne(uid, query) {

server/utils/isValidModelField.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict';
2+
3+
const _ = require('lodash');
4+
5+
const isValidModelField = (model, field) => _.get(model, ['attributes', field], false);
6+
7+
module.exports = {
8+
isValidModelField,
9+
};

server/utils/slugification.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
const slugify = require('@sindresorhus/slugify');
4+
const slugifyWithCount = slugify.counter();
5+
6+
const toSlug = (string, options) => slugify(string, options);
7+
const toSlugWithCount = (string, options) => slugifyWithCount(string, options);
8+
9+
module.exports = {
10+
toSlug,
11+
toSlugWithCount,
12+
};

server/utils/stringToSlug.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)