Swell

HandleInertiaRequests

Personnalisation du partage de données globales avec Inertia dans Swell.

Rappel rapide : le middleware Inertia

Par défaut, la méthode share du middleware HandleInertiaRequests permet de définir les données accessibles sur toutes les pages côté front. On peut y injecter des variables, des callbacks, ou même des closures pour du lazy loading.

Architecture : Services dédiés

Dans Swell, la logique métier des shared props a été extraite dans des services dédiés pour suivre les bonnes pratiques d'architecture :

  • SharedPropsService : Service principal qui orchestre toutes les shared props
  • CategoryCacheService : Gestion du cache des catégories
  • WorkspaceService : Gestion des équipes (teams) de l'utilisateur

Cette approche permet :

  • Séparation des responsabilités : Chaque service a un rôle clair
  • Testabilité : Les services peuvent être testés unitairement
  • Réutilisabilité : Les services peuvent être utilisés ailleurs dans l'application
  • Maintenabilité : Logique métier centralisée

Implémentation du middleware

Le middleware HandleInertiaRequests délègue maintenant la logique aux services :

app/Http/Middleware/HandleInertiaRequests.php
<?php

namespace App\Http\Middleware;

use App\Services\SharedPropsService;
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    public function __construct(private SharedPropsService $sharedPropsService) {}

    protected $rootView = 'app';

    public function share(Request $request): array
    {
        $shared = [
            ...parent::share($request),
            'name' => config('app.name'),
            'swell' => fn () => $this->sharedPropsService->getSwellConfig(),
            'auth' => fn () => $this->sharedPropsService->getAuthData($request),
            'sidebarOpen' => ! $request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true',
            'defaultSearchProducts' => fn () => $this->sharedPropsService->getDefaultSearchProducts(),
            'categories' => fn () => $this->sharedPropsService->getCategories(),
            'cart' => fn () => $this->sharedPropsService->getCart(),
        ];

        if (config('swell.banner.enabled', true)) {
            $shared['infoBanner'] = fn () => $this->sharedPropsService->getInfoBanner();
        }

        if (config('swell.workspace.enabled', true)) {
            $shared['workspaceMembers'] = fn () => $this->sharedPropsService->getWorkspaceMembers($request);
            $shared['invitableTeams'] = fn () => $this->sharedPropsService->getInvitableTeams($request);
        }

        return $shared;
    }
}

Services utilisés

SharedPropsService

Service principal qui centralise toute la logique des shared props.

app/Services/SharedPropsService.php
public function getSwellConfig(): array;
public function getAuthData(Request $request): array;
public function getDefaultSearchProducts();
public function getCategories();
public function getCart(): CartResource;
public function getInfoBanner();
public function getWorkspaceMembers(Request $request);
public function getInvitableTeams(Request $request);
public function clearAllCaches(): void;

Méthodes :

  • getSwellConfig() : Configuration des modules (wishlist, banner, review, loyalty, workspace)
  • getAuthData($request) : Données d'authentification (user + teams)
  • getDefaultSearchProducts() : Produits populaires pour la recherche (cache 1h)
  • getCategories() : Catégories en cache (via CategoryCacheService)
  • getCart() : Panier avec cache (30s)
  • getInfoBanner() : Messages de bannière si activés
  • getWorkspaceMembers($request) : Membres du workspace (uniquement sur routes workspace)
  • getInvitableTeams($request) : Équipes où l'utilisateur peut inviter (uniquement sur routes workspace)
  • clearAllCaches() : Vide tous les caches

CategoryCacheService

Service dédié à la gestion du cache des catégories.

app/Services/CategoryCacheService.php
public function getCachedCategories();
public function clearCache(): void;
public function refreshCache();

WorkspaceService

Service pour gérer les équipes (teams) de l'utilisateur.

app/Services/WorkspaceService.php
public function getUserTeams(?User $user);
public function clearUserTeamsCache(User $user): void;
public function refreshUserTeams(User $user);

Détail des données partagées

PropriétéDescription
nameLe nom de l'application, injecté depuis la config.
swellConfiguration des modules activables (wishlist, banner, review, loyalty, workspace). Mis en cache indéfiniment.
auth.userL'utilisateur connecté, avec ses rôles, chargé à la demande (closure pour éviter des requêtes inutiles si non utilisé).
auth.teamsLes équipes (teams) de l'utilisateur, avec membres et issues counts. Cache de 60s. Uniquement si workspace activé.
sidebarOpenÉtat d'ouverture de la sidebar, basé sur un cookie.
defaultSearchProductsProduits populaires pour la recherche rapide, mis en cache 1 heure.
categoriesLes catégories principales, avec leurs enfants et le nombre de produits, mises en cache indéfiniment.
cartLe panier de l'utilisateur (ou de la session), mis en cache 30 secondes.
infoBannerLes messages de bannière d'info, mis en cache indéfiniment. Conditionnel : uniquement si swell.banner.enabled.
workspaceMembersMembres du workspace actuel. Conditionnel : uniquement sur les routes workspace.*.
invitableTeamsÉquipes où l'utilisateur peut inviter des membres. Conditionnel : uniquement sur les routes workspace.*.

Les propriétés infoBanner, workspaceMembers et invitableTeams sont conditionnelles et ne sont incluses que si leurs modules respectifs sont activés ou sur des routes spécifiques.

Pourquoi ces choix ?

  • Services dédiés : Séparation claire des responsabilités, testabilité, réutilisabilité
  • Closures : Permettent de ne charger les données que si elles sont utilisées côté front (lazy loading)
  • Cache stratifié :
    • Cache indéfini pour les données quasi-statiques (catégories, config, bannières)
    • Cache court (30-60s) pour les données dynamiques (panier, équipes)
  • Injection de dépendances : Le middleware reçoit SharedPropsService via le constructeur

Typage côté front : SharedData

Pour garantir la cohérence entre le backend et le frontend, il est important d'adapter le typage du côté front (typiquement le type SharedData dans ton code TypeScript). Chaque clé partagée ici doit être déclarée et typée côté front pour bénéficier de l'autocomplétion et éviter les erreurs.

Pensez à mettre à jour le type SharedData à chaque ajout/modification dans le middleware.

Exemples d'utilisation côté front

Accéder à l'utilisateur connecté

Voici comment accéder à l'utilisateur connecté dans un composant React avec Inertia.js :

import type { SharedData } from "@/types";
import { Link, usePage } from '@inertiajs/react';

export default function Exemple() {
    const { auth } = usePage<SharedData>().props;

    return (
        <div>
            {auth.user ? (
                <UserDropdown user={auth.user} />
            ) : (
                <Link href="/login">
                    <User size={20} />
                </Link>
            )}
        </div>
    );
}

Afficher dynamiquement les équipes dans la sidebar

import { usePage } from '@inertiajs/react';
import { SharedData, NavItem } from '@/types';
import { Contact } from 'lucide-react';

export default function WorkspaceLayout({ children }: PropsWithChildren) {
    const { auth } = usePage<SharedData>().props;

    // Transformer les équipes en items de navigation
    const teamsNavItems: NavItem[] = auth.teams?.map(team => ({
        title: team.name,
        href: `/workspace/teams/${team.identifier}`,
        icon: Contact,
    })) ?? [];

    return (
        <WorkspaceLayoutTemplate
            teamsNavItems={teamsNavItems}
        >
            {children}
        </WorkspaceLayoutTemplate>
    );
}

Gestion du cache

Pour vider les caches, utiliser les services appropriés :

use App\Services\SharedPropsService;
use App\Services\CategoryCacheService;
use App\Services\WorkspaceService;

// Vider tous les caches
app(SharedPropsService::class)->clearAllCaches();

// Vider uniquement le cache des catégories
app(CategoryCacheService::class)->clearCache();

// Vider le cache des équipes d'un utilisateur spécifique
app(WorkspaceService::class)->clearUserTeamsCache($user);
Pour plus d'info sur le middleware HandleInertiaRequests. Voir la documentation de Inertia.