Skip to content

Commit ae5285e

Browse files
authored
Merge pull request #131 from pluginpal/feature/relational-fields
feat: relational patterns
2 parents 146cdf2 + 7fd38a5 commit ae5285e

File tree

3 files changed

+94
-36
lines changed

3 files changed

+94
-36
lines changed

.changeset/clever-planes-serve.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pluginpal/webtools-core": minor
3+
---
4+
5+
Relational URL patterns

packages/core/server/admin-api/services/query-layer-decorator.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const decorator = (service: IDecoratedService) => ({
1717
return service.create.call(this, uid, opts);
1818
}
1919

20+
// Fetch the URL pattern for this content type.
21+
const urlPattern = await getPluginService('urlPatternService').findByUid(uid);
22+
2023
// If a URL alias was created, fetch it.
2124
if (opts.data.url_alias) {
2225
urlAliasEntity = await getPluginService('urlAliasService').findOne(opts.data.url_alias);
@@ -34,7 +37,7 @@ const decorator = (service: IDecoratedService) => ({
3437
// the id, can we create the URL alias entity and can we update
3538
// the previously created entity.
3639
const newEntity = await service.create.call(this, uid, { ...opts, data: opts.data });
37-
const generatedPath = await getPluginService('urlPatternService').resolvePattern(uid, { ...newEntity, ...opts.data });
40+
const generatedPath = getPluginService('urlPatternService').resolvePattern(uid, { ...newEntity, ...opts.data }, urlPattern.pattern);
3841

3942
// If a URL alias was created and 'generated' is set to true, update the alias.
4043
if (urlAliasEntity?.generated === true) {
@@ -67,20 +70,20 @@ const decorator = (service: IDecoratedService) => ({
6770
return service.update.call(this, uid, entityId, opts);
6871
}
6972

73+
// Fetch the URL pattern for this content type.
74+
const urlPattern = await getPluginService('urlPatternService').findByUid(uid);
75+
7076
// Manually fetch the entity that's being updated.
7177
// We do this becuase not all it's data is present in opts.data.
72-
const entity = await service.findOne.call(this, uid, entityId, {
73-
populate: {
74-
url_alias: {
75-
fields: ['id', 'generated'],
76-
},
77-
},
78-
});
78+
const entity = await service.update.call(this, uid, entityId, opts);
7979

8080
// If a URL alias is allready present, fetch it.
8181
if (opts.data.url_alias) {
8282
urlAliasEntity = await getPluginService('urlAliasService').findOne(opts.data.url_alias);
83+
// @ts-ignore
8384
} else if (entity.url_alias) {
85+
// @ts-ignore
86+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
8487
urlAliasEntity = entity.url_alias;
8588
}
8689

@@ -90,7 +93,7 @@ const decorator = (service: IDecoratedService) => ({
9093
}
9194

9295
// Generate the path.
93-
const generatedPath = await getPluginService('urlPatternService').resolvePattern(uid, { ...entity, ...opts.data });
96+
const generatedPath = getPluginService('urlPatternService').resolvePattern(uid, entity, urlPattern.pattern);
9497

9598
// If a URL alias is present and 'generated' is set to true, update the alias.
9699
if (urlAliasEntity?.generated === true) {

packages/core/server/admin-api/services/url-pattern.ts

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,27 @@ export default () => ({
4242
return patternEntity;
4343
},
4444

45+
/**
46+
* FindByUid.
47+
*
48+
* @param {string} uid the uid.
49+
* @returns {void}
50+
*/
51+
findByUid: async (uid: string) => {
52+
const patterns = await getPluginService('urlPatternService').findMany({
53+
filters: {
54+
contenttype: uid,
55+
},
56+
limit: 1,
57+
});
58+
59+
if (!patterns[0]) {
60+
return null;
61+
}
62+
63+
return patterns[0];
64+
},
65+
4566
/**
4667
* FindMany.
4768
*
@@ -100,21 +121,41 @@ export default () => ({
100121
&& fieldName !== 'createdBy'
101122
&& fieldName !== 'updatedBy'
102123
) {
103-
// TODO: Relation fields.
104-
// const relation = strapi.contentTypes[field.target];
105-
106-
// if (
107-
// allowedFields.includes('id')
108-
// && !fields.includes(`${fieldName}.id`)
109-
// ) {
110-
// fields.push(`${fieldName}.id`);
111-
// }
112-
113-
// Object.entries(relation.attributes).map(([subFieldName, subField]) => {
114-
// if (subField.type === fieldType || subFieldName === fieldType) {
115-
// fields.push(`${fieldName}.${subFieldName}`);
116-
// }
117-
// });
124+
// @ts-ignore
125+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
126+
const relation = strapi.contentTypes[field.target];
127+
128+
if (
129+
allowedFields.includes('id')
130+
&& !fields.includes(`${fieldName}.id`)
131+
) {
132+
fields.push(`${fieldName}.id`);
133+
}
134+
135+
Object.entries(relation.attributes).forEach(([subFieldName, subField]) => {
136+
if (subField.type === fieldType || subFieldName === fieldType) {
137+
fields.push(`${fieldName}.${subFieldName}`);
138+
}
139+
});
140+
} else if (
141+
field.type === 'component'
142+
&& field.component
143+
&& field.repeatable !== true // TODO: implement repeatable components.
144+
) {
145+
const relation = strapi.components[field.component];
146+
147+
if (
148+
allowedFields.includes('id')
149+
&& !fields.includes(`${fieldName}.id`)
150+
) {
151+
fields.push(`${fieldName}.id`);
152+
}
153+
154+
Object.entries(relation.attributes).forEach(([subFieldName, subField]) => {
155+
if (subField.type === fieldType || subFieldName === fieldType) {
156+
fields.push(`${fieldName}.${subFieldName}`);
157+
}
158+
});
118159
}
119160
});
120161
});
@@ -146,27 +187,43 @@ export default () => ({
146187
return newFields;
147188
},
148189

190+
/**
191+
* Get all relations from a pattern.
192+
*
193+
* @param {string} pattern - The pattern.
194+
*
195+
* @returns {array} The relations.
196+
*/
197+
getRelationsFromPattern: (pattern: string) => {
198+
let fields = getPluginService('urlPatternService').getFieldsFromPattern(pattern);
199+
fields = fields.filter((field) => field.split('.').length > 1); // Filter on fields containing a dot (.)
200+
fields = fields.map((field) => field.split('.')[0]); // Extract the first part of the fields
201+
return fields;
202+
},
203+
204+
149205
/**
150206
* Resolve a pattern string from pattern to path for a single entity.
151207
*
152208
* @param {string} uid - The UID.
153209
* @param {object} entity - The entity.
210+
* @param {string} urlPattern - The URL pattern.
154211
*
155212
* @returns {string} The path.
156213
*/
157214

158-
resolvePattern: async (
215+
resolvePattern: (
159216
uid: Common.UID.ContentType,
160217
entity: { [key: string]: string | number },
161-
): Promise<string> => {
218+
urlPattern?: string,
219+
) => {
162220
const resolve = (pattern: string) => {
163221
let resolvedPattern: string = pattern;
164222
const fields = getPluginService('urlPatternService').getFieldsFromPattern(pattern);
165223

166224
fields.forEach((field) => {
167225
const relationalField = field.split('.').length > 1 ? field.split('.') : null;
168226

169-
// TODO: Relation fields.
170227
if (field === 'pluralName') {
171228
const fieldValue = strapi.contentTypes[uid].info.pluralName;
172229

@@ -191,18 +248,11 @@ export default () => ({
191248
return resolvedPattern;
192249
};
193250

194-
const patterns = await getPluginService('urlPatternService').findMany({
195-
filters: {
196-
contenttype: uid,
197-
},
198-
limit: 1,
199-
});
200-
201-
if (!patterns[0]) {
251+
if (!urlPattern) {
202252
return resolve(strapi.config.get('plugin.webtools.default_pattern'));
203253
}
204254

205-
const path = resolve(patterns[0].pattern);
255+
const path = resolve(urlPattern);
206256
return path;
207257
},
208258

0 commit comments

Comments
 (0)