---
title: "Chapitre 3 : Flux de données"
weight: 510
---

# 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

```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)

```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)

```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)

```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

```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 nav = 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
            nav.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

```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

```javascript
const gst = useGlobalStates();

const session = gst.get('session');
const cart = gst.get('cart');

// Écriture
gst.local.set('session', userData);
gst.set('cart', { items: [], total: 0 });
```

## 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

```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 gst = useGlobalStates();
        // gst.set('products', [...gst.get('products'), newProduct]);

        nav.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](/training/module5-architecture-smartmaker/configuration) | [Retour au module](/training/module5-architecture-smartmaker) | [Module suivant : SmartCommon Composants ->](/training/module6-smartcommon-composants)
