outlet-orm 5.5.1 → 5.5.2
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.
- package/README.md +259 -259
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/outlet-orm)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
Un ORM JavaScript
|
|
6
|
+
Un ORM JavaScript inspired de Laravel Eloquent pour Node.js avec support pour MySQL, PostgreSQL et SQLite.
|
|
7
7
|
|
|
8
|
-
📚 **[Documentation
|
|
8
|
+
📚 **[Documentation complete available dans `/docs`](./docs/INDEX.md)**
|
|
9
9
|
|
|
10
|
-
## ✅
|
|
10
|
+
## ✅ Prerequisites and compatibility
|
|
11
11
|
|
|
12
|
-
- Node.js >= 18 (
|
|
13
|
-
-
|
|
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,61 @@ 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
|
-
###
|
|
21
|
+
### Install the database driver
|
|
22
22
|
|
|
23
|
-
Outlet ORM utilise des peerDependencies optionnelles pour les drivers de
|
|
23
|
+
Outlet ORM utilise des peerDependencies optionnelles pour les drivers de database. Installez uniquement le driver dont vous avez besoin:
|
|
24
24
|
|
|
25
25
|
- MySQL/MariaDB: `npm install mysql2`
|
|
26
26
|
- PostgreSQL: `npm install pg`
|
|
27
27
|
- SQLite: `npm install sqlite3`
|
|
28
28
|
|
|
29
|
-
|
|
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
|
|
31
|
+
## 📁 Structure de Projet recommended
|
|
32
32
|
|
|
33
|
-
Organisez votre projet utilisant Outlet ORM avec une **architecture en couches** (
|
|
33
|
+
Organisez votre projet utilisant Outlet ORM avec une **architecture en couches** (recommended pour la production) :
|
|
34
34
|
|
|
35
|
-
> 🔐 **Sécurité** :
|
|
35
|
+
> 🔐 **Sécurité** : See the [Security Guide](./docs/SECURITY.md) pour les bonnes pratiques.
|
|
36
36
|
|
|
37
37
|
```
|
|
38
38
|
mon-projet/
|
|
39
|
-
├── .env # ⚠️ JAMAIS
|
|
40
|
-
├── .env.example # Template
|
|
39
|
+
├── .env # ⚠️ JAMAIS committed (dans .gitignore)
|
|
40
|
+
├── .env.example # Template without secrets
|
|
41
41
|
├── .gitignore
|
|
42
42
|
├── package.json
|
|
43
43
|
│
|
|
44
44
|
├── src/ # 📦 Code source centralisé
|
|
45
|
-
│ ├── index.js #
|
|
45
|
+
│ ├── index.js # Entry point de l'application
|
|
46
46
|
│ │
|
|
47
47
|
│ ├── config/ # ⚙️ Configuration
|
|
48
|
-
│ │ ├── app.js #
|
|
48
|
+
│ │ ├── app.js # General config (port, env)
|
|
49
49
|
│ │ ├── database.js # Config DB (lit .env)
|
|
50
50
|
│ │ └── security.js # CORS, helmet, rate limit
|
|
51
51
|
│ │
|
|
52
|
-
│ ├── models/ # 📊
|
|
52
|
+
│ ├── models/ # 📊 Data Layer (Entities)
|
|
53
53
|
│ │ ├── index.js # Export centralisé des models
|
|
54
54
|
│ │ ├── User.js
|
|
55
55
|
│ │ ├── Post.js
|
|
56
56
|
│ │ └── Comment.js
|
|
57
57
|
│ │
|
|
58
|
-
│ ├── repositories/ # 🗄️
|
|
59
|
-
│ │ ├── BaseRepository.js #
|
|
60
|
-
│ │ ├── UserRepository.js #
|
|
58
|
+
│ ├── repositories/ # 🗄️ Data Access Layer
|
|
59
|
+
│ │ ├── BaseRepository.js # Generic CRUD methods
|
|
60
|
+
│ │ ├── UserRepository.js # Specific queries User
|
|
61
61
|
│ │ └── PostRepository.js
|
|
62
62
|
│ │
|
|
63
|
-
│ ├── services/ # 💼
|
|
63
|
+
│ ├── services/ # 💼 Business Layer (Business Logic)
|
|
64
64
|
│ │ ├── AuthService.js # Logique d'authentification
|
|
65
|
-
│ │ ├── UserService.js #
|
|
65
|
+
│ │ ├── UserService.js # User business logic
|
|
66
66
|
│ │ ├── PostService.js
|
|
67
67
|
│ │ └── EmailService.js # Service externe (emails)
|
|
68
68
|
│ │
|
|
69
|
-
│ ├── controllers/ # 🎮
|
|
69
|
+
│ ├── controllers/ # 🎮 Presentation Layer (HTTP)
|
|
70
70
|
│ │ ├── AuthController.js
|
|
71
71
|
│ │ ├── UserController.js
|
|
72
72
|
│ │ └── PostController.js
|
|
73
73
|
│ │
|
|
74
|
-
│ ├── routes/ # 🛤️
|
|
75
|
-
│ │ ├── index.js #
|
|
74
|
+
│ ├── routes/ # 🛤️ Route definitions
|
|
75
|
+
│ │ ├── index.js # Route aggregator
|
|
76
76
|
│ │ ├── auth.routes.js
|
|
77
77
|
│ │ ├── user.routes.js
|
|
78
78
|
│ │ └── post.routes.js
|
|
@@ -82,9 +82,9 @@ mon-projet/
|
|
|
82
82
|
│ │ ├── authorize.js # RBAC / permissions
|
|
83
83
|
│ │ ├── rateLimiter.js # Protection DDoS
|
|
84
84
|
│ │ ├── validator.js # Validation request body
|
|
85
|
-
│ │ └── errorHandler.js # Gestion
|
|
85
|
+
│ │ └── errorHandler.js # Gestion centralisede erreurs
|
|
86
86
|
│ │
|
|
87
|
-
│ ├── validators/ # ✅
|
|
87
|
+
│ ├── validators/ # ✅ Validation schemas
|
|
88
88
|
│ │ ├── authValidator.js
|
|
89
89
|
│ │ └── userValidator.js
|
|
90
90
|
│ │
|
|
@@ -92,34 +92,34 @@ mon-projet/
|
|
|
92
92
|
│ ├── hash.js # bcrypt wrapper
|
|
93
93
|
│ ├── token.js # JWT helpers
|
|
94
94
|
│ ├── logger.js # Winston/Pino config
|
|
95
|
-
│ └── response.js #
|
|
95
|
+
│ └── response.js # API response formatting
|
|
96
96
|
│
|
|
97
97
|
├── database/
|
|
98
98
|
│ ├── config.js # Config migrations (outlet-init)
|
|
99
|
-
│ ├── migrations/ #
|
|
100
|
-
│ └── seeds/ #
|
|
99
|
+
│ ├── migrations/ # Migration files
|
|
100
|
+
│ └── seeds/ # Test/demo data
|
|
101
101
|
│ └── UserSeeder.js
|
|
102
102
|
│
|
|
103
|
-
├── public/ # ✅
|
|
103
|
+
├── public/ # ✅ Public static files
|
|
104
104
|
│ ├── images/
|
|
105
105
|
│ ├── css/
|
|
106
106
|
│ └── js/
|
|
107
107
|
│
|
|
108
|
-
├── uploads/ # ⚠️
|
|
108
|
+
├── uploads/ # ⚠️ Uploaded files
|
|
109
109
|
│
|
|
110
|
-
├── logs/ # 📋 Journaux (
|
|
110
|
+
├── logs/ # 📋 Journaux (not versioned)
|
|
111
111
|
│
|
|
112
112
|
└── tests/ # 🧪 Tests
|
|
113
113
|
├── unit/ # Tests unitaires
|
|
114
114
|
│ ├── services/
|
|
115
115
|
│ └── models/
|
|
116
|
-
├── integration/ #
|
|
116
|
+
├── integration/ # Integration tests
|
|
117
117
|
│ └── api/
|
|
118
|
-
└── fixtures/ #
|
|
118
|
+
└── fixtures/ # Test data
|
|
119
119
|
└── users.json
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
### 🏗️ Architecture
|
|
122
|
+
### 🏗️ Layered Architecture
|
|
123
123
|
|
|
124
124
|
```
|
|
125
125
|
┌─────────────────────────────────────────────────────────────┐
|
|
@@ -133,20 +133,20 @@ mon-projet/
|
|
|
133
133
|
│
|
|
134
134
|
▼
|
|
135
135
|
┌─────────────────────────────────────────────────────────────┐
|
|
136
|
-
│ ROUTES → CONTROLLERS (
|
|
137
|
-
│
|
|
136
|
+
│ ROUTES → CONTROLLERS (Presentation Layer) │
|
|
137
|
+
│ Receives the request, calls the service, returns a response │
|
|
138
138
|
└─────────────────────────────────────────────────────────────┘
|
|
139
139
|
│
|
|
140
140
|
▼
|
|
141
141
|
┌─────────────────────────────────────────────────────────────┐
|
|
142
|
-
│ SERVICES (
|
|
143
|
-
│ Logique métier, orchestration,
|
|
142
|
+
│ SERVICES (Business Layer / Business Logic) │
|
|
143
|
+
│ Logique métier, orchestration, rules business │
|
|
144
144
|
└─────────────────────────────────────────────────────────────┘
|
|
145
145
|
│
|
|
146
146
|
▼
|
|
147
147
|
┌─────────────────────────────────────────────────────────────┐
|
|
148
|
-
│ REPOSITORIES (
|
|
149
|
-
│ Abstraction des
|
|
148
|
+
│ REPOSITORIES (Data Access Layer) │
|
|
149
|
+
│ Abstraction des queries DB, utilise les Models │
|
|
150
150
|
└─────────────────────────────────────────────────────────────┘
|
|
151
151
|
│
|
|
152
152
|
▼
|
|
@@ -155,23 +155,23 @@ mon-projet/
|
|
|
155
155
|
└─────────────────────────────────────────────────────────────┘
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
### 📋
|
|
158
|
+
### 📋 Role of each layer
|
|
159
159
|
|
|
160
|
-
|
|
|
160
|
+
| Layer | Folder | Responsibility | Depends on |
|
|
161
161
|
|--------|---------|----------------|-----------|
|
|
162
|
-
| **Présentation** | `controllers/` |
|
|
163
|
-
| **Métier** | `services/` | Logique business, orchestration,
|
|
164
|
-
| **
|
|
165
|
-
| **
|
|
162
|
+
| **Présentation** | `controllers/` | Handle HTTP, validate entrées, format responses | Services |
|
|
163
|
+
| **Métier** | `services/` | Logique business, orchestration, rules | Repositories |
|
|
164
|
+
| **Data** | `repositories/` | Complex DB queries, abstraction | Models |
|
|
165
|
+
| **Entities** | `models/` | Entity definitions, relationships, validations | Outlet ORM |
|
|
166
166
|
|
|
167
|
-
### ✅
|
|
167
|
+
### ✅ Benefits of this architecture
|
|
168
168
|
|
|
169
|
-
- **
|
|
170
|
-
- **
|
|
171
|
-
- **
|
|
172
|
-
- **
|
|
169
|
+
- **Testability** : Each layer can be tested independently
|
|
170
|
+
- **Maintainability** : Clear separation of responsibilities
|
|
171
|
+
- **Scalability** : Easy to add new features
|
|
172
|
+
- **Reusability** : Services reusable from CLI, workers, etc.
|
|
173
173
|
|
|
174
|
-
### 📝
|
|
174
|
+
### 📝 Example workflow
|
|
175
175
|
|
|
176
176
|
```javascript
|
|
177
177
|
// routes/user.routes.js
|
|
@@ -196,71 +196,71 @@ async findWithPosts(id) {
|
|
|
196
196
|
}
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
## ✨
|
|
199
|
+
## ✨ Key features
|
|
200
200
|
|
|
201
|
-
- **API
|
|
201
|
+
- **API inspirede d'Eloquent** (Active Record) pour un usage fluide
|
|
202
202
|
- **Query Builder expressif**: where/joins/order/limit/offset/paginate
|
|
203
|
-
- **
|
|
204
|
-
- **Eager Loading** des
|
|
205
|
-
- **Relations
|
|
203
|
+
- **Relationship filters Laravel-style**: `whereHas()`, `has()`, `whereDoesntHave()`, `withCount()`
|
|
204
|
+
- **Eager Loading** des relationships via `.with(...)` avec constraints et dot-notation
|
|
205
|
+
- **Relations completes**:
|
|
206
206
|
- `hasOne`, `hasMany`, `belongsTo`, `belongsToMany` (avec attach/detach/sync)
|
|
207
|
-
- `hasManyThrough`, `hasOneThrough` (
|
|
208
|
-
- `morphOne`, `morphMany`, `morphTo` (
|
|
209
|
-
- **Transactions**
|
|
210
|
-
- **Soft Deletes**:
|
|
211
|
-
- **Scopes**:
|
|
207
|
+
- `hasManyThrough`, `hasOneThrough` (relationships transitives)
|
|
208
|
+
- `morphOne`, `morphMany`, `morphTo` (relationships polymorphiques)
|
|
209
|
+
- **Transactions** completes: `beginTransaction()`, `commit()`, `rollback()`, `transaction()`
|
|
210
|
+
- **Soft Deletes**: soft deletion avec `deleted_at`, `withTrashed()`, `onlyTrashed()`, `restore()`
|
|
211
|
+
- **Scopes**: global and local to reuse your filters
|
|
212
212
|
- **Events/Hooks**: `creating`, `created`, `updating`, `updated`, `deleting`, `deleted`, etc.
|
|
213
|
-
- **Validation**:
|
|
213
|
+
- **Validation**: rules built-in basic (`required`, `email`, `min`, `max`, etc.)
|
|
214
214
|
- **Query Logging**: mode debug avec `enableQueryLog()` et `getQueryLog()`
|
|
215
|
-
- **Pool PostgreSQL**:
|
|
215
|
+
- **Pool PostgreSQL**: pooled connections pour better performance
|
|
216
216
|
- **Protection SQL**: sanitization automatique des identifiants
|
|
217
217
|
- **Casts automatiques** (int, float, boolean, json, date...)
|
|
218
|
-
- **
|
|
219
|
-
- **
|
|
220
|
-
- **
|
|
218
|
+
- **Hidden attributes** (`hidden`) et timestamps automatiques
|
|
219
|
+
- **Visibility control** des attributs cachés: `withHidden()` et `withoutHidden()`
|
|
220
|
+
- **Atomic increment/decrement**: `increment()` et `decrement()`
|
|
221
221
|
- **Aliases ergonomiques**: `columns([...])`, `ordrer()` (alias typo de `orderBy`)
|
|
222
|
-
- **
|
|
223
|
-
- **Migrations
|
|
222
|
+
- **Raw queries**: `executeRawQuery()` et `execute()` (native driver results)
|
|
223
|
+
- **Migrations completes** (create/alter/drop, index, foreign keys, batch tracking)
|
|
224
224
|
- **CLI pratiques**: `outlet-init`, `outlet-migrate`, `outlet-convert`
|
|
225
|
-
- **Configuration via `.env`** (
|
|
226
|
-
- **Multi-
|
|
227
|
-
- **
|
|
225
|
+
- **Configuration via `.env`** (loaded automatically)
|
|
226
|
+
- **Multi-database**: MySQL, PostgreSQL et SQLite
|
|
227
|
+
- **Complete TypeScript types** avec Generic Model et Schema Builder typed (v4.0.0+)
|
|
228
228
|
|
|
229
|
-
## ⚡
|
|
229
|
+
## ⚡ Quick Start
|
|
230
230
|
|
|
231
231
|
### Initialisation du projet
|
|
232
232
|
|
|
233
233
|
```bash
|
|
234
|
-
#
|
|
234
|
+
# Create la configuration initiale
|
|
235
235
|
outlet-init
|
|
236
236
|
|
|
237
|
-
#
|
|
237
|
+
# Create une migration
|
|
238
238
|
outlet-migrate make create_users_table
|
|
239
239
|
|
|
240
|
-
#
|
|
240
|
+
# Run les migrations
|
|
241
241
|
outlet-migrate migrate
|
|
242
242
|
```
|
|
243
243
|
|
|
244
244
|
### 🌱 Seeding rapide
|
|
245
245
|
|
|
246
246
|
```bash
|
|
247
|
-
#
|
|
247
|
+
# Create un seeder
|
|
248
248
|
outlet-migrate make:seed UserSeeder
|
|
249
249
|
|
|
250
|
-
#
|
|
250
|
+
# Run les seeds (DatabaseSeeder prioritaire)
|
|
251
251
|
outlet-migrate seed
|
|
252
252
|
|
|
253
|
-
#
|
|
253
|
+
# Run un specific seeder
|
|
254
254
|
outlet-migrate seed --class UserSeeder
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
-
## 📖
|
|
257
|
+
## 📖 Usage
|
|
258
258
|
|
|
259
|
-
###
|
|
259
|
+
### Connection configuration
|
|
260
260
|
|
|
261
|
-
Outlet ORM charge automatiquement la
|
|
261
|
+
Outlet ORM charge automatiquement la connection depuis le file `.env`. **Plus besoin d'importer DatabaseConnection !**
|
|
262
262
|
|
|
263
|
-
####
|
|
263
|
+
#### `.env` file
|
|
264
264
|
|
|
265
265
|
```env
|
|
266
266
|
DB_DRIVER=mysql
|
|
@@ -271,7 +271,7 @@ DB_PASSWORD=secret
|
|
|
271
271
|
DB_PORT=3306
|
|
272
272
|
```
|
|
273
273
|
|
|
274
|
-
####
|
|
274
|
+
#### Simplified usage
|
|
275
275
|
|
|
276
276
|
```javascript
|
|
277
277
|
const { Model } = require('outlet-orm');
|
|
@@ -280,18 +280,18 @@ class User extends Model {
|
|
|
280
280
|
static table = 'users';
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
// C'est tout ! La
|
|
283
|
+
// C'est tout ! La connection est automatique
|
|
284
284
|
const users = await User.all();
|
|
285
285
|
```
|
|
286
286
|
|
|
287
|
-
####
|
|
287
|
+
#### Manual configuration (optional)
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
If you need to control the connection :
|
|
290
290
|
|
|
291
291
|
```javascript
|
|
292
292
|
const { DatabaseConnection, Model } = require('outlet-orm');
|
|
293
293
|
|
|
294
|
-
// Option 1 – via .env (
|
|
294
|
+
// Option 1 – via .env (no parameters required)
|
|
295
295
|
const db = new DatabaseConnection();
|
|
296
296
|
|
|
297
297
|
// Option 2 – via objet de configuration
|
|
@@ -304,48 +304,48 @@ const db = new DatabaseConnection({
|
|
|
304
304
|
port: 3306
|
|
305
305
|
});
|
|
306
306
|
|
|
307
|
-
// Définir la
|
|
307
|
+
// Définir la connection manuellement (optionnel)
|
|
308
308
|
Model.setConnection(db);
|
|
309
309
|
```
|
|
310
310
|
|
|
311
|
-
####
|
|
311
|
+
#### Environment variables (.env)
|
|
312
312
|
|
|
313
|
-
| Variable | Description |
|
|
313
|
+
| Variable | Description | Default |
|
|
314
314
|
|----------|-------------|------------|
|
|
315
315
|
| `DB_DRIVER` | `mysql`, `postgres`, `sqlite` | `mysql` |
|
|
316
316
|
| `DB_HOST` | Hôte de la base | `localhost` |
|
|
317
|
-
| `DB_PORT` | Port de
|
|
317
|
+
| `DB_PORT` | Port de connection | Selon driver |
|
|
318
318
|
| `DB_USER` / `DB_USERNAME` | Identifiant | - |
|
|
319
319
|
| `DB_PASSWORD` | Mot de passe | - |
|
|
320
320
|
| `DB_DATABASE` / `DB_NAME` | Nom de la base | - |
|
|
321
|
-
| `DB_FILE` / `SQLITE_DB` |
|
|
321
|
+
| `DB_FILE` / `SQLITE_DB` | SQLite file | `:memory:` |
|
|
322
322
|
|
|
323
323
|
### Importation
|
|
324
324
|
|
|
325
325
|
```javascript
|
|
326
|
-
// CommonJS - Import simple (
|
|
326
|
+
// CommonJS - Import simple (connection automatique via .env)
|
|
327
327
|
const { Model } = require('outlet-orm');
|
|
328
328
|
|
|
329
329
|
// ES Modules
|
|
330
330
|
import { Model } from 'outlet-orm';
|
|
331
331
|
|
|
332
|
-
// Si besoin de
|
|
332
|
+
// Si besoin de Manual control sur la connection
|
|
333
333
|
const { DatabaseConnection, Model } = require('outlet-orm');
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
-
###
|
|
336
|
+
### Define a model
|
|
337
337
|
|
|
338
338
|
```javascript
|
|
339
339
|
const { Model } = require('outlet-orm');
|
|
340
340
|
|
|
341
|
-
//
|
|
341
|
+
// Define related models (see Relationships)
|
|
342
342
|
class Post extends Model { static table = 'posts'; }
|
|
343
343
|
class Profile extends Model { static table = 'profiles'; }
|
|
344
344
|
|
|
345
345
|
class User extends Model {
|
|
346
346
|
static table = 'users';
|
|
347
|
-
static primaryKey = 'id'; //
|
|
348
|
-
static timestamps = true; //
|
|
347
|
+
static primaryKey = 'id'; // Default: 'id'
|
|
348
|
+
static timestamps = true; // Default: true
|
|
349
349
|
static fillable = ['name', 'email', 'password'];
|
|
350
350
|
static hidden = ['password'];
|
|
351
351
|
static casts = {
|
|
@@ -366,19 +366,19 @@ class User extends Model {
|
|
|
366
366
|
}
|
|
367
367
|
```
|
|
368
368
|
|
|
369
|
-
###
|
|
369
|
+
### CRUD operations
|
|
370
370
|
|
|
371
|
-
####
|
|
371
|
+
#### Create
|
|
372
372
|
|
|
373
373
|
```javascript
|
|
374
|
-
//
|
|
374
|
+
// Method 1: create()
|
|
375
375
|
const user = await User.create({
|
|
376
376
|
name: 'John Doe',
|
|
377
377
|
email: 'john@example.com',
|
|
378
378
|
password: 'secret123'
|
|
379
379
|
});
|
|
380
380
|
|
|
381
|
-
//
|
|
381
|
+
// Method 2: new + save()
|
|
382
382
|
const user = new User({
|
|
383
383
|
name: 'Jane Doe',
|
|
384
384
|
email: 'jane@example.com'
|
|
@@ -386,7 +386,7 @@ const user = new User({
|
|
|
386
386
|
user.setAttribute('password', 'secret456');
|
|
387
387
|
await user.save();
|
|
388
388
|
|
|
389
|
-
// Insert brut (sans
|
|
389
|
+
// Insert brut (sans create d'instance)
|
|
390
390
|
await User.insert({ name: 'Bob', email: 'bob@example.com' });
|
|
391
391
|
```
|
|
392
392
|
|
|
@@ -398,9 +398,9 @@ const users = await User.all();
|
|
|
398
398
|
|
|
399
399
|
// Par ID
|
|
400
400
|
const user = await User.find(1);
|
|
401
|
-
const user = await User.findOrFail(1); //
|
|
401
|
+
const user = await User.findOrFail(1); // Throws an error if not found
|
|
402
402
|
|
|
403
|
-
//
|
|
403
|
+
// First result
|
|
404
404
|
const firstUser = await User.first();
|
|
405
405
|
|
|
406
406
|
// Avec conditions
|
|
@@ -409,7 +409,7 @@ const activeUsers = await User
|
|
|
409
409
|
.where('age', '>', 18)
|
|
410
410
|
.get();
|
|
411
411
|
|
|
412
|
-
// Avec
|
|
412
|
+
// Avec relationships (Eager Loading)
|
|
413
413
|
const usersWithPosts = await User
|
|
414
414
|
.with('posts', 'profile')
|
|
415
415
|
.get();
|
|
@@ -421,7 +421,7 @@ const recentUsers = await User
|
|
|
421
421
|
.get();
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
-
####
|
|
424
|
+
#### Update
|
|
425
425
|
|
|
426
426
|
```javascript
|
|
427
427
|
// Instance
|
|
@@ -444,7 +444,7 @@ const user = await User.updateAndFetchById(1, { name: 'Trinity' }, ['profile']);
|
|
|
444
444
|
await User.updateById(2, { status: 'active' });
|
|
445
445
|
```
|
|
446
446
|
|
|
447
|
-
####
|
|
447
|
+
#### Delete
|
|
448
448
|
|
|
449
449
|
```javascript
|
|
450
450
|
// Instance
|
|
@@ -494,7 +494,7 @@ const result = await User
|
|
|
494
494
|
.select('users.*', 'profiles.bio', 'countries.name as country')
|
|
495
495
|
.get();
|
|
496
496
|
|
|
497
|
-
//
|
|
497
|
+
// Aggregations
|
|
498
498
|
const stats = await User
|
|
499
499
|
.distinct()
|
|
500
500
|
.groupBy('status')
|
|
@@ -506,7 +506,7 @@ await User.where('id', 1).increment('login_count');
|
|
|
506
506
|
await User.where('id', 1).decrement('credits', 10);
|
|
507
507
|
```
|
|
508
508
|
|
|
509
|
-
###
|
|
509
|
+
### Relationship filters
|
|
510
510
|
|
|
511
511
|
```javascript
|
|
512
512
|
// whereHas: Utilisateurs ayant au moins un post publié
|
|
@@ -519,7 +519,7 @@ const authors = await User
|
|
|
519
519
|
// has: Au moins N enfants
|
|
520
520
|
const prolific = await User.has('posts', '>=', 10).get();
|
|
521
521
|
|
|
522
|
-
// whereDoesntHave:
|
|
522
|
+
// whereDoesntHave: No children
|
|
523
523
|
const noPostUsers = await User.whereDoesntHave('posts').get();
|
|
524
524
|
|
|
525
525
|
// withCount: Ajouter une colonne {relation}_count
|
|
@@ -610,14 +610,14 @@ const user = await User.find(1);
|
|
|
610
610
|
const roles = await user.roles().get();
|
|
611
611
|
|
|
612
612
|
// Méthodes pivot
|
|
613
|
-
await user.roles().attach([1, 2]); //
|
|
614
|
-
await user.roles().detach(2); //
|
|
613
|
+
await user.roles().attach([1, 2]); // Attach roles
|
|
614
|
+
await user.roles().detach(2); // Detach a role
|
|
615
615
|
await user.roles().sync([1, 3]); // Synchroniser (remplace tout)
|
|
616
616
|
```
|
|
617
617
|
|
|
618
618
|
### Has Many Through (hasManyThrough)
|
|
619
619
|
|
|
620
|
-
|
|
620
|
+
Access a distant relationship via an intermediate model.
|
|
621
621
|
|
|
622
622
|
```javascript
|
|
623
623
|
const { Model } = require('outlet-orm');
|
|
@@ -649,9 +649,9 @@ const user = await User.find(1);
|
|
|
649
649
|
const country = await user.country().get();
|
|
650
650
|
```
|
|
651
651
|
|
|
652
|
-
###
|
|
652
|
+
### Polymorphic relationships
|
|
653
653
|
|
|
654
|
-
|
|
654
|
+
Polymorphic relationships allow a model to belong to multiple other models.
|
|
655
655
|
|
|
656
656
|
```javascript
|
|
657
657
|
const { Model } = require('outlet-orm');
|
|
@@ -662,7 +662,7 @@ Model.setMorphMap({
|
|
|
662
662
|
'videos': Video
|
|
663
663
|
});
|
|
664
664
|
|
|
665
|
-
//
|
|
665
|
+
// Models
|
|
666
666
|
class Post extends Model {
|
|
667
667
|
comments() {
|
|
668
668
|
return this.morphMany(Comment, 'commentable');
|
|
@@ -689,7 +689,7 @@ const comment = await Comment.find(1);
|
|
|
689
689
|
const parent = await comment.commentable().get(); // Post ou Video
|
|
690
690
|
```
|
|
691
691
|
|
|
692
|
-
**Relations polymorphiques
|
|
692
|
+
**Relations polymorphiques availables:**
|
|
693
693
|
- `morphOne(Related, 'morphName')` - One-to-One polymorphique
|
|
694
694
|
- `morphMany(Related, 'morphName')` - One-to-Many polymorphique
|
|
695
695
|
- `morphTo('morphName')` - Inverse polymorphique
|
|
@@ -697,15 +697,15 @@ const parent = await comment.commentable().get(); // Post ou Video
|
|
|
697
697
|
### Eager Loading
|
|
698
698
|
|
|
699
699
|
```javascript
|
|
700
|
-
// Charger plusieurs
|
|
700
|
+
// Charger plusieurs relationships
|
|
701
701
|
const users = await User.with('posts', 'profile', 'roles').get();
|
|
702
702
|
|
|
703
|
-
// Charger avec
|
|
703
|
+
// Charger avec constraints
|
|
704
704
|
const users = await User.with({
|
|
705
705
|
posts: (q) => q.where('status', 'published').orderBy('created_at', 'desc')
|
|
706
706
|
}).get();
|
|
707
707
|
|
|
708
|
-
//
|
|
708
|
+
// Load nested relationships (dot notation)
|
|
709
709
|
const users = await User.with('posts.comments.author').get();
|
|
710
710
|
|
|
711
711
|
// Charger sur une instance existante
|
|
@@ -713,10 +713,10 @@ const user = await User.find(1);
|
|
|
713
713
|
await user.load('posts', 'profile');
|
|
714
714
|
await user.load(['roles', 'posts.comments']);
|
|
715
715
|
|
|
716
|
-
//
|
|
716
|
+
// Access loaded relationships
|
|
717
717
|
users.forEach(user => {
|
|
718
|
-
console.log(user.
|
|
719
|
-
console.log(user.
|
|
718
|
+
console.log(user.relationships.posts);
|
|
719
|
+
console.log(user.relationships.profile);
|
|
720
720
|
});
|
|
721
721
|
```
|
|
722
722
|
|
|
@@ -742,7 +742,7 @@ class User extends Model {
|
|
|
742
742
|
}
|
|
743
743
|
```
|
|
744
744
|
|
|
745
|
-
###
|
|
745
|
+
### Hidden attributes
|
|
746
746
|
|
|
747
747
|
```javascript
|
|
748
748
|
const { Model } = require('outlet-orm');
|
|
@@ -755,21 +755,21 @@ const user = await User.find(1);
|
|
|
755
755
|
console.log(user.toJSON()); // password et secret_token exclus
|
|
756
756
|
```
|
|
757
757
|
|
|
758
|
-
####
|
|
758
|
+
#### Show hidden attributes
|
|
759
759
|
|
|
760
760
|
```javascript
|
|
761
|
-
//
|
|
761
|
+
// Include hidden attributes
|
|
762
762
|
const user = await User.withHidden().where('email', 'john@example.com').first();
|
|
763
763
|
console.log(user.toJSON()); // password inclus
|
|
764
764
|
|
|
765
|
-
//
|
|
765
|
+
// Control with a boolean
|
|
766
766
|
const user = await User.withoutHidden(true).first(); // true = afficher
|
|
767
|
-
const user = await User.withoutHidden(false).first(); // false =
|
|
767
|
+
const user = await User.withoutHidden(false).first(); // false = hide (default)
|
|
768
768
|
|
|
769
769
|
// Cas d'usage: authentification
|
|
770
770
|
const user = await User.withHidden().where('email', email).first();
|
|
771
771
|
if (user && await bcrypt.compare(password, user.getAttribute('password'))) {
|
|
772
|
-
//
|
|
772
|
+
// Authentication successful
|
|
773
773
|
}
|
|
774
774
|
```
|
|
775
775
|
|
|
@@ -778,12 +778,12 @@ if (user && await bcrypt.compare(password, user.getAttribute('password'))) {
|
|
|
778
778
|
```javascript
|
|
779
779
|
const { Model } = require('outlet-orm');
|
|
780
780
|
|
|
781
|
-
// Activés
|
|
781
|
+
// Activés Default (created_at, updated_at)
|
|
782
782
|
class User extends Model {
|
|
783
783
|
static timestamps = true;
|
|
784
784
|
}
|
|
785
785
|
|
|
786
|
-
//
|
|
786
|
+
// Disable
|
|
787
787
|
class Log extends Model {
|
|
788
788
|
static timestamps = false;
|
|
789
789
|
}
|
|
@@ -791,21 +791,21 @@ class Log extends Model {
|
|
|
791
791
|
|
|
792
792
|
## 🔄 Transactions
|
|
793
793
|
|
|
794
|
-
Outlet ORM
|
|
794
|
+
Outlet ORM supports transactions to guarantee data integrity:
|
|
795
795
|
|
|
796
796
|
```javascript
|
|
797
797
|
const { DatabaseConnection, Model } = require('outlet-orm');
|
|
798
798
|
|
|
799
|
-
//
|
|
799
|
+
// Method 1: Automatic callback (recommended)
|
|
800
800
|
const db = Model.connection;
|
|
801
801
|
const result = await db.transaction(async (connection) => {
|
|
802
802
|
const user = await User.create({ name: 'John', email: 'john@example.com' });
|
|
803
803
|
await Account.create({ user_id: user.getAttribute('id'), balance: 0 });
|
|
804
804
|
return user;
|
|
805
805
|
});
|
|
806
|
-
//
|
|
806
|
+
// Automatic commit, rollback on error
|
|
807
807
|
|
|
808
|
-
//
|
|
808
|
+
// Method 2: Manual control
|
|
809
809
|
await db.beginTransaction();
|
|
810
810
|
try {
|
|
811
811
|
await User.create({ name: 'Jane' });
|
|
@@ -829,28 +829,28 @@ class Post extends Model {
|
|
|
829
829
|
// static DELETED_AT = 'deleted_at'; // Personnalisable
|
|
830
830
|
}
|
|
831
831
|
|
|
832
|
-
// Les
|
|
833
|
-
const posts = await Post.all(); // Seulement les non-
|
|
832
|
+
// Les queries excluent automatiquement les deleteds
|
|
833
|
+
const posts = await Post.all(); // Seulement les non-deleteds
|
|
834
834
|
|
|
835
|
-
// Inclure les
|
|
835
|
+
// Inclure les deleteds
|
|
836
836
|
const allPosts = await Post.withTrashed().get();
|
|
837
837
|
|
|
838
|
-
// Seulement les
|
|
838
|
+
// Seulement les deleteds
|
|
839
839
|
const trashedPosts = await Post.onlyTrashed().get();
|
|
840
840
|
|
|
841
|
-
//
|
|
841
|
+
// Delete (soft delete)
|
|
842
842
|
const post = await Post.find(1);
|
|
843
|
-
await post.destroy(); //
|
|
843
|
+
await post.destroy(); // Sets deleted_at to the current date
|
|
844
844
|
|
|
845
|
-
//
|
|
845
|
+
// Check if deleted
|
|
846
846
|
if (post.trashed()) {
|
|
847
|
-
console.log('
|
|
847
|
+
console.log('This post is deleted');
|
|
848
848
|
}
|
|
849
849
|
|
|
850
850
|
// Restaurer
|
|
851
851
|
await post.restore();
|
|
852
852
|
|
|
853
|
-
//
|
|
853
|
+
// Delete permanently
|
|
854
854
|
await post.forceDelete();
|
|
855
855
|
```
|
|
856
856
|
|
|
@@ -858,7 +858,7 @@ await post.forceDelete();
|
|
|
858
858
|
|
|
859
859
|
### Scopes Globaux
|
|
860
860
|
|
|
861
|
-
|
|
861
|
+
Applied automatically to all queries:
|
|
862
862
|
|
|
863
863
|
```javascript
|
|
864
864
|
const { Model } = require('outlet-orm');
|
|
@@ -872,19 +872,19 @@ Post.addGlobalScope('published', (query) => {
|
|
|
872
872
|
query.where('status', 'published');
|
|
873
873
|
});
|
|
874
874
|
|
|
875
|
-
// Toutes les
|
|
876
|
-
const posts = await Post.all(); //
|
|
875
|
+
// Toutes les queries filtrent automatiquement
|
|
876
|
+
const posts = await Post.all(); // Published only
|
|
877
877
|
|
|
878
|
-
//
|
|
878
|
+
// Disable temporairement un scope
|
|
879
879
|
const allPosts = await Post.withoutGlobalScope('published').get();
|
|
880
880
|
|
|
881
|
-
//
|
|
881
|
+
// Disable tous les scopes
|
|
882
882
|
const rawPosts = await Post.withoutGlobalScopes().get();
|
|
883
883
|
```
|
|
884
884
|
|
|
885
885
|
## 📣 Events / Hooks
|
|
886
886
|
|
|
887
|
-
|
|
887
|
+
Intercept operations on your models:
|
|
888
888
|
|
|
889
889
|
```javascript
|
|
890
890
|
const { Model } = require('outlet-orm');
|
|
@@ -893,30 +893,30 @@ class User extends Model {
|
|
|
893
893
|
static table = 'users';
|
|
894
894
|
}
|
|
895
895
|
|
|
896
|
-
//
|
|
896
|
+
// Before creation
|
|
897
897
|
User.creating((user) => {
|
|
898
898
|
user.setAttribute('uuid', generateUUID());
|
|
899
|
-
// Retourner false pour
|
|
899
|
+
// Retourner false pour rollback
|
|
900
900
|
});
|
|
901
901
|
|
|
902
|
-
//
|
|
902
|
+
// After creation
|
|
903
903
|
User.created((user) => {
|
|
904
904
|
console.log(`Utilisateur ${user.getAttribute('id')} créé`);
|
|
905
905
|
});
|
|
906
906
|
|
|
907
|
-
//
|
|
907
|
+
// Before update
|
|
908
908
|
User.updating((user) => {
|
|
909
909
|
user.setAttribute('updated_at', new Date());
|
|
910
910
|
});
|
|
911
911
|
|
|
912
|
-
//
|
|
912
|
+
// After update
|
|
913
913
|
User.updated((user) => {
|
|
914
914
|
// Notifier les systèmes externes
|
|
915
915
|
});
|
|
916
916
|
|
|
917
|
-
//
|
|
917
|
+
// saving/saved events (creation AND update)
|
|
918
918
|
User.saving((user) => {
|
|
919
|
-
// Nettoyage des
|
|
919
|
+
// Nettoyage des data
|
|
920
920
|
});
|
|
921
921
|
|
|
922
922
|
User.saved((user) => {
|
|
@@ -925,11 +925,11 @@ User.saved((user) => {
|
|
|
925
925
|
|
|
926
926
|
// Avant/après suppression
|
|
927
927
|
User.deleting((user) => {
|
|
928
|
-
//
|
|
928
|
+
// Checks before deletion
|
|
929
929
|
});
|
|
930
930
|
|
|
931
931
|
User.deleted((user) => {
|
|
932
|
-
// Nettoyage des
|
|
932
|
+
// Nettoyage des relationships
|
|
933
933
|
});
|
|
934
934
|
|
|
935
935
|
// Pour les soft deletes
|
|
@@ -939,7 +939,7 @@ User.restored((user) => {});
|
|
|
939
939
|
|
|
940
940
|
## ✅ Validation
|
|
941
941
|
|
|
942
|
-
|
|
942
|
+
Built-in basic validation:
|
|
943
943
|
|
|
944
944
|
```javascript
|
|
945
945
|
const { Model } = require('outlet-orm');
|
|
@@ -979,24 +979,24 @@ try {
|
|
|
979
979
|
}
|
|
980
980
|
```
|
|
981
981
|
|
|
982
|
-
###
|
|
982
|
+
### Available rules
|
|
983
983
|
|
|
984
984
|
| Règle | Description |
|
|
985
985
|
|-------|-------------|
|
|
986
986
|
| `required` | Champ obligatoire |
|
|
987
|
-
| `string` |
|
|
988
|
-
| `number` / `numeric` |
|
|
987
|
+
| `string` | Must be a string |
|
|
988
|
+
| `number` / `numeric` | Must be a number |
|
|
989
989
|
| `email` | Format email valide |
|
|
990
|
-
| `boolean` |
|
|
990
|
+
| `boolean` | Must be a boolean |
|
|
991
991
|
| `date` | Date valide |
|
|
992
|
-
| `min:N` | Minimum N (longueur ou
|
|
993
|
-
| `max:N` | Maximum N (longueur ou
|
|
992
|
+
| `min:N` | Minimum N (longueur ou value) |
|
|
993
|
+
| `max:N` | Maximum N (longueur ou value) |
|
|
994
994
|
| `in:a,b,c` | Valeur parmi la liste |
|
|
995
995
|
| `regex:pattern` | Match le pattern regex |
|
|
996
996
|
|
|
997
997
|
## 📊 Query Logging
|
|
998
998
|
|
|
999
|
-
|
|
999
|
+
Debug mode to analyse your queries:
|
|
1000
1000
|
|
|
1001
1001
|
```javascript
|
|
1002
1002
|
const { Model } = require('outlet-orm');
|
|
@@ -1005,11 +1005,11 @@ const { Model } = require('outlet-orm');
|
|
|
1005
1005
|
const db = Model.getConnection();
|
|
1006
1006
|
db.enableQueryLog();
|
|
1007
1007
|
|
|
1008
|
-
//
|
|
1008
|
+
// Run des queries
|
|
1009
1009
|
await User.where('status', 'active').get();
|
|
1010
1010
|
await Post.with('author').get();
|
|
1011
1011
|
|
|
1012
|
-
//
|
|
1012
|
+
// Retrieve the log
|
|
1013
1013
|
const queries = db.getQueryLog();
|
|
1014
1014
|
console.log(queries);
|
|
1015
1015
|
// [
|
|
@@ -1020,10 +1020,10 @@ console.log(queries);
|
|
|
1020
1020
|
// Vider le log
|
|
1021
1021
|
db.flushQueryLog();
|
|
1022
1022
|
|
|
1023
|
-
//
|
|
1023
|
+
// Disable le logging
|
|
1024
1024
|
db.disableQueryLog();
|
|
1025
1025
|
|
|
1026
|
-
//
|
|
1026
|
+
// Check if active
|
|
1027
1027
|
if (db.isLogging()) {
|
|
1028
1028
|
console.log('Logging actif');
|
|
1029
1029
|
}
|
|
@@ -1033,39 +1033,39 @@ if (db.isLogging()) {
|
|
|
1033
1033
|
|
|
1034
1034
|
### DatabaseConnection
|
|
1035
1035
|
|
|
1036
|
-
|
|
|
1036
|
+
| Method | Description |
|
|
1037
1037
|
|---------|-------------|
|
|
1038
|
-
| `new DatabaseConnection(config?)` |
|
|
1039
|
-
| `connect()` |
|
|
1040
|
-
| `beginTransaction()` |
|
|
1038
|
+
| `new DatabaseConnection(config?)` | Creates a connection (lit `.env` si config omis) |
|
|
1039
|
+
| `connect()` | Establishes the connection (appelé automatiquement) |
|
|
1040
|
+
| `beginTransaction()` | Starts a transaction |
|
|
1041
1041
|
| `commit()` | Valide la transaction |
|
|
1042
1042
|
| `rollback()` | Annule la transaction |
|
|
1043
|
-
| `transaction(callback)` |
|
|
1044
|
-
| `select(table, query)` |
|
|
1045
|
-
| `insert(table, data)` |
|
|
1046
|
-
| `insertMany(table, data[])` |
|
|
1047
|
-
| `update(table, data, query)` |
|
|
1043
|
+
| `transaction(callback)` | Runs in a transaction (auto commit/rollback) |
|
|
1044
|
+
| `select(table, query)` | Runs a SELECT |
|
|
1045
|
+
| `insert(table, data)` | Inserts a record |
|
|
1046
|
+
| `insertMany(table, data[])` | Inserts multiple records |
|
|
1047
|
+
| `update(table, data, query)` | Updates records |
|
|
1048
1048
|
| `delete(table, query)` | Supprime des enregistrements |
|
|
1049
1049
|
| `count(table, query)` | Compte les enregistrements |
|
|
1050
1050
|
| `executeRawQuery(sql, params?)` | Requête brute (résultats normalisés) |
|
|
1051
1051
|
| `execute(sql, params?)` | Requête brute (résultats natifs driver) |
|
|
1052
1052
|
| `increment(table, column, query, amount?)` | Incrément atomique |
|
|
1053
1053
|
| `decrement(table, column, query, amount?)` | Décrément atomique |
|
|
1054
|
-
| `close()` / `disconnect()` | Ferme la
|
|
1054
|
+
| `close()` / `disconnect()` | Ferme la connection |
|
|
1055
1055
|
| **Query Logging (static)** | |
|
|
1056
|
-
| `enableQueryLog()` | Active le logging des
|
|
1056
|
+
| `enableQueryLog()` | Active le logging des queries |
|
|
1057
1057
|
| `disableQueryLog()` | Désactive le logging |
|
|
1058
|
-
| `getQueryLog()` |
|
|
1058
|
+
| `getQueryLog()` | Returns the query log |
|
|
1059
1059
|
| `flushQueryLog()` | Vide le log |
|
|
1060
|
-
| `isLogging()` |
|
|
1060
|
+
| `isLogging()` | Checks whether logging is active |
|
|
1061
1061
|
|
|
1062
|
-
### Model (
|
|
1062
|
+
### Model (methods statiques)
|
|
1063
1063
|
|
|
1064
|
-
|
|
|
1064
|
+
| Method | Description |
|
|
1065
1065
|
|---------|-------------|
|
|
1066
|
-
| `setConnection(db)` | Définit la
|
|
1067
|
-
| `getConnection()` |
|
|
1068
|
-
| `setMorphMap(map)` |
|
|
1066
|
+
| `setConnection(db)` | Définit la connection Default |
|
|
1067
|
+
| `getConnection()` | Gets the connection (v3.0.0+) |
|
|
1068
|
+
| `setMorphMap(map)` | Defines polymorphic mapping |
|
|
1069
1069
|
| `query()` | Retourne un QueryBuilder |
|
|
1070
1070
|
| `all()` | Tous les enregistrements |
|
|
1071
1071
|
| `find(id)` | Trouve par ID |
|
|
@@ -1075,61 +1075,61 @@ if (db.isLogging()) {
|
|
|
1075
1075
|
| `whereIn(col, vals)` | Clause WHERE IN |
|
|
1076
1076
|
| `whereNull(col)` | Clause WHERE NULL |
|
|
1077
1077
|
| `whereNotNull(col)` | Clause WHERE NOT NULL |
|
|
1078
|
-
| `create(attrs)` |
|
|
1078
|
+
| `create(attrs)` | Creates and saves |
|
|
1079
1079
|
| `insert(data)` | Insert brut |
|
|
1080
1080
|
| `update(attrs)` | Update bulk |
|
|
1081
1081
|
| `updateById(id, attrs)` | Update par ID |
|
|
1082
|
-
| `updateAndFetchById(id, attrs, rels?)` | Update + fetch avec
|
|
1082
|
+
| `updateAndFetchById(id, attrs, rels?)` | Update + fetch avec relationships |
|
|
1083
1083
|
| `delete()` | Delete bulk |
|
|
1084
1084
|
| `with(...rels)` | Eager loading |
|
|
1085
|
-
| `withHidden()` |
|
|
1085
|
+
| `withHidden()` | Includes hidden attributes |
|
|
1086
1086
|
| `withoutHidden(show?)` | Contrôle visibilité |
|
|
1087
1087
|
| `orderBy(col, dir?)` | Tri |
|
|
1088
1088
|
| `limit(n)` / `offset(n)` | Limite/Offset |
|
|
1089
1089
|
| `paginate(page, perPage)` | Pagination |
|
|
1090
1090
|
| `count()` | Compte |
|
|
1091
1091
|
| **Soft Deletes** | |
|
|
1092
|
-
| `withTrashed()` | Inclut les
|
|
1093
|
-
| `onlyTrashed()` | Seulement les
|
|
1092
|
+
| `withTrashed()` | Inclut les deleteds |
|
|
1093
|
+
| `onlyTrashed()` | Seulement les deleteds |
|
|
1094
1094
|
| **Scopes** | |
|
|
1095
|
-
| `addGlobalScope(name, cb)` |
|
|
1096
|
-
| `removeGlobalScope(name)` |
|
|
1097
|
-
| `withoutGlobalScope(name)` |
|
|
1098
|
-
| `withoutGlobalScopes()` |
|
|
1095
|
+
| `addGlobalScope(name, cb)` | Adds a global scope |
|
|
1096
|
+
| `removeGlobalScope(name)` | Removes a scope |
|
|
1097
|
+
| `withoutGlobalScope(name)` | Query without one scope |
|
|
1098
|
+
| `withoutGlobalScopes()` | Query without all scopes |
|
|
1099
1099
|
| **Events** | |
|
|
1100
1100
|
| `on(event, callback)` | Enregistre un listener |
|
|
1101
|
-
| `creating(cb)` / `created(cb)` |
|
|
1102
|
-
| `updating(cb)` / `updated(cb)` |
|
|
1101
|
+
| `creating(cb)` / `created(cb)` | Creation events |
|
|
1102
|
+
| `updating(cb)` / `updated(cb)` | Update events |
|
|
1103
1103
|
| `saving(cb)` / `saved(cb)` | Events sauvegarde |
|
|
1104
1104
|
| `deleting(cb)` / `deleted(cb)` | Events suppression |
|
|
1105
1105
|
| `restoring(cb)` / `restored(cb)` | Events restauration |
|
|
1106
1106
|
|
|
1107
|
-
### Model (
|
|
1107
|
+
### Model (methods d'instance)
|
|
1108
1108
|
|
|
1109
|
-
|
|
|
1109
|
+
| Method | Description |
|
|
1110
1110
|
|---------|-------------|
|
|
1111
|
-
| `fill(attrs)` |
|
|
1112
|
-
| `setAttribute(key, val)` |
|
|
1113
|
-
| `getAttribute(key)` |
|
|
1111
|
+
| `fill(attrs)` | Fills attributes |
|
|
1112
|
+
| `setAttribute(key, val)` | Sets an attribute |
|
|
1113
|
+
| `getAttribute(key)` | Gets an attribute |
|
|
1114
1114
|
| `save()` | Sauvegarde (insert ou update) |
|
|
1115
1115
|
| `destroy()` | Supprime l'instance (soft si activé) |
|
|
1116
|
-
| `load(...rels)` | Charge des
|
|
1116
|
+
| `load(...rels)` | Charge des relationships |
|
|
1117
1117
|
| `getDirty()` | Attributs modifiés |
|
|
1118
|
-
| `isDirty()` |
|
|
1118
|
+
| `isDirty()` | Has been modified? |
|
|
1119
1119
|
| `toJSON()` | Convertit en objet |
|
|
1120
1120
|
| **Soft Deletes** | |
|
|
1121
|
-
| `trashed()` |
|
|
1122
|
-
| `restore()` |
|
|
1123
|
-
| `forceDelete()` |
|
|
1121
|
+
| `trashed()` | Is deleted? |
|
|
1122
|
+
| `restore()` | Restore le model |
|
|
1123
|
+
| `forceDelete()` | Permanent deletion |
|
|
1124
1124
|
| **Validation** | |
|
|
1125
|
-
| `validate()` | Valide selon les
|
|
1125
|
+
| `validate()` | Valide selon les rules |
|
|
1126
1126
|
| `validateOrFail()` | Valide ou lance erreur |
|
|
1127
1127
|
|
|
1128
1128
|
### QueryBuilder
|
|
1129
1129
|
|
|
1130
|
-
|
|
|
1130
|
+
| Method | Description |
|
|
1131
1131
|
|---------|-------------|
|
|
1132
|
-
| `select(...cols)` / `columns([...])` |
|
|
1132
|
+
| `select(...cols)` / `columns([...])` | Column selection |
|
|
1133
1133
|
| `distinct()` | SELECT DISTINCT |
|
|
1134
1134
|
| `where(col, op?, val)` | Clause WHERE |
|
|
1135
1135
|
| `whereIn(col, vals)` | WHERE IN |
|
|
@@ -1151,16 +1151,16 @@ if (db.isLogging()) {
|
|
|
1151
1151
|
| `leftJoin(table, first, op?, second)` | LEFT JOIN |
|
|
1152
1152
|
| `with(...rels)` | Eager loading |
|
|
1153
1153
|
| `withCount(rels)` | Ajoute {rel}_count |
|
|
1154
|
-
| `withTrashed()` | Inclut les
|
|
1155
|
-
| `onlyTrashed()` | Seulement les
|
|
1154
|
+
| `withTrashed()` | Inclut les deleteds |
|
|
1155
|
+
| `onlyTrashed()` | Seulement les deleteds |
|
|
1156
1156
|
| `withoutGlobalScope(name)` | Sans un scope global |
|
|
1157
1157
|
| `withoutGlobalScopes()` | Sans tous les scopes |
|
|
1158
|
-
| `get()` |
|
|
1159
|
-
| `first()` |
|
|
1158
|
+
| `get()` | Runs and returns all |
|
|
1159
|
+
| `first()` | First result |
|
|
1160
1160
|
| `firstOrFail()` | Premier ou erreur |
|
|
1161
1161
|
| `paginate(page, perPage)` | Pagination |
|
|
1162
1162
|
| `count()` | Compte |
|
|
1163
|
-
| `exists()` |
|
|
1163
|
+
| `exists()` | Checks existence |
|
|
1164
1164
|
| `insert(data)` | Insert |
|
|
1165
1165
|
| `update(attrs)` | Update |
|
|
1166
1166
|
| `updateAndFetch(attrs, rels?)` | Update + fetch |
|
|
@@ -1169,37 +1169,37 @@ if (db.isLogging()) {
|
|
|
1169
1169
|
| `decrement(col, amount?)` | Décrément atomique |
|
|
1170
1170
|
| `clone()` | Clone le query builder |
|
|
1171
1171
|
|
|
1172
|
-
## 🛠️
|
|
1172
|
+
## 🛠️ CLI tools
|
|
1173
1173
|
|
|
1174
1174
|
### outlet-init
|
|
1175
1175
|
|
|
1176
|
-
Initialise un nouveau projet avec configuration de
|
|
1176
|
+
Initialise un nouveau projet avec configuration de database.
|
|
1177
1177
|
|
|
1178
1178
|
```bash
|
|
1179
1179
|
outlet-init
|
|
1180
1180
|
```
|
|
1181
1181
|
|
|
1182
|
-
|
|
1183
|
-
-
|
|
1184
|
-
-
|
|
1185
|
-
-
|
|
1186
|
-
-
|
|
1182
|
+
Generates:
|
|
1183
|
+
- Configuration file `database/config.js`
|
|
1184
|
+
- `.env` file with settings
|
|
1185
|
+
- Example model
|
|
1186
|
+
- Usage file
|
|
1187
1187
|
|
|
1188
1188
|
### outlet-migrate
|
|
1189
1189
|
|
|
1190
|
-
|
|
1190
|
+
complete migration system.
|
|
1191
1191
|
|
|
1192
1192
|
```bash
|
|
1193
|
-
#
|
|
1193
|
+
# Create une migration
|
|
1194
1194
|
outlet-migrate make create_users_table
|
|
1195
1195
|
|
|
1196
|
-
#
|
|
1196
|
+
# Run les migrations
|
|
1197
1197
|
outlet-migrate migrate
|
|
1198
1198
|
|
|
1199
|
-
#
|
|
1199
|
+
# See le statut
|
|
1200
1200
|
outlet-migrate status
|
|
1201
1201
|
|
|
1202
|
-
#
|
|
1202
|
+
# Roll back the latest migration
|
|
1203
1203
|
outlet-migrate rollback --steps 1
|
|
1204
1204
|
|
|
1205
1205
|
# Reset toutes les migrations
|
|
@@ -1212,35 +1212,35 @@ outlet-migrate refresh --yes
|
|
|
1212
1212
|
outlet-migrate fresh --yes
|
|
1213
1213
|
```
|
|
1214
1214
|
|
|
1215
|
-
**
|
|
1215
|
+
**Features des Migrations:**
|
|
1216
1216
|
|
|
1217
|
-
- ✅
|
|
1217
|
+
- ✅ Creation and management of migrations (create, alter, drop tables)
|
|
1218
1218
|
- ✅ Types de colonnes: id, string, text, integer, boolean, date, datetime, timestamp, decimal, float, json, enum, uuid, foreignId
|
|
1219
1219
|
- ✅ Modificateurs: nullable, default, unique, index, unsigned, autoIncrement, comment, after, first
|
|
1220
|
-
- ✅
|
|
1220
|
+
- ✅ Foreign keys: foreign(), constrained(), onDelete(), onUpdate(), CASCADE
|
|
1221
1221
|
- ✅ Index: index(), unique(), fullText()
|
|
1222
1222
|
- ✅ Manipulation: renameColumn(), dropColumn(), dropTimestamps()
|
|
1223
|
-
- ✅
|
|
1224
|
-
- ✅ Batch tracking:
|
|
1225
|
-
- ✅ SQL
|
|
1223
|
+
- ✅ Reversible migrations: Méthodes up() et down()
|
|
1224
|
+
- ✅ Batch tracking: Precise rollback by batch
|
|
1225
|
+
- ✅ Custom SQL: execute() pour advanced commands
|
|
1226
1226
|
|
|
1227
1227
|
### outlet-convert
|
|
1228
1228
|
|
|
1229
|
-
|
|
1229
|
+
Converts SQL schemas into ORM models.
|
|
1230
1230
|
|
|
1231
1231
|
```bash
|
|
1232
1232
|
outlet-convert
|
|
1233
1233
|
```
|
|
1234
1234
|
|
|
1235
1235
|
**Options:**
|
|
1236
|
-
1. Depuis un
|
|
1237
|
-
2.
|
|
1238
|
-
|
|
1239
|
-
**
|
|
1240
|
-
- ✅
|
|
1241
|
-
- ✅
|
|
1242
|
-
- ✅
|
|
1243
|
-
- ✅
|
|
1236
|
+
1. Depuis un file SQL local
|
|
1237
|
+
2. From a connected database
|
|
1238
|
+
|
|
1239
|
+
**Features:**
|
|
1240
|
+
- ✅ Detection automatique des types et casts
|
|
1241
|
+
- ✅ Automatic generation of ALL relationships (belongsTo, hasMany, hasOne, belongsToMany)
|
|
1242
|
+
- ✅ Recursive relationships (auto-relationships)
|
|
1243
|
+
- ✅ Detection des champs sensibles (password, token, etc.)
|
|
1244
1244
|
- ✅ Support des timestamps automatiques
|
|
1245
1245
|
- ✅ Conversion des noms en PascalCase
|
|
1246
1246
|
|
|
@@ -1248,16 +1248,16 @@ outlet-convert
|
|
|
1248
1248
|
|
|
1249
1249
|
- [Guide des Migrations](docs/MIGRATIONS.md)
|
|
1250
1250
|
- [Conversion SQL](docs/SQL_CONVERSION.md)
|
|
1251
|
-
- [
|
|
1252
|
-
- [
|
|
1251
|
+
- [Detection des Relations](docs/RELATIONS_DETECTION.md)
|
|
1252
|
+
- [Quick Start Guide](docs/QUICKSTART.md)
|
|
1253
1253
|
- [Architecture](docs/ARCHITECTURE.md)
|
|
1254
1254
|
- [**TypeScript (complet)**](docs/TYPESCRIPT.md)
|
|
1255
1255
|
|
|
1256
1256
|
## 📘 TypeScript Support
|
|
1257
1257
|
|
|
1258
|
-
Outlet ORM v4.0.0 inclut des définitions TypeScript
|
|
1258
|
+
Outlet ORM v4.0.0 inclut des définitions TypeScript completes avec support des **generics pour les attributs typeds**.
|
|
1259
1259
|
|
|
1260
|
-
###
|
|
1260
|
+
### Typed models
|
|
1261
1261
|
|
|
1262
1262
|
```typescript
|
|
1263
1263
|
import { Model, HasManyRelation } from 'outlet-orm';
|
|
@@ -1281,11 +1281,11 @@ class User extends Model<UserAttributes> {
|
|
|
1281
1281
|
|
|
1282
1282
|
// Type-safe getAttribute/setAttribute
|
|
1283
1283
|
const user = await User.find(1);
|
|
1284
|
-
const name: string = user.getAttribute('name'); // ✅
|
|
1284
|
+
const name: string = user.getAttribute('name'); // ✅ Inferred type
|
|
1285
1285
|
const role: 'admin' | 'user' = user.getAttribute('role');
|
|
1286
1286
|
```
|
|
1287
1287
|
|
|
1288
|
-
### Migrations
|
|
1288
|
+
### Migrations typedes
|
|
1289
1289
|
|
|
1290
1290
|
```typescript
|
|
1291
1291
|
import { MigrationInterface, Schema, TableBuilder } from 'outlet-orm';
|
|
@@ -1310,16 +1310,16 @@ export const migration: MigrationInterface = {
|
|
|
1310
1310
|
|
|
1311
1311
|
📖 [Guide TypeScript complet](docs/TYPESCRIPT.md)
|
|
1312
1312
|
|
|
1313
|
-
## 🤝
|
|
1313
|
+
## 🤝 Contributions
|
|
1314
1314
|
|
|
1315
|
-
|
|
1315
|
+
Contributions are welcome! Feel free to open an issue or pull request.
|
|
1316
1316
|
|
|
1317
|
-
|
|
1317
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
|
|
1318
1318
|
|
|
1319
1319
|
## 📄 Licence
|
|
1320
1320
|
|
|
1321
|
-
MIT -
|
|
1321
|
+
MIT - See [LICENSE](LICENSE) for details.
|
|
1322
1322
|
|
|
1323
1323
|
---
|
|
1324
1324
|
|
|
1325
|
-
|
|
1325
|
+
Created by [omgbwa-yasse](https://github.com/omgbwa-yasse)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "outlet-orm",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.2",
|
|
4
4
|
"description": "A Laravel Eloquent-inspired ORM for Node.js with support for MySQL, PostgreSQL, and SQLite",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"test": "jest",
|
|
22
22
|
"test:watch": "jest --watch",
|
|
23
23
|
"test:coverage": "jest --coverage",
|
|
24
|
+
"check:version": "node scripts/check-changelog-version.js",
|
|
25
|
+
"prepublishOnly": "npm run check:version",
|
|
24
26
|
"lint": "eslint \"{src,bin}/**/*.js\"",
|
|
25
27
|
"lint:fix": "eslint \"{src,bin}/**/*.js\" --fix",
|
|
26
28
|
"migrate": "node bin/migrate.js migrate",
|