Le modèle de données
Une page BWEB n'est pas un fichier. Il n'y a ni .html ni .vue à ouvrir, et chercher le fichier d'une page est la première fausse piste de quiconque arrive sur BWEB. Une page est un ensemble d'enregistrements rangés en arbre dans la base 4D. Le moteur de rendu parcourt cet arbre, le dev-panel modifie ces enregistrements, l'API les écrit. Il n'y a rien d'autre.
C'est le point le plus structurant du composant : tout ce que vous verrez ensuite — le panneau, les événements, les modèles, la mise à jour vers la production — manipule ces mêmes enregistrements.
Trois niveaux : domaine, page, bloc
Un site BWEB s'organise toujours de la même façon :
- Le domaine (BSPK_WEB_DOMAIN) : un site. Son nom de domaine, ses langues, ses paramètres généraux. Une même base 4D peut en servir plusieurs — c'est le cas de celle qui sert cette documentation.
- La page (BSPK_WEB_DOMAIN_MENU) : une entrée de menu et une URL par langue. C'est aussi elle qui porte la publication, les droits d'accès et le référencement.
- Le bloc (BSPK_WEB_CONTENT) : un élément de la page — un conteneur, un texte, un champ de saisie, une listbox. Les blocs s'emboîtent les uns dans les autres.
La page que vous lisez est donc une ligne dans BSPK_WEB_DOMAIN_MENU, et le texte que vous lisez est un bloc de type text dans BSPK_WEB_CONTENT, posé dans un bloc conteneur qui lui donne ses marges.
Un bloc, et ses champs
Un bloc tient en une poignée de champs. Les connaître évite de chercher ailleurs ce qui est forcément là.
| Champ | Rôle |
|---|---|
| uuidKey | La clé primaire. C'est elle que citent les événements et les sélecteurs de blocs. |
| type | Le type de bloc : wrapper, text, listbox, fieldText… Il y en a 28. |
| blockName | Le nom par lequel vous désignez le bloc depuis 4D et depuis le JavaScript, par exemple pour le recharger. |
| webDomainMenuUuid | La page à laquelle ce bloc appartient. |
| parentUuid | Le bloc parent direct. Vide pour un bloc de premier niveau. |
| parentCollection | Le chemin complet des ancêtres, séparés par des espaces. |
| sortOrder | La position parmi les frères. |
| properties | Le sac unique des réglages du bloc. |
| cssProperties | Le style, rangé par cible, par point de rupture et par variante. |
| events | Les actions déclenchées par les événements du bloc. |
S'y ajoutent les quatre champs d'audit — createdOn, createdBy, modifiedOn, modifiedBy — qui disent qui a touché le bloc et quand.
L'arbre : deux champs, deux métiers
parentUuid donne le parent direct : il suffit pour remonter d'un cran, et c'est lui qui fait l'emboîtement.
parentCollection donne le chemin complet des ancêtres, du plus ancien au plus proche, séparés par des espaces. Un bloc situé à trois niveaux de profondeur contient donc quelque chose comme « uuid-du-grand-parent uuid-du-parent ».
Pourquoi ce doublon ? Pour aller chercher tout un sous-arbre en une seule requête, au lieu de descendre niveau par niveau. Et pour que cette requête soit rapide, elle s'écrit avec l'opérateur % :
ds.BSPK_WEB_CONTENT.query("parentCollection % :1"; $uuidRacine)
% est l'opérateur « mot » de 4D. Comme les valeurs sont séparées par des espaces, il compare un uuid entier, exactement, et il a été mesuré environ 200 fois plus rapide que la forme avec jokers sur un banc de 30 576 lignes. La forme = "@uuid@" fonctionne aussi, mais elle balaie toute la table.
À savoir : parentCollection est un champ Texte, et 4D refuse d'y poser un index. Il n'y a donc pas d'index à ajouter — la bonne écriture de la requête est la solution.
Piège : si parentCollection est faux, le bloc disparaît de la page sans aucune erreur : le parcours de l'arbre ne l'atteint simplement jamais. Quand vous créez des blocs par programme, laissez le serveur calculer ce champ plutôt que de l'écrire vous-même.
L'ordre des blocs se règle en décimales
sortOrder est un réel, pas un entier. Pour insérer un bloc entre les positions 3 et 4, écrivez 3,5 : il n'y a rien à renuméroter derrière. C'est un détail, mais il change la façon dont on réorganise une page par programme.
Ce que la V21 a supprimé
Jusqu'à la V20, les réglages d'un bloc étaient répartis dans quatre objets : objectProperties, blockProperties, htmlProperties et constraints. La frontière entre eux ne voulait pas dire grand-chose — la même propriété se retrouvait dans deux sacs différents selon le code qui l'avait écrite.
La V21 les a fusionnés en un seul champ, properties. Les quatre anciens champs n'existent plus dans la table.
| Avant (V20) | Aujourd'hui (V21) |
|---|---|
| objectProperties | properties |
| blockProperties | properties |
| htmlProperties | properties, avec le drapeau htmlAttr dans le schéma |
| constraints | properties, avec le drapeau securityConstraint |
| vt_classNameElement_<cible> | cssProperties, dans la cible correspondante |
Piège : un exemple de code V20 qui écrit dans objectProperties n'écrit nulle part, et le lire ne renvoie pas une erreur mais undefined. C'est la façon la plus courante de perdre un après-midi sur une documentation périmée. À l'import, un ancien contenu est fusionné à la volée dans properties ; à l'export, seule la forme V21 est écrite.
La suite
Le champ properties n'accepte pas n'importe quoi : chaque type de bloc déclare ce qu'il attend, et une validation refuse le reste. C'est l'objet de la page Le contrat properties. Pour le style, voyez Le style : le cube CSS et Tailwind.

