outlet-orm 5.5.1 → 5.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +369 -395
  2. package/package.json +3 -1
package/README.md CHANGED
@@ -3,14 +3,14 @@
3
3
  [![npm version](https://badge.fury.io/js/outlet-orm.svg)](https://www.npmjs.com/package/outlet-orm)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Un ORM JavaScript inspiré de Laravel Eloquent pour Node.js avec support pour MySQL, PostgreSQL et SQLite.
6
+ A JavaScript ORM inspired by Laravel Eloquent for Node.js with support for MySQL, PostgreSQL and SQLite.
7
7
 
8
- 📚 **[Documentation complète disponible dans `/docs`](./docs/INDEX.md)**
8
+ 📚 **[Complete documentation available in `/docs`](./docs/INDEX.md)**
9
9
 
10
- ## ✅ Prérequis et compatibilité
10
+ ## ✅ Prerequisites and compatibility
11
11
 
12
- - Node.js >= 18 (recommandé/exigé)
13
- - Installez le driver de base de données correspondant à votre SGBD (voir ci-dessous)
12
+ - Node.js >= 18 (recommended/required)
13
+ - Install the database driver corresponding to your DBMS (see below)
14
14
 
15
15
  ## 🚀 Installation
16
16
 
@@ -18,61 +18,50 @@ Un ORM JavaScript inspiré de Laravel Eloquent pour Node.js avec support pour My
18
18
  npm install outlet-orm
19
19
  ```
20
20
 
21
- ### Installer le driver de base de données
21
+ ### Install the database driver
22
22
 
23
- Outlet ORM utilise des peerDependencies optionnelles pour les drivers de base de données. Installez uniquement le driver dont vous avez besoin:
23
+ Outlet ORM uses optional peer dependencies for database drivers. Install only the driver you need:
24
24
 
25
25
  - MySQL/MariaDB: `npm install mysql2`
26
26
  - PostgreSQL: `npm install pg`
27
27
  - SQLite: `npm install sqlite3`
28
28
 
29
- Si aucun driver n'est installé, un message d'erreur explicite vous indiquera lequel installer lors de la connexion.
29
+ If no driver is installed, an explicit error message will tell you which one to install when connecting.
30
30
 
31
- ## 📁 Structure de Projet Recommandée
31
+ ## 📁 Recommended Project Structure
32
32
 
33
- Organisez votre projet utilisant Outlet ORM avec une **architecture en couches** (recommandée pour la production) :
33
+ Organise your project using Outlet ORM with a **2-layer architecture** Controllers call Models directly, keeping the codebase lean and productive:
34
34
 
35
- > 🔐 **Sécurité** : Voir le [Guide de Sécurité](./docs/SECURITY.md) pour les bonnes pratiques.
35
+ > 🔐 **Security**: See the [Security Guide](./docs/SECURITY.md) for best practices.
36
36
 
37
37
  ```
38
- mon-projet/
39
- ├── .env # ⚠️ JAMAIS commité (dans .gitignore)
40
- ├── .env.example # Template sans secrets
38
+ my-project/
39
+ ├── .env # ⚠️ NEVER committed (in .gitignore)
40
+ ├── .env.example # Template without secrets
41
41
  ├── .gitignore
42
42
  ├── package.json
43
43
 
44
- ├── src/ # 📦 Code source centralisé
45
- │ ├── index.js # Point d'entrée de l'application
44
+ ├── src/ # 📦 Centralised source code
45
+ │ ├── index.js # Application entry point
46
46
  │ │
47
47
  │ ├── config/ # ⚙️ Configuration
48
- │ │ ├── app.js # Config générale (port, env)
49
- │ │ ├── database.js # Config DB (lit .env)
48
+ │ │ ├── app.js # General config (port, env)
49
+ │ │ ├── database.js # DB config (reads .env)
50
50
  │ │ └── security.js # CORS, helmet, rate limit
51
51
  │ │
52
- │ ├── models/ # 📊 Couche Data (Entities)
53
- │ │ ├── index.js # Export centralisé des models
52
+ │ ├── models/ # 📊 outlet-orm Models (entities)
53
+ │ │ ├── index.js # Centralised model exports
54
54
  │ │ ├── User.js
55
55
  │ │ ├── Post.js
56
56
  │ │ └── Comment.js
57
57
  │ │
58
- │ ├── repositories/ # 🗄️ Couche Accès Données
59
- │ │ ├── BaseRepository.js # Méthodes CRUD génériques
60
- │ │ ├── UserRepository.js # Requêtes spécifiques User
61
- │ │ └── PostRepository.js
62
- │ │
63
- │ ├── services/ # 💼 Couche Métier (Business Logic)
64
- │ │ ├── AuthService.js # Logique d'authentification
65
- │ │ ├── UserService.js # Logique métier utilisateur
66
- │ │ ├── PostService.js
67
- │ │ └── EmailService.js # Service externe (emails)
68
- │ │
69
- │ ├── controllers/ # 🎮 Couche Présentation (HTTP)
58
+ │ ├── controllers/ # 🎮 HTTP handling + business logic (direct ORM calls)
70
59
  │ │ ├── AuthController.js
71
60
  │ │ ├── UserController.js
72
61
  │ │ └── PostController.js
73
62
  │ │
74
- │ ├── routes/ # 🛤️ Définition des routes
75
- │ │ ├── index.js # Agrégateur de routes
63
+ │ ├── routes/ # 🛤️ Route definitions
64
+ │ │ ├── index.js # Route aggregator
76
65
  │ │ ├── auth.routes.js
77
66
  │ │ ├── user.routes.js
78
67
  │ │ └── post.routes.js
@@ -82,44 +71,42 @@ mon-projet/
82
71
  │ │ ├── authorize.js # RBAC / permissions
83
72
  │ │ ├── rateLimiter.js # Protection DDoS
84
73
  │ │ ├── validator.js # Validation request body
85
- │ │ └── errorHandler.js # Gestion centralisée erreurs
74
+ │ │ └── errorHandler.js # Centralised error handling
86
75
  │ │
87
- │ ├── validators/ # ✅ Schémas de validation
76
+ │ ├── validators/ # ✅ Validation schemas
88
77
  │ │ ├── authValidator.js
89
78
  │ │ └── userValidator.js
90
79
  │ │
91
- │ └── utils/ # 🔧 Utilitaires
80
+ │ └── utils/ # 🔧 Utilities
92
81
  │ ├── hash.js # bcrypt wrapper
93
82
  │ ├── token.js # JWT helpers
94
83
  │ ├── logger.js # Winston/Pino config
95
- │ └── response.js # Formatage réponses API
84
+ │ └── response.js # API response formatting
96
85
 
97
86
  ├── database/
98
87
  │ ├── config.js # Config migrations (outlet-init)
99
- │ ├── migrations/ # Fichiers de migration
100
- │ └── seeds/ # Données de test/démo
88
+ │ ├── migrations/ # Migration files
89
+ │ └── seeds/ # Test/demo data
101
90
  │ └── UserSeeder.js
102
91
 
103
- ├── public/ # ✅ Fichiers statiques publics
92
+ ├── public/ # ✅ Public static files
104
93
  │ ├── images/
105
94
  │ ├── css/
106
95
  │ └── js/
107
96
 
108
- ├── uploads/ # ⚠️ Fichiers uploadés
97
+ ├── uploads/ # ⚠️ Uploaded files
109
98
 
110
- ├── logs/ # 📋 Journaux (non versionnés)
99
+ ├── logs/ # 📋 Logs (not versioned)
111
100
 
112
101
  └── tests/ # 🧪 Tests
113
- ├── unit/ # Tests unitaires
114
- ├── services/
115
- │ └── models/
116
- ├── integration/ # Tests d'intégration
102
+ ├── unit/ # Unit tests (models)
103
+ ├── integration/ # Integration tests (API)
117
104
  │ └── api/
118
- └── fixtures/ # Données de test
105
+ └── fixtures/ # Test data
119
106
  └── users.json
120
107
  ```
121
108
 
122
- ### 🏗️ Architecture en Couches
109
+ ### 🏗️ Architecture Flow
123
110
 
124
111
  ```
125
112
  ┌─────────────────────────────────────────────────────────────┐
@@ -133,45 +120,32 @@ mon-projet/
133
120
 
134
121
 
135
122
  ┌─────────────────────────────────────────────────────────────┐
136
- │ ROUTES → CONTROLLERS (Couche Présentation)
137
- Reçoit la requête, appelle le service, retourne réponse
138
- └─────────────────────────────────────────────────────────────┘
139
-
140
-
141
- ┌─────────────────────────────────────────────────────────────┐
142
- │ SERVICES (Couche Métier / Business Logic) │
143
- │ Logique métier, orchestration, règles business │
144
- └─────────────────────────────────────────────────────────────┘
145
-
146
-
147
- ┌─────────────────────────────────────────────────────────────┐
148
- │ REPOSITORIES (Couche Accès Données) │
149
- │ Abstraction des requêtes DB, utilise les Models │
123
+ │ ROUTES → CONTROLLERS HTTP handling + business logic
124
+ Direct outlet-orm Model calls
150
125
  └─────────────────────────────────────────────────────────────┘
151
126
 
152
127
 
153
128
  ┌─────────────────────────────────────────────────────────────┐
154
- │ MODELS (Outlet ORM) → DATABASE │
129
+ │ MODELS (outlet-orm) → DATABASE │
155
130
  └─────────────────────────────────────────────────────────────┘
156
131
  ```
157
132
 
158
- ### 📋 Rôle de chaque couche
133
+ ### 📋 Role of each layer
159
134
 
160
- | Couche | Dossier | Responsabilité | Dépend de |
161
- |--------|---------|----------------|-----------|
162
- | **Présentation** | `controllers/` | Traiter HTTP, valider entrées, formater réponses | Services |
163
- | **Métier** | `services/` | Logique business, orchestration, règles | Repositories |
164
- | **Données** | `repositories/` | Requêtes DB complexes, abstraction | Models |
165
- | **Entités** | `models/` | Définition des entités, relations, validations | Outlet ORM |
135
+ | Layer | Folder | Responsibility | Security |
136
+ |--------|---------|----------------|----------|
137
+ | **Controllers** | `controllers/` | Handle HTTP, call ORM models, return responses | Input validation, ownership checks |
138
+ | **Models** | `models/` | Entity definition, relationships, validations | `fillable`, `hidden`, rules |
139
+ | **Middlewares** | `middlewares/` | Auth, rate limiting, error handling | 🔒 Critical |
166
140
 
167
- ### ✅ Avantages de cette architecture
141
+ ### ✅ Benefits of this architecture
168
142
 
169
- - **Testabilité** : Chaque couche peut être testée indépendamment
170
- - **Maintenabilité** : Séparation claire des responsabilités
171
- - **Scalabilité** : Facile d'ajouter de nouvelles fonctionnalités
172
- - **Réutilisabilité** : Services utilisables depuis CLI, workers, etc.
143
+ - **Simplicity**: Less files, less abstraction, faster to navigate
144
+ - **Productivity**: Outlet ORM's expressive API handles data access directly
145
+ - **Testability**: Integration tests with SQLite in-memory cover full request flows
146
+ - **Flexibility**: Add a `services/` or `repositories/` layer later if complexity grows
173
147
 
174
- ### 📝 Exemple de flux
148
+ ### 📝 Example workflow
175
149
 
176
150
  ```javascript
177
151
  // routes/user.routes.js
@@ -179,88 +153,88 @@ router.get('/users/:id', auth, UserController.show);
179
153
 
180
154
  // controllers/UserController.js
181
155
  async show(req, res) {
182
- const user = await userService.findById(req.params.id);
156
+ const user = await User.with('posts').find(req.params.id);
157
+ if (!user) return res.status(404).json({ message: 'User not found' });
183
158
  res.json({ data: user });
184
159
  }
185
160
 
186
- // services/UserService.js
187
- async findById(id) {
188
- const user = await userRepository.findWithPosts(id);
189
- if (!user) throw new NotFoundError('User not found');
190
- return user;
191
- }
161
+ async store(req, res) {
162
+ const existing = await User.where('email', req.body.email).first();
163
+ if (existing) return res.status(409).json({ message: 'Email already in use' });
164
+
165
+ const data = { ...req.body };
166
+ data.password = await bcrypt.hash(data.password, 10);
192
167
 
193
- // repositories/UserRepository.js
194
- async findWithPosts(id) {
195
- return User.with('posts').find(id);
168
+ const user = await User.create(data);
169
+ res.status(201).json({ success: true, data: user });
196
170
  }
197
171
  ```
198
172
 
199
- ## ✨ Fonctionnalités clés
200
-
201
- - **API inspirée d'Eloquent** (Active Record) pour un usage fluide
202
- - **Query Builder expressif**: where/joins/order/limit/offset/paginate
203
- - **Filtres relationnels façon Laravel**: `whereHas()`, `has()`, `whereDoesntHave()`, `withCount()`
204
- - **Eager Loading** des relations via `.with(...)` avec contraintes et dot-notation
205
- - **Relations complètes**:
206
- - `hasOne`, `hasMany`, `belongsTo`, `belongsToMany` (avec attach/detach/sync)
207
- - `hasManyThrough`, `hasOneThrough` (relations transitives)
208
- - `morphOne`, `morphMany`, `morphTo` (relations polymorphiques)
209
- - **Transactions** complètes: `beginTransaction()`, `commit()`, `rollback()`, `transaction()`
210
- - **Soft Deletes**: suppression logique avec `deleted_at`, `withTrashed()`, `onlyTrashed()`, `restore()`
211
- - **Scopes**: globaux et locaux pour réutiliser vos filtres
173
+ ## ✨ Key features
174
+
175
+ - **Eloquent-inspired API** (Active Record) for a fluent developer experience
176
+ - **Expressive Query Builder**: where/joins/order/limit/offset/paginate
177
+ - **Laravel-style relationship filters**: `whereHas()`, `has()`, `whereDoesntHave()`, `withCount()`
178
+ - **Eager Loading** of relationships via `.with(...)` with constraints and dot-notation
179
+ - **Complete relations**:
180
+ - `hasOne`, `hasMany`, `belongsTo`, `belongsToMany` (with attach/detach/sync)
181
+ - `hasManyThrough`, `hasOneThrough` (transitive relationships)
182
+ - `morphOne`, `morphMany`, `morphTo` (polymorphic relationships)
183
+ - **Complete Transactions**: `beginTransaction()`, `commit()`, `rollback()`, `transaction()`
184
+ - **Soft Deletes**: soft deletion with `deleted_at`, `withTrashed()`, `onlyTrashed()`, `restore()`
185
+ - **Scopes**: global and local, to reuse your query filters
212
186
  - **Events/Hooks**: `creating`, `created`, `updating`, `updated`, `deleting`, `deleted`, etc.
213
- - **Validation**: règles basiques intégrées (`required`, `email`, `min`, `max`, etc.)
214
- - **Query Logging**: mode debug avec `enableQueryLog()` et `getQueryLog()`
215
- - **Pool PostgreSQL**: connexions poolées pour de meilleures performances
216
- - **Protection SQL**: sanitization automatique des identifiants
217
- - **Casts automatiques** (int, float, boolean, json, date...)
218
- - **Attributs masqués** (`hidden`) et timestamps automatiques
219
- - **Contrôle de visibilité** des attributs cachés: `withHidden()` et `withoutHidden()`
220
- - **Incrément/Décrément atomiques**: `increment()` et `decrement()`
221
- - **Aliases ergonomiques**: `columns([...])`, `ordrer()` (alias typo de `orderBy`)
222
- - **Requêtes brutes**: `executeRawQuery()` et `execute()` (résultats natifs du driver)
223
- - **Migrations complètes** (create/alter/drop, index, foreign keys, batch tracking)
224
- - **CLI pratiques**: `outlet-init`, `outlet-migrate`, `outlet-convert`
225
- - **Configuration via `.env`** (chargée automatiquement)
226
- - **Multi-base de données**: MySQL, PostgreSQL et SQLite
227
- - **Types TypeScript complets** avec Generic Model et Schema Builder typé (v4.0.0+)
228
-
229
- ## ⚡ Démarrage Rapide
230
-
231
- ### Initialisation du projet
187
+ - **Validation**: built-in basic rules (`required`, `email`, `min`, `max`, etc.)
188
+ - **Query Logging**: debug mode with `enableQueryLog()` and `getQueryLog()`
189
+ - **PostgreSQL Pool**: pooled connections for better performance
190
+ - **SQL Protection**: automatic sanitisation of identifiers
191
+ - **Automatic Casts** (int, float, boolean, json, date...)
192
+ - **Hidden attributes** (`hidden`) and automatic timestamps
193
+ - **Visibility control** of hidden attributes: `withHidden()` and `withoutHidden()`
194
+ - **Atomic increment/decrement**: `increment()` and `decrement()`
195
+ - **Ergonomic aliases**: `columns([...])`, `ordrer()` (typo alias for `orderBy`)
196
+ - **Raw queries**: `executeRawQuery()` and `execute()` (native driver results)
197
+ - **Complete Migrations** (create/alter/drop, index, foreign keys, batch tracking)
198
+ - **Handy CLI tools**: `outlet-init`, `outlet-migrate`, `outlet-convert`
199
+ - **`.env` configuration** (loaded automatically)
200
+ - **Multi-database**: MySQL, PostgreSQL, and SQLite
201
+ - **Complete TypeScript types** with Generic Model and typed Schema Builder (v4.0.0+)
202
+
203
+ ## ⚡ Quick Start
204
+
205
+ ### Project Initialisation
232
206
 
233
207
  ```bash
234
- # Créer la configuration initiale
208
+ # Create initial configuration
235
209
  outlet-init
236
210
 
237
- # Créer une migration
211
+ # Create a migration
238
212
  outlet-migrate make create_users_table
239
213
 
240
- # Exécuter les migrations
214
+ # Run migrations
241
215
  outlet-migrate migrate
242
216
  ```
243
217
 
244
- ### 🌱 Seeding rapide
218
+ ### 🌱 Quick Seeding
245
219
 
246
220
  ```bash
247
- # Créer un seeder
221
+ # Create a seeder
248
222
  outlet-migrate make:seed UserSeeder
249
223
 
250
- # Exécuter les seeds (DatabaseSeeder prioritaire)
224
+ # Run seeders (DatabaseSeeder runs first)
251
225
  outlet-migrate seed
252
226
 
253
- # Exécuter un seeder spécifique
227
+ # Run a specific seeder
254
228
  outlet-migrate seed --class UserSeeder
255
229
  ```
256
230
 
257
- ## 📖 Utilisation
231
+ ## 📖 Usage
258
232
 
259
- ### Configuration de la connexion
233
+ ### Connection configuration
260
234
 
261
- Outlet ORM charge automatiquement la connexion depuis le fichier `.env`. **Plus besoin d'importer DatabaseConnection !**
235
+ Outlet ORM automatically loads the connection from the `.env` file. **No need to import DatabaseConnection!**
262
236
 
263
- #### Fichier `.env`
237
+ #### `.env` file
264
238
 
265
239
  ```env
266
240
  DB_DRIVER=mysql
@@ -271,7 +245,7 @@ DB_PASSWORD=secret
271
245
  DB_PORT=3306
272
246
  ```
273
247
 
274
- #### Utilisation simplifiée
248
+ #### Simplified usage
275
249
 
276
250
  ```javascript
277
251
  const { Model } = require('outlet-orm');
@@ -280,21 +254,21 @@ class User extends Model {
280
254
  static table = 'users';
281
255
  }
282
256
 
283
- // C'est tout ! La connexion est automatique
257
+ // That's it! The connection is automatic
284
258
  const users = await User.all();
285
259
  ```
286
260
 
287
- #### Configuration manuelle (optionnel)
261
+ #### Manual configuration (optional)
288
262
 
289
- Si vous avez besoin de contrôler la connexion :
263
+ If you need to control the connection :
290
264
 
291
265
  ```javascript
292
266
  const { DatabaseConnection, Model } = require('outlet-orm');
293
267
 
294
- // Option 1 – via .env (aucun paramètre nécessaire)
268
+ // Option 1 – via .env (no parameters required)
295
269
  const db = new DatabaseConnection();
296
270
 
297
- // Option 2 – via objet de configuration
271
+ // Option 2 – via configuration object
298
272
  const db = new DatabaseConnection({
299
273
  driver: 'mysql',
300
274
  host: 'localhost',
@@ -304,48 +278,48 @@ const db = new DatabaseConnection({
304
278
  port: 3306
305
279
  });
306
280
 
307
- // Définir la connexion manuellement (optionnel)
281
+ // Set the connection manually (optional)
308
282
  Model.setConnection(db);
309
283
  ```
310
284
 
311
- #### Variables d'environnement (.env)
285
+ #### Environment variables (.env)
312
286
 
313
- | Variable | Description | Par défaut |
287
+ | Variable | Description | Default |
314
288
  |----------|-------------|------------|
315
289
  | `DB_DRIVER` | `mysql`, `postgres`, `sqlite` | `mysql` |
316
- | `DB_HOST` | Hôte de la base | `localhost` |
317
- | `DB_PORT` | Port de connexion | Selon driver |
318
- | `DB_USER` / `DB_USERNAME` | Identifiant | - |
319
- | `DB_PASSWORD` | Mot de passe | - |
320
- | `DB_DATABASE` / `DB_NAME` | Nom de la base | - |
321
- | `DB_FILE` / `SQLITE_DB` | Fichier SQLite | `:memory:` |
290
+ | `DB_HOST` | Database host | `localhost` |
291
+ | `DB_PORT` | Connection port | Driver default |
292
+ | `DB_USER` / `DB_USERNAME` | Username | - |
293
+ | `DB_PASSWORD` | Password | - |
294
+ | `DB_DATABASE` / `DB_NAME` | Database name | - |
295
+ | `DB_FILE` / `SQLITE_DB` | SQLite file | `:memory:` |
322
296
 
323
297
  ### Importation
324
298
 
325
299
  ```javascript
326
- // CommonJS - Import simple (connexion automatique via .env)
300
+ // CommonJS - Simple import (automatic connection via .env)
327
301
  const { Model } = require('outlet-orm');
328
302
 
329
303
  // ES Modules
330
304
  import { Model } from 'outlet-orm';
331
305
 
332
- // Si besoin de contrôle manuel sur la connexion
306
+ // If you need manual control over the connection
333
307
  const { DatabaseConnection, Model } = require('outlet-orm');
334
308
  ```
335
309
 
336
- ### Définir un modèle
310
+ ### Define a model
337
311
 
338
312
  ```javascript
339
313
  const { Model } = require('outlet-orm');
340
314
 
341
- // Définition des modèles liés (voir Relations)
315
+ // Define related models (see Relationships)
342
316
  class Post extends Model { static table = 'posts'; }
343
317
  class Profile extends Model { static table = 'profiles'; }
344
318
 
345
319
  class User extends Model {
346
320
  static table = 'users';
347
- static primaryKey = 'id'; // Par défaut: 'id'
348
- static timestamps = true; // Par défaut: true
321
+ static primaryKey = 'id'; // Default: 'id'
322
+ static timestamps = true; // Default: true
349
323
  static fillable = ['name', 'email', 'password'];
350
324
  static hidden = ['password'];
351
325
  static casts = {
@@ -366,19 +340,19 @@ class User extends Model {
366
340
  }
367
341
  ```
368
342
 
369
- ### Opérations CRUD
343
+ ### CRUD operations
370
344
 
371
- #### Créer
345
+ #### Create
372
346
 
373
347
  ```javascript
374
- // Méthode 1: create()
348
+ // Method 1: create()
375
349
  const user = await User.create({
376
350
  name: 'John Doe',
377
351
  email: 'john@example.com',
378
352
  password: 'secret123'
379
353
  });
380
354
 
381
- // Méthode 2: new + save()
355
+ // Method 2: new + save()
382
356
  const user = new User({
383
357
  name: 'Jane Doe',
384
358
  email: 'jane@example.com'
@@ -386,30 +360,30 @@ const user = new User({
386
360
  user.setAttribute('password', 'secret456');
387
361
  await user.save();
388
362
 
389
- // Insert brut (sans créer d'instance)
363
+ // Raw insert (without creating an instance)
390
364
  await User.insert({ name: 'Bob', email: 'bob@example.com' });
391
365
  ```
392
366
 
393
- #### Lire
367
+ #### Read
394
368
 
395
369
  ```javascript
396
- // Tous les enregistrements
370
+ // All records
397
371
  const users = await User.all();
398
372
 
399
- // Par ID
373
+ // By ID
400
374
  const user = await User.find(1);
401
- const user = await User.findOrFail(1); // Lance une erreur si non trouvé
375
+ const user = await User.findOrFail(1); // Throws an error if not found
402
376
 
403
- // Premier résultat
377
+ // First result
404
378
  const firstUser = await User.first();
405
379
 
406
- // Avec conditions
380
+ // With conditions
407
381
  const activeUsers = await User
408
382
  .where('status', 'active')
409
383
  .where('age', '>', 18)
410
384
  .get();
411
385
 
412
- // Avec relations (Eager Loading)
386
+ // With relationships (Eager Loading)
413
387
  const usersWithPosts = await User
414
388
  .with('posts', 'profile')
415
389
  .get();
@@ -421,7 +395,7 @@ const recentUsers = await User
421
395
  .get();
422
396
  ```
423
397
 
424
- #### Mettre à jour
398
+ #### Update
425
399
 
426
400
  ```javascript
427
401
  // Instance
@@ -439,12 +413,12 @@ const updated = await User
439
413
  .where('id', 1)
440
414
  .updateAndFetch({ name: 'Neo' }, ['profile', 'posts']);
441
415
 
442
- // Helpers par ID
416
+ // Helpers by ID
443
417
  const user = await User.updateAndFetchById(1, { name: 'Trinity' }, ['profile']);
444
418
  await User.updateById(2, { status: 'active' });
445
419
  ```
446
420
 
447
- #### Supprimer
421
+ #### Delete
448
422
 
449
423
  ```javascript
450
424
  // Instance
@@ -494,37 +468,37 @@ const result = await User
494
468
  .select('users.*', 'profiles.bio', 'countries.name as country')
495
469
  .get();
496
470
 
497
- // Agrégations
471
+ // Aggregations
498
472
  const stats = await User
499
473
  .distinct()
500
474
  .groupBy('status')
501
475
  .having('COUNT(*)', '>', 5)
502
476
  .get();
503
477
 
504
- // Incrément / Décrément atomique
478
+ // Atomic increment / decrement
505
479
  await User.where('id', 1).increment('login_count');
506
480
  await User.where('id', 1).decrement('credits', 10);
507
481
  ```
508
482
 
509
- ### Filtres relationnels
483
+ ### Relationship filters
510
484
 
511
485
  ```javascript
512
- // whereHas: Utilisateurs ayant au moins un post publié
486
+ // whereHas: Users who have at least one published post
513
487
  const authors = await User
514
488
  .whereHas('posts', (q) => {
515
489
  q.where('status', 'published');
516
490
  })
517
491
  .get();
518
492
 
519
- // has: Au moins N enfants
493
+ // has: At least N children
520
494
  const prolific = await User.has('posts', '>=', 10).get();
521
495
 
522
- // whereDoesntHave: Aucun enfant
496
+ // whereDoesntHave: No children
523
497
  const noPostUsers = await User.whereDoesntHave('posts').get();
524
498
 
525
- // withCount: Ajouter une colonne {relation}_count
499
+ // withCount: Add a {relation}_count column
526
500
  const withCounts = await User.withCount('posts').get();
527
- // Chaque user aura: user.getAttribute('posts_count')
501
+ // Each user will have: user.getAttribute('posts_count')
528
502
  ```
529
503
 
530
504
  ## 🔗 Relations
@@ -609,15 +583,15 @@ class User extends Model {
609
583
  const user = await User.find(1);
610
584
  const roles = await user.roles().get();
611
585
 
612
- // Méthodes pivot
613
- await user.roles().attach([1, 2]); // Attacher des rôles
614
- await user.roles().detach(2); // Détacher un rôle
615
- await user.roles().sync([1, 3]); // Synchroniser (remplace tout)
586
+ // Pivot methods
587
+ await user.roles().attach([1, 2]); // Attach roles
588
+ await user.roles().detach(2); // Detach a role
589
+ await user.roles().sync([1, 3]); // Synchronise (replaces all)
616
590
  ```
617
591
 
618
592
  ### Has Many Through (hasManyThrough)
619
593
 
620
- Accéder à une relation distante via un modèle intermédiaire.
594
+ Access a distant relationship via an intermediate model.
621
595
 
622
596
  ```javascript
623
597
  const { Model } = require('outlet-orm');
@@ -649,20 +623,20 @@ const user = await User.find(1);
649
623
  const country = await user.country().get();
650
624
  ```
651
625
 
652
- ### Relations Polymorphiques
626
+ ### Polymorphic relationships
653
627
 
654
- Les relations polymorphiques permettent à un modèle d'appartenir à plusieurs autres modèles.
628
+ Polymorphic relationships allow a model to belong to multiple other models.
655
629
 
656
630
  ```javascript
657
631
  const { Model } = require('outlet-orm');
658
632
 
659
- // Configuration du morph map
633
+ // Set up the morph map
660
634
  Model.setMorphMap({
661
635
  'posts': Post,
662
636
  'videos': Video
663
637
  });
664
638
 
665
- // Modèles
639
+ // Models
666
640
  class Post extends Model {
667
641
  comments() {
668
642
  return this.morphMany(Comment, 'commentable');
@@ -686,37 +660,37 @@ const post = await Post.find(1);
686
660
  const comments = await post.comments().get();
687
661
 
688
662
  const comment = await Comment.find(1);
689
- const parent = await comment.commentable().get(); // Post ou Video
663
+ const parent = await comment.commentable().get(); // Post or Video
690
664
  ```
691
665
 
692
- **Relations polymorphiques disponibles:**
693
- - `morphOne(Related, 'morphName')` - One-to-One polymorphique
694
- - `morphMany(Related, 'morphName')` - One-to-Many polymorphique
695
- - `morphTo('morphName')` - Inverse polymorphique
666
+ **Available polymorphic relationships:**
667
+ - `morphOne(Related, 'morphName')` - One-to-One polymorphic
668
+ - `morphMany(Related, 'morphName')` - One-to-Many polymorphic
669
+ - `morphTo('morphName')` - Inverse polymorphic
696
670
 
697
671
  ### Eager Loading
698
672
 
699
673
  ```javascript
700
- // Charger plusieurs relations
674
+ // Load multiple relationships
701
675
  const users = await User.with('posts', 'profile', 'roles').get();
702
676
 
703
- // Charger avec contraintes
677
+ // Load with constraints
704
678
  const users = await User.with({
705
679
  posts: (q) => q.where('status', 'published').orderBy('created_at', 'desc')
706
680
  }).get();
707
681
 
708
- // Charger des relations imbriquées (dot notation)
682
+ // Load nested relationships (dot notation)
709
683
  const users = await User.with('posts.comments.author').get();
710
684
 
711
- // Charger sur une instance existante
685
+ // Load on an existing instance
712
686
  const user = await User.find(1);
713
687
  await user.load('posts', 'profile');
714
688
  await user.load(['roles', 'posts.comments']);
715
689
 
716
- // Accéder aux relations chargées
690
+ // Access loaded relationships
717
691
  users.forEach(user => {
718
- console.log(user.relations.posts);
719
- console.log(user.relations.profile);
692
+ console.log(user.relationships.posts);
693
+ console.log(user.relationships.profile);
720
694
  });
721
695
  ```
722
696
 
@@ -724,7 +698,7 @@ users.forEach(user => {
724
698
 
725
699
  ### Casts
726
700
 
727
- Les casts convertissent automatiquement les attributs:
701
+ Casts automatically convert attributes:
728
702
 
729
703
  ```javascript
730
704
  const { Model } = require('outlet-orm');
@@ -736,13 +710,13 @@ class User extends Model {
736
710
  balance: 'float', // ou 'double'
737
711
  email_verified: 'boolean', // ou 'bool'
738
712
  metadata: 'json', // Parse JSON
739
- settings: 'array', // Parse JSON en array
740
- birthday: 'date' // Convertit en Date
713
+ settings: 'array', // Parse JSON as array
714
+ birthday: 'date' // Converts to Date
741
715
  };
742
716
  }
743
717
  ```
744
718
 
745
- ### Attributs cachés
719
+ ### Hidden attributes
746
720
 
747
721
  ```javascript
748
722
  const { Model } = require('outlet-orm');
@@ -752,24 +726,24 @@ class User extends Model {
752
726
  }
753
727
 
754
728
  const user = await User.find(1);
755
- console.log(user.toJSON()); // password et secret_token exclus
729
+ console.log(user.toJSON()); // password and secret_token excluded
756
730
  ```
757
731
 
758
- #### Afficher les attributs cachés
732
+ #### Show hidden attributes
759
733
 
760
734
  ```javascript
761
- // Inclure les attributs cachés
735
+ // Include hidden attributes
762
736
  const user = await User.withHidden().where('email', 'john@example.com').first();
763
- console.log(user.toJSON()); // password inclus
737
+ console.log(user.toJSON()); // password included
764
738
 
765
- // Contrôler avec un booléen
766
- const user = await User.withoutHidden(true).first(); // true = afficher
767
- const user = await User.withoutHidden(false).first(); // false = masquer (défaut)
739
+ // Control with a boolean
740
+ const user = await User.withoutHidden(true).first(); // true = show
741
+ const user = await User.withoutHidden(false).first(); // false = hide (default)
768
742
 
769
- // Cas d'usage: authentification
743
+ // Use case: authentication
770
744
  const user = await User.withHidden().where('email', email).first();
771
745
  if (user && await bcrypt.compare(password, user.getAttribute('password'))) {
772
- // Authentification réussie
746
+ // Authentication successful
773
747
  }
774
748
  ```
775
749
 
@@ -778,12 +752,12 @@ if (user && await bcrypt.compare(password, user.getAttribute('password'))) {
778
752
  ```javascript
779
753
  const { Model } = require('outlet-orm');
780
754
 
781
- // Activés par défaut (created_at, updated_at)
755
+ // Enabled by default (created_at, updated_at)
782
756
  class User extends Model {
783
757
  static timestamps = true;
784
758
  }
785
759
 
786
- // Désactiver
760
+ // Disable
787
761
  class Log extends Model {
788
762
  static timestamps = false;
789
763
  }
@@ -791,21 +765,21 @@ class Log extends Model {
791
765
 
792
766
  ## 🔄 Transactions
793
767
 
794
- Outlet ORM supporte les transactions pour garantir l'intégrité des données:
768
+ Outlet ORM supports transactions to guarantee data integrity:
795
769
 
796
770
  ```javascript
797
771
  const { DatabaseConnection, Model } = require('outlet-orm');
798
772
 
799
- // Méthode 1: Callback automatique (recommandé)
773
+ // Method 1: Automatic callback (recommended)
800
774
  const db = Model.connection;
801
775
  const result = await db.transaction(async (connection) => {
802
776
  const user = await User.create({ name: 'John', email: 'john@example.com' });
803
777
  await Account.create({ user_id: user.getAttribute('id'), balance: 0 });
804
778
  return user;
805
779
  });
806
- // Commit automatique, rollback si erreur
780
+ // Automatic commit, rollback on error
807
781
 
808
- // Méthode 2: Contrôle manuel
782
+ // Method 2: Manual control
809
783
  await db.beginTransaction();
810
784
  try {
811
785
  await User.create({ name: 'Jane' });
@@ -818,7 +792,7 @@ try {
818
792
 
819
793
  ## 🗑️ Soft Deletes
820
794
 
821
- Suppression logique avec colonne `deleted_at`:
795
+ Soft deletion using a `deleted_at` column:
822
796
 
823
797
  ```javascript
824
798
  const { Model } = require('outlet-orm');
@@ -826,31 +800,31 @@ const { Model } = require('outlet-orm');
826
800
  class Post extends Model {
827
801
  static table = 'posts';
828
802
  static softDeletes = true;
829
- // static DELETED_AT = 'deleted_at'; // Personnalisable
803
+ // static DELETED_AT = 'deleted_at'; // Customisable
830
804
  }
831
805
 
832
- // Les requêtes excluent automatiquement les supprimés
833
- const posts = await Post.all(); // Seulement les non-supprimés
806
+ // Queries automatically exclude soft-deleted records
807
+ const posts = await Post.all(); // Only non-deleted records
834
808
 
835
- // Inclure les supprimés
809
+ // Include deleted records
836
810
  const allPosts = await Post.withTrashed().get();
837
811
 
838
- // Seulement les supprimés
812
+ // Only deleted records
839
813
  const trashedPosts = await Post.onlyTrashed().get();
840
814
 
841
- // Supprimer (soft delete)
815
+ // Delete (soft delete)
842
816
  const post = await Post.find(1);
843
- await post.destroy(); // Met deleted_at à la date actuelle
817
+ await post.destroy(); // Sets deleted_at to the current date
844
818
 
845
- // Vérifier si supprimé
819
+ // Check if deleted
846
820
  if (post.trashed()) {
847
- console.log('Ce post est supprimé');
821
+ console.log('This post is deleted');
848
822
  }
849
823
 
850
824
  // Restaurer
851
825
  await post.restore();
852
826
 
853
- // Supprimer définitivement
827
+ // Delete permanently
854
828
  await post.forceDelete();
855
829
  ```
856
830
 
@@ -858,7 +832,7 @@ await post.forceDelete();
858
832
 
859
833
  ### Scopes Globaux
860
834
 
861
- Appliqués automatiquement à toutes les requêtes:
835
+ Applied automatically to all queries:
862
836
 
863
837
  ```javascript
864
838
  const { Model } = require('outlet-orm');
@@ -867,24 +841,24 @@ class Post extends Model {
867
841
  static table = 'posts';
868
842
  }
869
843
 
870
- // Ajouter un scope global
844
+ // Add a global scope
871
845
  Post.addGlobalScope('published', (query) => {
872
846
  query.where('status', 'published');
873
847
  });
874
848
 
875
- // Toutes les requêtes filtrent automatiquement
876
- const posts = await Post.all(); // Seulement les publiés
849
+ // All queries filter automatically
850
+ const posts = await Post.all(); // Published only
877
851
 
878
- // Désactiver temporairement un scope
852
+ // Temporarily disable a scope
879
853
  const allPosts = await Post.withoutGlobalScope('published').get();
880
854
 
881
- // Désactiver tous les scopes
855
+ // Disable all scopes
882
856
  const rawPosts = await Post.withoutGlobalScopes().get();
883
857
  ```
884
858
 
885
859
  ## 📣 Events / Hooks
886
860
 
887
- Interceptez les opérations sur vos modèles:
861
+ Intercept operations on your models:
888
862
 
889
863
  ```javascript
890
864
  const { Model } = require('outlet-orm');
@@ -893,53 +867,53 @@ class User extends Model {
893
867
  static table = 'users';
894
868
  }
895
869
 
896
- // Avant création
870
+ // Before creation
897
871
  User.creating((user) => {
898
872
  user.setAttribute('uuid', generateUUID());
899
- // Retourner false pour annuler
873
+ // Return false to roll back
900
874
  });
901
875
 
902
- // Après création
876
+ // After creation
903
877
  User.created((user) => {
904
- console.log(`Utilisateur ${user.getAttribute('id')} créé`);
878
+ console.log(`User ${user.getAttribute('id')} created`);
905
879
  });
906
880
 
907
- // Avant mise à jour
881
+ // Before update
908
882
  User.updating((user) => {
909
883
  user.setAttribute('updated_at', new Date());
910
884
  });
911
885
 
912
- // Après mise à jour
886
+ // After update
913
887
  User.updated((user) => {
914
- // Notifier les systèmes externes
888
+ // Notify external systems
915
889
  });
916
890
 
917
- // Événements saving/saved (création ET mise à jour)
891
+ // saving/saved events (creation AND update)
918
892
  User.saving((user) => {
919
- // Nettoyage des données
893
+ // Data cleanup
920
894
  });
921
895
 
922
896
  User.saved((user) => {
923
897
  // Cache invalidation
924
898
  });
925
899
 
926
- // Avant/après suppression
900
+ // Before/after deletion
927
901
  User.deleting((user) => {
928
- // Vérifications avant suppression
902
+ // Checks before deletion
929
903
  });
930
904
 
931
905
  User.deleted((user) => {
932
- // Nettoyage des relations
906
+ // Clean up relationships
933
907
  });
934
908
 
935
- // Pour les soft deletes
909
+ // For soft deletes
936
910
  User.restoring((user) => {});
937
911
  User.restored((user) => {});
938
912
  ```
939
913
 
940
914
  ## ✅ Validation
941
915
 
942
- Validation basique intégrée:
916
+ Built-in basic validation:
943
917
 
944
918
  ```javascript
945
919
  const { Model } = require('outlet-orm');
@@ -971,7 +945,7 @@ console.log(errors);
971
945
  // age: ['age must not exceed 150']
972
946
  // }
973
947
 
974
- // Valider ou lancer une erreur
948
+ // Validate or throw an error
975
949
  try {
976
950
  user.validateOrFail();
977
951
  } catch (error) {
@@ -979,24 +953,24 @@ try {
979
953
  }
980
954
  ```
981
955
 
982
- ### Règles disponibles
956
+ ### Available rules
983
957
 
984
- | Règle | Description |
958
+ | Rule | Description |
985
959
  |-------|-------------|
986
- | `required` | Champ obligatoire |
987
- | `string` | Doit être une chaîne |
988
- | `number` / `numeric` | Doit être un nombre |
989
- | `email` | Format email valide |
990
- | `boolean` | Doit être un booléen |
991
- | `date` | Date valide |
992
- | `min:N` | Minimum N (longueur ou valeur) |
993
- | `max:N` | Maximum N (longueur ou valeur) |
960
+ | `required` | Required field |
961
+ | `string` | Must be a string |
962
+ | `number` / `numeric` | Must be a number |
963
+ | `email` | Valid email format |
964
+ | `boolean` | Must be a boolean |
965
+ | `date` | Valid date |
966
+ | `min:N` | Minimum N (longueur ou value) |
967
+ | `max:N` | Maximum N (longueur ou value) |
994
968
  | `in:a,b,c` | Valeur parmi la liste |
995
969
  | `regex:pattern` | Match le pattern regex |
996
970
 
997
971
  ## 📊 Query Logging
998
972
 
999
- Mode debug pour analyser vos requêtes:
973
+ Debug mode to analyse your queries:
1000
974
 
1001
975
  ```javascript
1002
976
  const { Model } = require('outlet-orm');
@@ -1005,11 +979,11 @@ const { Model } = require('outlet-orm');
1005
979
  const db = Model.getConnection();
1006
980
  db.enableQueryLog();
1007
981
 
1008
- // Exécuter des requêtes
982
+ // Run queries
1009
983
  await User.where('status', 'active').get();
1010
984
  await Post.with('author').get();
1011
985
 
1012
- // Récupérer le log
986
+ // Retrieve the log
1013
987
  const queries = db.getQueryLog();
1014
988
  console.log(queries);
1015
989
  // [
@@ -1020,12 +994,12 @@ console.log(queries);
1020
994
  // Vider le log
1021
995
  db.flushQueryLog();
1022
996
 
1023
- // Désactiver le logging
997
+ // Disable le logging
1024
998
  db.disableQueryLog();
1025
999
 
1026
- // Vérifier si actif
1000
+ // Check if active
1027
1001
  if (db.isLogging()) {
1028
- console.log('Logging actif');
1002
+ console.log('Logging active');
1029
1003
  }
1030
1004
  ```
1031
1005
 
@@ -1033,103 +1007,103 @@ if (db.isLogging()) {
1033
1007
 
1034
1008
  ### DatabaseConnection
1035
1009
 
1036
- | Méthode | Description |
1010
+ | Method | Description |
1037
1011
  |---------|-------------|
1038
- | `new DatabaseConnection(config?)` | Crée une connexion (lit `.env` si config omis) |
1039
- | `connect()` | Établit la connexion (appelé automatiquement) |
1040
- | `beginTransaction()` | Démarre une transaction |
1041
- | `commit()` | Valide la transaction |
1042
- | `rollback()` | Annule la transaction |
1043
- | `transaction(callback)` | Exécute dans une transaction (auto commit/rollback) |
1044
- | `select(table, query)` | Exécute un SELECT |
1045
- | `insert(table, data)` | Insère un enregistrement |
1046
- | `insertMany(table, data[])` | Insère plusieurs enregistrements |
1047
- | `update(table, data, query)` | Met à jour des enregistrements |
1048
- | `delete(table, query)` | Supprime des enregistrements |
1049
- | `count(table, query)` | Compte les enregistrements |
1050
- | `executeRawQuery(sql, params?)` | Requête brute (résultats normalisés) |
1051
- | `execute(sql, params?)` | Requête brute (résultats natifs driver) |
1052
- | `increment(table, column, query, amount?)` | Incrément atomique |
1053
- | `decrement(table, column, query, amount?)` | Décrément atomique |
1054
- | `close()` / `disconnect()` | Ferme la connexion |
1012
+ | `new DatabaseConnection(config?)` | Creates a connection (reads `.env` if config is omitted) |
1013
+ | `connect()` | Establishes the connection (called automatically) |
1014
+ | `beginTransaction()` | Starts a transaction |
1015
+ | `commit()` | Commits the transaction |
1016
+ | `rollback()` | Rolls back the transaction |
1017
+ | `transaction(callback)` | Runs in a transaction (auto commit/rollback) |
1018
+ | `select(table, query)` | Runs a SELECT |
1019
+ | `insert(table, data)` | Inserts a record |
1020
+ | `insertMany(table, data[])` | Inserts multiple records |
1021
+ | `update(table, data, query)` | Updates records |
1022
+ | `delete(table, query)` | Deletes records |
1023
+ | `count(table, query)` | Counts records |
1024
+ | `executeRawQuery(sql, params?)` | Raw query (normalised results) |
1025
+ | `execute(sql, params?)` | Raw query (native driver results) |
1026
+ | `increment(table, column, query, amount?)` | Atomic increment |
1027
+ | `decrement(table, column, query, amount?)` | Atomic decrement |
1028
+ | `close()` / `disconnect()` | Closes the connection |
1055
1029
  | **Query Logging (static)** | |
1056
- | `enableQueryLog()` | Active le logging des requêtes |
1057
- | `disableQueryLog()` | Désactive le logging |
1058
- | `getQueryLog()` | Retourne le log des requêtes |
1059
- | `flushQueryLog()` | Vide le log |
1060
- | `isLogging()` | Vérifie si le logging est actif |
1030
+ | `enableQueryLog()` | Enables query logging |
1031
+ | `disableQueryLog()` | Disables logging |
1032
+ | `getQueryLog()` | Returns the query log |
1033
+ | `flushQueryLog()` | Clears the log |
1034
+ | `isLogging()` | Checks whether logging is active |
1061
1035
 
1062
- ### Model (méthodes statiques)
1036
+ ### Model (static methods)
1063
1037
 
1064
- | Méthode | Description |
1038
+ | Method | Description |
1065
1039
  |---------|-------------|
1066
- | `setConnection(db)` | Définit la connexion par défaut |
1067
- | `getConnection()` | Récupère la connexion (v3.0.0+) |
1068
- | `setMorphMap(map)` | Définit le mapping polymorphique |
1069
- | `query()` | Retourne un QueryBuilder |
1070
- | `all()` | Tous les enregistrements |
1071
- | `find(id)` | Trouve par ID |
1072
- | `findOrFail(id)` | Trouve ou lance une erreur |
1073
- | `first()` | Premier enregistrement |
1040
+ | `setConnection(db)` | Sets the default connection |
1041
+ | `getConnection()` | Gets the connection (v3.0.0+) |
1042
+ | `setMorphMap(map)` | Defines polymorphic mapping |
1043
+ | `query()` | Returns a QueryBuilder |
1044
+ | `all()` | All records |
1045
+ | `find(id)` | Finds by ID |
1046
+ | `findOrFail(id)` | Finds or throws an error |
1047
+ | `first()` | First record |
1074
1048
  | `where(col, op?, val)` | Clause WHERE |
1075
1049
  | `whereIn(col, vals)` | Clause WHERE IN |
1076
1050
  | `whereNull(col)` | Clause WHERE NULL |
1077
1051
  | `whereNotNull(col)` | Clause WHERE NOT NULL |
1078
- | `create(attrs)` | Crée et sauvegarde |
1079
- | `insert(data)` | Insert brut |
1052
+ | `create(attrs)` | Creates and saves |
1053
+ | `insert(data)` | Raw insert |
1080
1054
  | `update(attrs)` | Update bulk |
1081
- | `updateById(id, attrs)` | Update par ID |
1082
- | `updateAndFetchById(id, attrs, rels?)` | Update + fetch avec relations |
1055
+ | `updateById(id, attrs)` | Update by ID |
1056
+ | `updateAndFetchById(id, attrs, rels?)` | Update + fetch with relationships |
1083
1057
  | `delete()` | Delete bulk |
1084
1058
  | `with(...rels)` | Eager loading |
1085
- | `withHidden()` | Inclut les attributs cachés |
1086
- | `withoutHidden(show?)` | Contrôle visibilité |
1087
- | `orderBy(col, dir?)` | Tri |
1088
- | `limit(n)` / `offset(n)` | Limite/Offset |
1059
+ | `withHidden()` | Includes hidden attributes |
1060
+ | `withoutHidden(show?)` | Control attribute visibility |
1061
+ | `orderBy(col, dir?)` | Sort |
1062
+ | `limit(n)` / `offset(n)` | Limit/Offset |
1089
1063
  | `paginate(page, perPage)` | Pagination |
1090
- | `count()` | Compte |
1064
+ | `count()` | Count |
1091
1065
  | **Soft Deletes** | |
1092
- | `withTrashed()` | Inclut les supprimés |
1093
- | `onlyTrashed()` | Seulement les supprimés |
1066
+ | `withTrashed()` | Includes soft-deleted records |
1067
+ | `onlyTrashed()` | Only soft-deleted records |
1094
1068
  | **Scopes** | |
1095
- | `addGlobalScope(name, cb)` | Ajoute un scope global |
1096
- | `removeGlobalScope(name)` | Supprime un scope |
1097
- | `withoutGlobalScope(name)` | Requête sans un scope |
1098
- | `withoutGlobalScopes()` | Requête sans tous les scopes |
1069
+ | `addGlobalScope(name, cb)` | Adds a global scope |
1070
+ | `removeGlobalScope(name)` | Removes a scope |
1071
+ | `withoutGlobalScope(name)` | Query without one scope |
1072
+ | `withoutGlobalScopes()` | Query without all scopes |
1099
1073
  | **Events** | |
1100
- | `on(event, callback)` | Enregistre un listener |
1101
- | `creating(cb)` / `created(cb)` | Events création |
1102
- | `updating(cb)` / `updated(cb)` | Events mise à jour |
1103
- | `saving(cb)` / `saved(cb)` | Events sauvegarde |
1104
- | `deleting(cb)` / `deleted(cb)` | Events suppression |
1105
- | `restoring(cb)` / `restored(cb)` | Events restauration |
1074
+ | `on(event, callback)` | Registers a listener |
1075
+ | `creating(cb)` / `created(cb)` | Creation events |
1076
+ | `updating(cb)` / `updated(cb)` | Update events |
1077
+ | `saving(cb)` / `saved(cb)` | Save events |
1078
+ | `deleting(cb)` / `deleted(cb)` | Delete events |
1079
+ | `restoring(cb)` / `restored(cb)` | Restore events |
1106
1080
 
1107
- ### Model (méthodes d'instance)
1081
+ ### Model (instance methods)
1108
1082
 
1109
- | Méthode | Description |
1083
+ | Method | Description |
1110
1084
  |---------|-------------|
1111
- | `fill(attrs)` | Remplit les attributs |
1112
- | `setAttribute(key, val)` | Définit un attribut |
1113
- | `getAttribute(key)` | Récupère un attribut |
1114
- | `save()` | Sauvegarde (insert ou update) |
1115
- | `destroy()` | Supprime l'instance (soft si activé) |
1116
- | `load(...rels)` | Charge des relations |
1117
- | `getDirty()` | Attributs modifiés |
1118
- | `isDirty()` | A été modifié? |
1119
- | `toJSON()` | Convertit en objet |
1085
+ | `fill(attrs)` | Fills attributes |
1086
+ | `setAttribute(key, val)` | Sets an attribute |
1087
+ | `getAttribute(key)` | Gets an attribute |
1088
+ | `save()` | Save (insert or update) |
1089
+ | `destroy()` | Delete the instance (soft if enabled) |
1090
+ | `load(...rels)` | Load relationships |
1091
+ | `getDirty()` | Modified attributes |
1092
+ | `isDirty()` | Has been modified? |
1093
+ | `toJSON()` | Convert to plain object |
1120
1094
  | **Soft Deletes** | |
1121
- | `trashed()` | Est supprimé? |
1122
- | `restore()` | Restaure le modèle |
1123
- | `forceDelete()` | Suppression définitive |
1095
+ | `trashed()` | Is deleted? |
1096
+ | `restore()` | Restore the model |
1097
+ | `forceDelete()` | Permanent deletion |
1124
1098
  | **Validation** | |
1125
- | `validate()` | Valide selon les règles |
1126
- | `validateOrFail()` | Valide ou lance erreur |
1099
+ | `validate()` | Validate against rules |
1100
+ | `validateOrFail()` | Validate or throw error |
1127
1101
 
1128
1102
  ### QueryBuilder
1129
1103
 
1130
- | Méthode | Description |
1104
+ | Method | Description |
1131
1105
  |---------|-------------|
1132
- | `select(...cols)` / `columns([...])` | Sélection de colonnes |
1106
+ | `select(...cols)` / `columns([...])` | Column selection |
1133
1107
  | `distinct()` | SELECT DISTINCT |
1134
1108
  | `where(col, op?, val)` | Clause WHERE |
1135
1109
  | `whereIn(col, vals)` | WHERE IN |
@@ -1139,70 +1113,70 @@ if (db.isLogging()) {
1139
1113
  | `orWhere(col, op?, val)` | OR WHERE |
1140
1114
  | `whereBetween(col, [min, max])` | WHERE BETWEEN |
1141
1115
  | `whereLike(col, pattern)` | WHERE LIKE |
1142
- | `whereHas(rel, cb?)` | Filtre par relation |
1143
- | `has(rel, op?, count)` | Existence relationnelle |
1144
- | `whereDoesntHave(rel)` | Absence de relation |
1145
- | `orderBy(col, dir?)` / `ordrer(...)` | Tri |
1146
- | `limit(n)` / `take(n)` | Limite |
1116
+ | `whereHas(rel, cb?)` | Filter by existence of relation |
1117
+ | `has(rel, op?, count)` | Relational existence |
1118
+ | `whereDoesntHave(rel)` | Absence of relation |
1119
+ | `orderBy(col, dir?)` / `ordrer(...)` | Sort |
1120
+ | `limit(n)` / `take(n)` | Limit |
1147
1121
  | `offset(n)` / `skip(n)` | Offset |
1148
1122
  | `groupBy(...cols)` | GROUP BY |
1149
1123
  | `having(col, op, val)` | HAVING |
1150
1124
  | `join(table, first, op?, second)` | INNER JOIN |
1151
1125
  | `leftJoin(table, first, op?, second)` | LEFT JOIN |
1152
1126
  | `with(...rels)` | Eager loading |
1153
- | `withCount(rels)` | Ajoute {rel}_count |
1154
- | `withTrashed()` | Inclut les supprimés |
1155
- | `onlyTrashed()` | Seulement les supprimés |
1156
- | `withoutGlobalScope(name)` | Sans un scope global |
1157
- | `withoutGlobalScopes()` | Sans tous les scopes |
1158
- | `get()` | Exécute et retourne tous |
1159
- | `first()` | Premier résultat |
1127
+ | `withCount(rels)` | Adds `{rel}_count` |
1128
+ | `withTrashed()` | Includes soft-deleted records |
1129
+ | `onlyTrashed()` | Only soft-deleted records |
1130
+ | `withoutGlobalScope(name)` | Without one global scope |
1131
+ | `withoutGlobalScopes()` | Without all global scopes |
1132
+ | `get()` | Runs and returns all |
1133
+ | `first()` | First result |
1160
1134
  | `firstOrFail()` | Premier ou erreur |
1161
1135
  | `paginate(page, perPage)` | Pagination |
1162
1136
  | `count()` | Compte |
1163
- | `exists()` | Vérifie l'existence |
1137
+ | `exists()` | Checks existence |
1164
1138
  | `insert(data)` | Insert |
1165
1139
  | `update(attrs)` | Update |
1166
1140
  | `updateAndFetch(attrs, rels?)` | Update + fetch |
1167
1141
  | `delete()` | Delete |
1168
- | `increment(col, amount?)` | Incrément atomique |
1169
- | `decrement(col, amount?)` | Décrément atomique |
1170
- | `clone()` | Clone le query builder |
1142
+ | `increment(col, amount?)` | Atomic increment |
1143
+ | `decrement(col, amount?)` | Atomic decrement |
1144
+ | `clone()` | Clones the query builder |
1171
1145
 
1172
- ## 🛠️ Outils CLI
1146
+ ## 🛠️ CLI tools
1173
1147
 
1174
1148
  ### outlet-init
1175
1149
 
1176
- Initialise un nouveau projet avec configuration de base de données.
1150
+ Initialises a new project with database configuration.
1177
1151
 
1178
1152
  ```bash
1179
1153
  outlet-init
1180
1154
  ```
1181
1155
 
1182
- Génère:
1183
- - Fichier de configuration `database/config.js`
1184
- - Fichier `.env` avec les paramètres
1185
- - Modèle exemple
1186
- - Fichier d'utilisation
1156
+ Generates:
1157
+ - Configuration file `database/config.js`
1158
+ - `.env` file with settings
1159
+ - Example model
1160
+ - Usage file
1187
1161
 
1188
1162
  ### outlet-migrate
1189
1163
 
1190
- Système complet de migrations.
1164
+ complete migration system.
1191
1165
 
1192
1166
  ```bash
1193
- # Créer une migration
1167
+ # Create a migration
1194
1168
  outlet-migrate make create_users_table
1195
1169
 
1196
- # Exécuter les migrations
1170
+ # Run migrations
1197
1171
  outlet-migrate migrate
1198
1172
 
1199
- # Voir le statut
1173
+ # See migration status
1200
1174
  outlet-migrate status
1201
1175
 
1202
- # Annuler la dernière migration
1176
+ # Roll back the latest migration
1203
1177
  outlet-migrate rollback --steps 1
1204
1178
 
1205
- # Reset toutes les migrations
1179
+ # Reset all migrations
1206
1180
  outlet-migrate reset --yes
1207
1181
 
1208
1182
  # Refresh (reset + migrate)
@@ -1212,52 +1186,52 @@ outlet-migrate refresh --yes
1212
1186
  outlet-migrate fresh --yes
1213
1187
  ```
1214
1188
 
1215
- **Fonctionnalités des Migrations:**
1189
+ **Migration Features:**
1216
1190
 
1217
- - ✅ Création et gestion des migrations (create, alter, drop tables)
1218
- - ✅ Types de colonnes: id, string, text, integer, boolean, date, datetime, timestamp, decimal, float, json, enum, uuid, foreignId
1219
- - ✅ Modificateurs: nullable, default, unique, index, unsigned, autoIncrement, comment, after, first
1220
- - ✅ Clés étrangères: foreign(), constrained(), onDelete(), onUpdate(), CASCADE
1191
+ - ✅ Creation and management of migrations (create, alter, drop tables)
1192
+ - ✅ Column types: id, string, text, integer, boolean, date, datetime, timestamp, decimal, float, json, enum, uuid, foreignId
1193
+ - ✅ Modifiers: nullable, default, unique, index, unsigned, autoIncrement, comment, after, first
1194
+ - ✅ Foreign keys: foreign(), constrained(), onDelete(), onUpdate(), CASCADE
1221
1195
  - ✅ Index: index(), unique(), fullText()
1222
1196
  - ✅ Manipulation: renameColumn(), dropColumn(), dropTimestamps()
1223
- - ✅ Migrations réversibles: Méthodes up() et down()
1224
- - ✅ Batch tracking: Rollback précis par batch
1225
- - ✅ SQL personnalisé: execute() pour commandes avancées
1197
+ - ✅ Reversible migrations: up() and down() methods
1198
+ - ✅ Batch tracking: Precise rollback by batch
1199
+ - ✅ Custom SQL: execute() for advanced commands
1226
1200
 
1227
1201
  ### outlet-convert
1228
1202
 
1229
- Convertit des schémas SQL en modèles ORM.
1203
+ Converts SQL schemas into ORM models.
1230
1204
 
1231
1205
  ```bash
1232
1206
  outlet-convert
1233
1207
  ```
1234
1208
 
1235
1209
  **Options:**
1236
- 1. Depuis un fichier SQL local
1237
- 2. Depuis une base de données connectée
1210
+ 1. From a local SQL file
1211
+ 2. From a connected database
1238
1212
 
1239
- **Fonctionnalités:**
1240
- - ✅ Détection automatique des types et casts
1241
- - ✅ Génération automatique de TOUTES les relations (belongsTo, hasMany, hasOne, belongsToMany)
1242
- - ✅ Relations récursives (auto-relations)
1243
- - ✅ Détection des champs sensibles (password, token, etc.)
1244
- - ✅ Support des timestamps automatiques
1245
- - ✅ Conversion des noms en PascalCase
1213
+ **Features:**
1214
+ - ✅ Automatic type and cast detection
1215
+ - ✅ Automatic generation of ALL relationships (belongsTo, hasMany, hasOne, belongsToMany)
1216
+ - ✅ Recursive relationships (auto-relationships)
1217
+ - ✅ Detection of sensitive fields (password, token, etc.)
1218
+ - ✅ Automatic timestamps support
1219
+ - ✅ Class names converted to PascalCase
1246
1220
 
1247
1221
  ## 📚 Documentation
1248
1222
 
1249
- - [Guide des Migrations](docs/MIGRATIONS.md)
1250
- - [Conversion SQL](docs/SQL_CONVERSION.md)
1251
- - [Détection des Relations](docs/RELATIONS_DETECTION.md)
1252
- - [Guide de démarrage rapide](docs/QUICKSTART.md)
1223
+ - [Migrations Guide](docs/MIGRATIONS.md)
1224
+ - [SQL Conversion](docs/SQL_CONVERSION.md)
1225
+ - [Relation Detection](docs/RELATIONS_DETECTION.md)
1226
+ - [Quick Start Guide](docs/QUICKSTART.md)
1253
1227
  - [Architecture](docs/ARCHITECTURE.md)
1254
1228
  - [**TypeScript (complet)**](docs/TYPESCRIPT.md)
1255
1229
 
1256
1230
  ## 📘 TypeScript Support
1257
1231
 
1258
- Outlet ORM v4.0.0 inclut des définitions TypeScript complètes avec support des **generics pour les attributs typés**.
1232
+ Outlet ORM v4.0.0 includes complete TypeScript definitions with support for **generics for typed model attributes**.
1259
1233
 
1260
- ### Modèles typés
1234
+ ### Typed models
1261
1235
 
1262
1236
  ```typescript
1263
1237
  import { Model, HasManyRelation } from 'outlet-orm';
@@ -1281,11 +1255,11 @@ class User extends Model<UserAttributes> {
1281
1255
 
1282
1256
  // Type-safe getAttribute/setAttribute
1283
1257
  const user = await User.find(1);
1284
- const name: string = user.getAttribute('name'); // ✅ Type inféré
1258
+ const name: string = user.getAttribute('name'); // ✅ Inferred type
1285
1259
  const role: 'admin' | 'user' = user.getAttribute('role');
1286
1260
  ```
1287
1261
 
1288
- ### Migrations typées
1262
+ ### Migrations typedes
1289
1263
 
1290
1264
  ```typescript
1291
1265
  import { MigrationInterface, Schema, TableBuilder } from 'outlet-orm';
@@ -1310,16 +1284,16 @@ export const migration: MigrationInterface = {
1310
1284
 
1311
1285
  📖 [Guide TypeScript complet](docs/TYPESCRIPT.md)
1312
1286
 
1313
- ## 🤝 Contribution
1287
+ ## 🤝 Contributions
1314
1288
 
1315
- Les contributions sont les bienvenues! N'hésitez pas à ouvrir une issue ou un pull request.
1289
+ Contributions are welcome! Feel free to open an issue or pull request.
1316
1290
 
1317
- Voir [CONTRIBUTING.md](CONTRIBUTING.md) pour les guidelines.
1291
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
1318
1292
 
1319
1293
  ## 📄 Licence
1320
1294
 
1321
- MIT - Voir [LICENSE](LICENSE) pour plus de détails.
1295
+ MIT - See [LICENSE](LICENSE) for details.
1322
1296
 
1323
1297
  ---
1324
1298
 
1325
- Créé par [omgbwa-yasse](https://github.com/omgbwa-yasse)
1299
+ Created by [omgbwa-yasse](https://github.com/omgbwa-yasse)