Aller au contenu

Stack technique

ArtisanCMS est construit sur une stack technique moderne et cohérente. Cette page détaille chaque technologie utilisée, son rôle dans l’architecture et les versions requises.

CoucheTechnologieVersion
BackendLaravel13.x
Langage backendPHP8.3+
FrontendReact19.x
Typage frontendTypeScript5.x
Bridge backend/frontendInertia.js2.x
UI Componentsshadcn/uilatest
CSSTailwind CSS4.x
Base de donnéesMySQL / MariaDB8.0+ / 10.6+
BuildVite6.x
Monoreponpm workspaces + Turborepo-
Tests backendPHPUnit11.x
Tests frontendVitest3.x

Laravel est le framework PHP qui structure l’ensemble du backend. ArtisanCMS utilise les conventions et fonctionnalités clés de Laravel :

  • Service Providers : Point d’entrée pour l’enregistrement des services du CMS et des plugins.
  • Eloquent ORM : Modèles avec relations (polymorphiques, many-to-many, etc.), scopes et observers.
  • Policies : Autorisation granulaire sur chaque ressource via les policies Laravel.
  • Form Requests : Validation des données entrantes avec des règles strictes.
  • Queues : Traitement asynchrone des jobs (webhooks, analytics, médias, emails).
  • Cache : Abstraction multi-drivers (file, Redis) avec support des tags.
  • Events & Listeners : Système d’événements natif complété par le HookManager du CMS.
  • Scheduler : Planification des tâches récurrentes (agrégation analytics, nettoyage cache, sauvegardes).

Standards de code :

  • PSR-12 pour le style de code
  • declare(strict_types=1) dans tous les fichiers PHP
  • Typage strict des paramètres et retours de fonctions
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Page;
use Illuminate\Support\Collection;
final class PageService
{
public function __construct(
private readonly PageRepository $repository,
private readonly CacheService $cache,
) {}
public function getPublished(): Collection
{
return $this->cache->remember('pages.published', 3600, function (): Collection {
return $this->repository->findPublished();
});
}
}

Laravel Breeze fournit l’authentification de base :

  • Inscription et connexion
  • Réinitialisation de mot de passe
  • Vérification d’email
  • Middleware d’authentification sur les routes admin et API

L’authentification API utilise Laravel Sanctum pour les tokens d’accès.

Laravel Scout fournit l’abstraction pour la recherche full-text :

  • Indexation automatique des contenus (pages, articles, produits, médias)
  • Recherche globale depuis l’administration
  • Compatible avec les drivers Meilisearch, Algolia ou base de données

React est utilisé pour l’ensemble de l’interface d’administration. La version 19 apporte des améliorations de performance et le support des Server Components (utilisés de manière sélective via Inertia).

Chaque page de l’administration est un composant React recevant ses données en props depuis le controller Laravel via Inertia.

L’intégralité du code frontend est écrit en TypeScript strict :

// Types partagés pour les modèles
interface Page {
id: number;
title: string;
slug: string;
content: BlockNode[];
status: 'draft' | 'review' | 'approved' | 'published';
parent_id: number | null;
template: string;
created_at: string;
updated_at: string;
}
interface BlockNode {
id: string;
type: string;
props: Record<string, unknown>;
children?: BlockNode[];
}

Inertia est le lien entre Laravel et React. Il élimine le besoin d’une API REST pour l’administration :

  • Les controllers retournent Inertia::render() au lieu de JSON ou Blade.
  • La navigation entre pages se fait côté client (pas de rechargement complet).
  • Les formulaires utilisent le helper useForm() d’Inertia pour la soumission.
  • Le partage de données globales (utilisateur connecté, permissions, paramètres) se fait via Inertia::share().
import { useForm } from '@inertiajs/react';
export function CreatePageForm() {
const { data, setData, post, processing, errors } = useForm({
title: '',
slug: '',
template: 'default',
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
post('/admin/pages');
};
return (
<form onSubmit={handleSubmit}>
<Input
label="Titre"
value={data.title}
onChange={(e) => setData('title', e.target.value)}
error={errors.title}
/>
<Button type="submit" disabled={processing}>
Créer la page
</Button>
</form>
);
}

Zustand gère l’état local complexe qui dépasse les props Inertia, notamment dans le page builder :

import { create } from 'zustand';
interface PageBuilderState {
blocks: BlockNode[];
selectedBlockId: string | null;
undoStack: BlockNode[][];
redoStack: BlockNode[][];
addBlock: (block: BlockNode, parentId: string) => void;
moveBlock: (blockId: string, targetId: string, position: number) => void;
updateBlockProps: (blockId: string, props: Record<string, unknown>) => void;
undo: () => void;
redo: () => void;
selectBlock: (blockId: string | null) => void;
}
export const usePageBuilderStore = create<PageBuilderState>((set, get) => ({
blocks: [],
selectedBlockId: null,
undoStack: [],
redoStack: [],
addBlock: (block, parentId) => {
// Sauvegarde dans l'historique + ajout du bloc
},
// ...
}));

TipTap est utilisé pour l’édition de texte riche dans les blocs de contenu :

  • Formatage (gras, italique, souligné, barré)
  • Titres, listes, citations, code
  • Liens et images inline
  • Tableaux
  • Extensions personnalisées pour les variables dynamiques

Les composants d’interface sont basés sur shadcn/ui, encapsulés dans le package @artisan/ui :

  • Composants accessibles (conformes WAI-ARIA)
  • Personnalisables via les design tokens
  • Légers et tree-shakeable
  • Composants principaux : Button, Input, Select, Dialog, Sheet, Tabs, Table, Form, Toast, Command, Popover, etc.

Tailwind CSS v4 gère l’ensemble du styling :

  • Configuration via les design tokens du CMS
  • Classes utilitaires pour un développement rapide
  • Purge automatique des classes non utilisées en production
  • Support du dark mode (préparé, non activé par défaut)

La bibliothèque dnd-kit fournit les interactions de glisser-déposer du page builder :

  • Drag & drop accessible (support clavier et lecteurs d’écran)
  • Sortable pour la réorganisation des blocs
  • Droppable zones pour l’insertion entre blocs
  • Overlays visuels pendant le déplacement

ArtisanCMS supporte les deux moteurs de base de données :

Fonctionnalité utiliséeMySQLMariaDB
JSON columns8.0+10.2+
Full-text search (InnoDB)5.7+10.0+
Common Table Expressions (CTE)8.0+10.2+
Window functions8.0+10.2+

Les colonnes JSON sont utilisées pour stocker le contenu des blocs, les métadonnées de configuration des plugins et les paramètres des design tokens.

Version minimale recommandée : MySQL 8.0 ou MariaDB 10.6 pour bénéficier de toutes les optimisations.

Vite est le bundler utilisé pour le développement et la production :

  • Hot Module Replacement (HMR) instantané en développement
  • Build optimisé avec tree-shaking et code splitting en production
  • Plugin laravel-vite-plugin pour l’intégration avec Laravel
  • Support natif de TypeScript et JSX
vite.config.ts
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.tsx'],
refresh: true,
}),
react(),
],
resolve: {
alias: {
'@': '/resources/js',
'@artisan/ui': '/packages/ui/src',
'@artisan/blocks': '/packages/blocks/src',
'@artisan/page-builder': '/packages/page-builder/src',
'@artisan/theme-engine': '/packages/theme-engine/src',
},
},
});

La gestion des packages partagés repose sur npm workspaces, orchestrée par Turborepo :

  • npm workspaces : Résolution des dépendances entre packages/* et l’application principale.
  • Turborepo : Parallélisation et cache des tâches de build (turbo run build, turbo run test).
// package.json (racine)
{
"workspaces": [
"packages/*",
"plugins/*/resources/js"
]
}
turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^build"]
},
"lint": {}
}
}

Les tests backend couvrent les couches service, controller et intégration :

class PageServiceTest extends TestCase
{
public function test_published_pages_are_cached(): void
{
$page = Page::factory()->published()->create();
Cache::shouldReceive('remember')
->once()
->andReturn(collect([$page]));
$result = $this->pageService->getPublished();
$this->assertCount(1, $result);
}
}
Fenêtre de terminal
# Lancer les tests backend
php artisan test
php artisan test --filter=PageServiceTest
php artisan test --coverage

Les tests frontend utilisent Vitest avec React Testing Library :

import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { BlockRenderer } from '@artisan/blocks';
describe('BlockRenderer', () => {
it('renders a heading block correctly', () => {
const block: BlockNode = {
id: '1',
type: 'heading',
props: { level: 2, content: 'Mon titre' },
};
render(<BlockRenderer block={block} />);
expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent('Mon titre');
});
});
Fenêtre de terminal
# Lancer les tests frontend
npx vitest
npx vitest run
npx vitest --coverage
OutilRôleConfiguration
ESLintLinting JavaScript/TypeScripteslint.config.js
PrettierFormatage du code frontend.prettierrc
Laravel PintFormatage du code PHP (PSR-12)pint.json
PHPStanAnalyse statique PHPphpstan.neon
TypeScriptVérification de typestsconfig.json
Fenêtre de terminal
# Démarrer le serveur de développement complet (Laravel + Vite)
composer run dev
# Lancer uniquement le serveur Laravel
php artisan serve
# Lancer uniquement Vite (HMR)
npx vite
# Build de production
npm run build
# Linting et formatage
npx eslint . # Lint frontend
./vendor/bin/pint # Format PHP
npx prettier --write . # Format frontend
# Analyse statique
./vendor/bin/phpstan analyse # Analyse PHP
npx tsc --noEmit # Vérification TypeScript
# Tests
php artisan test # Tests backend
npx vitest run # Tests frontend
npx turbo run test # Tous les tests (Turborepo)
PackageRôle
laravel/frameworkFramework PHP principal
laravel/breezeAuthentification (login, register, password reset)
laravel/sanctumAuthentification API par tokens
laravel/scoutRecherche full-text
inertiajs/inertia-laravelAdaptateur Inertia pour Laravel
intervention/imageManipulation d’images (resize, WebP, EXIF)
spatie/laravel-permissionGestion des rôles et permissions
spatie/laravel-activitylogJournal d’activité
spatie/laravel-backupSauvegarde de base de données et fichiers
spatie/laravel-sitemapGénération de sitemap XML
stripe/stripe-phpSDK Stripe pour le e-commerce
openai-php/laravelSDK OpenAI pour l’assistant IA
PackageRôle
react / react-domBibliothèque d’interface utilisateur
@inertiajs/reactAdaptateur Inertia pour React
typescriptTypage statique
tailwindcssFramework CSS utilitaire
@dnd-kit/core / @dnd-kit/sortableDrag & drop pour le page builder
zustandGestion d’état légère
@tiptap/react / @tiptap/starter-kitEditeur riche
lucide-reactIcônes
rechartsGraphiques pour les analytics
date-fnsManipulation de dates
zodValidation de schémas
react-hook-formGestion de formulaires
@radix-ui/*Primitives UI accessibles (base de shadcn/ui)
PackageRôle
viteBundler et serveur de développement
@vitejs/plugin-reactSupport React dans Vite
laravel-vite-pluginIntégration Vite/Laravel
vitestFramework de tests frontend
@testing-library/reactUtilitaires de test React
eslintLinting du code
prettierFormatage du code
turboOrchestration monorepo