Application web collaborative de gestion de contenu — workspaces, pages, commentaires et invitations. Construite en PHP natif, MySQL et JavaScript vanilla, sans frameworks.
- Authentification par sessions PHP (inscription, connexion, déconnexion)
- CRUD complet sur les workspaces et les pages
- Collaboration : invitation de membres avec rôles (viewer, editor, admin)
- Commentaires sur les pages avec modération par l'owner
- Dashboard avec statistiques agrégées et activité récente
- API REST — 30+ routes, codes HTTP sémantiques
- Suite de tests d'intégration — 22 tests, 56 assertions
| Couche | Technologie | Décision |
|---|---|---|
| Conteneurisation | Docker Compose | Lancement reproductible en une commande |
| Backend | PHP 8.3 natif | Comprendre le cycle HTTP sans abstraction |
| Base de données | MySQL 8 | Relations, index, agrégations SQL |
| Frontend | JavaScript vanilla | DOM, Event Loop, Fetch API, gestion d'état manuelle |
| Auth | Sessions PHP + bcrypt | Stateful, adapté à un frontend sur le même domaine |
| Tests | PHPUnit 12 | Tests d'intégration HTTP sur DB dédiée |
workspace-app/
├── public/
│ └── index.php # Front Controller — point d'entrée unique
├── backend/
│ ├── config/
│ │ └── Database.php # Singleton PDO, utf8mb4, requêtes préparées
│ ├── controllers/ # Logique HTTP — validation, réponses
│ ├── middleware/
│ │ └── AuthMiddleware.php # Vérifie la session avant les routes protégées
│ ├── models/ # Requêtes SQL — aucune logique HTTP
│ └── Router.php # Routing par regex, support {params} dynamiques
├── frontend/
│ ├── js/
│ │ ├── api.js # Couche fetch + gestion du loader (DOM minimal)
│ │ ├── state.js # Source de vérité unique de l'UI
│ │ ├── ui.js # Fonctions render* — aucun fetch
│ │ └── app.js # Orchestration — events, state, api, ui
│ └── css/
├── database/
│ └── workspace_app.sql # Schéma complet avec FK, index, contraintes
├── Dockerfile # Image PHP 8.3 + extensions PDO MySQL
├── docker-compose.yml # Services app + db
└── tests/
└── Integration/ # Tests HTTP sur DB dédiée
erDiagram
USERS {
INT id PK
VARCHAR email
CHAR password_hash
VARCHAR name
VARCHAR avatar_url
TIMESTAMP created_at
}
WORKSPACES {
INT id PK
INT owner_id FK
VARCHAR name
TIMESTAMP created_at
}
WORKSPACE_MEMBERS {
INT id PK
INT workspace_id FK
INT user_id FK
ENUM role
TIMESTAMP created_at
}
PAGES {
INT id PK
INT workspace_id FK
INT owner_id FK
VARCHAR title
TEXT content
TIMESTAMP created_at
TIMESTAMP updated_at
}
PAGE_SHARES {
INT id PK
INT page_id FK
INT user_id FK
ENUM permission
TIMESTAMP created_at
}
COMMENTS {
INT id PK
INT page_id FK
INT user_id FK
TEXT content
TIMESTAMP created_at
TIMESTAMP updated_at
}
%% Relations
USERS ||--o{ WORKSPACES : owns
USERS ||--o{ WORKSPACE_MEMBERS : member_of
WORKSPACES ||--o{ WORKSPACE_MEMBERS : has
WORKSPACES ||--o{ PAGES : contains
USERS ||--o{ PAGES : owns
PAGES ||--o{ PAGE_SHARES : shared_with
USERS ||--o{ PAGE_SHARES : receives
PAGES ||--o{ COMMENTS : has
USERS ||--o{ COMMENTS : writes
Décision notable : ON DELETE CASCADE sur les dépendances de workspace (pages, commentaires, membres, partages). Supprimer un workspace nettoie ses pages/commentaires et ses membres. Supprimer un user supprime ses memberships/partages/commentaires, mais ses workspaces/pages passent avec owner_id = NULL (pas de cascade). Choix conscient pour la simplicité (alternative : anonymisation pour la conformité RGPD).
- Mots de passe hashés avec bcrypt (cost par défaut de PHP 8.3 = 10, non surchargé)
- Requêtes préparées PDO sur toutes les opérations DB — protection injections SQL
session_regenerate_id()après chaque connexion — protection session fixation- Cookie de session
HttpOnly: dépend dephp.ini(recommandésession.cookie_httponly=1si pas déjà activé) - Messages d'erreur login volontairement vagues — prévention énumération users
- Contrôle d'accès vérifié à chaque route : authentification → existence ressource → autorisation
Prérequis : Docker et Docker Compose
git clone https://github.com/rommy-dev/workspace-app
cd workspace-app
# Configure l'environnement pour l'application et Docker Compose
cp backend/.env.example backend/.env
cp backend/.env.example .env
# Lance l'application et la base de données
docker compose up --buildL'application est disponible sur :
http://localhost:8080Le conteneur db initialise automatiquement MySQL avec database/workspace_app.sql au premier démarrage. Les variables MYSQL_* du fichier .env racine sont utilisées par docker-compose.yml, tandis que les variables DB_* sont utilisées par le backend PHP.
En mode Docker, garde cette logique :
DB_HOST=db
DB_PORT=3306Si la base doit être recréée depuis zéro, supprime le volume Docker :
docker compose down -v
docker compose up --build| Service | Rôle | Port |
|---|---|---|
app |
Serveur PHP exposant public/ |
8080 |
db |
MySQL 8 avec initialisation automatique | 3307 côté machine, 3306 côté Docker |
docker compose exec app ./vendor/bin/phpunit --testdoxPourquoi PHP natif sans framework ? Comprendre ce que Laravel et Symfony font sous le capot : routing par regex, Front Controller, PDO, middleware chain. Ces abstractions sont utiles mais opaques si on ne les a jamais implémentées.
Pourquoi les sessions plutôt que JWT ? Le frontend consomme l'API depuis le même domaine. Les sessions sont plus simples, le cookie est HttpOnly (inaccessible au JS), et invalider une session côté serveur est immédiat. JWT brille pour les APIs consommées par des tiers ou dans une architecture microservices.
Pourquoi les tests d'intégration plutôt qu'unitaires ? Les tests unitaires des Controllers nécessiteraient de refactoriser pour injecter les dépendances — un chantier qui n'apporte pas de valeur ici. Les tests d'intégration HTTP couvrent toute la stack (routing → controller → model → DB) et vérifient les vrais comportements.