outlet-orm 5.5.2 โ 6.0.0
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 +217 -243
- package/package.json +1 -1
- package/src/Backup/BackupEncryption.js +153 -0
- package/src/Backup/BackupManager.js +422 -0
- package/src/Backup/BackupScheduler.js +175 -0
- package/src/Backup/BackupSocketClient.js +275 -0
- package/src/Backup/BackupSocketServer.js +347 -0
- package/src/index.js +15 -1
- package/types/index.d.ts +225 -0
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/outlet-orm)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
A JavaScript ORM inspired by Laravel Eloquent for Node.js with support for MySQL, PostgreSQL and SQLite.
|
|
7
7
|
|
|
8
|
-
๐ **[
|
|
8
|
+
๐ **[Complete documentation available in `/docs`](./docs/INDEX.md)**
|
|
9
9
|
|
|
10
10
|
## โ
Prerequisites and compatibility
|
|
11
11
|
|
|
@@ -20,7 +20,7 @@ npm install outlet-orm
|
|
|
20
20
|
|
|
21
21
|
### Install the database driver
|
|
22
22
|
|
|
23
|
-
Outlet ORM
|
|
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`
|
|
@@ -28,45 +28,34 @@ Outlet ORM utilise des peerDependencies optionnelles pour les drivers de databas
|
|
|
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
|
-
## ๐
|
|
31
|
+
## ๐ Recommended Project Structure
|
|
32
32
|
|
|
33
|
-
|
|
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
|
-
> ๐ **
|
|
35
|
+
> ๐ **Security**: See the [Security Guide](./docs/SECURITY.md) for best practices.
|
|
36
36
|
|
|
37
37
|
```
|
|
38
|
-
|
|
39
|
-
โโโ .env # โ ๏ธ
|
|
38
|
+
my-project/
|
|
39
|
+
โโโ .env # โ ๏ธ NEVER committed (in .gitignore)
|
|
40
40
|
โโโ .env.example # Template without secrets
|
|
41
41
|
โโโ .gitignore
|
|
42
42
|
โโโ package.json
|
|
43
43
|
โ
|
|
44
|
-
โโโ src/ # ๐ฆ
|
|
45
|
-
โ โโโ index.js #
|
|
44
|
+
โโโ src/ # ๐ฆ Centralised source code
|
|
45
|
+
โ โโโ index.js # Application entry point
|
|
46
46
|
โ โ
|
|
47
47
|
โ โโโ config/ # โ๏ธ Configuration
|
|
48
48
|
โ โ โโโ app.js # General config (port, env)
|
|
49
|
-
โ โ โโโ database.js #
|
|
49
|
+
โ โ โโโ database.js # DB config (reads .env)
|
|
50
50
|
โ โ โโโ security.js # CORS, helmet, rate limit
|
|
51
51
|
โ โ
|
|
52
|
-
โ โโโ models/ # ๐
|
|
53
|
-
โ โ โโโ index.js #
|
|
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
|
-
โ โโโ
|
|
59
|
-
โ โ โโโ BaseRepository.js # Generic CRUD methods
|
|
60
|
-
โ โ โโโ UserRepository.js # Specific queries User
|
|
61
|
-
โ โ โโโ PostRepository.js
|
|
62
|
-
โ โ
|
|
63
|
-
โ โโโ services/ # ๐ผ Business Layer (Business Logic)
|
|
64
|
-
โ โ โโโ AuthService.js # Logique d'authentification
|
|
65
|
-
โ โ โโโ UserService.js # User business logic
|
|
66
|
-
โ โ โโโ PostService.js
|
|
67
|
-
โ โ โโโ EmailService.js # Service externe (emails)
|
|
68
|
-
โ โ
|
|
69
|
-
โ โโโ controllers/ # ๐ฎ Presentation Layer (HTTP)
|
|
58
|
+
โ โโโ controllers/ # ๐ฎ HTTP handling + business logic (direct ORM calls)
|
|
70
59
|
โ โ โโโ AuthController.js
|
|
71
60
|
โ โ โโโ UserController.js
|
|
72
61
|
โ โ โโโ PostController.js
|
|
@@ -82,13 +71,13 @@ mon-projet/
|
|
|
82
71
|
โ โ โโโ authorize.js # RBAC / permissions
|
|
83
72
|
โ โ โโโ rateLimiter.js # Protection DDoS
|
|
84
73
|
โ โ โโโ validator.js # Validation request body
|
|
85
|
-
โ โ โโโ errorHandler.js #
|
|
74
|
+
โ โ โโโ errorHandler.js # Centralised error handling
|
|
86
75
|
โ โ
|
|
87
76
|
โ โโโ validators/ # โ
Validation schemas
|
|
88
77
|
โ โ โโโ authValidator.js
|
|
89
78
|
โ โ โโโ userValidator.js
|
|
90
79
|
โ โ
|
|
91
|
-
โ โโโ utils/ # ๐ง
|
|
80
|
+
โ โโโ utils/ # ๐ง Utilities
|
|
92
81
|
โ โโโ hash.js # bcrypt wrapper
|
|
93
82
|
โ โโโ token.js # JWT helpers
|
|
94
83
|
โ โโโ logger.js # Winston/Pino config
|
|
@@ -107,19 +96,17 @@ mon-projet/
|
|
|
107
96
|
โ
|
|
108
97
|
โโโ uploads/ # โ ๏ธ Uploaded files
|
|
109
98
|
โ
|
|
110
|
-
โโโ logs/ # ๐
|
|
99
|
+
โโโ logs/ # ๐ Logs (not versioned)
|
|
111
100
|
โ
|
|
112
101
|
โโโ tests/ # ๐งช Tests
|
|
113
|
-
โโโ unit/ #
|
|
114
|
-
|
|
115
|
-
โ โโโ models/
|
|
116
|
-
โโโ integration/ # Integration tests
|
|
102
|
+
โโโ unit/ # Unit tests (models)
|
|
103
|
+
โโโ integration/ # Integration tests (API)
|
|
117
104
|
โ โโโ api/
|
|
118
105
|
โโโ fixtures/ # Test data
|
|
119
106
|
โโโ users.json
|
|
120
107
|
```
|
|
121
108
|
|
|
122
|
-
### ๐๏ธ
|
|
109
|
+
### ๐๏ธ Architecture Flow
|
|
123
110
|
|
|
124
111
|
```
|
|
125
112
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -133,43 +120,30 @@ mon-projet/
|
|
|
133
120
|
โ
|
|
134
121
|
โผ
|
|
135
122
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
136
|
-
โ ROUTES โ CONTROLLERS
|
|
137
|
-
โ
|
|
138
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
139
|
-
โ
|
|
140
|
-
โผ
|
|
141
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
142
|
-
โ SERVICES (Business Layer / Business Logic) โ
|
|
143
|
-
โ Logique mรฉtier, orchestration, rules business โ
|
|
144
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
145
|
-
โ
|
|
146
|
-
โผ
|
|
147
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
148
|
-
โ REPOSITORIES (Data Access Layer) โ
|
|
149
|
-
โ Abstraction des queries 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 (
|
|
129
|
+
โ MODELS (outlet-orm) โ DATABASE โ
|
|
155
130
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
156
131
|
```
|
|
157
132
|
|
|
158
133
|
### ๐ Role of each layer
|
|
159
134
|
|
|
160
|
-
| Layer | Folder | Responsibility |
|
|
161
|
-
|
|
162
|
-
| **
|
|
163
|
-
| **
|
|
164
|
-
| **
|
|
165
|
-
| **Entities** | `models/` | Entity definitions, relationships, 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
141
|
### โ
Benefits of this architecture
|
|
168
142
|
|
|
169
|
-
- **
|
|
170
|
-
- **
|
|
171
|
-
- **
|
|
172
|
-
- **
|
|
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
148
|
### ๐ Example workflow
|
|
175
149
|
|
|
@@ -179,78 +153,78 @@ router.get('/users/:id', auth, UserController.show);
|
|
|
179
153
|
|
|
180
154
|
// controllers/UserController.js
|
|
181
155
|
async show(req, res) {
|
|
182
|
-
const user = await
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
173
|
## โจ Key features
|
|
200
174
|
|
|
201
|
-
- **API
|
|
202
|
-
- **Query Builder
|
|
203
|
-
- **
|
|
204
|
-
- **Eager Loading**
|
|
205
|
-
- **
|
|
206
|
-
- `hasOne`, `hasMany`, `belongsTo`, `belongsToMany` (
|
|
207
|
-
- `hasManyThrough`, `hasOneThrough` (relationships
|
|
208
|
-
- `morphOne`, `morphMany`, `morphTo` (relationships
|
|
209
|
-
- **Transactions
|
|
210
|
-
- **Soft Deletes**: soft deletion
|
|
211
|
-
- **Scopes**: global and local to reuse your filters
|
|
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**:
|
|
214
|
-
- **Query Logging**: mode
|
|
215
|
-
- **Pool
|
|
216
|
-
- **Protection
|
|
217
|
-
- **Casts
|
|
218
|
-
- **Hidden attributes** (`hidden`)
|
|
219
|
-
- **Visibility control**
|
|
220
|
-
- **Atomic increment/decrement**: `increment()`
|
|
221
|
-
- **
|
|
222
|
-
- **Raw queries**: `executeRawQuery()`
|
|
223
|
-
- **Migrations
|
|
224
|
-
- **CLI
|
|
225
|
-
- **
|
|
226
|
-
- **Multi-database**: MySQL, PostgreSQL
|
|
227
|
-
- **Complete TypeScript types**
|
|
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+)
|
|
228
202
|
|
|
229
203
|
## โก Quick Start
|
|
230
204
|
|
|
231
|
-
### Initialisation
|
|
205
|
+
### Project Initialisation
|
|
232
206
|
|
|
233
207
|
```bash
|
|
234
|
-
# Create
|
|
208
|
+
# Create initial configuration
|
|
235
209
|
outlet-init
|
|
236
210
|
|
|
237
|
-
# Create
|
|
211
|
+
# Create a migration
|
|
238
212
|
outlet-migrate make create_users_table
|
|
239
213
|
|
|
240
|
-
# Run
|
|
214
|
+
# Run migrations
|
|
241
215
|
outlet-migrate migrate
|
|
242
216
|
```
|
|
243
217
|
|
|
244
|
-
### ๐ฑ Seeding
|
|
218
|
+
### ๐ฑ Quick Seeding
|
|
245
219
|
|
|
246
220
|
```bash
|
|
247
|
-
# Create
|
|
221
|
+
# Create a seeder
|
|
248
222
|
outlet-migrate make:seed UserSeeder
|
|
249
223
|
|
|
250
|
-
# Run
|
|
224
|
+
# Run seeders (DatabaseSeeder runs first)
|
|
251
225
|
outlet-migrate seed
|
|
252
226
|
|
|
253
|
-
# Run
|
|
227
|
+
# Run a specific seeder
|
|
254
228
|
outlet-migrate seed --class UserSeeder
|
|
255
229
|
```
|
|
256
230
|
|
|
@@ -258,7 +232,7 @@ outlet-migrate seed --class UserSeeder
|
|
|
258
232
|
|
|
259
233
|
### Connection configuration
|
|
260
234
|
|
|
261
|
-
Outlet ORM
|
|
235
|
+
Outlet ORM automatically loads the connection from the `.env` file. **No need to import DatabaseConnection!**
|
|
262
236
|
|
|
263
237
|
#### `.env` file
|
|
264
238
|
|
|
@@ -280,7 +254,7 @@ class User extends Model {
|
|
|
280
254
|
static table = 'users';
|
|
281
255
|
}
|
|
282
256
|
|
|
283
|
-
//
|
|
257
|
+
// That's it! The connection is automatic
|
|
284
258
|
const users = await User.all();
|
|
285
259
|
```
|
|
286
260
|
|
|
@@ -294,7 +268,7 @@ const { DatabaseConnection, Model } = require('outlet-orm');
|
|
|
294
268
|
// Option 1 โ via .env (no parameters required)
|
|
295
269
|
const db = new DatabaseConnection();
|
|
296
270
|
|
|
297
|
-
// Option 2 โ via
|
|
271
|
+
// Option 2 โ via configuration object
|
|
298
272
|
const db = new DatabaseConnection({
|
|
299
273
|
driver: 'mysql',
|
|
300
274
|
host: 'localhost',
|
|
@@ -304,7 +278,7 @@ const db = new DatabaseConnection({
|
|
|
304
278
|
port: 3306
|
|
305
279
|
});
|
|
306
280
|
|
|
307
|
-
//
|
|
281
|
+
// Set the connection manually (optional)
|
|
308
282
|
Model.setConnection(db);
|
|
309
283
|
```
|
|
310
284
|
|
|
@@ -313,23 +287,23 @@ Model.setConnection(db);
|
|
|
313
287
|
| Variable | Description | Default |
|
|
314
288
|
|----------|-------------|------------|
|
|
315
289
|
| `DB_DRIVER` | `mysql`, `postgres`, `sqlite` | `mysql` |
|
|
316
|
-
| `DB_HOST` |
|
|
317
|
-
| `DB_PORT` |
|
|
318
|
-
| `DB_USER` / `DB_USERNAME` |
|
|
319
|
-
| `DB_PASSWORD` |
|
|
320
|
-
| `DB_DATABASE` / `DB_NAME` |
|
|
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 | - |
|
|
321
295
|
| `DB_FILE` / `SQLITE_DB` | SQLite file | `:memory:` |
|
|
322
296
|
|
|
323
297
|
### Importation
|
|
324
298
|
|
|
325
299
|
```javascript
|
|
326
|
-
// CommonJS -
|
|
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
|
-
//
|
|
306
|
+
// If you need manual control over the connection
|
|
333
307
|
const { DatabaseConnection, Model } = require('outlet-orm');
|
|
334
308
|
```
|
|
335
309
|
|
|
@@ -386,30 +360,30 @@ const user = new User({
|
|
|
386
360
|
user.setAttribute('password', 'secret456');
|
|
387
361
|
await user.save();
|
|
388
362
|
|
|
389
|
-
//
|
|
363
|
+
// Raw insert (without creating an instance)
|
|
390
364
|
await User.insert({ name: 'Bob', email: 'bob@example.com' });
|
|
391
365
|
```
|
|
392
366
|
|
|
393
|
-
####
|
|
367
|
+
#### Read
|
|
394
368
|
|
|
395
369
|
```javascript
|
|
396
|
-
//
|
|
370
|
+
// All records
|
|
397
371
|
const users = await User.all();
|
|
398
372
|
|
|
399
|
-
//
|
|
373
|
+
// By ID
|
|
400
374
|
const user = await User.find(1);
|
|
401
375
|
const user = await User.findOrFail(1); // Throws an error if not found
|
|
402
376
|
|
|
403
377
|
// First result
|
|
404
378
|
const firstUser = await User.first();
|
|
405
379
|
|
|
406
|
-
//
|
|
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
|
-
//
|
|
386
|
+
// With relationships (Eager Loading)
|
|
413
387
|
const usersWithPosts = await User
|
|
414
388
|
.with('posts', 'profile')
|
|
415
389
|
.get();
|
|
@@ -439,7 +413,7 @@ const updated = await User
|
|
|
439
413
|
.where('id', 1)
|
|
440
414
|
.updateAndFetch({ name: 'Neo' }, ['profile', 'posts']);
|
|
441
415
|
|
|
442
|
-
// Helpers
|
|
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
|
```
|
|
@@ -501,7 +475,7 @@ const stats = await User
|
|
|
501
475
|
.having('COUNT(*)', '>', 5)
|
|
502
476
|
.get();
|
|
503
477
|
|
|
504
|
-
//
|
|
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
|
```
|
|
@@ -509,22 +483,22 @@ await User.where('id', 1).decrement('credits', 10);
|
|
|
509
483
|
### Relationship filters
|
|
510
484
|
|
|
511
485
|
```javascript
|
|
512
|
-
// whereHas:
|
|
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:
|
|
493
|
+
// has: At least N children
|
|
520
494
|
const prolific = await User.has('posts', '>=', 10).get();
|
|
521
495
|
|
|
522
496
|
// whereDoesntHave: No children
|
|
523
497
|
const noPostUsers = await User.whereDoesntHave('posts').get();
|
|
524
498
|
|
|
525
|
-
// withCount:
|
|
499
|
+
// withCount: Add a {relation}_count column
|
|
526
500
|
const withCounts = await User.withCount('posts').get();
|
|
527
|
-
//
|
|
501
|
+
// Each user will have: user.getAttribute('posts_count')
|
|
528
502
|
```
|
|
529
503
|
|
|
530
504
|
## ๐ Relations
|
|
@@ -609,10 +583,10 @@ class User extends Model {
|
|
|
609
583
|
const user = await User.find(1);
|
|
610
584
|
const roles = await user.roles().get();
|
|
611
585
|
|
|
612
|
-
//
|
|
586
|
+
// Pivot methods
|
|
613
587
|
await user.roles().attach([1, 2]); // Attach roles
|
|
614
588
|
await user.roles().detach(2); // Detach a role
|
|
615
|
-
await user.roles().sync([1, 3]); //
|
|
589
|
+
await user.roles().sync([1, 3]); // Synchronise (replaces all)
|
|
616
590
|
```
|
|
617
591
|
|
|
618
592
|
### Has Many Through (hasManyThrough)
|
|
@@ -656,7 +630,7 @@ Polymorphic relationships allow a model to belong to multiple other models.
|
|
|
656
630
|
```javascript
|
|
657
631
|
const { Model } = require('outlet-orm');
|
|
658
632
|
|
|
659
|
-
//
|
|
633
|
+
// Set up the morph map
|
|
660
634
|
Model.setMorphMap({
|
|
661
635
|
'posts': Post,
|
|
662
636
|
'videos': Video
|
|
@@ -686,21 +660,21 @@ 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
|
|
663
|
+
const parent = await comment.commentable().get(); // Post or Video
|
|
690
664
|
```
|
|
691
665
|
|
|
692
|
-
**
|
|
693
|
-
- `morphOne(Related, 'morphName')` - One-to-One
|
|
694
|
-
- `morphMany(Related, 'morphName')` - One-to-Many
|
|
695
|
-
- `morphTo('morphName')` - Inverse
|
|
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
|
-
//
|
|
674
|
+
// Load multiple relationships
|
|
701
675
|
const users = await User.with('posts', 'profile', 'roles').get();
|
|
702
676
|
|
|
703
|
-
//
|
|
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();
|
|
@@ -708,7 +682,7 @@ const users = await User.with({
|
|
|
708
682
|
// Load nested relationships (dot notation)
|
|
709
683
|
const users = await User.with('posts.comments.author').get();
|
|
710
684
|
|
|
711
|
-
//
|
|
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']);
|
|
@@ -724,7 +698,7 @@ users.forEach(user => {
|
|
|
724
698
|
|
|
725
699
|
### Casts
|
|
726
700
|
|
|
727
|
-
|
|
701
|
+
Casts automatically convert attributes:
|
|
728
702
|
|
|
729
703
|
```javascript
|
|
730
704
|
const { Model } = require('outlet-orm');
|
|
@@ -736,8 +710,8 @@ 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
|
|
740
|
-
birthday: 'date' //
|
|
713
|
+
settings: 'array', // Parse JSON as array
|
|
714
|
+
birthday: 'date' // Converts to Date
|
|
741
715
|
};
|
|
742
716
|
}
|
|
743
717
|
```
|
|
@@ -752,7 +726,7 @@ class User extends Model {
|
|
|
752
726
|
}
|
|
753
727
|
|
|
754
728
|
const user = await User.find(1);
|
|
755
|
-
console.log(user.toJSON()); // password
|
|
729
|
+
console.log(user.toJSON()); // password and secret_token excluded
|
|
756
730
|
```
|
|
757
731
|
|
|
758
732
|
#### Show hidden attributes
|
|
@@ -760,13 +734,13 @@ console.log(user.toJSON()); // password et secret_token exclus
|
|
|
760
734
|
```javascript
|
|
761
735
|
// Include hidden attributes
|
|
762
736
|
const user = await User.withHidden().where('email', 'john@example.com').first();
|
|
763
|
-
console.log(user.toJSON()); // password
|
|
737
|
+
console.log(user.toJSON()); // password included
|
|
764
738
|
|
|
765
739
|
// Control with a boolean
|
|
766
|
-
const user = await User.withoutHidden(true).first(); // true =
|
|
740
|
+
const user = await User.withoutHidden(true).first(); // true = show
|
|
767
741
|
const user = await User.withoutHidden(false).first(); // false = hide (default)
|
|
768
742
|
|
|
769
|
-
//
|
|
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
746
|
// Authentication successful
|
|
@@ -778,7 +752,7 @@ if (user && await bcrypt.compare(password, user.getAttribute('password'))) {
|
|
|
778
752
|
```javascript
|
|
779
753
|
const { Model } = require('outlet-orm');
|
|
780
754
|
|
|
781
|
-
//
|
|
755
|
+
// Enabled by default (created_at, updated_at)
|
|
782
756
|
class User extends Model {
|
|
783
757
|
static timestamps = true;
|
|
784
758
|
}
|
|
@@ -818,7 +792,7 @@ try {
|
|
|
818
792
|
|
|
819
793
|
## ๐๏ธ Soft Deletes
|
|
820
794
|
|
|
821
|
-
|
|
795
|
+
Soft deletion using a `deleted_at` column:
|
|
822
796
|
|
|
823
797
|
```javascript
|
|
824
798
|
const { Model } = require('outlet-orm');
|
|
@@ -826,16 +800,16 @@ 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'; //
|
|
803
|
+
// static DELETED_AT = 'deleted_at'; // Customisable
|
|
830
804
|
}
|
|
831
805
|
|
|
832
|
-
//
|
|
833
|
-
const posts = await Post.all(); //
|
|
806
|
+
// Queries automatically exclude soft-deleted records
|
|
807
|
+
const posts = await Post.all(); // Only non-deleted records
|
|
834
808
|
|
|
835
|
-
//
|
|
809
|
+
// Include deleted records
|
|
836
810
|
const allPosts = await Post.withTrashed().get();
|
|
837
811
|
|
|
838
|
-
//
|
|
812
|
+
// Only deleted records
|
|
839
813
|
const trashedPosts = await Post.onlyTrashed().get();
|
|
840
814
|
|
|
841
815
|
// Delete (soft delete)
|
|
@@ -867,18 +841,18 @@ class Post extends Model {
|
|
|
867
841
|
static table = 'posts';
|
|
868
842
|
}
|
|
869
843
|
|
|
870
|
-
//
|
|
844
|
+
// Add a global scope
|
|
871
845
|
Post.addGlobalScope('published', (query) => {
|
|
872
846
|
query.where('status', 'published');
|
|
873
847
|
});
|
|
874
848
|
|
|
875
|
-
//
|
|
849
|
+
// All queries filter automatically
|
|
876
850
|
const posts = await Post.all(); // Published only
|
|
877
851
|
|
|
878
|
-
//
|
|
852
|
+
// Temporarily disable a scope
|
|
879
853
|
const allPosts = await Post.withoutGlobalScope('published').get();
|
|
880
854
|
|
|
881
|
-
// Disable
|
|
855
|
+
// Disable all scopes
|
|
882
856
|
const rawPosts = await Post.withoutGlobalScopes().get();
|
|
883
857
|
```
|
|
884
858
|
|
|
@@ -896,12 +870,12 @@ class User extends Model {
|
|
|
896
870
|
// Before creation
|
|
897
871
|
User.creating((user) => {
|
|
898
872
|
user.setAttribute('uuid', generateUUID());
|
|
899
|
-
//
|
|
873
|
+
// Return false to roll back
|
|
900
874
|
});
|
|
901
875
|
|
|
902
876
|
// After creation
|
|
903
877
|
User.created((user) => {
|
|
904
|
-
console.log(`
|
|
878
|
+
console.log(`User ${user.getAttribute('id')} created`);
|
|
905
879
|
});
|
|
906
880
|
|
|
907
881
|
// Before update
|
|
@@ -911,28 +885,28 @@ User.updating((user) => {
|
|
|
911
885
|
|
|
912
886
|
// After update
|
|
913
887
|
User.updated((user) => {
|
|
914
|
-
//
|
|
888
|
+
// Notify external systems
|
|
915
889
|
});
|
|
916
890
|
|
|
917
891
|
// saving/saved events (creation AND update)
|
|
918
892
|
User.saving((user) => {
|
|
919
|
-
//
|
|
893
|
+
// Data cleanup
|
|
920
894
|
});
|
|
921
895
|
|
|
922
896
|
User.saved((user) => {
|
|
923
897
|
// Cache invalidation
|
|
924
898
|
});
|
|
925
899
|
|
|
926
|
-
//
|
|
900
|
+
// Before/after deletion
|
|
927
901
|
User.deleting((user) => {
|
|
928
902
|
// Checks before deletion
|
|
929
903
|
});
|
|
930
904
|
|
|
931
905
|
User.deleted((user) => {
|
|
932
|
-
//
|
|
906
|
+
// Clean up relationships
|
|
933
907
|
});
|
|
934
908
|
|
|
935
|
-
//
|
|
909
|
+
// For soft deletes
|
|
936
910
|
User.restoring((user) => {});
|
|
937
911
|
User.restored((user) => {});
|
|
938
912
|
```
|
|
@@ -971,7 +945,7 @@ console.log(errors);
|
|
|
971
945
|
// age: ['age must not exceed 150']
|
|
972
946
|
// }
|
|
973
947
|
|
|
974
|
-
//
|
|
948
|
+
// Validate or throw an error
|
|
975
949
|
try {
|
|
976
950
|
user.validateOrFail();
|
|
977
951
|
} catch (error) {
|
|
@@ -981,14 +955,14 @@ try {
|
|
|
981
955
|
|
|
982
956
|
### Available rules
|
|
983
957
|
|
|
984
|
-
|
|
|
958
|
+
| Rule | Description |
|
|
985
959
|
|-------|-------------|
|
|
986
|
-
| `required` |
|
|
960
|
+
| `required` | Required field |
|
|
987
961
|
| `string` | Must be a string |
|
|
988
962
|
| `number` / `numeric` | Must be a number |
|
|
989
|
-
| `email` |
|
|
963
|
+
| `email` | Valid email format |
|
|
990
964
|
| `boolean` | Must be a boolean |
|
|
991
|
-
| `date` |
|
|
965
|
+
| `date` | Valid date |
|
|
992
966
|
| `min:N` | Minimum N (longueur ou value) |
|
|
993
967
|
| `max:N` | Maximum N (longueur ou value) |
|
|
994
968
|
| `in:a,b,c` | Valeur parmi la liste |
|
|
@@ -1005,7 +979,7 @@ const { Model } = require('outlet-orm');
|
|
|
1005
979
|
const db = Model.getConnection();
|
|
1006
980
|
db.enableQueryLog();
|
|
1007
981
|
|
|
1008
|
-
// Run
|
|
982
|
+
// Run queries
|
|
1009
983
|
await User.where('status', 'active').get();
|
|
1010
984
|
await Post.with('author').get();
|
|
1011
985
|
|
|
@@ -1025,7 +999,7 @@ db.disableQueryLog();
|
|
|
1025
999
|
|
|
1026
1000
|
// Check if active
|
|
1027
1001
|
if (db.isLogging()) {
|
|
1028
|
-
console.log('Logging
|
|
1002
|
+
console.log('Logging active');
|
|
1029
1003
|
}
|
|
1030
1004
|
```
|
|
1031
1005
|
|
|
@@ -1035,95 +1009,95 @@ if (db.isLogging()) {
|
|
|
1035
1009
|
|
|
1036
1010
|
| Method | Description |
|
|
1037
1011
|
|---------|-------------|
|
|
1038
|
-
| `new DatabaseConnection(config?)` | Creates a connection (
|
|
1039
|
-
| `connect()` | Establishes the connection (
|
|
1012
|
+
| `new DatabaseConnection(config?)` | Creates a connection (reads `.env` if config is omitted) |
|
|
1013
|
+
| `connect()` | Establishes the connection (called automatically) |
|
|
1040
1014
|
| `beginTransaction()` | Starts a transaction |
|
|
1041
|
-
| `commit()` |
|
|
1042
|
-
| `rollback()` |
|
|
1015
|
+
| `commit()` | Commits the transaction |
|
|
1016
|
+
| `rollback()` | Rolls back the transaction |
|
|
1043
1017
|
| `transaction(callback)` | Runs in a transaction (auto commit/rollback) |
|
|
1044
1018
|
| `select(table, query)` | Runs a SELECT |
|
|
1045
1019
|
| `insert(table, data)` | Inserts a record |
|
|
1046
1020
|
| `insertMany(table, data[])` | Inserts multiple records |
|
|
1047
1021
|
| `update(table, data, query)` | Updates records |
|
|
1048
|
-
| `delete(table, query)` |
|
|
1049
|
-
| `count(table, query)` |
|
|
1050
|
-
| `executeRawQuery(sql, params?)` |
|
|
1051
|
-
| `execute(sql, params?)` |
|
|
1052
|
-
| `increment(table, column, query, amount?)` |
|
|
1053
|
-
| `decrement(table, column, query, amount?)` |
|
|
1054
|
-
| `close()` / `disconnect()` |
|
|
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()` |
|
|
1057
|
-
| `disableQueryLog()` |
|
|
1030
|
+
| `enableQueryLog()` | Enables query logging |
|
|
1031
|
+
| `disableQueryLog()` | Disables logging |
|
|
1058
1032
|
| `getQueryLog()` | Returns the query log |
|
|
1059
|
-
| `flushQueryLog()` |
|
|
1033
|
+
| `flushQueryLog()` | Clears the log |
|
|
1060
1034
|
| `isLogging()` | Checks whether logging is active |
|
|
1061
1035
|
|
|
1062
|
-
### Model (methods
|
|
1036
|
+
### Model (static methods)
|
|
1063
1037
|
|
|
1064
1038
|
| Method | Description |
|
|
1065
1039
|
|---------|-------------|
|
|
1066
|
-
| `setConnection(db)` |
|
|
1040
|
+
| `setConnection(db)` | Sets the default connection |
|
|
1067
1041
|
| `getConnection()` | Gets the connection (v3.0.0+) |
|
|
1068
1042
|
| `setMorphMap(map)` | Defines polymorphic mapping |
|
|
1069
|
-
| `query()` |
|
|
1070
|
-
| `all()` |
|
|
1071
|
-
| `find(id)` |
|
|
1072
|
-
| `findOrFail(id)` |
|
|
1073
|
-
| `first()` |
|
|
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
1052
|
| `create(attrs)` | Creates and saves |
|
|
1079
|
-
| `insert(data)` |
|
|
1053
|
+
| `insert(data)` | Raw insert |
|
|
1080
1054
|
| `update(attrs)` | Update bulk |
|
|
1081
|
-
| `updateById(id, attrs)` | Update
|
|
1082
|
-
| `updateAndFetchById(id, attrs, rels?)` | Update + fetch
|
|
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
1059
|
| `withHidden()` | Includes hidden attributes |
|
|
1086
|
-
| `withoutHidden(show?)` |
|
|
1087
|
-
| `orderBy(col, dir?)` |
|
|
1088
|
-
| `limit(n)` / `offset(n)` |
|
|
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()` |
|
|
1064
|
+
| `count()` | Count |
|
|
1091
1065
|
| **Soft Deletes** | |
|
|
1092
|
-
| `withTrashed()` |
|
|
1093
|
-
| `onlyTrashed()` |
|
|
1066
|
+
| `withTrashed()` | Includes soft-deleted records |
|
|
1067
|
+
| `onlyTrashed()` | Only soft-deleted records |
|
|
1094
1068
|
| **Scopes** | |
|
|
1095
1069
|
| `addGlobalScope(name, cb)` | Adds a global scope |
|
|
1096
1070
|
| `removeGlobalScope(name)` | Removes a scope |
|
|
1097
1071
|
| `withoutGlobalScope(name)` | Query without one scope |
|
|
1098
1072
|
| `withoutGlobalScopes()` | Query without all scopes |
|
|
1099
1073
|
| **Events** | |
|
|
1100
|
-
| `on(event, callback)` |
|
|
1074
|
+
| `on(event, callback)` | Registers a listener |
|
|
1101
1075
|
| `creating(cb)` / `created(cb)` | Creation events |
|
|
1102
1076
|
| `updating(cb)` / `updated(cb)` | Update events |
|
|
1103
|
-
| `saving(cb)` / `saved(cb)` |
|
|
1104
|
-
| `deleting(cb)` / `deleted(cb)` |
|
|
1105
|
-
| `restoring(cb)` / `restored(cb)` |
|
|
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 (methods
|
|
1081
|
+
### Model (instance methods)
|
|
1108
1082
|
|
|
1109
1083
|
| Method | Description |
|
|
1110
1084
|
|---------|-------------|
|
|
1111
1085
|
| `fill(attrs)` | Fills attributes |
|
|
1112
1086
|
| `setAttribute(key, val)` | Sets an attribute |
|
|
1113
1087
|
| `getAttribute(key)` | Gets an attribute |
|
|
1114
|
-
| `save()` |
|
|
1115
|
-
| `destroy()` |
|
|
1116
|
-
| `load(...rels)` |
|
|
1117
|
-
| `getDirty()` |
|
|
1088
|
+
| `save()` | Save (insert or update) |
|
|
1089
|
+
| `destroy()` | Delete the instance (soft if enabled) |
|
|
1090
|
+
| `load(...rels)` | Load relationships |
|
|
1091
|
+
| `getDirty()` | Modified attributes |
|
|
1118
1092
|
| `isDirty()` | Has been modified? |
|
|
1119
|
-
| `toJSON()` |
|
|
1093
|
+
| `toJSON()` | Convert to plain object |
|
|
1120
1094
|
| **Soft Deletes** | |
|
|
1121
1095
|
| `trashed()` | Is deleted? |
|
|
1122
|
-
| `restore()` | Restore
|
|
1096
|
+
| `restore()` | Restore the model |
|
|
1123
1097
|
| `forceDelete()` | Permanent deletion |
|
|
1124
1098
|
| **Validation** | |
|
|
1125
|
-
| `validate()` |
|
|
1126
|
-
| `validateOrFail()` |
|
|
1099
|
+
| `validate()` | Validate against rules |
|
|
1100
|
+
| `validateOrFail()` | Validate or throw error |
|
|
1127
1101
|
|
|
1128
1102
|
### QueryBuilder
|
|
1129
1103
|
|
|
@@ -1139,22 +1113,22 @@ 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?)` |
|
|
1143
|
-
| `has(rel, op?, count)` |
|
|
1144
|
-
| `whereDoesntHave(rel)` | Absence
|
|
1145
|
-
| `orderBy(col, dir?)` / `ordrer(...)` |
|
|
1146
|
-
| `limit(n)` / `take(n)` |
|
|
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)` |
|
|
1154
|
-
| `withTrashed()` |
|
|
1155
|
-
| `onlyTrashed()` |
|
|
1156
|
-
| `withoutGlobalScope(name)` |
|
|
1157
|
-
| `withoutGlobalScopes()` |
|
|
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 |
|
|
1158
1132
|
| `get()` | Runs and returns all |
|
|
1159
1133
|
| `first()` | First result |
|
|
1160
1134
|
| `firstOrFail()` | Premier ou erreur |
|
|
@@ -1165,15 +1139,15 @@ if (db.isLogging()) {
|
|
|
1165
1139
|
| `update(attrs)` | Update |
|
|
1166
1140
|
| `updateAndFetch(attrs, rels?)` | Update + fetch |
|
|
1167
1141
|
| `delete()` | Delete |
|
|
1168
|
-
| `increment(col, amount?)` |
|
|
1169
|
-
| `decrement(col, amount?)` |
|
|
1170
|
-
| `clone()` |
|
|
1142
|
+
| `increment(col, amount?)` | Atomic increment |
|
|
1143
|
+
| `decrement(col, amount?)` | Atomic decrement |
|
|
1144
|
+
| `clone()` | Clones the query builder |
|
|
1171
1145
|
|
|
1172
1146
|
## ๐ ๏ธ CLI tools
|
|
1173
1147
|
|
|
1174
1148
|
### outlet-init
|
|
1175
1149
|
|
|
1176
|
-
|
|
1150
|
+
Initialises a new project with database configuration.
|
|
1177
1151
|
|
|
1178
1152
|
```bash
|
|
1179
1153
|
outlet-init
|
|
@@ -1190,19 +1164,19 @@ Generates:
|
|
|
1190
1164
|
complete migration system.
|
|
1191
1165
|
|
|
1192
1166
|
```bash
|
|
1193
|
-
# Create
|
|
1167
|
+
# Create a migration
|
|
1194
1168
|
outlet-migrate make create_users_table
|
|
1195
1169
|
|
|
1196
|
-
# Run
|
|
1170
|
+
# Run migrations
|
|
1197
1171
|
outlet-migrate migrate
|
|
1198
1172
|
|
|
1199
|
-
# See
|
|
1173
|
+
# See migration status
|
|
1200
1174
|
outlet-migrate status
|
|
1201
1175
|
|
|
1202
1176
|
# Roll back the latest migration
|
|
1203
1177
|
outlet-migrate rollback --steps 1
|
|
1204
1178
|
|
|
1205
|
-
# Reset
|
|
1179
|
+
# Reset all migrations
|
|
1206
1180
|
outlet-migrate reset --yes
|
|
1207
1181
|
|
|
1208
1182
|
# Refresh (reset + migrate)
|
|
@@ -1212,17 +1186,17 @@ outlet-migrate refresh --yes
|
|
|
1212
1186
|
outlet-migrate fresh --yes
|
|
1213
1187
|
```
|
|
1214
1188
|
|
|
1215
|
-
**Features
|
|
1189
|
+
**Migration Features:**
|
|
1216
1190
|
|
|
1217
1191
|
- โ
Creation and management of migrations (create, alter, drop tables)
|
|
1218
|
-
- โ
|
|
1219
|
-
- โ
|
|
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
|
|
1220
1194
|
- โ
Foreign keys: foreign(), constrained(), onDelete(), onUpdate(), CASCADE
|
|
1221
1195
|
- โ
Index: index(), unique(), fullText()
|
|
1222
1196
|
- โ
Manipulation: renameColumn(), dropColumn(), dropTimestamps()
|
|
1223
|
-
- โ
Reversible migrations:
|
|
1197
|
+
- โ
Reversible migrations: up() and down() methods
|
|
1224
1198
|
- โ
Batch tracking: Precise rollback by batch
|
|
1225
|
-
- โ
Custom SQL: execute()
|
|
1199
|
+
- โ
Custom SQL: execute() for advanced commands
|
|
1226
1200
|
|
|
1227
1201
|
### outlet-convert
|
|
1228
1202
|
|
|
@@ -1233,29 +1207,29 @@ outlet-convert
|
|
|
1233
1207
|
```
|
|
1234
1208
|
|
|
1235
1209
|
**Options:**
|
|
1236
|
-
1.
|
|
1210
|
+
1. From a local SQL file
|
|
1237
1211
|
2. From a connected database
|
|
1238
1212
|
|
|
1239
1213
|
**Features:**
|
|
1240
|
-
- โ
|
|
1214
|
+
- โ
Automatic type and cast detection
|
|
1241
1215
|
- โ
Automatic generation of ALL relationships (belongsTo, hasMany, hasOne, belongsToMany)
|
|
1242
1216
|
- โ
Recursive relationships (auto-relationships)
|
|
1243
|
-
- โ
Detection
|
|
1244
|
-
- โ
|
|
1245
|
-
- โ
|
|
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
|
|
1250
|
-
- [Conversion
|
|
1251
|
-
- [Detection
|
|
1223
|
+
- [Migrations Guide](docs/MIGRATIONS.md)
|
|
1224
|
+
- [SQL Conversion](docs/SQL_CONVERSION.md)
|
|
1225
|
+
- [Relation Detection](docs/RELATIONS_DETECTION.md)
|
|
1252
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
|
|
1232
|
+
Outlet ORM v4.0.0 includes complete TypeScript definitions with support for **generics for typed model attributes**.
|
|
1259
1233
|
|
|
1260
1234
|
### Typed models
|
|
1261
1235
|
|