SmarMaker - Documentation
Docs» 15_training:module5-architecture-smartmaker:flux-donnees

Chapitre 3 : Flux de données

Vue d'ensemble

Comprendre comment les données circulent dans SmartMaker est essentiel. Ce chapitre trace le chemin complet d'une donnée, du clic utilisateur jusqu'au rendu final.

Les 3 types de données

Type Stockage Persistance Hook
État local Mémoire composant Non useState, useStates
État global Redux store Optionnel (localStorage) useGlobalStates
Données serveur API → Dolibarr Base de données useApi

Flux d'une requête API

┌──────────────────────────────────────────────────────────────┐
│                        FRONTEND                               │
├──────────────────────────────────────────────────────────────┤
│  1. Utilisateur clique "Charger"                             │
│           ↓                                                   │
│  2. Composant appelle useApi()                               │
│           ↓                                                   │
│  3. api.private.get('items')                                 │
│           ↓                                                   │
│  4. ky ajoute automatiquement :                              │
│     - Authorization: Bearer <accessToken>                    │
│     - X-DEVICEID: <deviceId>                                 │
└──────────────────────────────────────────────────────────────┘
                            ↓ HTTPS
┌──────────────────────────────────────────────────────────────┐
│                        BACKEND                                │
├──────────────────────────────────────────────────────────────┤
│  5. pwa/api.php reçoit la requête                            │
│           ↓                                                   │
│  6. SmartAuth valide le JWT                                  │
│           ↓                                                   │
│  7. Router appelle ItemController::index()                   │
│           ↓                                                   │
│  8. Controller charge les objets Dolibarr                    │
│           ↓                                                   │
│  9. Mapper convertit en DTO                                  │
│           ↓                                                   │
│ 10. Retourne JSON                                            │
└──────────────────────────────────────────────────────────────┘
                            ↓ HTTPS
┌──────────────────────────────────────────────────────────────┐
│                        FRONTEND                               │
├──────────────────────────────────────────────────────────────┤
│ 11. ky parse la réponse JSON                                 │
│           ↓                                                   │
│ 12. Composant met à jour l'état                              │
│           ↓                                                   │
│ 13. React re-rend avec les nouvelles données                 │
└──────────────────────────────────────────────────────────────┘

Exemple concret : Liste de produits

1. Le composant React

snippet.javascript
// components/pages/private/ProductsPage/index.jsx
import { useEffect } from 'react';
import { Page, Block, List, ListItem, Spinner } from '@cap-rel/smartcommon';
import { useApi, useStates } from '@cap-rel/smartcommon';
 
export const ProductsPage = () => {
    const api = useApi();
 
    const st = useStates({
        initialStates: {
            products: [],
            loading: true,
            error: null
        }
    });
 
    // Chargement au montage
    useEffect(() => {
        loadProducts();
    }, []);
 
    const loadProducts = async () => {
        st.set('loading', true);
        st.set('error', null);
 
        try {
            // Requête API avec JWT automatique
            const data = await api.private.get('products').json();
            st.set('products', data.products);
        } catch (err) {
            st.set('error', err.message);
        } finally {
            st.set('loading', false);
        }
    };
 
    // Affichage conditionnel
    if (st.get('loading')) {
        return <Page><Spinner /></Page>;
    }
 
    if (st.get('error')) {
        return (
            <Page>
                <Block>Erreur : {st.get('error')}</Block>
            </Page>
        );
    }
 
    return (
        <Page title="Produits">
            <List>
                {st.get('products').map(product => (
                    <ListItem
                        key={product.id}
                        title={product.label}
                        subtitle={`${product.price} €`}
                    />
                ))}
            </List>
        </Page>
    );
};

2. Le routeur API (PHP)

snippet.php
<?php
// pwa/api.php
require_once '../smartmaker-api-prepend.php';
 
use SmartAuth\Api\AuthController;
use SmartAuth\Api\RouteController as Route;
use MonModule\Api\ProductController;
 
// Routes d'authentification
Route::get('login', AuthController::class, 'index');
Route::post('login', AuthController::class, 'login');
Route::get('refresh', AuthController::class, 'refresh');
Route::post('logout', AuthController::class, 'logout', true);
 
// Routes produits (protégées)
Route::get('products', ProductController::class, 'index', true);
Route::get('products/{id}', ProductController::class, 'show', true);
Route::post('products', ProductController::class, 'create', true);
Route::put('products/{id}', ProductController::class, 'update', true);
Route::delete('products/{id}', ProductController::class, 'delete', true);
 
// Fallback
json_reply('Access denied', 403);

3. Le Controller (PHP)

snippet.php
<?php
// smartmaker-api/Controllers/ProductController.php
namespace MonModule\Api;
 
use MonModule\Api\Mappers\dmProduct;
 
class ProductController
{
    public function index($payload = null)
    {
        global $db, $user;
 
        // Charger les produits Dolibarr
        require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
 
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "product";
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
        $sql .= " ORDER BY label ASC";
 
        $result = $db->query($sql);
        $products = [];
 
        if ($result) {
            $mapper = new dmProduct();
 
            while ($obj = $db->fetch_object($result)) {
                $product = new \Product($db);
                $product->fetch($obj->rowid);
 
                // Mapper vers DTO React
                $products[] = $mapper->exportMappedData($product);
            }
        }
 
        return [['products' => $products], 200];
    }
 
    public function show($payload = null)
    {
        global $db;
 
        $id = $payload['id'];
 
        require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
        $product = new \Product($db);
 
        if ($product->fetch($id) <= 0) {
            return [['error' => 'Produit non trouvé'], 404];
        }
 
        $mapper = new dmProduct();
        return [$mapper->exportMappedData($product), 200];
    }
}

4. Le Mapper (PHP)

snippet.php
<?php
// smartmaker-api/Mappers/dmProduct.php
namespace MonModule\Api\Mappers;
 
class dmProduct
{
    public function exportMappedData($product)
    {
        return [
            'id' => (int) $product->id,
            'ref' => $product->ref,
            'label' => $product->label,
            'description' => $product->description,
            'price' => (float) $product->price,
            'price_ttc' => (float) $product->price_ttc,
            'stock' => (float) $product->stock_reel,
            'status' => (int) $product->status,
            'created_at' => $product->datec,
            'updated_at' => $product->tms
        ];
    }
 
    public function importMappedData($data)
    {
        // Conversion DTO → Dolibarr pour création/modification
        return [
            'ref' => $data['ref'] ?? '',
            'label' => $data['label'] ?? '',
            'description' => $data['description'] ?? '',
            'price' => $data['price'] ?? 0
        ];
    }
}

Authentification JWT

Flux de login

1. Utilisateur saisit email/password
           ↓
2. api.login({ login, password })
           ↓
3. SmartAuth vérifie les credentials
           ↓
4. Retourne { accessToken, refreshToken, user }
           ↓
5. SmartCommon stocke dans session (localStorage)
           ↓
6. Toutes les requêtes futures ont le token

Exemple login

snippet.javascript
// components/pages/public/LoginPage/index.jsx
import { Page, Block, Form, Input, Button } from '@cap-rel/smartcommon';
import { useApi, useNavigation } from '@cap-rel/smartcommon';
 
export const LoginPage = () => {
    const api = useApi();
    const navigate = useNavigation();
 
    const handleSubmit = async (values) => {
        try {
            // api.login gère tout automatiquement
            await api.login({
                login: values.email,
                password: values.password
            });
 
            // Redirige vers l'accueil
            navigate('/');
        } catch (err) {
            alert('Identifiants incorrects');
        }
    };
 
    return (
        <Page title="Connexion">
            <Block>
                <Form onSubmit={handleSubmit}>
                    <Input name="email" label="Email" type="email" required />
                    <Input name="password" label="Mot de passe" type="password" required />
                    <Button type="submit">Connexion</Button>
                </Form>
            </Block>
        </Page>
    );
};

Refresh automatique

Le token d'accès expire après 15 minutes. SmartCommon gère automatiquement :

  1. Détecte l'erreur 401 (token expiré)
  2. Appelle /refresh avec le refreshToken
  3. Obtient un nouveau accessToken
  4. Relance la requête originale

Tout est transparent pour le développeur.

État global vs État local

Quand utiliser useStates (local)

  • Données propres à un composant
  • État de formulaire
  • État de chargement
  • Données temporaires
snippet.javascript
const st = useStates({
    initialStates: {
        loading: false,
        formData: { name: '', email: '' }
    }
});

Quand utiliser useGlobalStates

  • Données partagées entre composants
  • Session utilisateur
  • Préférences
  • Données métier principales
snippet.javascript
const [session, setSession] = useGlobalStates('session');
const [cart, setCart] = useGlobalStates('cart');

Création de données

Flux de création

1. Formulaire rempli
           ↓
2. api.private.post('products', { json: data })
           ↓
3. Controller::create() valide et crée
           ↓
4. Retourne le produit créé
           ↓
5. Mise à jour de l'état local/global

Exemple

snippet.javascript
const handleCreate = async (formData) => {
    try {
        const newProduct = await api.private
            .post('products', { json: formData })
            .json();
 
        // Ajouter à la liste locale
        st.set('products', [...st.get('products'), newProduct]);
 
        // Ou mettre à jour l'état global
        // const [products, setProducts] = useGlobalStates('products');
        // setProducts([...products, newProduct]);
 
        navigate('/products');
    } catch (err) {
        st.set('error', err.message);
    }
};

Résumé du flux complet

Étape Couche Action
1 React Utilisateur interagit
2 Hook useApi() prépare la requête
3 ky Ajoute JWT + envoie
4 PHP api.php route la requête
5 SmartAuth Valide le token
6 Controller Exécute la logique métier
7 Dolibarr Accès base de données
8 Mapper Convertit en DTO
9 PHP Retourne JSON
10 ky Parse la réponse
11 Hook Met à jour l'état
12 React Re-rend l'interface

Points clés à retenir

  1. useApi gère automatiquement l'authentification JWT
  2. Controller charge les objets Dolibarr
  3. Mapper convertit Dolibarr → DTO React
  4. useStates pour l'état local
  5. useGlobalStates pour l'état partagé
  6. Le refresh token est géré automatiquement

← Chapitre précédent | Retour au module | Module suivant : SmartCommon Composants →

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

  • Chapitre 3 : Flux de données
    • Vue d'ensemble
    • Les 3 types de données
    • Flux d'une requête API
    • Exemple concret : Liste de produits
      • 1. Le composant React
      • 2. Le routeur API (PHP)
      • 3. Le Controller (PHP)
      • 4. Le Mapper (PHP)
    • Authentification JWT
      • Flux de login
      • Exemple login
      • Refresh automatique
    • État global vs État local
      • Quand utiliser useStates (local)
      • Quand utiliser useGlobalStates
    • Création de données
      • Flux de création
      • Exemple
    • Résumé du flux complet
    • Points clés à retenir
  • 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