Aller au contenu

Slugs

slugify transforme un titre en segment d'URL, des deux côtés, et uniqueSlug trouve un slug libre en base.

Un titre devient un segment d'URL dans les pages, les articles, les formulaires et les catégories. slugify fait cette transformation partout de la même façon, et uniqueSlug cherche en base la première variante libre.

Import

import { slugify, SLUG_PATTERN } from '@fraza/core/shared/slug';
import { uniqueSlug } from '@fraza/core/database/sql';

@fraza/core/shared/slug est neutre : un formulaire d'admin l'importe dans un fichier 'use client' pour montrer le slug pendant la saisie du titre. uniqueSlug interroge la base et vit donc dans le sous-chemin serveur @fraza/core/database/sql.

API

SignatureRetourRôle
slugify(text, { maxLength }?)stringSegment d'URL tiré d'un titre, éventuellement vide.
SLUG_PATTERNRegExpCe que slugify produit : ^[a-z0-9]+(?:-[a-z0-9]+)*$.
uniqueSlug({ table, column, base, scope, excludeId, fallback, maxLength, client })Promise<string>Premier slug libre parmi base, base-2, base-3…

slugify(text, { maxLength }?)

Minuscules, accents retirés, chaque suite d'espaces devient un tiret, tout autre caractère disparaît. Un caractère retiré soude ses voisins au lieu de les séparer : « L'été » donne lete, pas l-ete. Les ligatures que la décomposition Unicode ne sépare pas sont écrites en toutes lettres : « Œuvre » donne oeuvre, pas uvre.

slugify('Été à Montréal');                         // 'ete-a-montreal'
slugify("C'est l'œuvre");                          // 'cest-loeuvre'
slugify('Rendez-vous annuel', { maxLength: 12 }); // 'rendez-vous'
slugify('🎉');                                     // ''

maxLength coupe le résultat à la longueur de la colonne, sans laisser de tiret final. Le retour peut être vide : c'est à l'appelant de choisir un repli, ce que uniqueSlug fait avec fallback.

uniqueSlug(options)

OptionDéfautRôle
tablerequisTable qui porte les slugs.
column'slug'Colonne du slug.
baserequisSlug souhaité, déjà passé par slugify.
scope{}Colonnes dans lesquelles le slug est unique, par exemple { parent_id: 12 } ou { post_type: 'news' }. Une valeur null désigne NULL.
excludeIdnullLigne en cours de modification, comparée à la colonne id.
fallback'sans-titre'Slug utilisé quand base est vide.
maxLengthaucunLongueur de la colonne : la base est raccourcie pour que le suffixe tienne.
clientpool du coreClient d'une transaction(), pour lire dans la transaction en cours.
const base = slugify(data.slug || data.title, { maxLength: 200 });
const slug = await uniqueSlug({
  table: 'fraza_pages_page',
  base,
  scope: { parent_id: parentId },
  excludeId: id,
  fallback: 'page',
  maxLength: 200,
});

Les candidats sont vérifiés par lots de vingt avec = ANY($1) : une collision coûte une requête, pas une par suffixe. Les noms de table et de colonnes passent par safeIdentifier, les valeurs sont des paramètres liés.

Notes

uniqueSlug ne réserve rien. Deux écritures simultanées peuvent obtenir le même slug. La contrainte UNIQUE sur (portée, slug) reste la garantie : l'appelant recommence sur l'erreur 23505.

Le slug d'un contenu publié ne se recalcule pas. Changer le titre ne doit pas casser les liens déjà partagés : n'appeler slugify que si le slug est vide ou modifié explicitement.

Sur cette page