SmarMaker - Documentation
Docs» 15_training:module8-backend-api:extrafields

Extrafields et formulaires dynamiques

Les extrafields Dolibarr permettent d'ajouter des champs personnalisés aux objets métier. SmartMaker offre un système complet pour les exposer à l'application React avec génération automatique de formulaires.

Vue d'ensemble

Le système extrafields SmartMaker fonctionne en trois étapes :

1. Admin Dolibarr : Configure quels extrafields exposer (RO/RW)
              ↓
2. Mapper PHP : Lit la configuration, expose les métadonnées
              ↓
3. React : Génère dynamiquement les formulaires

1. Configuration admin

Page de configuration

SmartBoot génère une page d'administration admin/smartmaker.php qui permet de configurer les extrafields exposés à l'application.

snippet.php
// Définir les objets Dolibarr à configurer
$elementsToConfig = array(
    'fichinter' => 'Interventions',
    'societe' => 'Tiers',
    'projet_task' => 'Tâches',
);

Cette page affiche tous les extrafields de chaque objet avec deux colonnes :

Option Description
RO (Read-Only) Le champ est visible dans l'application mais non modifiable
RW (Read-Write) Le champ est visible ET modifiable

Stockage de la configuration

La configuration est stockée dans les constantes Dolibarr :

MONMODULE_SMARTMAKER_EXTRAFIELDS_RO = "champ1,champ2,champ3"
MONMODULE_SMARTMAKER_EXTRAFIELDS_RW = "champ4,champ5"

2. Mapper PHP (dmGenericObject)

Structure de base

Le mapper hérite de dmBase et utilise dmTrait pour la gestion des extrafields :

snippet.php
<?php
namespace MonModule\Api;
 
use SmartAuth\DolibarrMapping\dmBase;
use SmartAuth\DolibarrMapping\dmTrait;
 
class dmItem extends dmBase
{
    use dmTrait;
 
    // Classe Dolibarr source
    protected $parentClassName = 'Fichinter';
 
    // Element pour les extrafields (table llx_extrafields)
    protected $parentElementToUseForExtraFields = 'fichinter';
 
    // Mapping des champs standards
    protected $listOfPublishedFields = [
        'rowid'       => 'id',
        'ref'         => 'ref',
        'description' => 'description',
        'fk_statut'   => 'status',
    ];
 
    // Champs modifiables via API
    protected $writableFields = [];
}

Chargement automatique des extrafields

Le constructeur charge la configuration depuis Dolibarr :

snippet.php
public function __construct()
{
    global $db;
    $this->db = $db;
 
    // Extrafields en lecture seule
    $extRO = getDolGlobalString('MONMODULE_SMARTMAKER_EXTRAFIELDS_RO');
    if (!empty($extRO)) {
        foreach (explode(',', $extRO) as $field) {
            $field = trim($field);
            if (!empty($field)) {
                $key = 'options_' . $field;
                $this->listOfPublishedFields[$key] = $key;
            }
        }
    }
 
    // Extrafields en lecture-écriture
    $extRW = getDolGlobalString('MONMODULE_SMARTMAKER_EXTRAFIELDS_RW');
    if (!empty($extRW)) {
        foreach (explode(',', $extRW) as $field) {
            $field = trim($field);
            if (!empty($field)) {
                $key = 'options_' . $field;
                $this->listOfPublishedFields[$key] = $key;
                $this->writableFields[] = $key;
            }
        }
    }
 
    $this->boot();
}

Méthode objectDesc()

La méthode objectDesc() (fournie par dmTrait) retourne les métadonnées des champs pour le frontend :

snippet.php
// Dans le controller
$mapping = new dmItem();
$config = $mapping->objectDesc();
 
// Retourne un tableau avec les infos de chaque champ :
// [
//     'options_niveau' => [
//         'type' => 'select',
//         'label' => 'Niveau',
//         'required' => true,
//         'options' => ['1' => 'Facile', '2' => 'Moyen', '3' => 'Difficile'],
//         'writable' => true,
//     ],
//     ...
// ]

3. Controller : Exposer la configuration

Le controller doit retourner la configuration au frontend :

snippet.php
class ItemController
{
    private $mapping;
 
    public function __construct()
    {
        $this->mapping = new dmItem();
    }
 
    // GET /items (liste + config)
    public function index($arr = null)
    {
        // ... charger les items ...
 
        return [[
            'statusCode' => 200,
            'items' => $items,
            'config' => $this->mapping->objectDesc(),  // Métadonnées des champs
        ], 200];
    }
 
    // GET /items/123 (détail + config)
    public function show($arr = null)
    {
        $id = $arr['id'] ?? 0;
        // ... charger l'item ...
 
        return [[
            'statusCode' => 200,
            'item' => $this->mapping->map($dolibarrObject),
            'config' => $this->mapping->objectDesc(),
        ], 200];
    }
}

4. Frontend : Formulaires dynamiques

Stockage de la configuration

Stocker la configuration reçue de l'API dans Redux ou le state local :

snippet.jsx
// Dans le composant ou via Redux
const [config, setConfig] = useState({});
 
useEffect(() => {
    api.private.get('items')
        .then(response => {
            setConfig(response.config || {});
            // ... traiter les items ...
        });
}, []);

FormComponentsMap

SmartCommon fournit FormComponentsMap qui mappe les types Dolibarr vers les composants React :

snippet.jsx
import { FormComponentsMap } from "@cap-rel/smartcommon";
 
// FormComponentsMap retourne le composant approprié selon le type :
// 'varchar'  → Input
// 'text'     → Textarea
// 'int'      → Input type="number"
// 'double'   → Input type="number" step="0.01"
// 'date'     → DatePicker
// 'datetime' → DateTimePicker
// 'boolean'  → Checkbox
// 'select'   → Select
// 'sellist'  → Select (avec options depuis la DB)
// 'radio'    → RadioGroup
// 'checkbox' → CheckboxGroup
// 'link'     → SearchSelect (lien vers autre objet)

Génération dynamique du formulaire

snippet.jsx
const DynamicForm = ({ config, values, onChange }) => {
    return (
        <div className="flex flex-col gap-4">
            {Object.entries(config).map(([fieldName, fieldConfig]) => {
                // Récupérer le composant selon le type
                const Component = FormComponentsMap(fieldConfig.type);
 
                if (!Component) return null;
 
                return (
                    <Component
                        key={fieldName}
                        label={fieldConfig.label}
                        value={values[fieldName] || ''}
                        onChange={(val) => onChange(fieldName, val)}
                        required={fieldConfig.required}
                        disabled={!fieldConfig.writable}
                        options={fieldConfig.options}  // Pour select/radio
                    />
                );
            })}
        </div>
    );
};

Exemple complet avec useApi

snippet.jsx
import { useStates, useApi, Page, Button, Block } from "@cap-rel/smartcommon";
import { FormComponentsMap } from "@cap-rel/smartcommon";
 
export const ItemEditPage = ({ itemId }) => {
    const { states, set } = useStates({
        item: null,
        config: {},
        isLoading: true,
        isSaving: false,
    });
 
    const { item, config, isLoading, isSaving } = states;
    const api = useApi();
 
    // Charger l'item et sa configuration
    useEffect(() => {
        api.private.get(`items/${itemId}`)
            .then(response => {
                set("item", response.item);
                set("config", response.config);
            })
            .finally(() => set("isLoading", false));
    }, [itemId]);
 
    // Modifier une valeur
    const handleFieldChange = (fieldName, value) => {
        set("item", { ...item, [fieldName]: value });
    };
 
    // Sauvegarder
    const handleSave = () => {
        set("isSaving", true);
        api.private.put(`items/${itemId}`, item)
            .then(() => {
                // Succès
            })
            .finally(() => set("isSaving", false));
    };
 
    if (isLoading) return <div>Chargement...</div>;
 
    return (
        <Page>
            <Block title="Modifier l'item">
                {/* Champs standards */}
                <Input
                    label="Référence"
                    value={item?.ref || ''}
                    disabled
                />
 
                {/* Extrafields dynamiques */}
                {Object.entries(config).map(([fieldName, fieldConfig]) => {
                    // Ne montrer que les extrafields (commencent par options_)
                    if (!fieldName.startsWith('options_')) return null;
 
                    const Component = FormComponentsMap(fieldConfig.type);
                    if (!Component) return null;
 
                    return (
                        <Component
                            key={fieldName}
                            label={fieldConfig.label}
                            value={item?.[fieldName] || ''}
                            onChange={(val) => handleFieldChange(fieldName, val)}
                            disabled={!fieldConfig.writable}
                            required={fieldConfig.required}
                            options={fieldConfig.options}
                        />
                    );
                })}
 
                <Button
                    onClick={handleSave}
                    loading={isSaving}
                >
                    Enregistrer
                </Button>
            </Block>
        </Page>
    );
};

Types d'extrafields supportés

Type Dolibarr Composant React Notes
varchar Input Texte court
text Textarea Texte long
int Input (number) Entier
double Input (number) Décimal avec step
date DatePicker Date seule
datetime DateTimePicker Date et heure
boolean Checkbox Oui/Non
select Select Liste déroulante
sellist Select Liste depuis SQL
radio RadioGroup Boutons radio
checkbox CheckboxGroup Cases à cocher multiples
link SearchSelect Lien vers objet Dolibarr
price Input (number) Prix avec formatage

Bonnes pratiques

Sécurité

  • Ne jamais exposer d'extrafields sensibles (mots de passe, tokens)
  • Vérifier les permissions côté backend avant modification
  • Valider les données reçues du frontend

Performance

  • Mettre en cache la configuration si elle ne change pas souvent
  • Ne charger que les extrafields nécessaires

UX

  • Grouper les extrafields par catégorie si nombreux
  • Utiliser des labels clairs et traduits
  • Afficher les champs obligatoires distinctement

Résumé

Le système extrafields SmartMaker permet de :

  1. Configurer via l'admin Dolibarr quels champs exposer
  2. Mapper automatiquement avec le constructeur dmGenericObject
  3. Exposer les métadonnées via objectDesc()
  4. Générer dynamiquement les formulaires React avec FormComponentsMap

Ce système évite de modifier le code à chaque ajout d'extrafield : la configuration admin suffit.

← Mappers | Module 9 : Intégration →

Previous Next

Made with ❤ by CAP-REL · SmartMaker · GNU AGPL v3+
Code source · Faire un don
SmarMaker - Documentation
Traductions de cette page:
  • Français
  • Deutsch
  • English
  • Español
  • Italiano
  • Nederlands

Table of Contents

Table des matières

  • Extrafields et formulaires dynamiques
    • Vue d'ensemble
    • 1. Configuration admin
      • Page de configuration
      • Stockage de la configuration
    • 2. Mapper PHP (dmGenericObject)
      • Structure de base
      • Chargement automatique des extrafields
      • Méthode objectDesc()
    • 3. Controller : Exposer la configuration
    • 4. Frontend : Formulaires dynamiques
      • Stockage de la configuration
      • FormComponentsMap
      • Génération dynamique du formulaire
      • Exemple complet avec useApi
    • Types d'extrafields supportés
    • Bonnes pratiques
      • Sécurité
      • Performance
      • UX
    • Résumé
  • SmartAuth
  • SmartMaker - Back (PHP)
    • Mapping Dolibarr - React
  • SmartMaker - Front (React)
    • Animations de pages
    • Architecture
    • Astuces
    • Calendar
    • Composants et pages
    • Configuration du Provider
    • Debug et Logs
    • Hooks SmartCommon
    • PWA (Progressive Web App)
    • Requêtes API
    • Routage
    • SmartCommon
    • Stockage de données
    • Thèmes
    • Traductions
  • HowTo - Pas à pas - Votre première application
    • Développement PHP (back)
    • Développement React (front)
    • Première étape : Module Builder Dolibarr
    • SmartAuth
    • SmartBoot : Un "squelette" quasiment prêt à l'emploi
  • Formation SmartMaker
    • Module 1 : Fondamentaux JavaScript ES6+
      • Chapitre 1 : Variables et Scope
      • Chapitre 2 : Fonctions
      • Chapitre 3 : Programmation Asynchrone
      • Chapitre 4 : Modules ES6
    • Module 2 : Introduction à React
      • Chapitre 1 : Philosophie React
      • Chapitre 2 : JSX
      • Chapitre 3 : Composants
    • Module 3 : Hooks React Fondamentaux
      • Chapitre 1 : useState
      • Chapitre 2 : useEffect
      • Chapitre 3 : useRef
      • Chapitre 4 : useContext
    • Module 4 : React Avancé
      • Chapitre 1 : useCallback et useMemo
      • Chapitre 2 : Custom Hooks
      • Chapitre 3 : Redux et Redux Toolkit
    • Module 5 : Architecture SmartMaker
      • Chapitre 1 : Structure du projet
      • Chapitre 2 : Configuration
      • Chapitre 3 : Flux de données
    • Module 6 : SmartCommon - Composants
      • Chapitre 1 : Mise en page
      • Chapitre 2 : Navigation
      • Chapitre 3 : Formulaires
      • Chapitre 4 : Affichage
    • Module 7 : SmartCommon - Hooks
      • Chapitre 1 : useApi
      • Chapitre 2 : Gestion d'état
      • Chapitre 3 : Hooks utilitaires
    • Module 8 : Backend API (PHP)
      • Chapitre 1 : Routage
      • Chapitre 2 : Controllers
      • Chapitre 3 : Mappers
      • Extrafields et formulaires dynamiques
    • Module 9 : Intégration complète
      • Chapitre 1 : Backend
      • Chapitre 2 : Frontend
      • Chapitre 3 : Déploiement
    • Module 10 : Fonctionnalités avancées
      • Chapitre 1 : Mode offline
      • Chapitre 2 : Internationalisation (i18n)
      • Chapitre 3 : Autres fonctionnalités
    • Module 11 : Bonnes pratiques
  • Démonstration
  • Start
  • Composants et pages
  • Afficher le texte source
  • Anciennes révisions
  • Liens de retour
  • Haut de page