@@ -214,55 +214,55 @@ const customServices = () => ({
214214 /**
215215 * Validate if a pattern is correctly structured.
216216 *
217- * @param {string[] } pattern - The pattern to validate.
218- * @param {string[] } allowedFieldNames - The allowed field names in the pattern.
217+ * @param {string } pattern - The pattern to validate.
218+ * @param {string[] } allowedFieldNames - The allowed fields.
219+ * @param {Schema.ContentType } contentType - The content type.
219220 * @returns {object } The validation result.
220- * @returns {boolean } object.valid - Validation boolean.
221- * @returns {string } object.message - Validation message.
222221 */
223- validatePattern : ( pattern : string , allowedFieldNames : string [ ] ) => {
224- if ( ! pattern . length ) {
222+ validatePattern : ( pattern : string , allowedFieldNames : string [ ] , contentType ?: Schema . ContentType ) : { valid : boolean , message : string } => {
223+ if ( ! pattern ) {
225224 return {
226225 valid : false ,
227226 message : 'Pattern cannot be empty' ,
228227 } ;
229228 }
230229
231- const preCharCount = pattern . split ( '[' ) . length - 1 ;
232- const postCharCount = pattern . split ( ']' ) . length - 1 ;
230+ const fields = getPluginService ( 'url-pattern' ) . getFieldsFromPattern ( pattern ) ;
231+ let valid = true ;
232+ let message = '' ;
233233
234- if ( preCharCount < 1 || postCharCount < 1 ) {
235- return {
236- valid : false ,
237- message : 'Pattern should contain at least one field' ,
238- } ;
239- }
240-
241- if ( preCharCount !== postCharCount ) {
242- return {
243- valid : false ,
244- message : 'Fields in the pattern are not escaped correctly' ,
245- } ;
246- }
247-
248- let fieldsAreAllowed = true ;
249-
250- // Pass the original `pattern` array to getFieldsFromPattern
251- getPluginService ( 'url-pattern' ) . getFieldsFromPattern ( pattern ) . forEach ( ( field ) => {
234+ fields . forEach ( ( field ) => {
235+ // Check if the field is allowed.
236+ // We strip the array index from the field name to check if it is allowed.
237+ // e.g. private_categories[0].slug -> private_categories.slug
252238 const fieldName = field . replace ( / \[ \d + \] / g, '' ) ;
253- if ( ! allowedFieldNames . includes ( fieldName ) ) fieldsAreAllowed = false ;
239+ if ( ! allowedFieldNames . includes ( fieldName ) ) {
240+ valid = false ;
241+ message = `Pattern contains forbidden fields: ${ field } ` ;
242+ }
243+
244+ // Check if the field is a ToMany relation and has an array index.
245+ if ( contentType && field . includes ( '.' ) ) {
246+ const [ relationName ] = field . split ( '.' ) ;
247+ // Strip array index to get the attribute name
248+ const attributeName = relationName . replace ( / \[ \d + \] / g, '' ) ;
249+ const attribute = contentType . attributes [ attributeName ] ;
250+
251+ if (
252+ attribute
253+ && attribute . type === 'relation'
254+ && ! attribute . relation . endsWith ( 'ToOne' )
255+ && ! relationName . includes ( '[' )
256+ ) {
257+ valid = false ;
258+ message = `The relation ${ attributeName } is a ToMany relation and must include an array index (e.g. ${ attributeName } [0]).` ;
259+ }
260+ }
254261 } ) ;
255262
256- if ( ! fieldsAreAllowed ) {
257- return {
258- valid : false ,
259- message : 'Pattern contains forbidden fields' ,
260- } ;
261- }
262-
263263 return {
264- valid : true ,
265- message : 'Valid pattern' ,
264+ valid,
265+ message,
266266 } ;
267267 } ,
268268} ) ;
0 commit comments