tspace-mysql 1.9.0 → 1.9.1-beta.1

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 (118) hide show
  1. package/README.md +4580 -133
  2. package/dist/lib/config/index.d.ts +2 -2
  3. package/dist/lib/config/index.js +24 -16
  4. package/dist/lib/config/index.js.map +1 -1
  5. package/dist/lib/constants/index.d.ts +10 -1
  6. package/dist/lib/constants/index.js +11 -2
  7. package/dist/lib/constants/index.js.map +1 -1
  8. package/dist/lib/core/Abstracts/AbstractBuilder.d.ts +27 -23
  9. package/dist/lib/core/Abstracts/AbstractBuilder.js +11 -13
  10. package/dist/lib/core/Abstracts/AbstractBuilder.js.map +1 -1
  11. package/dist/lib/core/Abstracts/AbstractDB.d.ts +5 -2
  12. package/dist/lib/core/Abstracts/AbstractDB.js.map +1 -1
  13. package/dist/lib/core/Abstracts/AbstractModel.d.ts +7 -2
  14. package/dist/lib/core/Abstracts/AbstractModel.js.map +1 -1
  15. package/dist/lib/core/Abstracts/AbstractView.js.map +1 -1
  16. package/dist/lib/core/Blueprint.d.ts +35 -15
  17. package/dist/lib/core/Blueprint.js +38 -14
  18. package/dist/lib/core/Blueprint.js.map +1 -1
  19. package/dist/lib/core/Builder.d.ts +314 -42
  20. package/dist/lib/core/Builder.js +686 -391
  21. package/dist/lib/core/Builder.js.map +1 -1
  22. package/dist/lib/core/Cache/DBCache.js +19 -20
  23. package/dist/lib/core/Cache/DBCache.js.map +1 -1
  24. package/dist/lib/core/Cache/MemoryCache.js +3 -3
  25. package/dist/lib/core/Cache/MemoryCache.js.map +1 -1
  26. package/dist/lib/core/Cache/RedisCache.js +27 -17
  27. package/dist/lib/core/Cache/RedisCache.js.map +1 -1
  28. package/dist/lib/core/Cache/index.js +37 -17
  29. package/dist/lib/core/Cache/index.js.map +1 -1
  30. package/dist/lib/core/Contracts/AlterTable.d.ts +152 -0
  31. package/dist/lib/core/Contracts/AlterTable.js +243 -0
  32. package/dist/lib/core/Contracts/AlterTable.js.map +1 -0
  33. package/dist/lib/core/Contracts/Audit.d.ts +1 -1
  34. package/dist/lib/core/Contracts/Audit.js +2 -5
  35. package/dist/lib/core/Contracts/Audit.js.map +1 -1
  36. package/dist/lib/core/Contracts/Logger.d.ts +1 -1
  37. package/dist/lib/core/Contracts/Logger.js.map +1 -1
  38. package/dist/lib/core/DB.d.ts +103 -11
  39. package/dist/lib/core/DB.js +158 -32
  40. package/dist/lib/core/DB.js.map +1 -1
  41. package/dist/lib/core/Decorator.d.ts +144 -16
  42. package/dist/lib/core/Decorator.js +156 -14
  43. package/dist/lib/core/Decorator.js.map +1 -1
  44. package/dist/lib/core/Driver/index.d.ts +50 -14
  45. package/dist/lib/core/Driver/index.js +13 -9
  46. package/dist/lib/core/Driver/index.js.map +1 -1
  47. package/dist/lib/core/Driver/mariadb/MariadbDriver.js +84 -29
  48. package/dist/lib/core/Driver/mariadb/MariadbDriver.js.map +1 -1
  49. package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.d.ts +48 -12
  50. package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js +189 -28
  51. package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js.map +1 -1
  52. package/dist/lib/core/Driver/mongodb/MongodbDriver.d.ts +24 -0
  53. package/dist/lib/core/Driver/mongodb/MongodbDriver.js +261 -0
  54. package/dist/lib/core/Driver/mongodb/MongodbDriver.js.map +1 -0
  55. package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.d.ts +141 -0
  56. package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js +571 -0
  57. package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js.map +1 -0
  58. package/dist/lib/core/Driver/mysql/MysqlDriver.js +115 -97
  59. package/dist/lib/core/Driver/mysql/MysqlDriver.js.map +1 -1
  60. package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.d.ts +48 -12
  61. package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js +189 -28
  62. package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js.map +1 -1
  63. package/dist/lib/core/Driver/postgres/PostgresDriver.js +106 -73
  64. package/dist/lib/core/Driver/postgres/PostgresDriver.js.map +1 -1
  65. package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.d.ts +53 -12
  66. package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js +318 -48
  67. package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js.map +1 -1
  68. package/dist/lib/core/Driver/sqlite/SqliteDriver.d.ts +21 -0
  69. package/dist/lib/core/Driver/sqlite/SqliteDriver.js +213 -0
  70. package/dist/lib/core/Driver/sqlite/SqliteDriver.js.map +1 -0
  71. package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.d.ts +146 -0
  72. package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js +705 -0
  73. package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js.map +1 -0
  74. package/dist/lib/core/Join.d.ts +63 -5
  75. package/dist/lib/core/Join.js +92 -15
  76. package/dist/lib/core/Join.js.map +1 -1
  77. package/dist/lib/core/JoinModel.d.ts +65 -9
  78. package/dist/lib/core/JoinModel.js +81 -52
  79. package/dist/lib/core/JoinModel.js.map +1 -1
  80. package/dist/lib/core/Meta.d.ts +22 -16
  81. package/dist/lib/core/Meta.js +28 -21
  82. package/dist/lib/core/Meta.js.map +1 -1
  83. package/dist/lib/core/Model.d.ts +644 -134
  84. package/dist/lib/core/Model.js +1361 -631
  85. package/dist/lib/core/Model.js.map +1 -1
  86. package/dist/lib/{tools/index.d.ts → core/Package.d.ts} +11 -3
  87. package/dist/lib/{tools/index.js → core/Package.js} +20 -7
  88. package/dist/lib/core/Package.js.map +1 -0
  89. package/dist/lib/core/Pool.js +42 -30
  90. package/dist/lib/core/Pool.js.map +1 -1
  91. package/dist/lib/core/Queue.d.ts +249 -0
  92. package/dist/lib/core/Queue.js +726 -0
  93. package/dist/lib/core/Queue.js.map +1 -0
  94. package/dist/lib/core/RelationManager.d.ts +4 -4
  95. package/dist/lib/core/RelationManager.js +168 -41
  96. package/dist/lib/core/RelationManager.js.map +1 -1
  97. package/dist/lib/core/Repository.d.ts +73 -207
  98. package/dist/lib/core/Repository.js +122 -234
  99. package/dist/lib/core/Repository.js.map +1 -1
  100. package/dist/lib/core/Schema.d.ts +211 -1
  101. package/dist/lib/core/Schema.js +370 -65
  102. package/dist/lib/core/Schema.js.map +1 -1
  103. package/dist/lib/core/StateManager.d.ts +31 -12
  104. package/dist/lib/core/StateManager.js +11 -5
  105. package/dist/lib/core/StateManager.js.map +1 -1
  106. package/dist/lib/core/UtilityTypes.d.ts +88 -77
  107. package/dist/lib/core/UtilityTypes.js.map +1 -1
  108. package/dist/lib/core/index.d.ts +4 -0
  109. package/dist/lib/core/index.js +6 -2
  110. package/dist/lib/core/index.js.map +1 -1
  111. package/dist/lib/types/decorator/index.d.ts +1 -1
  112. package/dist/lib/types/index.d.ts +119 -30
  113. package/dist/lib/types/repository/index.d.ts +68 -37
  114. package/dist/lib/utils/index.d.ts +13 -2
  115. package/dist/lib/utils/index.js +81 -2
  116. package/dist/lib/utils/index.js.map +1 -1
  117. package/package.json +14 -6
  118. package/dist/lib/tools/index.js.map +0 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@ tspace-mysql is an Object-Relational Mapping (ORM) tool designed to run seamless
10
10
 
11
11
  | **Feature** | **Description** |
12
12
  |--------------------------------|---------------------------------------------------------------------------------------------------------|
13
- | **Supports Driver** | MySQL ✅ / MariaDB ✅ / Postgres / ✅ MSSQL / SQLite3 / Oracle |
13
+ | **Supports Driver** | MySQL ✅ / MariaDB ✅ / Postgres / SQLite / Mongodb (yes, even MongoDB 😏) / MSSQL |
14
14
  | **Query Builder** | Create flexible queries like `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. You can also use raw SQL. |
15
15
  | **Join Clauses** | Use `INNER JOIN`, `LEFT JOIN`, `RIGHT JOIN`, and `CROSS JOIN` to combine data from multiple tables. |
16
16
  | **Model** | Provides a way to interact with database records as objects in code. You can perform create, read, update, and delete (CRUD) operations. Models also support soft deletes and relationship methods. |
@@ -24,6 +24,7 @@ tspace-mysql is an Object-Relational Mapping (ORM) tool designed to run seamless
24
24
  | **Repository** | Follows a pattern for managing database operations like `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. It helps keep the code organized. |
25
25
  | **Decorators** | Use decorators to add extra functionality or information to model classes and methods, making the code easier to read. |
26
26
  | **Caching** | Improves performance by storing frequently requested data. Supports in-memory caching (like memory DB) and Redis for distributed caching. |
27
+ | **Queue** | Job queue for background and async processing. Runs on top of databases for distributed workers (similar to pg-boss). |
27
28
  | **Migrations** | Use CLI commands to create models, make migrations, and apply changes to the database structure. |
28
29
  | **Blueprints** | Create a clear layout of the database structure and how models and tables relate to each other. |
29
30
  | **CLI** | A Command Line Interface for managing models, running migrations, executing queries, and performing other tasks using commands (like `make:model`, `migrate`, and `query`). |
@@ -33,148 +34,4594 @@ tspace-mysql is an Object-Relational Mapping (ORM) tool designed to run seamless
33
34
  Install with [npm](https://www.npmjs.com/):
34
35
 
35
36
  ```sh
36
- # Install tspace-mysql locally for your project
37
+ ## Install tspace-mysql locally for your project
37
38
  npm install tspace-mysql --save
38
39
 
39
- # Install tspace-mysql globally (optional)
40
+ ## Install tspace-mysql globally (optional)
40
41
  npm install -g tspace-mysql
42
+ ```
43
+
44
+ ## Documentation
45
+
46
+ See the [`docs`](https://thanathip41.github.io/tspace-mysql) directory for full documentation.
47
+
48
+ ## TypeScript
49
+
50
+ The TypeScript version is specified only for **development and build-time** purposes and does **not** restrict how consumers use the library.
51
+
52
+ This library is built using **TypeScript 5.9.3**.
53
+ The minimum supported **TypeScript version is >= 5.6.2**.
54
+
55
+ If you are contributing to this library or building it locally, install the pinned TypeScript version:
56
+
57
+ ```sh
58
+
59
+ npm install -D typescript@5.9.3
60
+
61
+ ```
62
+
63
+ ## Basic Usage
41
64
 
42
- # Install database drivers if needed:
43
- # For MariaDB
65
+ - [Configuration](#configuration)
66
+ - [MySQL Database](#mysql-database)
67
+ - [Mariadb Database](#mariadb-database)
68
+ - [Postgres Database](#postgres-database)
69
+ - [SQLite Database](#sqlite-database)
70
+ - [Mongodb Database](#mongodb-database)
71
+ - [Cluster Database](#cluster-database)
72
+ - [SQL Like](#sql-Like)
73
+ - [Query Builder](#query-builder)
74
+ - [Table Name & Alias Name](#table-name--alias-name)
75
+ - [Returning Results](#returning-results)
76
+ - [Query Statement](#query-statements)
77
+ - [Select Statements](#select-statements)
78
+ - [Raw Expressions](#raw-expressions)
79
+ - [Ordering, Grouping, Limit and Offset](#ordering-grouping-limit-and-offset)
80
+ - [Ordering](#ordering)
81
+ - [Grouping](#grouping)
82
+ - [Limit and Offset](#limit-and-offset)
83
+ - [Joins](#joins)
84
+ - [Inner Join Clause](#inner-join-clause)
85
+ - [Left Join, Right Join Clause](#left-join-right-join-clause)
86
+ - [Cross Join Clause](#cross-join-clause)
87
+ - [Basic Where Clauses](#basic-where-clauses)
88
+ - [Where Clauses](#where-clauses)
89
+ - [Or Where Clauses](#or-where-clauses)
90
+ - [Where cases](#where-cases)
91
+ - [Where Object Clauses](#where-object-clauses)
92
+ - [JSON Where Clauses](#json-where-clauses)
93
+ - [Additional Where Clauses](#additional-where-clauses)
94
+ - [Logical Grouping](#logical-grouping)
95
+ - [Advanced Where Clauses](#advanced-where-clauses)
96
+ - [Where Exists Clauses](#where-exists-clauses)
97
+ - [Subquery Where Clauses](#subquery-where-clauses)
98
+ - [Conditional Where Clauses](#conditional-where-clauses)
99
+ - [GetGroupBy](#getgroupby)
100
+ - [Paginating](#paginating)
101
+ - [Insert Statements](#insert-statements)
102
+ - [Update Statements](#update-statements)
103
+ - [Delete Statements](#delete-statements)
104
+ - [Hook Statements](#hook-statements)
105
+ - [Faker Statements](#faker-statements)
106
+ - [Unset Statements](#unset-statements)
107
+ - [Common Table Expressions](#common-table-expressions)
108
+ - [Union](#union)
109
+ - [More Methods](#more-methods)
110
+ - [Database Transactions](#database-transactions)
111
+ - [Race Condition](#race-condition)
112
+ - [Lock Table](#lock-table)
113
+ - [Connection](#connection)
114
+ - [Backup](#backup)
115
+ - [Injection](#injection)
116
+ - [Generating Model Classes](#generating-model-classes)
117
+ - [Model Conventions](#model-conventions)
118
+ - [Basic Model Setup](#basic-model-setup)
119
+ - [Table Name](#table-name)
120
+ - [Pattern](#pattern)
121
+ - [UUID](#uuid)
122
+ - [Timestamp](#timestamp)
123
+ - [Debug](#debug)
124
+ - [Observer](#observer)
125
+ - [Logger](#logger)
126
+ - [Hooks](#hooks)
127
+ - [Global Scope](#global-scope)
128
+ - [Schema](#schema)
129
+ - [Schema Model](#schema-model)
130
+ - [Virtual Column](#virtual-column)
131
+ - [Validation](#validation)
132
+ - [Sync](#sync)
133
+ - [SoftDelete](#softdelete)
134
+ - [Joins Model](#joins-model)
135
+ - [Inner Join Model Clause](#inner-join-model-clause)
136
+ - [Left Join , Right Join Model Clause](#left-join-right-join-model-clause)
137
+ - [Cross Join Model Clause](#cross-join-model-clause)
138
+ - [Relationships](#relationships)
139
+ - [One To One](#one-to-one)
140
+ - [One To Many](#one-to-many)
141
+ - [Belongs To](#belongs-to)
142
+ - [Many To Many](#many-to-many)
143
+ - [Relation](#relation)
144
+ - [Deeply Nested Relations](#deeply-nested-relations)
145
+ - [Relation Exists](#relation-exists)
146
+ - [Relation Count](#relation-count)
147
+ - [Relation Trashed](#relation-trashed)
148
+ - [Built in Relation Functions](#built-in-relation-functions)
149
+ - [Cache](#cache)
150
+ - [Decorator](#decorator)
151
+ - [Type Safety](#type-safety)
152
+ - [Type Safety Select](#type-safety-select)
153
+ - [Type Safety OrderBy](#type-safety-order-by)
154
+ - [Type Safety GroupBy](#type-safety-group-by)
155
+ - [Type Safety Where](#type-safety-where)
156
+ - [Type Safety Insert](#type-safety-insert)
157
+ - [Type Safety Update](#type-safety-update)
158
+ - [Type Safety Delete](#type-safety-delete)
159
+ - [Type Safety Relationships](#type-safety-relationships)
160
+ - [Type Safety Results](#type-safety-results)
161
+ - [Metadata](#metadata)
162
+ - [Audit](#audit)
163
+ - [Repository](#repository)
164
+ - [Repository Select Statements](#repository-select-statements)
165
+ - [Repository Insert Statements](#repository-insert-statements)
166
+ - [Repository Update Statements](#repository-update-statements)
167
+ - [Repository Delete Statements](#repository-delete-statements)
168
+ - [Repository Transactions](#repository-transactions)
169
+ - [Repository Relations](#repository-relations)
170
+ - [Queue](#queue)
171
+ - [View](#view)
172
+ - [Stored Procedure](#stored-procedure)
173
+ - [Blueprint](#blueprint)
174
+ - [Cli](#cli)
175
+ - [Make Model](#make-model)
176
+ - [Make Migration](#make-migration)
177
+ - [Migrate](#migrate)
178
+ - [Query](#query)
179
+ - [Dump](#dump)
180
+ - [Generate Models](#generate-models)
181
+ - [Migration Models](#migration-models)
182
+ - [Migration DB](#migration-db)
183
+
184
+ ## Configuration
185
+
186
+ To establish a connection, the recommended method for creating your environment variables is by using a '.env' file. using the following:
187
+
188
+ ```js
189
+ DB_HOST = localhost
190
+ DB_PORT = 3306
191
+ DB_USERNAME = root
192
+ DB_PASSWORD = password
193
+ DB_DATABASE = database
194
+ /**
195
+ * @default
196
+ * DB_CONNECTION_LIMIT = 20
197
+ * DB_QUEUE_LIMIT = 0
198
+ * DB_TIMEOUT = 60000
199
+ * DB_DATE_STRINGS = false
200
+ */
201
+ ```
202
+
203
+ ### MySQL Database
204
+
205
+ To connect the application to a MySQL database, using the following:
206
+ ```sh
207
+ npm install mysql2 --save
208
+ ```
209
+
210
+ ```js
211
+ DB_DRIVER = mysql
212
+ DB_HOST = localhost
213
+ DB_PORT = 3306
214
+ DB_USERNAME = root
215
+ DB_PASSWORD = password
216
+ DB_DATABASE = database
217
+ ```
218
+
219
+ ### Mariadb Database
220
+
221
+ To connect the application to a Mariadb database, using the following:
222
+
223
+ ```sh
44
224
  npm install mariadb --save
225
+ ```
226
+
227
+ ```js
228
+ DB_DRIVER = mariadb
229
+ DB_HOST = localhost
230
+ DB_PORT = 3306
231
+ DB_USERNAME = root
232
+ DB_PASSWORD = password
233
+ DB_DATABASE = database
234
+ ```
235
+
236
+ ### Postgres Database
45
237
 
46
- # For PostgreSQL
238
+ To connect the application to a Postgres database, using the following:
239
+
240
+ ```sh
47
241
  npm install pg --save
242
+ ```
48
243
 
49
- # MySQL2 driver is installed by default with tspace-mysql
244
+ ```js
245
+ DB_DRIVER = postgres
246
+ DB_HOST = localhost
247
+ DB_PORT = 5432
248
+ DB_USERNAME = root
249
+ DB_PASSWORD = password
250
+ DB_DATABASE = database
50
251
  ```
51
252
 
52
- ## Documentation
253
+ ### SQLite Database
53
254
 
54
- See the [`docs`](https://thanathip41.github.io/tspace-mysql) directory for full documentation.
255
+ To connect the application to a SQLite database, using the following:
55
256
 
56
- ## Basic Usage
257
+ ```sh
258
+ npm install better-sqlite3 --save
259
+ ```
260
+
261
+ ```js
262
+ DB_DRIVER = sqlite
263
+ DB_DATABASE = app.db
264
+ ```
265
+ ⚠️ Requirements for better-sqlite3
266
+ Node.js 22 or higher is required
267
+
268
+ ### Mongodb Database
269
+
270
+ To connect the application to a Mongodb database, using the following:
271
+ ```sh
272
+ npm install mongodb --save
273
+ ```
274
+
275
+ ```js
276
+ DB_DRIVER = mongodb
277
+ DB_HOST = localhost
278
+ DB_PORT = 27017
279
+ DB_USERNAME = root
280
+ DB_PASSWORD = password
281
+ DB_DATABASE = database
282
+ ```
283
+ ✅ Supported Features
284
+
285
+ CRUD operations (create, read, update, delete)
286
+ Basic joins & relations (via abstraction layer)
287
+
288
+ ⚠️ Limitations
289
+
290
+ MongoDB support is partially implemented and may not fully match SQL-based drivers like MySQL or PostgreSQL
291
+
292
+ * Advanced ORM features may be limited
293
+ * Complex joins rely on abstraction (not native MongoDB behavior)
294
+ * Some features may behave differently compared to relational databases
295
+ * Transactions / advanced optimizations may be limited
296
+
297
+ ### Cluster Database
298
+ If you need strict race condition control, it is required to use multiple nodes for write and read. <br>
299
+ Avoid using a node load balancer in this case, as it may bypass proper write/read distribution and compromise consistency.<br>
300
+ To connect your application to a Cluster database, use the following configuration:
301
+
302
+ ```js
303
+ // ----------------------------------------------------
304
+ // example MariaDB Galera Cluster
305
+
306
+ DB_DRIVER = mariadb
307
+ DB_HOST = host-load-balncer ❌
308
+ DB_PORT = 3306
309
+ DB_USERNAME = root1
310
+ DB_PASSWORD = password1
311
+ DB_DATABASE = database
312
+ ```
313
+
314
+ ```js
315
+ // ----------------------------------------------------
316
+ // MariaDB Galera Cluster
317
+ // host1 -> Master node
318
+ // host2, host3 -> slave nodes
319
+ DB_CLUSTER = true
320
+ DB_DRIVER = mariadb
321
+ DB_HOST = host1,host2,host3 ✅ // host1 still master by default
322
+ // if you want to specific master or slave
323
+ // master can be more than 1
324
+ // DB_HOST = master@host1,slave@host2,slave@host3
325
+ DB_PORT = 3306,3307,3308
326
+ DB_USERNAME = root1,root2,root3
327
+ DB_PASSWORD = password1,password2,password3
328
+ DB_DATABASE = database
329
+ ```
330
+
331
+ ## SQL Like
332
+ SQL (Structured Query Language) is used to manage data in relational databases.
333
+ Here are the four most common commands with **tspace-mysql** examples:
334
+ ```js
335
+ import { sql , OP } from 'tspace-mysql'
336
+
337
+ // select
338
+ await sql()
339
+ .select('id','name')
340
+ .from('users')
341
+ .where({
342
+ 'name' : 'tspace'
343
+ 'id' : OP.in([1,2,3])
344
+ })
345
+ .limit(3)
346
+ .orderBy('name')
347
+
348
+ // insert
349
+ await sql()
350
+ .insert('users')
351
+ .values({
352
+ email : 'tspace@example.com'
353
+ })
354
+
355
+ // insert return data
356
+ await sql()
357
+ .insert('users')
358
+ .values({
359
+ email : 'tspace@example.com'
360
+ })
361
+ .returning({
362
+ id : true,
363
+ email : true,
364
+ enum : true
365
+ })
366
+
367
+ // update
368
+ await sql()
369
+ .update('users')
370
+ .where({
371
+ id : 1
372
+ })
373
+ .set({
374
+ email : 'tspace@example.com'
375
+ })
376
+
377
+ // update return data
378
+ await sql()
379
+ .update('users')
380
+ .where({
381
+ id : 1
382
+ })
383
+ .set({
384
+ email : 'tspace@example.com'
385
+ })
386
+ .returning()
387
+
388
+ //delete
389
+ await sql()
390
+ .delete('users')
391
+ .where({
392
+ id : 1
393
+ })
394
+
395
+
396
+ ```
397
+
398
+ ## Query Builder
399
+
400
+ How a database query builder works with a simple example using the following:
401
+
402
+ ```js
403
+ +-------------+--------------+----------------------------+
404
+ | table users |
405
+ +-------------+--------------+----------------------------+
406
+ | id | username | email |
407
+ |-------------|--------------|----------------------------|
408
+ | 1 | tspace | tspace@gmail.com |
409
+ | 2 | tspace2 | tspace2@gmail.com |
410
+ +-------------+--------------+----------------------------+
411
+
412
+
413
+ +-------------+--------------+----------------------------+
414
+ | table posts |
415
+ +-------------+--------------+----------------------------+
416
+ | id | user_id | title |
417
+ |-------------|--------------|----------------------------|
418
+ | 1 | 1 | posts tspace |
419
+ | 2 | 2 | posts tspace2 |
420
+ +-------------+--------------+----------------------------+
421
+
422
+ ```
423
+
424
+ ### Table Name & Alias Name
425
+
426
+ ```js
427
+ import { DB } from 'tspace-mysql'
428
+
429
+ await new DB().from('users').find(1)
430
+ // SELECT * FROM `users` WHERE `users`.`id` = '1' LIMIT 1;
431
+
432
+ await new DB().table('users').find(1)
433
+ // SELECT * FROM `users` WHERE `users`.`id` = '1' LIMIT 1;
434
+
435
+ await new DB().table('users').alias('u').find(1)
436
+ // SELECT * FROM `users` AS `u` WHERE `u`.`id` = '1' LIMIT 1;
437
+
438
+ await new DB().fromRaw('u',new DB('users').select('*').limit(1).toString()).find(1)
439
+ // SELECT * FROM ( SELECT * FROM `users` LIMIT 1 ) AS `u` WHERE `u`.`id` = '1' LIMIT 1;
440
+
441
+ await new DB().alias('u',new DB('users').select('*').limit(1).toString()).find(1)
442
+ // SELECT * FROM ( SELECT * FROM `users` LIMIT 1 ) AS `u` WHERE `u`.`id` = '1' LIMIT 1;
443
+
444
+ ```
445
+
446
+ ### Returning Results
447
+
448
+ ```js
449
+ const user = await new DB("users").find(1); // Object or null
450
+
451
+ const user = await new DB("users").findOne(); // Object or null
452
+
453
+ const user = await new DB("users").first(); // Object or null
454
+
455
+ const user = await new DB("users").firstOrError(message); // Object or error
456
+
457
+ const users = await new DB("users").findMany(); // Array-object of users
458
+
459
+ const users = await new DB("users").get(); // Array-object of users
460
+
461
+ const users = await new DB("users").getGroupBy('name') // Map
462
+
463
+ const users = await new DB("users").findGroupBy('name') // Map
464
+
465
+ const users = await new DB("users").toArray(); // Array of users
466
+
467
+ const users = await new DB("users").toJSON(); // JSON of users
468
+
469
+ const user = await new DB("users").exists(); // Boolean true if user exists otherwise false
470
+
471
+ const user = await new DB("users").count(); // Number of users counted
472
+
473
+ const user = await new DB("users").avg(); // Number of users avg
474
+
475
+ const user = await new DB("users").sum(); // Number of users sum
476
+
477
+ const user = await new DB("users").max(); // Number of users max
478
+
479
+ const user = await new DB("user").min(); // Number of users min
480
+
481
+ const users = await new DB("users").toString(); // sql query string
482
+
483
+ const users = await new DB("users").toSQL(); // sql query string
484
+
485
+ const users = await new DB("users").toRawSQL(); // sql query string
486
+
487
+ const users = await new DB("users").pagination(); // Object of pagination
488
+
489
+ const users = await new DB("users").makeSelectStatement() // query string for select statement
490
+
491
+ const users = await new DB("users").makeInsertStatement() // query string for insert statement
492
+
493
+ const users = await new DB("users").makeUpdateStatement() // query string for update statement
494
+
495
+ const users = await new DB("users").makeDeleteStatement() // query string for delete statement
496
+
497
+ const users = await new DB("users").makeCreateTableStatement() // query string for create table statement
498
+
499
+ ```
500
+
501
+ ## Query Statements
502
+
503
+ ```js
504
+ const query = await DB.query(
505
+ "SELECT * FROM users WHERE id = :id AND email IS :email AND name IN :username", {
506
+ id : 1,
507
+ email : null,
508
+ username : ['name1','name2']
509
+ })
510
+ // SELECT * FROM users WHERE id = '1' AND email IS NULL AND username in ('name1','name2');
511
+ ```
512
+
513
+ ## Select Statements
514
+
515
+ ```js
516
+ const select = await new DB("users").select("id", "username").findOne();
517
+ // SELECT `users`.`id`, `users`.`username` FROM `users` LIMIT 1;
518
+
519
+ const selectRaw = await new DB("users").selectRaw("COUNT(id)").findMany();
520
+ // SELECT COUNT(id) FROM `users`;
521
+ // You can also use the DB.raw() function
522
+ // const selectRaw = await new DB("users").selec(DB.raw("COUNT(id)")).findMany();
523
+
524
+ const selectObject = await new DB("posts")
525
+ .join("posts.user_id", "users.id")
526
+ .select("posts.*")
527
+ .selectObject(
528
+ { id: "users.id", name: "users.name", email: "users.email" },
529
+ "user"
530
+ )
531
+ .findOne();
532
+
533
+ /**
534
+ SELECT
535
+ posts.*, JSON_OBJECT('id' , `users`.`id` , 'name' , `users`.`name` , 'email' , `users`.`email`) AS `user`
536
+ FROM `posts`
537
+ INNER JOIN `users` ON `posts`.`user_id` = `users`.`id` LIMIT 1;
538
+ */
539
+
540
+ const selectArray = await new DB("users")
541
+ .select('id','name','email')
542
+ .join("users.id", "posts.user_id")
543
+ .select("posts.*")
544
+ .selectArray(
545
+ { id: "posts.id", user_id: "posts.user_id", title: "posts.title" },
546
+ "posts"
547
+ )
548
+ .findOne();
549
+ /**
550
+ SELECT
551
+ `users`.`id`, `users`.`name`, `users`.`email`,
552
+ CASE WHEN COUNT(`posts`.`id`) = 0 THEN JSON_ARRAY()
553
+ ELSE JSON_ARRAYAGG(JSON_OBJECT('id' , `posts`.`id` , 'user_id' , `posts`.`user_id` , 'email' , `posts`.`title`))
554
+ END AS `posts`
555
+ FROM `users`
556
+ INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id` WHERE `users`.`deletedAt` IS NULL GROUP BY `users`.`id` LIMIT 1;
557
+ */
558
+
559
+ await new DB("users").except("id").findOne();
560
+ // SELECT `users`.`email`, `users`.`username` FROM `users` LIMIT 1;
561
+
562
+ await new DB("users").distinct().select("id").findOne();
563
+ // SELECT DISTINCT `users`.`id` FROM `users` LIMIT 1;
564
+ ```
565
+
566
+ ## Raw Expressions
567
+
568
+ ```js
569
+ const users = await new DB("users")
570
+ .select(DB.raw("COUNT(`username`) as c"), "username")
571
+ .groupBy("username")
572
+ .having("c > 1")
573
+ .findMany();
574
+ // SELECT COUNT(`username`) as c, `users`.`username` FROM `users` GROUP BY `username` HAVING c > 1;
575
+
576
+ const users = await new DB("users")
577
+ .where(
578
+ "id",
579
+ DB.raw(new DB("users").select("id").where("id", "1").limit(1).toString())
580
+ )
581
+ .findMany();
582
+ // SELECT * FROM `users` WHERE `users`.`id` = (SELECT `users`.`id` FROM `users` WHERE `users`.`id` = '1' LIMIT 1);
583
+
584
+ const findFullName = await new User()
585
+ .select('name',`${DB.raw('CONCAT(firstName," ",lastName) as fullName')}`)
586
+ .whereRaw(`CONCAT(firstName," ",lastName) LIKE '%${search}%'`)
587
+ .findOne()
588
+ // SELECT `users`.`name`, CONCAT(firstName," ",lastName) as fullName FROM `users` WHERE CONCAT(firstName," ",lastName) LIKE '%search%' LIMIT 1;
589
+
590
+ ```
591
+
592
+ ## Ordering, Grouping, Limit and Offset
593
+
594
+ ### Ordering
595
+
596
+ ```js
597
+ await new DB("users").orderBy("id", "asc").findOne();
598
+ // SELECT * FROM `users` ORDER BY `id` ASC LIMIT 1;
599
+
600
+ await new DB("users").orderBy("id", "desc").findOne();
601
+ // SELECT * FROM `users` ORDER BY `id` DESC LIMIT 1;
602
+
603
+ await new DB("users").oldest("id").findOne();
604
+ // SELECT * FROM `users` ORDER BY `id` ASC LIMIT 1;
605
+
606
+ await new DB("users").latest("id").findOne();
607
+ // SELECT * FROM `users` ORDER BY `id` DESC LIMIT 1;
608
+
609
+ await new DB("users").random().findMany();
610
+ // SELECT * FROM `users` ORDER BY RAND();
611
+ ```
612
+
613
+ ### Grouping
614
+
615
+ ```js
616
+ await new DB("users").groupBy("id").findOne();
617
+ // SELECT * FROM `users` GROUP BY `id` LIMIT 1;
618
+
619
+ await new DB("users").groupBy("id", "username").findOne();
620
+ // SELECT * FROM `users` GROUP BY `id`, `username` LIMIT 1;
621
+
622
+ await new DB("users")
623
+ .select(DB.raw("COUNT(username) as c"), "username")
624
+ .groupBy("username")
625
+ .having("c > 1")
626
+ .findMany();
627
+ // SELECT COUNT(username) as c, `users`.`username` FROM `users` GROUP BY `username` HAVING c > 1;
628
+ ```
629
+
630
+ ### Limit and Offset
631
+
632
+ ```js
633
+ await new DB("users").limit(5).findMany();
634
+ // SELECT * FROM `users` LIMIT 5;
635
+
636
+ await new DB("users").limit(-1).findMany();
637
+ // SELECT * FROM `users` LIMIT 2147483647; // int-32 2**31 - 1
638
+
639
+ await new DB("users").offset(1).findOne();
640
+ // SELECT * FROM `users` LIMIT 1 OFFSET 1;
641
+ ```
642
+
643
+ ## Joins
644
+
645
+ ### Inner Join Clause
646
+
647
+ ```js
648
+ await new DB("posts").join("posts.user_id", "users.id").findMany();
649
+ // SELECT * FROM `posts` INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`;
650
+
651
+ await new DB("posts")
652
+ .join((join) => {
653
+ return join
654
+ .on('posts.user_id','users.id')
655
+ .on('users.id','post_user.user_id')
656
+ .and('users.id','posts.user_id')
657
+ })
658
+ .findMany();
659
+
660
+ // SELECT * FROM `posts`
661
+ // INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
662
+ // INNER JOIN `post_user` ON `users`.`id` = `post_user`.`user_id` AND `users`.`id` = `posts`.`user_id`;
663
+ ```
664
+
665
+ ### Left Join, Right Join Clause
666
+
667
+ ```js
668
+ await new DB("posts").leftJoin("posts.user_id", "users.id").findMany();
669
+ // SELECT * FROM `posts` LEFT JOIN `users` ON `posts`.`user_id` = `users`.`id`;
670
+
671
+ await new DB("posts").rightJoin("posts.user_id", "users.id").findMany();
672
+ // SELECT * FROM `posts` RIGHT JOIN `users` ON `posts`.`user_id` = `users`.`id`;
673
+ ```
674
+
675
+ ### Cross Join Clause
676
+
677
+ ```js
678
+ await new DB("posts").crossJoin("posts.user_id", "users.id").findMany();
679
+ // SELECT * FROM `posts` CROSS JOIN `users` ON `posts`.`user_id` = `users`.`id`;
680
+ ```
681
+
682
+ ## Basic Where Clauses
683
+
684
+ ### Where Clauses
685
+
686
+ ```js
687
+ const users = await new DB("users").where("id", 1).findMany();
688
+ // SELECT * FROM `users` WHERE `users`.`id` = '1'
689
+
690
+ const users = await new DB("users")
691
+ .where("id", 1)
692
+ .where("username", "try to find")
693
+ .findMany();
694
+ // SELECT * FROM `users` WHERE `users`.`id` = '1' and `users`.`username` = 'try to find'
695
+
696
+ const users = await new DB("users").where("id", ">", 1).findMany();
697
+ // SELECT * FROM `users` WHERE `users`.`id` > '1';
698
+
699
+ const users = await new DB("users").where("id", "<>", 1).findMany();
700
+ // SELECT * FROM `users` WHERE `users`.`id` <> '1';
701
+ ```
702
+
703
+ ### Or Where Clauses
704
+
705
+ ```js
706
+ const users = await new DB("users").where("id", 1).orWhere("id", 2).findMany();
707
+ // SELECT * FROM `users` WHERE `users`.`id` = 1 OR `users`.`id` = 2
708
+
709
+ const users = await new DB("users")
710
+ .where("id", 1)
711
+ .whereQuery((query) => {
712
+ return query
713
+ .where("id", "<>", 2)
714
+ .orWhere("username", "try to find")
715
+ .orWhere("email", "find@example.com");
716
+ })
717
+ .findMany();
718
+ // SELECT * FROM `users` WHERE `users`.`id` = 1
719
+ // AND
720
+ // ( `users`.`id` <> 2 OR `users`.`username` = 'try to find' OR `users`.`email` = 'find@example.com');
721
+
722
+ ```
723
+
724
+ ### Where cases
725
+
726
+ ```js
727
+ const payments = await new DB('payments')
728
+ .whereCases([
729
+ {
730
+ when : "payment_type = 'credit'",
731
+ then : "status = 'approved'"
732
+ },
733
+ {
734
+ when : "payment_type = 'paypal'",
735
+ then : "status = 'pending'"
736
+ }
737
+ ],"FALSE")
738
+ .findMany()
739
+
740
+ // SELECT * FROM `payments`
741
+ // WHERE (
742
+ // CASE
743
+ // WHEN payment_type = 'credit' THEN status = 'approved'
744
+ // WHEN payment_type = 'paypal' THEN status = 'pending'
745
+ // ELSE FALSE
746
+ // END
747
+ // );
748
+
749
+ const tasks = await new DB("tasks")
750
+ .whereCases([
751
+ {
752
+ when : "priority = 'high'",
753
+ then : "DATEDIFF(due_date, NOW()) <= 3"
754
+ },
755
+ ],"DATEDIFF(due_date, NOW()) <= 7")
756
+ .findMany()
757
+
758
+ // SELECT * FROM `tasks`
759
+ // WHERE (
760
+ // CASE
761
+ // WHEN priority = 'high' THEN DATEDIFF(due_date, NOW()) <= 3
762
+ // ELSE DATEDIFF(due_date, NOW()) <= 7
763
+ // END
764
+ // );
765
+
766
+ ```
767
+
768
+ ### Where Object Clauses
769
+
770
+ ```js
771
+ import { OP } from 'tspace-mysql'
772
+
773
+ const whereObject = await new DB("users")
774
+ .whereObject({
775
+ id : OP.notEq(1),
776
+ username : OP.in(['user1','user2']),
777
+ name : OP.like('%value%')
778
+ })
779
+ .findMany();
780
+
781
+ // SELECT * FROM `users` WHERE `users`.`id` <> '1' AND `users`.`username` = 'user1' AND `users`.`name` LIKE '%value%';
782
+
783
+ ```
784
+
785
+ ### JSON Where Clauses
786
+
787
+ ```js
788
+ const whereJSON = await new DB("users")
789
+ .whereJSON("json", { key: "id", value: "1234" })
790
+ .findMany();
791
+ // SELECT * FROM `users` WHERE `users`.`json`->>'$.id' = '1234';
792
+ ```
793
+
794
+ ### Additional Where Clauses
795
+
796
+ ```js
797
+ const users = await new DB("users").whereIn("id", [1, 2]).findMany();
798
+ // SELECT * FROM `users` WHERE `users`.`id` IN ('1','2');
799
+
800
+ const users = await new DB("users").whereNotIn("id", [1, 2]).findMany();
801
+ // SELECT * FROM `users` WHERE `users`.`id` NOT IN ('1','2');
802
+
803
+ const users = await new DB("users").whereBetween("id", [1, 2]).findMany();
804
+ // SELECT * FROM `users` WHERE `users`.`id` BETWEEN '1' AND '2';
805
+
806
+ const users = await new DB("users").whereNotBetween("id", [1, 2]).findMany();
807
+ // SELECT * FROM `users` WHERE `users`.`id` NOT BETWEEN '1' AND '2';
808
+
809
+ const users = await new DB("users").whereNull("username").findMany();
810
+ // SELECT * FROM `users` WHERE `users`.`username` IS NULL;
811
+
812
+ const users = await new DB("users").whereNotNull("username").findMany();
813
+ // SELECT * FROM `users` WHERE `users`.`username` IS NOT NULL;
814
+ ```
815
+
816
+ ### Logical Grouping
817
+
818
+ ```js
819
+ const users = await new DB("users")
820
+ .whereQuery((query) => query.where("id", 1).where("username", "values"))
821
+ .whereIn("id", [1, 2])
822
+ .findOne();
823
+ // SELECT * FROM `users` WHERE ( `users`.`id` = '1' AND `users`.`username` = 'values') AND `users`.`id` IN ('1','2'') LIMIT 1;
824
+
825
+ const users = await new DB("users")
826
+ .where("id", 1)
827
+ .whereQuery((query) => {
828
+ return query
829
+ .where("id", "<>", 2)
830
+ .where("username", "try to find")
831
+ .where("email", "find@example.com");
832
+ })
833
+ .findMany();
834
+ // SELECT * FROM `users` WHERE `users`.`id` = '1'
835
+ // AND
836
+ // ( `users`.`id` <> '2' AND `users`.`username` = 'try to find' AND `users`.`email` = 'find@example.com');
837
+
838
+ const users = await new DB("users")
839
+ .whereAny(["name", "username", "email"], "like", `%v%`)
840
+ .findMany();
841
+ // SELECT * FROM `users` WHERE ( `users`.`name` LIKE '%v%' OR `users`.`username` LIKE '%v%' OR `users`.`email` LIKE '%v%');
842
+
843
+ const users = await new DB("users")
844
+ .whereAll(["name", "username", "email"], "like", `%v%`)
845
+ .findMany();
846
+ // SELECT * FROM `users` WHERE ( `users`.`name` LIKE '%v%' AND `users`.`username` LIKE '%v%' AND `users`.`email` LIKE '%v%');
847
+ ```
848
+
849
+ ## Advanced Where Clauses
850
+
851
+ ### Where Exists Clauses
852
+
853
+ ```js
854
+ const users = await new DB("users")
855
+ .whereExists(new DB("users").select("id").where("id", 1).toString())
856
+ .findMany();
857
+ // SELECT * FROM `users` WHERE EXISTS (SELECT `id` FROM `users` WHERE id = 1);
858
+
859
+ const users = await new DB("users")
860
+ .wherNoteExists(new DB("users").select("id").where("id", 1).toString())
861
+ .findMany();
862
+ // SELECT * FROM `users` WHERE NOT EXISTS (SELECT `id` FROM `users` WHERE id = 1);
863
+ ```
864
+
865
+ ### Subquery Where Clauses
866
+
867
+ ```js
868
+ const users = await new DB("users")
869
+ .whereSubQuery("id", "SELECT id FROM users")
870
+ .findMany();
871
+ // SELECT * FROM `users` WHERE `users`.`id` IN (SELECT id FROM users);
872
+
873
+ const users = await new DB("users")
874
+ .whereSubQuery("id", new DB("users").select("id").toString())
875
+ .findMany();
876
+ // SELECT * FROM `users` WHERE `users`.`id` IN (SELECT id FROM users);
877
+
878
+ const users = await new DB("users")
879
+ .whereSubQuery(
880
+ "id",
881
+ new DB("users")
882
+ .select("id")
883
+ .whereSubQuery("id", new DB("posts").select("user_id").toString())
884
+ .toString()
885
+ )
886
+ .findMany();
887
+ /*
888
+ SELECT * FROM `users`
889
+ WHERE `users`.`id`
890
+ IN (
891
+ SELECT `users`.`id` FROM `users`
892
+ WHERE `users`.`id`
893
+ IN (
894
+ SELECT `posts`.`user_id` FROM `posts`
895
+ )
896
+ );
897
+ */
898
+ ```
899
+
900
+ ### Conditional Where Clauses
901
+
902
+ ```js
903
+ const users = await new DB("users")
904
+ .where("id", 1)
905
+ .when(true, (query) => query.where("username", "when is actived"))
906
+ .findMany();
907
+ // SELECT * FROM `users` WHERE `users`.`id` = '1' AND `users`.`username` = 'when is actived';
908
+
909
+ const users = await new DB("users")
910
+ .where("id", 1)
911
+ .when(false, (query) => query.where("username", "when is actived"))
912
+ .findMany();
913
+ // SELECT * FROM `users` WHERE `users`.`id` = '1';
914
+ ```
915
+
916
+ ## GetGroupBy
917
+
918
+ ```js
919
+ const data = await new DB("posts").getGroupBy('user_id')
920
+
921
+ // return new Map()
922
+ // find posts by user id
923
+ const userHasPosts = data.get(1)
924
+
925
+ console.log(userHasPosts)
926
+
927
+ ```
928
+
929
+ ## Paginating
930
+
931
+ ```js
932
+ const users = await new DB("users").paginate();
933
+ // SELECT * FROM `users` LIMIT 15 OFFSET 0;
934
+ // SELECT COUNT(*) AS total FROM `users`;
935
+
936
+ const pageTwoUsers = await new DB("users").paginate({ page: 2, limit: 5 });
937
+
938
+ /*
939
+ SELECT * FROM `users` LIMIT 5 OFFSET 5;
940
+ SELECT COUNT(*) AS total FROM `users`;
941
+
942
+ the results are returned
943
+ {
944
+ meta: {
945
+ total: n,
946
+ limit: 5,
947
+ total_page: 5,
948
+ current_page: 2,
949
+ last_page: n,
950
+ next_page: 3,
951
+ prev_page: 1
952
+ },
953
+ data: [...your data here]
954
+ }
955
+
956
+ */
957
+ ```
958
+
959
+ ## Insert Statements
960
+
961
+ ```js
962
+ const user = await new DB("users")
963
+ .create({
964
+ name: "tspace3",
965
+ email: "tspace3@gmail.com",
966
+ })
967
+ .save();
968
+ /**
969
+ INSERT INTO `users`
970
+ (`users`.`name`,`users`.`email`)
971
+ VALUES
972
+ ('tspace3','tspace3@gmail.com');
973
+
974
+ -- then return the result inserted --
975
+ SELECT * FROM `users` WHERE `users`.`id` = ${INSERT ID};
976
+ */
977
+
978
+ const users = await new DB("users")
979
+ .createMultiple([
980
+ {
981
+ name: "tspace4",
982
+ email: "tspace4@gmail.com",
983
+ },
984
+ {
985
+ name: "tspace5",
986
+ email: "tspace5@gmail.com",
987
+ },
988
+ {
989
+ name: "tspace6",
990
+ email: "tspace6@gmail.com",
991
+ },
992
+ ])
993
+ .save();
994
+
995
+ /**
996
+ INSERT INTO `users`
997
+ (`users`.`name`,`users`.`email`)
998
+ VALUES
999
+ ('tspace4','tspace4@gmail.com'),
1000
+ ('tspace5','tspace5@gmail.com'),
1001
+ ('tspace6','tspace6@gmail.com');
1002
+ */
1003
+
1004
+ const users = await new DB("users")
1005
+ .where("name", "tspace4")
1006
+ .where("email", "tspace4@gmail.com")
1007
+ .createNotExists({
1008
+ name: "tspace4",
1009
+ email: "tspace4@gmail.com",
1010
+ })
1011
+ .save();
1012
+ /*
1013
+ -- if exists return null, if not exists created new data --
1014
+ SELECT EXISTS(
1015
+ SELECT 1 FROM `users`
1016
+ WHERE `users`.`name` = 'tspace4'
1017
+ AND `users`.`email` = 'tspace4@gmail.com'
1018
+ LIMIT 1
1019
+ ) AS 'exists';
1020
+
1021
+ INSERT INTO `users` (`users`.`name`,`users`.`email`) VALUES ('tspace4','tspace4@gmail.com');
1022
+ */
1023
+
1024
+ const users = await new DB("users")
1025
+ .where("name", "tspace4")
1026
+ .where("email", "tspace4@gmail.com")
1027
+ .createOrSelect({
1028
+ name: "tspace4",
1029
+ email: "tspace4@gmail.com",
1030
+ })
1031
+ .save();
1032
+ /**
1033
+ -- if has exists return data, if not exists created new data --
1034
+ SELECT EXISTS(
1035
+ SELECT 1 FROM `users`
1036
+ WHERE `users`.`name` = 'tspace4'
1037
+ AND `users`.`email` = 'tspace4@gmail.com'
1038
+ LIMIT 1
1039
+ ) AS 'exists';
1040
+
1041
+ INSERT INTO `users` (`users`.`name`,`users`.`email`) VALUES ('tspace4','tspace4@gmail.com');
1042
+
1043
+ SELECT * FROM `users` WHERE `users`.`id` = '4';
1044
+ */
1045
+ ```
1046
+
1047
+ ## Update Statements
1048
+
1049
+ ```js
1050
+ const user = await new DB("users")
1051
+ .where("id", 1)
1052
+ .update({
1053
+ name: "tspace1**",
1054
+ email: "tspace1@gmail.com",
1055
+ })
1056
+ .save();
1057
+ /**
1058
+
1059
+ UPDATE `users` SET
1060
+ `users`.`name` = 'tspace1',
1061
+ `users`.`email` = 'tspace1@gmail.com'
1062
+ WHERE `users`.`id` = '1' LIMIT 1;
1063
+
1064
+ */
1065
+
1066
+ const user = await new DB("users")
1067
+ .where("id", 1)
1068
+ .updateMany({
1069
+ name: "tspace1",
1070
+ email: "tspace1@gmail.com",
1071
+ })
1072
+ .save();
1073
+ /**
1074
+ UPDATE `users` SET
1075
+ `users`.`name` = 'tspace1',
1076
+ `users`.`email` = 'tspace1@gmail.com'
1077
+ WHERE `users`.`id` = '1';
1078
+ */
1079
+
1080
+ const user = await new DB("users")
1081
+ .where("id", 1)
1082
+ .update(
1083
+ {
1084
+ name: "tspace1",
1085
+ email: "tspace1@gmail.com",
1086
+ },
1087
+ ["name"]
1088
+ )
1089
+ .save();
1090
+ /**
1091
+ UPDATE `users` SET
1092
+ `name` =
1093
+ CASE WHEN (`name` = '' OR `name` IS NULL)
1094
+ THEN 'tspace1' ELSE `name`
1095
+ END,
1096
+ `email` =
1097
+ 'tspace1@gmail.com'
1098
+ WHERE `users`.`id` = '1' LIMIT 1;
1099
+ */
1100
+
1101
+ const user = await new DB("users")
1102
+ .updateMultiple([
1103
+ {
1104
+ when: {
1105
+ id: 1,
1106
+ name: "name1",
1107
+ },
1108
+ columns: {
1109
+ name: "update row1",
1110
+ email: "row1@example.com",
1111
+ },
1112
+ },
1113
+ {
1114
+ when: {
1115
+ id: 2,
1116
+ },
1117
+ columns: {
1118
+ name: "update row2",
1119
+ email: "row2@example.com",
1120
+ },
1121
+ },
1122
+ ])
1123
+ .save();
1124
+
1125
+ /**
1126
+ UPDATE `users` SET
1127
+ `users`.`name` = (
1128
+ CASE WHEN `users`.`id` = '1'
1129
+ AND `users`.`name` = 'name1'
1130
+ THEN 'update row1'
1131
+ WHEN `users`.`id` = '2'
1132
+ THEN 'update row2'
1133
+ ELSE `users`.`name`
1134
+ END
1135
+ ),
1136
+ `users`.`email` = (
1137
+ CASE WHEN `users`.`id` = '1'
1138
+ AND `users`.`name` = 'name1'
1139
+ THEN 'row1@example.com'
1140
+ WHEN `users`.`id` = '2'
1141
+ THEN 'row2@example.com'
1142
+ ELSE `users`.`email`
1143
+ END
1144
+ )
1145
+ WHERE `users`.`id` IN ('1','2') LIMIT 2;
1146
+
1147
+ */
1148
+
1149
+ const user = await new DB("users")
1150
+ .where("id", 1)
1151
+ .updateOrCreate({
1152
+ name: "tspace1**",
1153
+ email: "tspace1@gmail.com",
1154
+ })
1155
+ .save();
1156
+ // if has exists return update, if not exists created new data
1157
+ // UPDATE `users` SET `name` = 'tspace1**',`email` = 'tspace1@gmail.com' WHERE `users`.`id` = '1' LIMIT 1;
1158
+ // INSERT INTO `users` (`name`,`email`) VALUES ('tspace1**','tspace1@gmail.com');
1159
+ ```
1160
+
1161
+ ## Delete Statements
1162
+
1163
+ ```js
1164
+ const deleted = await new DB("users").where("id", 1).delete();
1165
+ // DELETE FROM `users` WHERE `users`.`id` = '1' LIMIT 1;
1166
+
1167
+ const deleted = await new DB("users").where("id", 1).deleteMany();
1168
+ // DELETE FROM `users` WHERE `users`.`id` = '1' ;
1169
+ ```
1170
+
1171
+ ## Hook Statements
1172
+
1173
+ ```js
1174
+ const hookImage = async (results) => {
1175
+ for(const result of results) {
1176
+ result.image = await ...getImage()
1177
+ }
1178
+ };
1179
+ const user = await new DB("users").where("id", 1).hook(hookResult).findMany();
1180
+ ```
1181
+
1182
+ ## Faker Statements
1183
+
1184
+ ```js
1185
+ await new DB("users").faker(2);
1186
+ /**
1187
+ INSERT INTO `users`
1188
+ (`users`.`username`,`users`.`email`)
1189
+ VALUES
1190
+ ('ivsvtagyta86n571z9d81maz','fxcwkubccdi5ewos521uqexy'),
1191
+ ('rnr4esoki7fgekmdtarqewt','gv0mzb1m3rlbinsdyb6')
1192
+ */
1193
+
1194
+ // custom faker
1195
+ await new DB("users").faker(5, (row, index) => {
1196
+ return {
1197
+ username: `username-${index + 1}`,
1198
+ email: `email-${index + 1}`,
1199
+ };
1200
+ });
1201
+
1202
+ /**
1203
+
1204
+ INSERT INTO `users`
1205
+ (`users`.`username`,`users`.`email`)
1206
+ VALUES
1207
+ ('username-1','email-1'),
1208
+ ('username-2','email-2'),
1209
+ ('username-3','email-3'),
1210
+ ('username-4','email-4'),
1211
+ ('username-5','email-5');
1212
+
1213
+ */
1214
+
1215
+ // fast to create
1216
+ await new DB("users").faker(40_000);
1217
+ ```
1218
+
1219
+ ## Unset Statements
1220
+
1221
+ ```js
1222
+
1223
+ const userInstance = new User().where('email','test@gmail.com')
1224
+
1225
+ const exits = await userInstance.exists()
1226
+ // SELECT EXISTS (SELECT 1 FROM `users` WHERE `users`.`email` = 'test@gmail.com' LIMIT 1) AS `aggregate`;
1227
+
1228
+ const user = await userInstance.orderBy('id').findOne()
1229
+ // SELECT * FROM `users` WHERE `users`.`email` = 'test@gmail.com' ORDER BY `users`.`id` DESC LIMIT 1;
1230
+
1231
+ const users = await userInstance.select('id').unset({ limit : true }).findMany()
1232
+ // SELECT `users`.`id` FROM `users` WHERE `users`.`email` = 'test@gmail.com' ORDER BY `users`.`id` DESC;
1233
+
1234
+ const usersUnsetWhereStatement = await userInstance.unset({ select : true, where : true , orderBy : true }).findMany()
1235
+ // SELECT * FROM `users` WHERE `users`.`deletedAt` IS NULL;
1236
+
1237
+ ```
1238
+
1239
+ ## Common Table Expressions
1240
+
1241
+ ```js
1242
+
1243
+ const user = await new User()
1244
+ .CTEs('z', (query) => {
1245
+ return query
1246
+ .from('posts')
1247
+ })
1248
+ .CTEs('x', (query) => {
1249
+ return query
1250
+ .from('post_user')
1251
+ })
1252
+ .select('users.*','x.*','z.*')
1253
+ .join('users.id','x.user_id')
1254
+ .join('users.id','z.user_id')
1255
+ .findOne()
1256
+
1257
+ // WITH z AS (SELECT posts.* FROM `posts`),
1258
+ // x AS (SELECT * FROM `post_user`)
1259
+ // SELECT users.*, z.*, x.* FROM `users` INNER JOIN `x` ON `users`.`id` = `x`.`user_id` INNER JOIN `z` ON `users`.`id` = `z`.`user_id` WHERE `users`.`deleted_at` IS NULL LIMIT 1;
1260
+
1261
+ ```
1262
+
1263
+ ### Union
1264
+
1265
+ ```js
1266
+ const users = await new DB('users')
1267
+ .where('id',1)
1268
+ .union(new DB('users').whereIn('id',[2]))
1269
+ .union(new DB('users').whereIn('id',[3,4]))
1270
+ .findMany()
1271
+
1272
+ // (SELECT * FROM `users` WHERE `users`.`id` = 1)
1273
+ // UNION (SELECT * FROM `users` WHERE `users`.`id` IN (2))
1274
+ // UNION (SELECT * FROM `users` WHERE `users`.`id` IN (3,4));
1275
+
1276
+
1277
+ const users = await new DB('users')
1278
+ .unionAll(new DB('users'))
1279
+ .unionAll(new DB('users'))
1280
+ .findMany()
1281
+
1282
+ // (SELECT * FROM `users`)
1283
+ // UNION ALL (SELECT * FROM `users`)
1284
+ // UNION ALL (SELECT * FROM `users`);
1285
+
1286
+ ```
1287
+
1288
+ ## More Methods
1289
+
1290
+ ```js
1291
+ where(column , OP , value)
1292
+ whereSensitive(column , OP , value)
1293
+ whereId(id)
1294
+ whereUser(userId)
1295
+ whereEmail(value)
1296
+ whereIn(column , [])
1297
+ whereNotIn(column , [])
1298
+ whereNull(column)
1299
+ whereNotNull(column)
1300
+ whereBetween (column , [value1 , value2])
1301
+ whereQuery(query)
1302
+ whereJson(column, { targetKey, value , OP })
1303
+ whereRaw(sql)
1304
+ whereExists(sql)
1305
+ whereSubQuery(colmn , rawSQL)
1306
+ whereNotSubQuery(colmn , rawSQL)
1307
+ orWhere(column , OP , value)
1308
+ orWhereRaw(sql)
1309
+ orWhereIn(column , [])
1310
+ orWhereSubQuery(colmn , rawSQL)
1311
+ when(contition , query)
1312
+ select(column1 ,column2 ,...N)
1313
+ distinct()
1314
+ selectRaw(column1 ,column2 ,...N)
1315
+ except(column1 ,column2 ,...N)
1316
+ exceptTimestamp()
1317
+ only(column1 ,column2 ,...N)
1318
+ hidden(column1 ,column2 ,...N)
1319
+ join(primary key , table.foreign key)
1320
+ rightJoin (primary key , table.foreign key)
1321
+ leftJoin (primary key , table.foreign key)
1322
+ limit (limit)
1323
+ having (condition)
1324
+ havingRaw (condition)
1325
+ orderBy (column ,'ASC' || 'DSCE')
1326
+ orderByRaw(column ,'ASC' || 'DSCE')
1327
+ latest (column)
1328
+ latestRaw (column)
1329
+ oldest (column)
1330
+ oldestRaw (column)
1331
+ groupBy (column)
1332
+ groupByRaw (column)
1333
+ create(objects)
1334
+ createMultiple(array objects)
1335
+ update (objects)
1336
+ updateMany (objects)
1337
+ updateMultiple(array objects)
1338
+ createNotExists(objects)
1339
+ updateOrCreate (objects)
1340
+ onlyTrashed()
1341
+ connection(options)
1342
+ backup({ database , connection })
1343
+ backupToFile({ filePath, database , connection })
1344
+ hook((result) => ...) // query result to function
1345
+ sleep(seconds)
1346
+
1347
+ /**
1348
+ * registry relation in your models
1349
+ * @relationship
1350
+ */
1351
+ hasOne({ name, model, localKey, foreignKey, freezeTable , as })
1352
+ hasMany({ name, model, localKey, foreignKey, freezeTable , as })
1353
+ belongsTo({ name, model, localKey, foreignKey, freezeTable , as })
1354
+ belongsToMany({ name, model, localKey, foreignKey, freezeTable, as, pivot })
1355
+ /**
1356
+ * @relation using registry in your models
1357
+ */
1358
+ relations(name1 , name2,...nameN) // with(name1, name2,...nameN)
1359
+ /**
1360
+ * @relation using registry in your models ignore soft delete
1361
+ */
1362
+ relationsAll(name1 , name2,...nameN) // withAll(name1, name2,...nameN)
1363
+ /**
1364
+ * @relation using registry in your models. if exists child data remove this data
1365
+ */
1366
+ relationsExists(name1 , name2,...nameN) // withExists(name1, name2,...nameN)
1367
+ /**
1368
+ * @relation using registry in your models return only in trash (soft delete)
1369
+ */
1370
+ relationsTrashed(name1 , name2,...nameN) // withTrashed(name1, name2,...nameN)
1371
+ /**
1372
+ * @relation call a name of relation in registry, query query of data
1373
+ */
1374
+ relationQuery(name, (query) ) // withQuery(name1, (query))
1375
+
1376
+
1377
+ /**
1378
+ * queries statements
1379
+ * @execute data of statements
1380
+ */
1381
+ findMany() // get()
1382
+ findOne() // first()
1383
+ find(id)
1384
+ delelte()
1385
+ delelteMany()
1386
+ exists()
1387
+ toString()
1388
+ toJSON()
1389
+ toArray(column)
1390
+ count(column)
1391
+ sum(column)
1392
+ avg(column)
1393
+ max(column)
1394
+ min(column)
1395
+ pagination({ limit , page })
1396
+ save() /* for actions statements insert or update */
1397
+ makeSelectStatement()
1398
+ makeInsertStatement()
1399
+ makeUpdateStatement()
1400
+ makeDeleteStatement()
1401
+ makeCreateTableStatement()
1402
+
1403
+ ```
1404
+
1405
+ ## Database Transactions
1406
+
1407
+ Within a database transaction, you can utilize the following:
1408
+
1409
+ ```js
1410
+ import { DB } from 'tspace-mysql';
1411
+ import { User } from '../Models/User';
1412
+ import { Post } from '../Models/Post';
1413
+
1414
+ const trx = await new DB().beginTransaction();
1415
+
1416
+ try {
1417
+ /**
1418
+ *
1419
+ * @startTransaction start transaction in scopes function
1420
+ */
1421
+ await trx.startTransaction();
1422
+
1423
+ const user = await new User()
1424
+ .create({
1425
+ name: `tspace`,
1426
+ email: "tspace@example.com",
1427
+ })
1428
+ /**
1429
+ *
1430
+ * bind method for make sure this trx has same transaction in trx
1431
+ * @params {Function} trx
1432
+ */
1433
+ .bind(trx)
1434
+ .save();
1435
+
1436
+ const posts = await new Post()
1437
+ .createMultiple([
1438
+ {
1439
+ user_id: user.id,
1440
+ title: `tspace-post1`,
1441
+ },
1442
+ {
1443
+ user_id: user.id,
1444
+ title: `tspace-post2`,
1445
+ },
1446
+ {
1447
+ user_id: user.id,
1448
+ title: `tspace-post3`,
1449
+ },
1450
+ ])
1451
+ .bind(trx) // don't forget this
1452
+ .save();
1453
+
1454
+ /**
1455
+ *
1456
+ * @commit commit transaction to database
1457
+ */
1458
+ // After your use commit if use same trx for actions this transction will auto commit
1459
+ await trx.commit();
1460
+
1461
+ // If you need to start a new transaction again, just use wait trx.startTransaction();
1462
+
1463
+ const postsAfterCommited = await new Post()
1464
+ .createMultiple([
1465
+ {
1466
+ user_id: user.id,
1467
+ title: `tspace-post1`,
1468
+ },
1469
+ {
1470
+ user_id: user.id,
1471
+ title: `tspace-post2`,
1472
+ },
1473
+ {
1474
+ user_id: user.id,
1475
+ title: `tspace-post3`,
1476
+ },
1477
+ ])
1478
+ // Using this trx now will auto-commit to the database.
1479
+ .bind(trx) // If you need to perform additional operations, use await trx.startTransaction(); again.
1480
+ .save();
1481
+
1482
+
1483
+ // Do not perform any operations with this trx.
1484
+ // The transaction has already been committed, and the trx is closed.
1485
+ // Just ensure everything is handled at the end of the transaction.
1486
+ await trx.end();
1487
+
1488
+ } catch (err) {
1489
+ /**
1490
+ *
1491
+ * @rollback rollback transaction
1492
+ */
1493
+ await trx.rollback();
1494
+ }
1495
+ ```
1496
+ Or you can use transaction method for auto commit and rollback, you can utilize the following:
1497
+
1498
+ ```js
1499
+ import { DB } from 'tspace-mysql';
1500
+ import { User } from '../Models/User';
1501
+ import { Post } from '../Models/Post';
1502
+
1503
+ try {
1504
+
1505
+ const { user, posts } = await DB.transaction(async (trx) => {
1506
+
1507
+ const user = await new User()
1508
+ .create({
1509
+ name: `tspace`,
1510
+ email: "tspace@example.com",
1511
+ })
1512
+ .bind(trx) // don't forget this
1513
+ .save();
1514
+
1515
+ const posts = await new Post()
1516
+ .createMultiple([
1517
+ {
1518
+ user_id: user.id,
1519
+ title: `tspace-post1`,
1520
+ },
1521
+ {
1522
+ user_id: user.id,
1523
+ title: `tspace-post2`,
1524
+ },
1525
+ {
1526
+ user_id: user.id,
1527
+ title: `tspace-post3`,
1528
+ },
1529
+ ])
1530
+ .bind(trx) // don't forget this
1531
+ .save();
1532
+
1533
+ return { user, posts };
1534
+
1535
+ });
1536
+ } catch (err) {
1537
+ // error here is from transaction method, you don't need to worry about rollback, just catch error and handle it.
1538
+ console.error(err);
1539
+ }
1540
+
1541
+ ```
1542
+
1543
+ ## Race Condition
1544
+
1545
+ Within a race condition, you can utilize the following:
1546
+
1547
+ ```js
1548
+
1549
+ import { Model, DB } from 'tspace-mysql'
1550
+
1551
+ class Product extends Model {}
1552
+
1553
+ class Order extends Model {}
1554
+
1555
+ async function purchaseForUpdate({userId , productId , qty } : {
1556
+ userId: number;
1557
+ productId: number;
1558
+ qty: number
1559
+ }): Promise<void> {
1560
+ const trx = await DB.beginTransaction()
1561
+ try {
1562
+
1563
+ await trx.startTransaction()
1564
+
1565
+ const product = await new Product()
1566
+ .where('id',productId)
1567
+ .rowLock('FOR_UPDATE') // don't forget this, lock this product for update
1568
+ .bind(trx)
1569
+ .first()
1570
+
1571
+ if (product == null) throw new Error("Product not found");
1572
+
1573
+ if (product.stock < qty) throw new Error("Not enough stock");
1574
+
1575
+ await new Product()
1576
+ .where('id',productId)
1577
+ .update({
1578
+ stock : `${DB.raw('stock')} - ${qty}`,
1579
+ id: productId
1580
+ })
1581
+ .bind(trx)
1582
+ .save()
1583
+
1584
+ await new Order()
1585
+ .create({
1586
+ user_id : userId,
1587
+ product_id : productId,
1588
+ qty
1589
+ })
1590
+ .bind(trx)
1591
+ .save()
1592
+
1593
+ await trx.commit();
1594
+ console.log(`✅ [FOR UPDATE] User ${userId} purchased ${qty}`);
1595
+
1596
+ } catch (err: any) {
1597
+ await trx.rollback();
1598
+ console.log(`❌ [FOR UPDATE] User ${userId} failed: ${err.message}`);
1599
+ } finally {
1600
+ await trx.end();
1601
+ }
1602
+ }
1603
+
1604
+ async function simulateRaceConnection(): Promise<void> {
1605
+ const MAX_CONNECTION = 500;
1606
+ const TASKS: Function[] = [];
1607
+ const STOCK = MAX_CONNECTION * 10
1608
+
1609
+ await new Product()
1610
+ .where('id',1)
1611
+ .update({
1612
+ stock : STOCK,
1613
+ })
1614
+ .save()
1615
+
1616
+ const successes : number[] = []
1617
+ const fails : number[] = []
1618
+ let purchased : number = 0
1619
+ let outOfStock : number = 0
1620
+ for (let i = 1; i <= MAX_CONNECTION; i++) {
1621
+ TASKS.push(async () => {
1622
+ const qty = Math.floor(Math.random() * 30) + 1
1623
+ await purchaseForUpdate({
1624
+ userId : i,
1625
+ productId : 1 ,
1626
+ qty
1627
+ })
1628
+ .then(_ => {
1629
+ successes.push(1)
1630
+ purchased += qty
1631
+ })
1632
+ .catch(_ => {
1633
+ fails.push(1)
1634
+ outOfStock += qty
1635
+ })
1636
+ });
1637
+ }
1638
+
1639
+ console.log("=== Simulation ===");
1640
+ const start = Date.now();
1641
+ await Promise.all(TASKS.map(v => v()));
1642
+ const end = Date.now();
1643
+ console.log(`USING TIME TO TEST IN ${end - start} ms`)
1644
+ console.log(`ALL STOCK: ${STOCK} qty`)
1645
+ console.log(`✅ [SUCCESS(${successes.length})] [Purchased]: ${purchased} qty`);
1646
+ console.log(`❌ [FAIL(${fails.length})] [OutOfStock]: ${outOfStock} qty`);
1647
+ console.log('======== DONE ============')
1648
+ process.exit(0)
1649
+ }
1650
+
1651
+ simulateRaceConnection();
1652
+
1653
+ ```
1654
+ ## Lock Table
1655
+
1656
+ Within a lock table, you can utilize the following:
1657
+
1658
+ This approach is not recommended in clustered or load-balance environments,
1659
+ because locks are session-bound and requests may be routed to different database connections.
1660
+
1661
+ READ:
1662
+ - Other sessions can read.
1663
+ - Other sessions must wait before writing.
1664
+ - SELECT: allowed
1665
+ - INSERT: waits
1666
+ - UPDATE: waits
1667
+ - DELETE: waits
1668
+
1669
+ WRITE:
1670
+ - Other sessions must wait before reading or writing.
1671
+ - SELECT: waits
1672
+ - INSERT: waits
1673
+ - UPDATE: waits
1674
+ - DELETE: waits
1675
+
1676
+ The lock is released automatically after the query completes.
1677
+
1678
+ ```js
1679
+ import { Model, Blueprint } from 'tspace-mysql'
1680
+
1681
+ class User extends Model {
1682
+ protected boot() {
1683
+ this.useSchema({
1684
+ id : Blueprint.int().notNull().primary().autoIncrement(),
1685
+ email : Blueprint.varchar(20).notNull()
1686
+ });
1687
+ }
1688
+ }
1689
+
1690
+ async function requestA() {
1691
+
1692
+ try {
1693
+
1694
+ console.log("A: locking table...");
1695
+
1696
+ // READ | WRITE
1697
+ await User.lockTable('WRITE', async (query) => {
1698
+ console.log("A: table LOCKED");
1699
+
1700
+ // Simulate a long-running write transaction.
1701
+ // WRITE operations from other sessions will wait
1702
+ // until the table lock is released.
1703
+ //
1704
+ // READ behavior depends on the selected lock mode:
1705
+ // - READ lock: other readers are allowed
1706
+ // - WRITE lock: readers/writers may be blocked
1707
+ await new Promise((r) => setTimeout(r, 1000 * 10));
1708
+
1709
+ // IMPORTANT:
1710
+ // While the table is locked, all database operations
1711
+ // must use the provided `query` instance.
1712
+ //
1713
+ // This guarantees the operation runs on the same
1714
+ // database session/connection that owns the lock.
1715
+ //
1716
+ // ❌ May fail because it can use a different connection:
1717
+ // await new User().insert({...}).save();
1718
+ //
1719
+ // ✅ Uses the lock owner session:
1720
+ await query.insert({...}).save();
1721
+
1722
+ // In READ mode this write operation will throw,
1723
+ // because writes are not permitted under a READ lock.
1724
+ // In WRITE mode it executes successfully.
1725
+
1726
+ console.log("A: unlocking table...");
1727
+ });
1728
+
1729
+ console.log("A: done");
1730
+
1731
+ } catch (err:any) {
1732
+
1733
+ console.error('❌ SHOULD NOT FAIL:', err.message);
1734
+ }
1735
+ }
1736
+
1737
+ async function requestB(n: number = 1) {
1738
+ console.log(`B(${n}): waiting 1s before query...`);
1739
+ await new Promise((r) => setTimeout(r, 1000));
1740
+
1741
+ console.log(`B(${n}): trying SELECT...`);
1742
+
1743
+ const start = Date.now();
1744
+
1745
+ try {
1746
+
1747
+ const users = await new User().select('id').get();
1748
+ console.log(`B(${n}): result`, users.length);
1749
+
1750
+ console.log(`B(${n}): finished in`, Date.now() - start, "ms");
1751
+ } catch (err:any) {
1752
+ console.error(`B(${n}): ERROR`, err.message);
1753
+ }
1754
+ }
1755
+
1756
+ async function requestC(n: number = 1) {
1757
+ console.log(`C(${n}): waiting 1s before query...`);
1758
+ await new Promise((r) => setTimeout(r, 1000));
1759
+
1760
+ console.log(`C(${n}): trying Insert...Select`);
1761
+
1762
+ const start = Date.now();
1763
+
1764
+ try {
1765
+
1766
+ await new User().faker(1);
1767
+
1768
+ const users = await new User().select('id').get();
1769
+ console.log(`C(${n}): result`, users.length);
1770
+
1771
+ console.log(`C(${n}): finished in`, Date.now() - start, "ms");
1772
+ } catch (err:any) {
1773
+ console.error(`C(${n}): ERROR`, err.message);
1774
+ }
1775
+ }
1776
+
1777
+ (async () => {
1778
+ // Test scenario:
1779
+ //
1780
+ // 1. Request A acquires a table lock.
1781
+ // 2. Requests B perform concurrent reads.
1782
+ // 3. Request C performs a concurrent write.
1783
+ // 4. Observe whether operations are blocked, queued, or rejected
1784
+ // according to the selected lock mode.
1785
+ await Promise.all([
1786
+ requestA(),
1787
+ requestB(1),
1788
+ requestB(2),
1789
+ requestC(3)
1790
+ ])
1791
+ .then(r => process.exit(1))
1792
+
1793
+ })()
1794
+
1795
+ ```
1796
+
1797
+ ## Connection
1798
+
1799
+ When establishing a connection, you can specify options as follows:
1800
+
1801
+ ```js
1802
+ const connection = await new DB().getConnection({
1803
+ host: 'localhost',
1804
+ port : 3306,
1805
+ database: 'database'
1806
+ username: 'username',
1807
+ password: 'password',
1808
+ })
1809
+
1810
+ const users = await new DB('users')
1811
+ .bind(connection) // don't forget this
1812
+ .findMany()
1813
+ ```
1814
+
1815
+ ## Backup
1816
+
1817
+ To backup a database, you can perform the following steps:
1818
+
1819
+ ```js
1820
+ /**
1821
+ *
1822
+ * @param {string} database Database selected
1823
+ * @param {object | null} to defalut new current connection
1824
+ */
1825
+ const backup = await new DB().backup({
1826
+ database: 'try-to-backup', // clone current database to this database
1827
+ to ?: {
1828
+ host: 'localhost',
1829
+ port : 3306,
1830
+ username: 'username',
1831
+ password: 'password',
1832
+ }
1833
+ })
1834
+ /**
1835
+ *
1836
+ * @param {string} database Database selected
1837
+ * @param {string} filePath file path
1838
+ * @param {object | null} conection defalut current connection
1839
+ */
1840
+ const backupToFile = await new DB().backupToFile({
1841
+ database: 'try-to-backup',
1842
+ filePath: 'backup.sql',
1843
+ connection ?: {
1844
+ host: 'localhost',
1845
+ port : 3306,
1846
+ database: 'database'
1847
+ username: 'username',
1848
+ password: 'password',
1849
+ }
1850
+ })
1851
+ // backupToFile => backup.sql
1852
+
1853
+ /**
1854
+ *
1855
+ * @param {string} database new db name
1856
+ */
1857
+ await new DB().cloneDB('try-to-clone')
1858
+
1859
+ ```
1860
+
1861
+ ## Injection
1862
+
1863
+ The 'tspace-mysql' library is configured to automatically escape SQL injection by default.
1864
+ Let's example a escape SQL injection and XSs injection:
1865
+
1866
+ ```js
1867
+ const input = "admin' OR '1'='1";
1868
+ DB.escape(input);
1869
+ // "admin\' OR \'1\'=\'1"
1870
+
1871
+ //XSS
1872
+ const input = "text hello!<script>alert('XSS attack');</script>";
1873
+ DB.escapeXSS(input);
1874
+ // "text hello!"
1875
+ ```
1876
+
1877
+ ## Generating Model Classes
1878
+
1879
+ To get started, install the 'tspace-mysql' package globally using the following npm command:
1880
+
1881
+ ```js
1882
+ /**
1883
+ *
1884
+ * @install global command
1885
+ */
1886
+ npm install tspace-mysql -g
1887
+
1888
+ /**
1889
+ *
1890
+ * @make Model
1891
+ */
1892
+ tspace-mysql make:model <model name> --dir=< directory >
1893
+
1894
+ # tspace-mysql make:model User --dir=App/Models
1895
+ # App/Models/User.ts
1896
+ ```
1897
+
1898
+ ## Model Conventions
1899
+
1900
+ Your database schema using models. These models represent tables in the database
1901
+ Let's example a basic model class:
1902
+
1903
+ ```js
1904
+ import { Model } from "tspace-mysql";
1905
+ // If you want to specify a global setting for the 'Model'
1906
+ Model.global({
1907
+ uuid: true,
1908
+ softDelete: true,
1909
+ timestamp: true,
1910
+ logger: true,
1911
+ });
1912
+
1913
+ class User extends Model {
1914
+ constructor() {
1915
+ super();
1916
+ /**
1917
+ *
1918
+ * Assign setting global in your model
1919
+ * @useMethod
1920
+ * this.usePattern('camelCase') // => default 'snake_case'
1921
+ * this.useCamelCase()
1922
+ * this.useSnakeCase()
1923
+ * this.useLogger()
1924
+ * this.useDebug()
1925
+ * this.usePrimaryKey('id')
1926
+ * this.useTimestamp({
1927
+ * createdAt : 'created_at',
1928
+ * updatedAt : 'updated_at'
1929
+ * }) // runing a timestamp when insert or update
1930
+ * this.useSoftDelete('deletedAt') // => default target to colmun deleted_at
1931
+ * this.useTable('users')
1932
+ * this.useTableSingular() // => 'user'
1933
+ * this.useTablePlural() // => 'users'
1934
+ * this.useUUID('uuid') // => runing a uuid (universally unique identifier) when insert new data
1935
+ * this.useRegistry() // => build-in functions registry
1936
+ * this.useLoadRelationsInRegistry() // => auto generated result from relationship to results
1937
+ * this.useBuiltInRelationFunctions() // => build-in functions relationships to results
1938
+ * this.useHooks([(r) => console.log(r)])
1939
+ * this.useObserver(Observe)
1940
+ * this.useSchema ({
1941
+ * id : Blueprint.int().notNull().primary().autoIncrement(),
1942
+ * uuid : Blueprint.varchar(50).null(),
1943
+ * name : Blueprint.varchar(191).notNull(),
1944
+ * email : Blueprint.varchar(191).notNull(),
1945
+ * created_at : Blueprint.timestamp().null(),
1946
+ * updated_at : Blueprint.timestamp().null(),
1947
+ * deleted_at : Blueprint.timestamp().null()
1948
+ * }) // auto-generated table when table is not exists and auto-create column when column not exists
1949
+ *
1950
+ * this.useLifecycle('beforeInsert', () => console.log('beforeInsert'))
1951
+ * this.useLifecycle('afterInsert', () => console.log('afterInsert'))
1952
+ * this.useLifecycle('beforeUpdate', () => console.log('beforeUpdate'))
1953
+ * this.useLifecycle('afterUpdate', () => console.log('afterUpdate'))
1954
+ * this.useLifecycle('beforeRemove', () => console.log('beforeRemove'))
1955
+ * this.useLifecycle('afterRemove', () => console.log('afterRemove'))
1956
+ *
1957
+ * this.useTransform({
1958
+ * name : {
1959
+ * to : async (name) => `${name}-> transform@before`,
1960
+ * from : async (name) => `${name}-> transform@after`,
1961
+ * }
1962
+ * })
1963
+ *
1964
+ * this.useHooks([
1965
+ * (r:any) => console.log(r,'hook1'),
1966
+ * (r:any) => console.log(r,'hook2'),
1967
+ * (r:any) => console.log(r,'hook3')
1968
+ * ])
1969
+ *
1970
+ * // validate input when create or update reference to the schema in 'this.useSchema'
1971
+ * this.useValidateSchema({
1972
+ * id : Number,
1973
+ * uuid : Number,
1974
+ * name : {
1975
+ * type : String,
1976
+ * length : 191
1977
+ * require : true
1978
+ * },
1979
+ * email : {
1980
+ * type : String,
1981
+ * require : true,
1982
+ * length : 191,
1983
+ * match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
1984
+ * unique : true,
1985
+ * fn : (email : string) => !/^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)
1986
+ * },
1987
+ * created_at : Date,
1988
+ * updated_at : Date,
1989
+ * deleted_at : Date
1990
+ * })
1991
+ */
1992
+
1993
+ /*
1994
+ * the "snake case", plural name of the class will be used as the table name
1995
+ *
1996
+ * @param {string} name The table associated with the model.
1997
+ */
1998
+ this.useTable("users");
1999
+ }
2000
+ }
2001
+ export { User };
2002
+ export default User;
2003
+ ```
2004
+
2005
+ ### Basic Model Setup
2006
+
2007
+ #### Table Name
2008
+
2009
+ ```js
2010
+ import { Model } from 'tspace-mysql'
2011
+ class User extends Model {
2012
+ constructor() {
2013
+ super()
2014
+ // By default, the model knows that the table name for this User is 'users'
2015
+
2016
+ this.useTable('fix_table') // fixtable
2017
+ this.useTablePlural() // users
2018
+ this.useTableSingular() // user
2019
+ }
2020
+ }
2021
+
2022
+ ```
2023
+
2024
+ #### Pattern
2025
+
2026
+ ```js
2027
+
2028
+ import { Model } from 'tspace-mysql'
2029
+ class UserPhone extends Model {
2030
+ constructor() {
2031
+ super()
2032
+ // By default, the model is pattern snake_case
2033
+ // The table name is user_phones
2034
+ this.useSnakeCase()
2035
+
2036
+ this.useCamelCase()
2037
+ // The table name is userPhones
2038
+ }
2039
+ }
2040
+
2041
+ // set the pattern CamelCase for the model
2042
+ const userPhone = await new UserPhone().where('user_id',1).findOne()
2043
+ // covert 'user_id' to 'userId'
2044
+ // SELECT * FROM `userPhones` WHERE `userPhones`.`userId` = '1' LIMIT 1;
2045
+
2046
+ // avoid the pattern CamelCase for the model
2047
+ const userPhone = await new UserPhone().where(DB.freeze('user_id'),1).findOne()
2048
+ // SELECT * FROM `userPhones` WHERE `userPhones`.`user_id` = '1' LIMIT 1;
2049
+
2050
+ ```
2051
+
2052
+ #### UUID
2053
+
2054
+ ```js
2055
+
2056
+ import { Model } from 'tspace-mysql'
2057
+ class User extends Model {
2058
+ constructor() {
2059
+ super()
2060
+ this.useUUID() // insert uuid when creating
2061
+ }
2062
+ }
2063
+
2064
+ ```
2065
+
2066
+ #### Timestamp
2067
+
2068
+ ```js
2069
+
2070
+ import { Model } from 'tspace-mysql'
2071
+ class User extends Model {
2072
+ constructor() {
2073
+ super()
2074
+ // insert created_at and updated_at when creating
2075
+ // update updated_at when updating
2076
+ // 'created_at' and 'updated_at' still relate to pettern the model
2077
+ // this.useCamelCase() will covert 'created_at' to 'createdAt' and 'updated_at' to 'updatedAt'
2078
+ this.useTimestamp()
2079
+
2080
+ // custom the columns
2081
+ this.useTimestamp({
2082
+ createdAt : 'createdAtCustom',
2083
+ updatedAt : 'updatedAtCustom'
2084
+ })
2085
+
2086
+ }
2087
+ }
2088
+
2089
+ ```
2090
+
2091
+ #### Debug
2092
+
2093
+ ```js
2094
+
2095
+ import { Model } from 'tspace-mysql'
2096
+ class User extends Model {
2097
+ constructor() {
2098
+ super()
2099
+ this.useDebug() // show the query sql in console when executing
2100
+ }
2101
+ }
2102
+
2103
+ ```
2104
+ #### Observer
2105
+
2106
+ ```js
2107
+
2108
+ class Observe {
2109
+
2110
+ public selected(results) {
2111
+ console.log({ results , selected : true })
2112
+ }
2113
+
2114
+ public created(results) {
2115
+ console.log({ results , created : true })
2116
+ }
2117
+
2118
+ public updated(results) {
2119
+ console.log({ results , updated : true })
2120
+ }
2121
+
2122
+ public deleted(results) {
2123
+ console.log({ results , deleted : true })
2124
+ }
2125
+ }
2126
+
2127
+ import { Model } from 'tspace-mysql'
2128
+ class User extends Model {
2129
+ constructor() {
2130
+ super()
2131
+ this.useObserver(Observe) // returning to the observers by statements
2132
+ }
2133
+ }
2134
+
2135
+ ```
2136
+
2137
+ #### Logger
2138
+
2139
+ ```js
2140
+
2141
+ import { Model } from 'tspace-mysql'
2142
+ class User extends Model {
2143
+ constructor() {
2144
+ super()
2145
+ // keep logging everything except select to the table '$loggers'
2146
+ // the table will automatically be created
2147
+ this.useLogger()
2148
+
2149
+ // keep logging everything
2150
+ this.useLogger({
2151
+ selected : true,
2152
+ inserted : true,
2153
+ updated : true,
2154
+ deleted : true,
2155
+ })
2156
+ }
2157
+ }
2158
+
2159
+ ```
2160
+
2161
+ #### Hooks
2162
+
2163
+ ```js
2164
+
2165
+ import { Model } from 'tspace-mysql'
2166
+ class User extends Model {
2167
+ constructor() {
2168
+ super()
2169
+ // when executed will returning the results to any hooks function
2170
+ this.useHooks([
2171
+ (results1) => console.log(results1),
2172
+ (results2) => console.log(results2),
2173
+ (results3) => console.log(results3)
2174
+ ])
2175
+ }
2176
+ }
2177
+
2178
+ ```
2179
+
2180
+ ### Global Scope
2181
+ ```js
2182
+
2183
+ class User extends Model {
2184
+ constructor() {
2185
+ super()
2186
+
2187
+ // Every query will have the global scope applied.
2188
+ this.globalScope((query : User) => {
2189
+ return query.select('id').where('id' , '>' , 10).orderBy('id')
2190
+ })
2191
+ }
2192
+ }
2193
+
2194
+ const user = await new User().findMany()
2195
+
2196
+ // SELECT `users`.`id` FROM `users` WHERE `users`.`id` > '10' ORDER BY `users`.`id` ASC LIMIT 1
2197
+
2198
+ ```
2199
+
2200
+ ## Joins Model
2201
+
2202
+ ### Inner Join Model Clause
2203
+
2204
+ ```js
2205
+ await new User().joinModel(User, Post).findMany();
2206
+ // SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
2207
+
2208
+ // if the model use soft delete
2209
+ await new User().joinModel(User, Post).findMany();
2210
+ // SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users`
2211
+ // INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id`
2212
+ // WHERE `posts`.`deleted_at` IS NULL AND `users`.`deleted_at` IS NULL;
2213
+
2214
+ await new User().select(`${User.table}.*`,`${Post.table}.*`).joinModel(User, Post).findMany();
2215
+ // SELECT users.*, posts.* FROM `users`
2216
+ // INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id`
2217
+ // WHERE `posts`.`deleted_at` IS NULL AND `users`.`deleted_at` IS NULL;
2218
+
2219
+ await new User().select('u.*','p.*')
2220
+ .joinModel({ model : User , key : 'id' , alias : 'u' }, { model : Post , key : 'user_id', alias : 'p'})
2221
+ .findMany();
2222
+ // SELECT u.*, p.* FROM `users` AS `u`
2223
+ // INNER JOIN `posts` AS `p` ON `u`.`id` = `p`.`user_id`
2224
+ // WHERE `p`.`deleted_at` IS NULL AND `u`.`deleted_at` IS NULL;
2225
+
2226
+ await new DB("posts")
2227
+ .join((join) => {
2228
+ return join
2229
+ .on('posts.user_id','users.id')
2230
+ .on('users.id','post_user.user_id')
2231
+ .and('users.id','posts.user_id')
2232
+ })
2233
+ .findMany()
2234
+ // SELECT * FROM `posts`
2235
+ // INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
2236
+ // INNER JOIN `post_user` ON `users`.`id` = `post_user`.`user_id` AND `users`.`id` = `posts`.`user_id`;
2237
+ ```
2238
+ ### Left Join, Right Join Model Clause
2239
+
2240
+ ```js
2241
+ await new User().leftJoinModel(User, Post).findMany();
2242
+ // SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` LEFT JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
2243
+
2244
+ await new User().rightJoinModel(User, Post).findMany();
2245
+ // SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` RIGHT JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
2246
+ ```
2247
+
2248
+ ### Cross Join Model Clause
2249
+
2250
+ ```js
2251
+ await new User().crossJoinModel(User, Post).findMany();
2252
+ // SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` CROSS JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
2253
+ ```
2254
+
2255
+ ### Relationships
2256
+
2257
+ Relationships are defined as methods on your Model classes.
2258
+ Let's example a basic relationship:
2259
+
2260
+ #### One To One
2261
+
2262
+ A one-to-one relationship is used to define relationships where a single model is the parent to one child models
2263
+
2264
+ ```js
2265
+ import { Model , type T } from 'tspace-mysql';
2266
+
2267
+ import Phone from '../Phone'
2268
+ class User extends Model {
2269
+ constructor(){
2270
+ super()
2271
+ this.useTimestamp()
2272
+ /**
2273
+ *
2274
+ * @hasOne Get the phone associated with the user.
2275
+ * @relationship users.id -> phones.user_id
2276
+ */
2277
+ this.hasOne({ name : 'phone' , model : Phone })
2278
+ }
2279
+ /**
2280
+ * Mark a method for relationship
2281
+ * @hasOne Get the phone associated with the user. using function query
2282
+ * @function
2283
+ */
2284
+ public phone (query ?: T.QueryModifier<Phone>) {
2285
+ return this.hasOneBuilder({ model : Phone } , query)
2286
+ }
2287
+
2288
+ // or
2289
+ public phone = this.createRelation(Phone,{ type : 'hasOne'})
2290
+ }
2291
+ export default User
2292
+
2293
+ +--------------------------------------------------------------------------+
2294
+
2295
+ import User from '../User'
2296
+ const user = await new User().relations('phone').findOne() // You can also use the method .with('roles').
2297
+ // user?.phone => {...}
2298
+ const userUsingFunction = await new User().phone().findOne()
2299
+ // userUsingFunction?.phone => {...}
2300
+ ```
2301
+
2302
+ ### One To Many
2303
+
2304
+ A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models.
2305
+
2306
+ ```js
2307
+ import { Model, type T } from 'tspace-mysql';
2308
+ import Comment from '../Comment';
2309
+ class Post extends Model {
2310
+ constructor(){
2311
+ super()
2312
+ this.useTimestamp()
2313
+ /**
2314
+ *
2315
+ * @hasMany Get the comments for the post.
2316
+ * @relationship posts.id -> comments.post_id
2317
+ */
2318
+ this.hasMany({ name : 'comments' , model : Comment })
2319
+ }
2320
+ /**
2321
+ *
2322
+ * @hasManyQuery Get the comments for the post. using function query
2323
+ * @function
2324
+ */
2325
+ public comments (query?: T.QueryModifier<Comment>) {
2326
+ return this.hasManyBuilder({ model : Comment } , query)
2327
+ }
2328
+
2329
+ // or
2330
+ public comments = this.createRelation(Comment,{ type : 'hasMany'})
2331
+ }
2332
+ export default Post
2333
+
2334
+ +--------------------------------------------------------------------------+
2335
+
2336
+ import Post from '../Post'
2337
+ const posts = await new Post().relations('comments').findOne()
2338
+ // posts?.comments => [{...}]
2339
+ const postsUsingFunction = await new Post().comments().findOne()
2340
+ // postsUsingFunction?.comments => [{...}]
2341
+ ```
2342
+
2343
+ #### Belongs To
2344
+
2345
+ A belongsto relationship is used to define relationships where a single model is the child to parent models.
2346
+
2347
+ ```js
2348
+ import { Model, type T } from 'tspace-mysql'
2349
+ import User from '../User'
2350
+ class Phone extends Model {
2351
+ constructor(){
2352
+ super()
2353
+ this.useTimestamp()
2354
+ /**
2355
+ *
2356
+ * @belongsTo Get the user that owns the phone.
2357
+ * @relationship phones.user_id -> users.id
2358
+ */
2359
+ this.belognsTo({ name : 'user' , model : User })
2360
+ }
2361
+ /**
2362
+ *
2363
+ * @belongsToBuilder Get the user that owns the phone.. using function query
2364
+ * @function
2365
+ */
2366
+ public user (query?: T.QueryModifier<user>) {
2367
+ return this.belongsToBuilder({ model : User }, query)
2368
+ }
2369
+
2370
+ // or
2371
+ public user = this.createRelation(User,{ type : 'belognsTo'})
2372
+ }
2373
+ export default Phone
2374
+
2375
+ +--------------------------------------------------------------------------+
2376
+
2377
+ import Phone from '../Phone'
2378
+ const phone = await new Phone().relations('user').findOne()
2379
+ // phone?.user => {...}
2380
+ const phoneUsingFunction = await new Phone().user().findOne()
2381
+ // phoneUsingFunction?.user => {...}
2382
+ ```
2383
+
2384
+ #### Many To Many
2385
+
2386
+ Many-to-many relations are slightly more complicated than hasOne and hasMany relationships.
2387
+
2388
+ ```js
2389
+ import { Model, type T } from 'tspace-mysql'
2390
+ import Role from '../Role'
2391
+ class User extends Model {
2392
+ constructor(){
2393
+ super()
2394
+ this.useTimestamp()
2395
+ /**
2396
+ *
2397
+ * @belongsToMany Get The roles that belong to the user.
2398
+ * @relationship users.id , roles.id => role_user.user_id , role_user.role_id
2399
+ */
2400
+ this.belognsToMany({ name : 'roles' , model : Role })
2401
+ }
2402
+ /**
2403
+ * @belongsToBuilder Get the user that owns the phone.. using function query
2404
+ * @function
2405
+ */
2406
+ public roles (query?: T.QueryModifier<Role>) {
2407
+ return this.belognsToManyBuilder({ model : Role } , query)
2408
+ }
2409
+
2410
+ // or
2411
+ public roles = this.createRelation(Role, { type : 'belognsToMany'})
2412
+ }
2413
+ export default User
2414
+
2415
+ +--------------------------------------------------------------------------+
2416
+
2417
+ import User from '../User'
2418
+ const user = await new User().relations('roles').findOne()
2419
+ // user?.roles => [{...}]
2420
+ const userUsingFunction = await new User().roles().findOne()
2421
+ // user?.roles => [{...}]
2422
+ ```
2423
+
2424
+ #### Relation
2425
+
2426
+ Relationships are connections between entities.
2427
+ Let's consider an example of a relationship:
2428
+
2429
+ ```js
2430
+
2431
+ +-------------+--------------+----------------------------+
2432
+ | table users |
2433
+ +-------------+--------------+----------------------------+
2434
+ | id | username | email |
2435
+ |-------------|--------------|----------------------------|
2436
+ | 1 | tspace1 | tspace1@gmail.com |
2437
+ | 2 | tspace2 | tspace2@gmail.com |
2438
+ | 3 | tspace3 | tspace3@gmail.com |
2439
+ +-------------+--------------+----------------------------+
2440
+
2441
+ +-------------+--------------+----------------------------+
2442
+ | table posts |
2443
+ +-------------+--------------+----------------------------+
2444
+ | id | user_id | title |
2445
+ |-------------|--------------|----------------------------|
2446
+ | 1 | 1 | posts 1 |
2447
+ | 2 | 1 | posts 2 |
2448
+ | 3 | 3 | posts 3 |
2449
+ +-------------+--------------+----------------------------+
2450
+
2451
+ import { Model } from 'tspace-mysql'
2452
+
2453
+ class User extends Model {
2454
+ constructor(){
2455
+ super()
2456
+ this.hasMany({ name : 'posts' , model : Post })
2457
+ }
2458
+ }
2459
+
2460
+ class Post extends Model {
2461
+ constructor(){
2462
+ super()
2463
+ this.belongsTo({ name : 'user' , model : User })
2464
+ }
2465
+ }
2466
+
2467
+ await new User()
2468
+ .relations('posts')
2469
+ .findOne()
2470
+ // SELECT * FROM `users` LIMIT 1;
2471
+ // SELECT * FROM `posts` WHERE `posts`.`userId` IN (...);
2472
+
2473
+ /*
2474
+ * @returns
2475
+ * {
2476
+ * id : 1,
2477
+ * username: "tspace1",
2478
+ * email : "tspace1@gmail.com",
2479
+ * posts : [
2480
+ * {
2481
+ * id : 1 ,
2482
+ * user_id : 1,
2483
+ * title : "post 1"
2484
+ * },
2485
+ * {
2486
+ * id : 2 ,
2487
+ * user_id : 1,
2488
+ * title : "post 2"
2489
+ * }
2490
+ * ]
2491
+ * }
2492
+ */
2493
+
2494
+ ```
2495
+
2496
+ #### Deeply Nested Relations
2497
+
2498
+ Relationships can involve deep connections.
2499
+ Let's consider an example of a deep relationship:
2500
+
2501
+ ```js
2502
+ import { Model } from 'tspace-mysql'
2503
+
2504
+ class User extends Model {
2505
+ constructor(){
2506
+ super()
2507
+ this.hasMany({ name : 'posts' , model : Post })
2508
+ }
2509
+ }
2510
+ +--------------------------------------------------------------------------+
2511
+ class Post extends Model {
2512
+ constructor(){
2513
+ super()
2514
+ this.hasMany({ name : 'comments' , model : Comment })
2515
+ this.belongsTo({ name : 'user' , model : User })
2516
+ this.belongsToMany({ name : 'users' , model : User , modelPivot : PostUser })
2517
+ }
2518
+ }
2519
+ +--------------------------------------------------------------------------+
2520
+ class Comment extends Model {
2521
+ constructor(){
2522
+ super()
2523
+ this.hasMany({ name : 'users' , model : User })
2524
+ this.belongsTo({ name : 'post' , model : Post })
2525
+ }
2526
+ }
2527
+
2528
+ class PostUser extends Model {}
2529
+ +--------------------------------------------------------------------------+
2530
+ // Deeply nested relations
2531
+ await new User()
2532
+ .relations('posts')
2533
+ .relationQuery('posts', (query : Post) => {
2534
+ return query
2535
+ .relations('comments','user','users')
2536
+ .relationQuery('comments', (query : Comment) => {
2537
+ return query.relations('user','post')
2538
+ })
2539
+ .relationQuery('user', (query : User) => {
2540
+ return query.relations('posts').relationQuery('posts',(query : Post)=> {
2541
+ return query.relations('comments','user')
2542
+ // relation n, n, ...n
2543
+ })
2544
+ })
2545
+ .relationQuery('users', (query : User) => {
2546
+ return query
2547
+ })
2548
+ .relationQuery('users', (query : PostUser) => {
2549
+ return query
2550
+ }, { pivot : true })
2551
+ })
2552
+ .findMany()
2553
+
2554
+ // Select some columns in nested relations
2555
+ await new User()
2556
+ .relations('posts')
2557
+ .relationQuery('posts', (query : Post) => query.select('id','user_id','title'))
2558
+ .findMany()
2559
+
2560
+ // Where some columns in nested relations
2561
+ await new User()
2562
+ .relations('posts')
2563
+ .relationQuery('posts', (query : Post) => query.whereIn('id',[1,3,5]))
2564
+ .findMany()
2565
+
2566
+ // Sort data in nested relations
2567
+ await new User()
2568
+ .relations('posts')
2569
+ .relationQuery('posts', (query : Post) => query.latest('id'))
2570
+ .findMany()
2571
+
2572
+ // Limit data in nested relations
2573
+ await new User()
2574
+ .relations('posts')
2575
+ .relationQuery('posts', (query : Post) => {
2576
+ return query
2577
+ .limit(1)
2578
+ .relations('comments')
2579
+ .relationQuery('comments', (query : Comment) => query.limit(1))
2580
+ })
2581
+ .findMany()
2582
+
2583
+ ```
2584
+
2585
+ #### Relation Exists
2586
+
2587
+ Relationships can return results only if they are not empty in relations, considering soft deletes.
2588
+ Let's illustrate this with an example of an existence check in relations:
2589
+
2590
+ ```js
2591
+ +-------------+--------------+----------------------------+--------------------+
2592
+ | table users | |
2593
+ +-------------+--------------+----------------------------+--------------------+
2594
+ | id | username | email | deleted_at |
2595
+ |-------------|--------------|----------------------------|--------------------|
2596
+ | 1 | tspace1 | tspace1@gmail.com | |
2597
+ | 2 | tspace2 | tspace2@gmail.com | |
2598
+ | 3 | tspace3 | tspace3@gmail.com | |
2599
+ +-------------+--------------+----------------------------+--------------------+
2600
+
2601
+
2602
+ +-------------+--------------+----------------------------+--------------------+
2603
+ | table posts | |
2604
+ +-------------+--------------+----------------------------+--------------------+
2605
+ | id | user_id | title | deleted_at |
2606
+ |-------------|--------------|----------------------------|--------------------|
2607
+ | 1 | 1 | posts 1 |2020-07-15 00:00:00 |
2608
+ | 2 | 2 | posts 2 | |
2609
+ | 3 | 3 | posts 3 |2020-07-15 00:00:00 |
2610
+ +-------------+--------------+----------------------------+--------------------+
2611
+
2612
+ import { Model } from 'tspace-mysql'
2613
+
2614
+ class User extends Model {
2615
+ constructor(){
2616
+ super()
2617
+ this.hasMany({ name : 'posts' , model : Post })
2618
+ this.useSoftDelete()
2619
+ }
2620
+ }
2621
+
2622
+ +--------------------------------------------------------------------------+
2623
+
2624
+ class Post extends Model {
2625
+ constructor(){
2626
+ super()
2627
+ this.hasMany({ name : 'comments' , model : Comment })
2628
+ this.belongsTo({ name : 'user' , model : User })
2629
+ this.useSoftDelete()
2630
+ }
2631
+ }
2632
+ // normal relations
2633
+ await new User().relations('posts').findMany()
2634
+ // SELECT * FROM `users` WHERE `users`.`deleted_at`;
2635
+ // SELECT * FROM `posts` WHERE `posts`.`userId` IN (...) AND `posts`.`deleted_at` IS NULL;
2636
+
2637
+ /*
2638
+ * @returns [
2639
+ * {
2640
+ * id : 1,
2641
+ * username: "tspace1",
2642
+ * email : "tspace1@gmail.com",
2643
+ * posts : []
2644
+ * },
2645
+ * {
2646
+ * id : 2,
2647
+ * username: "tspace2",
2648
+ * email : "tspace2@gmail.com",
2649
+ * posts : [
2650
+ * {
2651
+ * id : 2,
2652
+ * user_id : 2,
2653
+ * title : "posts 2"
2654
+ * }
2655
+ * ]
2656
+ * },
2657
+ * {
2658
+ * id : 3,
2659
+ * username: "tspace3",
2660
+ * email : "tspace3@gmail.com",
2661
+ * posts : []
2662
+ * }
2663
+ * ]
2664
+ */
2665
+
2666
+ await new User().relationsExists('posts').findMany()
2667
+ // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL
2668
+ // AND EXISTS (SELECT 1 FROM `posts` WHERE `users`.`id` = `posts`.`user_id` AND `posts`.`deletedA_at` IS NULL);
2669
+
2670
+ // SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NULL;
2671
+
2672
+ /*
2673
+ * @returns [
2674
+ * {
2675
+ * id : 2,
2676
+ * username: "tspace2",
2677
+ * email : "tspace2@gmail.com",
2678
+ * posts : [
2679
+ * {
2680
+ * id : 2,
2681
+ * user_id : 2,
2682
+ * title : "posts 2"
2683
+ * }
2684
+ * ]
2685
+ * }
2686
+ * ]
2687
+ * because posts id 1 and id 3 has been removed from database (using soft delete)
2688
+ */
2689
+
2690
+ ```
2691
+
2692
+ #### Relation Count
2693
+ Relationships will retrieving the count of related records without loading the data of related models
2694
+ Let's illustrate this with an example of an existence check in relations:
2695
+ ```js
2696
+
2697
+ +-------------+--------------+----------------------------+
2698
+ | table users |
2699
+ +-------------+--------------+----------------------------+
2700
+ | id | username | email |
2701
+ |-------------|--------------|----------------------------|
2702
+ | 1 | tspace1 | tspace1@gmail.com |
2703
+ | 2 | tspace2 | tspace2@gmail.com |
2704
+ +-------------+--------------+----------------------------+
2705
+
2706
+ +-------------+--------------+----------------------------+
2707
+ | table posts |
2708
+ +-------------+--------------+----------------------------+
2709
+ | id | user_id | title |
2710
+ |-------------|--------------|----------------------------|
2711
+ | 1 | 1 | posts 1 |
2712
+ | 2 | 1 | posts 2 |
2713
+ | 3 | 2 | posts 3 |
2714
+ +-------------+--------------+----------------------------+
2715
+
2716
+ import { Model } from 'tspace-mysql'
2717
+
2718
+ class User extends Model {
2719
+ constructor(){
2720
+ super()
2721
+ this.hasMany({ name : 'posts' , model : Post })
2722
+ this.useSoftDelete()
2723
+ }
2724
+ }
2725
+
2726
+ // you also use .withCount()
2727
+ await new User().relationsCount('posts').findMany()
2728
+ // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL;
2729
+
2730
+ // SELECT `posts`.`user_id`, COUNT(`user_id`) AS `aggregate` FROM `posts`
2731
+ // WHERE `posts`.`user_id` IN ('1','2') AND `posts`.`deleted_at` IS NULL GROUP BY `posts`.`user_id`;
2732
+
2733
+ /*
2734
+ * @returns [
2735
+ * {
2736
+ * id : 1,
2737
+ * username: "tspace1",
2738
+ * email : "tspace1@gmail.com",
2739
+ * posts : 2
2740
+ * }
2741
+ * {
2742
+ * id : 2,
2743
+ * username: "tspace2",
2744
+ * email : "tspace2@gmail.com",
2745
+ * posts : 1
2746
+ * }
2747
+ * ]
2748
+ */
2749
+
2750
+ ```
2751
+
2752
+ #### Relation Trashed
2753
+ Relationships can return results only if they are deleted in table, considering soft deletes.
2754
+ Let's illustrate this with an example:
2755
+ ```js
2756
+
2757
+ +-------------+--------------+----------------------------+--------------------+
2758
+ | table users | |
2759
+ +-------------+--------------+----------------------------+--------------------+
2760
+ | id | username | email | deleted_at |
2761
+ |-------------|--------------|----------------------------|--------------------|
2762
+ | 1 | tspace1 | tspace1@gmail.com | |
2763
+ | 2 | tspace2 | tspace2@gmail.com | |
2764
+ | 3 | tspace3 | tspace3@gmail.com |2020-07-15 00:00:00 |
2765
+ +-------------+--------------+----------------------------+--------------------+
2766
+
2767
+ +-------------+--------------+----------------------------+--------------------+
2768
+ | table posts | |
2769
+ +-------------+--------------+----------------------------+--------------------+
2770
+ | id | user_id | title | deleted_at |
2771
+ |-------------|--------------|----------------------------|--------------------|
2772
+ | 1 | 1 | posts 1 |2020-07-15 00:00:00 |
2773
+ | 2 | 2 | posts 2 | |
2774
+ | 3 | 3 | posts 3 |2020-07-15 00:00:00 |
2775
+ +-------------+--------------+----------------------------+--------------------+
2776
+
2777
+ import { Model } from 'tspace-mysql'
2778
+
2779
+ class User extends Model {
2780
+ constructor(){
2781
+ super()
2782
+ this.hasMany({ name : 'posts' , model : Post })
2783
+ this.useSoftDelete()
2784
+ }
2785
+ }
2786
+
2787
+ +--------------------------------------------------------------------------+
2788
+
2789
+ class Post extends Model {
2790
+ constructor(){
2791
+ super()
2792
+ this.hasMany({ name : 'comments' , model : Comment })
2793
+ this.belongsTo({ name : 'user' , model : User })
2794
+ this.useSoftDelete()
2795
+ }
2796
+ }
2797
+
2798
+ // normal relations
2799
+ await new User().relations('posts').findMany()
2800
+ // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL;
2801
+ // SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NULL;
2802
+
2803
+ /*
2804
+ * @returns [
2805
+ * {
2806
+ * id : 1,
2807
+ * username: "tspace1",
2808
+ * email : "tspace1@gmail.com",
2809
+ * posts : []
2810
+ * }
2811
+ * {
2812
+ * id : 2,
2813
+ * username: "tspace2",
2814
+ * email : "tspace2@gmail.com",
2815
+ * posts : [
2816
+ * {
2817
+ * id : 2,
2818
+ * user_id : 2,
2819
+ * title : "posts 2"
2820
+ * }
2821
+ * ]
2822
+ * }
2823
+ * ]
2824
+ */
2825
+
2826
+ // relationsTrashed
2827
+ await new User().relationsTrashed('posts').findMany()
2828
+ // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL;
2829
+ // SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NOT NULL;
2830
+
2831
+ /*
2832
+ * @returns [
2833
+ * {
2834
+ * id : 1,
2835
+ * username: "tspace1",
2836
+ * email : "tspace1@gmail.com",
2837
+ * posts : [
2838
+ * {
2839
+ * id : 1,
2840
+ * user_id : 1,
2841
+ * title : "posts 1"
2842
+ * }
2843
+ * ]
2844
+ * }
2845
+ * {
2846
+ * id : 2,
2847
+ * username: "tspace2",
2848
+ * email : "tspace2@gmail.com",
2849
+ * posts : []
2850
+ * }
2851
+ * ]
2852
+ */
2853
+
2854
+ // relationsTrashed + trashed
2855
+ await new User().relationsTrashed('posts').trashed().findMany()
2856
+ // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NOT NULL;
2857
+ // SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NOT NULL;
2858
+ /*
2859
+ * @returns [
2860
+ * {
2861
+ * id : 3,
2862
+ * username: "tspace3",
2863
+ * email : "tspace3@gmail.com",
2864
+ * posts : [
2865
+ * {
2866
+ * id : 3,
2867
+ * user_id : 3,
2868
+ * title : "posts 3"
2869
+ * }
2870
+ * ]
2871
+ * }
2872
+ * ]
2873
+ */
2874
+
2875
+ ```
2876
+
2877
+ ### Built in Relation Functions
2878
+ Certainly, let's illustrate the use of a built-in function in the results of relationships:
2879
+
2880
+ ```js
2881
+ import { Model } from 'tspace-mysql'
2882
+
2883
+ class User extends Model {
2884
+ constructor(){
2885
+ super()
2886
+ this.hasMany({ name : 'posts' , model : Post })
2887
+ this.useBuiltInRelationFunctions()
2888
+ }
2889
+ }
2890
+ +--------------------------------------------------------------------------+
2891
+ class Post extends Model {
2892
+ constructor(){
2893
+ super()
2894
+ this.hasMany({ name : 'comments' , model : Comment })
2895
+ this.belongsTo({ name : 'user' , model : User })
2896
+ this.useBuiltInRelationFunctions()
2897
+ }
2898
+ }
2899
+ +--------------------------------------------------------------------------+
2900
+ class Comment extends Model {
2901
+ constructor(){
2902
+ super()
2903
+ this.hasMany({ name : 'users' , model : User })
2904
+ this.belongsTo({ name : 'post' , model : Post })
2905
+ this.useBuiltInRelationFunctions()
2906
+ }
2907
+ }
2908
+ +--------------------------------------------------------------------------+
2909
+ const user = await new User().findOne()
2910
+ const posts = await user.$posts()
2911
+
2912
+ /** Warning built-in function has Big-O effect */
2913
+ for (const post of posts) {
2914
+ const comments = await post.$comments()
2915
+ }
2916
+
2917
+ ```
2918
+
2919
+ ### Cache
2920
+
2921
+ Cache can be used in a Model.
2922
+ Let's illustrate this with an example of a cache:
2923
+
2924
+ ```js
2925
+ // support memory , db , redis
2926
+ // set cache in file config .env , .env.development ... etc
2927
+ DB_CACHE = memory // by default
2928
+
2929
+ // for db
2930
+ DB_CACHE = db
2931
+
2932
+ // for redis
2933
+ // npm install redis@5.6.0
2934
+ DB_CACHE = redis://username:password@server:6379
2935
+
2936
+ const users = await new User()
2937
+ .cache({
2938
+ key : 'users1', // key of the cache
2939
+ expires : 1000 * 60, // cache expires in 60 seconds
2940
+ namespace : true // add namespace to the key of the cache like that 'db:users:users1' to avoid conflicts with other keys in cache
2941
+ })
2942
+ .sleep(5) // assume the query takes longer than 5 seconds...
2943
+ .findMany()
2944
+
2945
+ // delete cache by key
2946
+ User.cache.delete('users1', { namespace : true })
2947
+
2948
+ ```
2949
+
2950
+ ### Decorator
2951
+
2952
+ Decorators can be used in a Model.
2953
+ Let's illustrate this with an example of a decorators:
2954
+
2955
+ ```js
2956
+
2957
+ import {
2958
+ Blueprint, Model ,
2959
+ Table ,TableSingular, TablePlural,
2960
+ UUID, SoftDelete, Timestamp,
2961
+ Pattern, CamelCase , snakeCase ,
2962
+ Column, Validate, Observer
2963
+ } from 'tspace-mysql'
2964
+ import { Post } from './Post'
2965
+ import { PostUser } from './PostUser'
2966
+
2967
+ class UserObserve {
2968
+
2969
+ public selected(results) {
2970
+ console.log({ results , selected : true })
2971
+ }
2972
+
2973
+ public created(results) {
2974
+ console.log({ results , created : true })
2975
+ }
2976
+
2977
+ public updated(results) {
2978
+ console.log({ results , updated : true })
2979
+ }
2980
+
2981
+ public deleted(results) {
2982
+ console.log({ results , deleted : true })
2983
+ }
2984
+ }
2985
+
2986
+ @Pattern('camelCase')
2987
+ @Observer(UserObserve)
2988
+ @UUID()
2989
+ @SoftDelete()
2990
+ @Timestamp()
2991
+ @Table('users')
2992
+ class User extends Model {
2993
+
2994
+ @Column(() => Blueprint.int().notNull().primary().autoIncrement())
2995
+ public id!: number
2996
+
2997
+ @Column(() => Blueprint.varchar(50).null())
2998
+ public uuid!: string
2999
+
3000
+ @Column(() => Blueprint.varchar(50).null())
3001
+ @Validate({
3002
+ type : String,
3003
+ require : true,
3004
+ length : 50,
3005
+ match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
3006
+ unique : true,
3007
+ fn : (email : string) => /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)
3008
+ })
3009
+ public email!: string
3010
+
3011
+ @Column(() => Blueprint.varchar(50).null())
3012
+ public name !: string
3013
+
3014
+ @Column(() => Blueprint.varchar(50).null())
3015
+ public username !: string
3016
+
3017
+ @Column(() => Blueprint.varchar(50).null())
3018
+ public password !: string
3019
+
3020
+ @Column(() => Blueprint.timestamp().null())
3021
+ public createdAt!: Date
3022
+
3023
+ @Column(() => Blueprint.timestamp().null())
3024
+ public updatedAt!: Date
3025
+
3026
+ @Column(() => Blueprint.timestamp().null())
3027
+ public deletedAt!: Date
3028
+
3029
+ }
3030
+
3031
+ export { User }
3032
+ export default User
3033
+
3034
+ ```
3035
+
3036
+ ### Schema
3037
+
3038
+ The schema refers to the structure of the database as it pertains to the objects and classes in the model.
3039
+ using the following:
3040
+
3041
+ #### Schema Model
3042
+
3043
+ ```js
3044
+ import { Model, Blueprint , type T } from "tspace-mysql";
3045
+
3046
+ const schema = {
3047
+ id: Blueprint.int().notNull().primary().autoIncrement(),
3048
+ uuid: Blueprint.varchar(50).null().index(),
3049
+ name: Blueprint.varchar(191).notNull(),
3050
+ email: Blueprint.varchar(191).notNull(),
3051
+ createdAt: Blueprint.timestamp().null(),
3052
+ updatedAt: Blueprint.timestamp().null(),
3053
+ deletedAt: Blueprint.timestamp().null()
3054
+ }
3055
+
3056
+
3057
+ // make type in TS
3058
+ type TS = T.Schema<typeof Schema>
3059
+
3060
+ // the TSchemaUser will be created like that
3061
+ /**
3062
+ {
3063
+ id : number,
3064
+ uuid : string | null,
3065
+ name : string,
3066
+ email : string,
3067
+ createdAt : Date | string | null,
3068
+ updatedAt : Date | string | null,
3069
+ deletedAt : Date | string | null
3070
+ }
3071
+ */
3072
+
3073
+
3074
+ class User extends Model<TS> // use the schema for this User model
3075
+ {
3076
+ constructor() {
3077
+ super();
3078
+ this.useCamelCase()
3079
+ this.useSchema(schema)
3080
+ }
3081
+ }
3082
+
3083
+ ```
3084
+
3085
+ #### Virtual Column
3086
+ ```js
3087
+
3088
+ import { Model, Blueprint , type T } from "tspace-mysql";
3089
+
3090
+ const schema = {
3091
+ id: Blueprint.int().notNull().primary().autoIncrement(),
3092
+ uuid: Blueprint.varchar(50).null().index(),
3093
+ firstName: Blueprint.varchar(191).notNull(),
3094
+ lastName : Blueprint.varchar(191).notNull(),
3095
+ email: Blueprint.varchar(191).notNull(),
3096
+ createdAt: Blueprint.timestamp().null(),
3097
+ updatedAt: Blueprint.timestamp().null(),
3098
+ deletedAt: Blueprint.timestamp().null(),
3099
+
3100
+ // Define you virtual column to schema
3101
+ fullName : new Blueprint().virtualColumn(`CONCAT(firstName,' ', lastName)`),
3102
+ countPosts : new Blueprint().virtualColumn(`(SELECT COUNT(*) FROM posts WHERE posts.userid = users.id)`)
3103
+
3104
+ // if you need to custom the virtualColumn column for some method.
3105
+ // fullName : new Blueprint().virtualColumn({
3106
+ // select : `CONCAT(firstName,' ', lastName)`,
3107
+ // where : `CONCAT(firstName,' ', lastName)`,
3108
+ // orderBy : `CONCAT(firstName,' ', lastName)`,
3109
+ // groupBy : `CONCAT(firstName,' ', lastName)`,
3110
+ // }),
3111
+ }
3112
+
3113
+ type TS = T.Schema<typeof Schema>
3114
+
3115
+ class User extends Model<TS> {
3116
+ constructor() {
3117
+ super();
3118
+ this.useSchema(schema)
3119
+ }
3120
+ }
3121
+ const users = await new User()
3122
+ .select('id','firstName','lastName','fullName','countPosts')
3123
+ .where('fullName','LIKE',`%tspace-mysql%`)
3124
+ .orderBy('fullName','desc')
3125
+ .groupBy('fullName')
3126
+ .findMany()
3127
+
3128
+ // SELECT
3129
+ // `users`.`id`, `users`.`firstName`, `users`.`lastName`,
3130
+ // CONCAT(firstName,' ', lastName) AS fullName ,
3131
+ // (SELECT COUNT(*) FROM posts WHERE posts.userid = users.id) AS countPosts
3132
+ // FROM `users`
3133
+ // WHERE CONCAT(firstName,' ', lastName) LIKE '%tspace-mysql%'
3134
+ // GROUP BY CONCAT(firstName,' ', lastName)
3135
+ // ORDER BY CONCAT(firstName,' ', lastName) DESC
3136
+
3137
+ ```
3138
+
3139
+ #### Validation
3140
+
3141
+ Validate the schema of Model
3142
+ let's example a validator model:
3143
+
3144
+ ```js
3145
+ import { Model, Blueprint } from "tspace-mysql";
3146
+ class User extends Model {
3147
+ constructor() {
3148
+ super();
3149
+ this.useCamelCase();
3150
+ this.useSchema({
3151
+ id: Blueprint.int().notNull().primary().autoIncrement(),
3152
+ uuid: Blueprint.varchar(50).null(),
3153
+ name: Blueprint.varchar(191).notNull(),
3154
+ email: Blueprint.varchar(191).notNull(),
3155
+ createdAt: Blueprint.timestamp().null(),
3156
+ updatedAt: Blueprint.timestamp().null(),
3157
+ deletedAt: Blueprint.timestamp().null(),
3158
+ });
3159
+
3160
+ // validate input when create or update reference to the schema in 'this.useSchema'
3161
+ this.useValidateSchema({
3162
+ id: Number,
3163
+ uuid: Number,
3164
+ name: {
3165
+ type: String,
3166
+ length: 191,
3167
+ require: true,
3168
+ json: true,
3169
+ },
3170
+ email: {
3171
+ type: String,
3172
+ require: true,
3173
+ length: 191,
3174
+ match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
3175
+ unique: true,
3176
+ fn: (email: string) => {
3177
+ return /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)
3178
+ }
3179
+ },
3180
+ createdAt: Date,
3181
+ updatedAt: Date,
3182
+ deletedAt: Date,
3183
+ });
3184
+ }
3185
+ }
3186
+ ```
3187
+
3188
+ #### Sync
3189
+
3190
+ Sync the schema with the "Models" setting in your directory.
3191
+ This process will verify and update table columns and foreign keys as needed.
3192
+ Ensure that the relationships are correctly established through the 'useSchema' method in your models.
3193
+ Let's examine a basic sync class:
3194
+
3195
+ ```js
3196
+ /**
3197
+ *
3198
+ * @Ex directory
3199
+ *
3200
+ * - node_modules
3201
+ * - src
3202
+ * - index.ts
3203
+ * - Models
3204
+ * - User.ts
3205
+ * - Post.ts
3206
+ */
3207
+
3208
+ // file User
3209
+ class User extends Model {
3210
+ constructor() {
3211
+ super();
3212
+ this.hasMany({ name: "posts", model: Post });
3213
+
3214
+ // if you need to initialize data when creating the table, you can use the following.
3215
+ this.whenCreatingTable(async () => {
3216
+ return await new User()
3217
+ .create({
3218
+ ...columns,
3219
+ })
3220
+ .void()
3221
+ .save();
3222
+ });
3223
+
3224
+ this.useSchema({
3225
+ id: Blueprint.int().notNull().primary().autoIncrement(),
3226
+ uuid: Blueprint.varchar(50).null(),
3227
+ email: Blueprint.int().notNull().unique(),
3228
+ name: Blueprint.varchar(255).null(),
3229
+ created_at: Blueprint.timestamp().null(),
3230
+ updated_at: Blueprint.timestamp().null(),
3231
+ deleted_at: Blueprint.timestamp().null(),
3232
+ });
3233
+ }
3234
+ }
3235
+
3236
+ // file Post
3237
+ import User from "./User";
3238
+ class Post extends Model {
3239
+ constructor() {
3240
+ super();
3241
+ this.hasMany({ name: "comments", model: Comment });
3242
+ this.belongsTo({ name: "user", model: User });
3243
+ this.useSchema({
3244
+ id: Blueprint.int().notNull().primary().autoIncrement(),
3245
+ uuid: Blueprint.varchar(50).null(),
3246
+ user_id: Blueprint.int().notNull().foreign({
3247
+ references: "id",
3248
+ on: User,
3249
+ onDelete: "CASCADE",
3250
+ onUpdate: "CASCADE",
3251
+ }),
3252
+ title: Blueprint.varchar(255).null(),
3253
+ created_at: Blueprint.timestamp().null(),
3254
+ updated_at: Blueprint.timestamp().null(),
3255
+ deleted_at: Blueprint.timestamp().null(),
3256
+ });
3257
+ }
3258
+ }
3259
+
3260
+ await Schema.sync(`/src/Models`, {
3261
+ force: true,
3262
+ log: true,
3263
+ foreign: true,
3264
+ changed: true,
3265
+ });
3266
+
3267
+ // You can also synchronize using the Model.
3268
+ await new User().sync({ force: true, foreign: true, changed: true });
3269
+ ```
3270
+
3271
+ ### SoftDelete
3272
+
3273
+ ```js
3274
+
3275
+ import { Model } from 'tspace-mysql'
3276
+ class User extends Model {
3277
+ constructor() {
3278
+ super()
3279
+ this.useSoftDelete() // All query will be where 'deleted_at' is null
3280
+
3281
+ // You can also use patterns camelCase to covert the 'deleted_at' to 'deletedAt'
3282
+ // You can also customize the column 'deleted_at'
3283
+ this.useSoftDelete('deletedAtCustom')
3284
+ }
3285
+ }
3286
+
3287
+ const user = await new User().where('user_id',1).findOne()
3288
+ // SELECT * FROM `users` WHERE `users`.`userId` = '1' and `users`.`deletedAtCustom` IS NULL LIMIT 1;
3289
+
3290
+ // find in trashed
3291
+ const user = await new User().trashed().findMany()
3292
+ // SELECT * FROM `users` WHERE `users`.`userId` = '1' and `users`.`deletedAtCustom` IS NOT NULL;
3293
+
3294
+ ```
3295
+
3296
+ ### Type Safety
3297
+ Type Type Safety in TypeScript refers to the ability of the language to detect and prevent type errors during compile-time.
3298
+ Type Type Safety still works when you add additional types to your model, using the following:
3299
+
3300
+ ```js
3301
+ // in file User.ts
3302
+ import { Model , Blueprint , type T } from 'tspace-mysql'
3303
+ import Phone from '../Phone'
3304
+
3305
+ const schemaUser = {
3306
+ id :Blueprint.int().notNull().primary().autoIncrement(),
3307
+ uuid :Blueprint.varchar(50).null(),
3308
+ email :Blueprint.varchar(50).null(),
3309
+ name :Blueprint.varchar(255).null(),
3310
+ username : Blueprint.varchar(255).null(),
3311
+ password : Blueprint.varchar(255).null(),
3312
+ createdAt :Blueprint.timestamp().null(),
3313
+ updatedAt :Blueprint.timestamp().null()
3314
+ }
3315
+
3316
+ type TSchemaUser = T.SchemaStatic<typeof schemaUser>
3317
+ // TSchemaUser = T.Schema<typeof schemaUser>
3318
+
3319
+ // TSchema allowed to set any new keys without in the schema to results
3320
+ // TSchemaStatic not allowed to set any new keys without in the schema to results
3321
+
3322
+ class User extends Model<TSchemaUser> { // Add this '<TSchemaUser>' to activate the type for the Model.
3323
+ constructor() {
3324
+ super()
3325
+ this.useSchema(schemaUser)
3326
+ this.hasOne({ model : Phone, name : 'phone' })
3327
+ this.hasMany({ model : Phone, name : 'phones' })
3328
+ }
3329
+ }
3330
+
3331
+ export { User }
3332
+ export default User
3333
+
3334
+ +--------------------------------------------------------------------------+
3335
+
3336
+ // in file Phone.ts
3337
+ import { Model , Blueprint , type T } from 'tspace-mysql'
3338
+ import { User } from './User.ts'
3339
+ const schemaPhone = {
3340
+ id :Blueprint.int().notNull().primary().autoIncrement(),
3341
+ uuid :Blueprint.varchar(50).null(),
3342
+ userId : Blueprint.int().notNull(),
3343
+ number :Blueprint.varchar(50).notNull(),
3344
+ createdAt :Blueprint.timestamp().null(),
3345
+ updatedAt :Blueprint.timestamp().null()
3346
+ }
3347
+
3348
+ type TSchemaPhone = T.SchemaStatic<typeof schemaPhone>
3349
+
3350
+ class Phone extends Model<TSchemaPhone> {
3351
+ constructor() {
3352
+ super()
3353
+ this.useSchema(schemaPhone)
3354
+ this.useBelongsTo({ model : User, name : 'user'})
3355
+ }
3356
+ }
3357
+
3358
+ export { Phone }
3359
+ export default Phone
3360
+
3361
+ // example basic
3362
+ type TS = T.Schema<typeof TSchemaUser>
3363
+ type TR = T.Relation<{ phone : Phone }>
3364
+
3365
+ type TSM = T.SchemaModel<User>
3366
+ type TRM = T.RelationModel<User>
3367
+
3368
+ type TColumn = T.Column<User>
3369
+
3370
+ type TResults = T.Results<User>
3371
+ type TPaginateResults = T.Results<User, { paginate : true }>
3372
+
3373
+ type TRepository = T.Repository<User>
3374
+ type TRepositoryTypeOf = T.RepositoryTypeOf<User>
3375
+
3376
+ +--------------------------------------------------------------------------+
3377
+ ```
3378
+
3379
+ ### Type Safety Select
3380
+
3381
+ ```js
3382
+ import { User } from './User.ts'
3383
+ import { Phone } from './Phone.ts'
3384
+
3385
+ const user = await new User().select('id','username').findOne() ✅
3386
+ const user = await new User().select('idx','username').findOne() ❌
3387
+
3388
+ const user = await new User().except('id','username').findOne() ✅
3389
+ const user = await new User().except('idx','username').findOne() ❌
3390
+
3391
+ // T.SchemaStatic not allowed to set any new keys without in the schema to results
3392
+ user.withoutSchema = 1 ✅ // T.Schema<User>
3393
+ user.withoutSchema = 1 ❌ // T.SchemaStatic<User>
3394
+ // But can you make like this for cases
3395
+ const user = await new User().except('idx','username').findOne<{ withoutSchema : number }>()
3396
+ user.withoutSchema = 1 ✅
3397
+ ```
3398
+
3399
+ ### Type Safety OrderBy
3400
+
3401
+ ```js
3402
+ import { User } from './User.ts'
3403
+ import { Phone } from './Phone.ts'
3404
+
3405
+ const users = await new User().orderBy('id','DESC').findMany() ✅
3406
+ const users = await new User().orderBy('idx','DESC').findMany() ❌
3407
+
3408
+ const users = await new User().latest('id').findMany() ✅
3409
+ const users = await new User().latest('idx').findMany() ❌
3410
+
3411
+ const users = await new User().oldest('id').findMany() ✅
3412
+ const users = await new User().oldest('idx').findMany() ❌
3413
+
3414
+ ```
3415
+
3416
+ ### Type Safety GroupBy
3417
+
3418
+ ```js
3419
+ import { User } from './User.ts'
3420
+ import { Phone } from './Phone.ts'
3421
+
3422
+ const users = await new User().groupBy('id').findMany() ✅
3423
+ const users = await new User().groupBy('idx').findMany() ❌
3424
+
3425
+ ```
3426
+
3427
+ ### Type Safety Where
3428
+
3429
+ ```js
3430
+ import { User } from './User.ts'
3431
+ import { Phone } from './Phone.ts'
3432
+
3433
+ const users = await new User().where('id',1).findMany() ✅
3434
+ const users = await new User().where('idxx',1).findMany() ❌
3435
+
3436
+ const users = await new User().where('id',1).orWhere('id',5).findMany() ✅
3437
+ const users = await new User().where('id',1).orWhere('idxx',5).findMany() ❌
3438
+
3439
+ const users = await new User().whereIn('id',[1]).findMany() ✅
3440
+ const users = await new User().whereIn('idx',[1]).findMany() ❌
3441
+
3442
+ const users = await new User().whereNull('id').findMany() ✅
3443
+ const users = await new User().whereNull('idx').findMany() ❌
3444
+
3445
+ const users = await new User().whereNotNull('id').findMany()
3446
+ const users = await new User().whereNotNull('idx').findMany()
3447
+
3448
+ const users = await new User().whereBetween('id',[1,2]).findMany() ✅
3449
+ const users = await new User().whereBetween('idx',[1,2]).findMany() ❌
3450
+
3451
+ const users = await new User()
3452
+ .whereSubQuery(
3453
+ 'id',
3454
+ new User().select('id').toString()
3455
+ ).findMany() ✅
3456
+
3457
+ const users = await new User()
3458
+ .whereSubQuery(
3459
+ 'idx',
3460
+ new User().select('id').toString()
3461
+ ).findMany() ❌
3462
+
3463
+ ```
3464
+
3465
+ ### Type Safety Insert
3466
+
3467
+ ```js
3468
+ import { User } from './User.ts'
3469
+ import { Phone } from './Phone.ts'
3470
+
3471
+ const users = await new User().create({ id : 10 }).save() ✅
3472
+
3473
+ const users = await new User().create({ id : "10" }).save() ❌
3474
+
3475
+ const users = await new User().create({ idx : 10 }).save() ❌
3476
+
3477
+ ```
3478
+
3479
+ ### Type Safety Update
3480
+
3481
+ ```js
3482
+ import { User } from './User.ts'
3483
+ import { Phone } from './Phone.ts'
3484
+
3485
+ const users = await new User().update({ id : 10 }).where('id',1).save() ✅
3486
+ const users = await new User().update({ id : 10 }).where('idx',1).save() ❌
3487
+ const users = await new User().update({ id : "10" }).where('id',1).save() ❌
3488
+ const users = await new User().update({ idx : 10 }).where('idx',1).save() ❌
3489
+
3490
+ ```
3491
+
3492
+ ### Type Safety Delete
3493
+
3494
+ ```js
3495
+ import { User } from './User.ts'
3496
+ import { Phone } from './Phone.ts'
3497
+
3498
+ const users = await new User().where('id',1).delete() ✅
3499
+ const users = await new User().where('idx',1).delete() ❌
3500
+
3501
+ ```
3502
+
3503
+ ### Type Safety Relationships
3504
+
3505
+ ```js
3506
+ import { type T } from 'tspace-mysql'
3507
+ import { User } from './User.ts'
3508
+ import { Phone } from './Phone.ts'
3509
+ // Case #1 : Relationship with 2 relations 'phone' and 'phones'
3510
+ const users = await new User()
3511
+ .relations('phone','phones')
3512
+ .findMany()
3513
+
3514
+ for(const user of users) {
3515
+ user.phone ❌
3516
+ user.phones ❌
3517
+ }
3518
+
3519
+ // You can also specify the type for the results.
3520
+ // bad 👎👎👎
3521
+ const users = await new User()
3522
+ .relations('phone','phones')
3523
+ .findMany<{ phone : Record<string,any> , phones : any[]}>()
3524
+
3525
+ for(const user of users) {
3526
+ user.phone ✅
3527
+ user.phones ✅
3528
+ user.phone.id ✅
3529
+ user.phone.idx ✅💩💩💩
3530
+ user.phones.map(phone => phone.id) ✅
3531
+ user.phones.map(phone => phone.idx) ✅💩💩💩
3532
+ }
3533
+
3534
+ // good 👍👍👍
3535
+ const users = await new User()
3536
+ .relations('phone','phones')
3537
+ .findMany<{ phone : T.SchemaModel<Phone> , phones : T.SchemaModel<Phone>[] }>()
3538
+
3539
+ for(const user of users) {
3540
+ user.phone ✅
3541
+ user.phones ✅
3542
+ user.phone?.id ✅
3543
+ user.phone?.idx ❌
3544
+ user.phones.map(phone => phone?.id) ✅
3545
+ user.phones.map(phone => phone?.idx) ❌
3546
+ }
3547
+
3548
+ +--------------------------------------------------------------------------+
3549
+
3550
+ // Case #2 : There is a relationship between two entities, 'phone' and 'phones', both of which are related to the 'user' entity through nested relations
3551
+ const users = await new User()
3552
+ .relations('phone','phones')
3553
+ .relationQuery('phone' , (query : Phone) => query.relations('user'))
3554
+ .relationQuery('phones' , (query : Phone) => query.relations('user'))
3555
+ .findMany<{ phone : T.SchemaModel<Phone> , phones : T.SchemaModel<Phone>[] }>()
3556
+
3557
+ for(const user of users) {
3558
+ user.phone.user ❌
3559
+ user.phones.map(phone =>phone.user) ❌
3560
+ }
3561
+
3562
+ // You can also specify the type for the results.
3563
+ // bad 👎👎👎
3564
+ const users = await new User()
3565
+ .relations('phone','phones')
3566
+ .relationQuery('phone' , (query : Phone) => query.relations('user'))
3567
+ .relationQuery('phones' , (query : Phone) => query.relations('user'))
3568
+ .findMany<{ phone : Record<string,any> , phones : Record<string,any>[] }>()
3569
+
3570
+ for(const user of users) {
3571
+ user.phone.user ✅💩💩💩
3572
+ user.phones.map(phone =>phone.user) ✅💩💩💩
3573
+ user.phone.user.idx ✅💩💩💩
3574
+ user.phones.map(phone =>phone.user.idx) ✅💩💩💩
3575
+ }
3576
+
3577
+ // good 👍👍👍
3578
+ const users = await new User()
3579
+ .relations('phone','phones')
3580
+ .relationQuery('phone' , (query : Phone) => query.relations('user'))
3581
+ .relationQuery('phones' , (query : Phone) => query.relations('user'))
3582
+ .findMany<{
3583
+ phone : Partial<T.SchemaModel<Phone>> & { user : T.SchemaModel<User>};
3584
+ phones : (Partial<T.SchemaModel<Phone>> & { user : T.SchemaModel<User>})[];
3585
+ }>()
3586
+
3587
+ for(const user of users) {
3588
+ user.phone.user ✅
3589
+ user.phone.user.id ✅
3590
+ user.phone.userx ❌
3591
+ user.phone.user.idx ❌
3592
+ user.phones.map(phone =>phone.user.id) ✅
3593
+ user.phones.map(phone =>phone.user.idx) ❌
3594
+ }
3595
+
3596
+ +--------------------------------------------------------------------------+
3597
+ // If you don't want to set types for every returning method such as 'findOne', 'findMany', and so on...
3598
+
3599
+ import { Model , Blueprint , type T } from 'tspace-mysql'
3600
+ import { Phone } from '../Phone'
3601
+
3602
+ const schemaUser = {
3603
+ id :Blueprint.int().notNull().primary().autoIncrement(),
3604
+ uuid :Blueprint.varchar(50).null(),
3605
+ email :Blueprint.varchar(50).null(),
3606
+ name :Blueprint.varchar(255).null(),
3607
+ username :Blueprint.varchar(255).null(),
3608
+ password :Blueprint.varchar(255).null(),
3609
+ createdAt :Blueprint.timestamp().null(),
3610
+ updatedAt :Blueprint.timestamp().null()
3611
+ }
3612
+
3613
+ type TSchemaUser = T.SchemaStatic<typeof schemaUser>
3614
+
3615
+ type TRelationUser = T.Relation<{
3616
+ phones : Phone[]
3617
+ phone : Phone
3618
+ }>
3619
+
3620
+ // Add this '<TSchemaUser, RelationUserType>' to activate the type for the Model.
3621
+ class User extends Model< TSchemaUser, TRelationUser > {
3622
+ constructor() {
3623
+ super()
3624
+ this.useSchema(schemaUser)
3625
+ this.hasOne({ model : Phone, name : 'phonex' }) ❌
3626
+ this.hasMany({ model : Phone, name : 'phonesx' }) ❌
3627
+ this.hasOne({ model : Phone, name : 'phone' }) ✅
3628
+ this.hasMany({ model : Phone, name : 'phones' }) ✅
3629
+ }
3630
+ }
3631
+
3632
+ export { User }
3633
+
3634
+ +--------------------------------------------------------------------------+
3635
+
3636
+ // in file Phone.ts
3637
+ import { Model , Blueprint , type T } from 'tspace-mysql'
3638
+ import { User } from './User.ts'
3639
+
3640
+ const schemaPhone = {
3641
+ id :Blueprint.int().notNull().primary().autoIncrement(),
3642
+ uuid :Blueprint.varchar(50).null(),
3643
+ userId :Blueprint.int().notNull(),
3644
+ number :Blueprint.varchar(50).notNull(),
3645
+ createdAt :Blueprint.timestamp().null(),
3646
+ updatedAt :Blueprint.timestamp().null()
3647
+ }
3648
+
3649
+ type TSchemaPhone = T.Schema<typeof schemaPhone>
3650
+
3651
+ type TRelationPhone = T.Relation<{
3652
+ user : User[]
3653
+ }>
3654
+
3655
+ class Phone extends Model<TSchemaPhone,TRelationPhone> {
3656
+ constructor() {
3657
+ super()
3658
+ this.useSchema(schemaPhone)
3659
+ this.useBelongsTo({ model : User, name : 'userx'}) ❌
3660
+ this.useBelongsTo({ model : User, name : 'user'}) ✅
3661
+ }
3662
+ }
3663
+
3664
+ export { Phone }
3665
+
3666
+ +--------------------------------------------------------------------------+
3667
+
3668
+ const users = await new User()
3669
+ .relations('phonex','phonesx') ❌
3670
+ .relationQuery('phonex' ❌ , (query : Phone) => query.relations('user')) ✅
3671
+ .relationQuery('phonesx' ❌ , (query : Phone) => query.relations('user')) ✅
3672
+ .findMany()
3673
+
3674
+ const users = await new User()
3675
+ .relations('phone','phones') ✅
3676
+ .relationQuery('phonex' ❌ , (query : Phone) => query.relations('user')) ✅
3677
+ .relationQuery('phonesx' ❌ , (query : Phone) => query.relations('user')) ✅
3678
+ .findMany()
3679
+
3680
+ const users = await new User()
3681
+ .relations('phone','phones')
3682
+ .relationQuery('phone' , (query : Phone) => query.relations('userx')) ❌
3683
+ .relationQuery('phones' , (query : Phone) => query.relations('userx')) ❌
3684
+ .findMany()
3685
+
3686
+ const users = await new User()
3687
+ .relations('phone','phones') ✅
3688
+ .relationQuery('phone' ✅ , (query : Phone) => query.relations('user')) ✅
3689
+ .relationQuery('phones'✅ , (query : Phone) => query.relations('user')) ✅
3690
+ .findMany()
3691
+
3692
+ for(const user of users) {
3693
+ user.phone.user ❌
3694
+ user.phone?.user ✅
3695
+ user.phone?.user.id ✅
3696
+ user.phone?.userx ❌
3697
+ user.phone?.user.idx ❌
3698
+ user.phones.map(phone =>phone?.user.id) ❌
3699
+ user.phones?.map(phone =>phone?.user.id) ✅
3700
+ user.phones?.map(phone =>phone?.user.idx) ❌
3701
+ }
3702
+
3703
+ ```
3704
+
3705
+ ## Type Safety Results
3706
+ ```js
3707
+ import { type T } from 'tspace-mysql'
3708
+
3709
+ const fError = async () : Promise<T.Results<User>[]> => {
3710
+
3711
+ const users = [{
3712
+ id : 1,
3713
+ uuid: "12d4f08a-a20d-4f41-abac-81391e135d60",
3714
+ email: "tspace@example.com"
3715
+ }]
3716
+
3717
+ return users // ❌
3718
+ }
3719
+
3720
+ const fCorrect = async () : Promise<T.Results<User>[]> => {
3721
+
3722
+ const users = await new User().findMany()
3723
+
3724
+ return users // ✅
3725
+ }
3726
+
3727
+ ```
3728
+
3729
+ ## Metadata
3730
+ Get the metadata of a Model works only when a schema is added to the Model.
3731
+
3732
+ ```js
3733
+ import { Meta, Model , Blueprint , type T } from 'tspace-mysql';
3734
+
3735
+ const schema = {
3736
+ id : Blueprint.int().notNull().primary().autoIncrement(),
3737
+ uuid : Blueprint.varchar(50).null(),
3738
+ email : Blueprint.varchar(255).notNull().index('users.email@index'),
3739
+ name : Blueprint.varchar(255).null(),
3740
+ username : Blueprint.varchar(255).notNull(),
3741
+ password : Blueprint.varchar(255).notNull(),
3742
+ status : Blueprint.tinyInt().notNull().default(0),
3743
+ role : Blueprint.enum('admin','user').default('user'),
3744
+ createdAt : Blueprint.timestamp().null(),
3745
+ updatedAt : Blueprint.timestamp().null()
3746
+ }
3747
+
3748
+ type TS = T.Schema<typeof schema>
3749
+
3750
+ class User extends Model<TS> {
3751
+ constructor() {
3752
+ super()
3753
+ this.useSchema(schema)
3754
+ }
3755
+ }
3756
+
3757
+ const meta = Meta(User)
3758
+
3759
+ const table = meta.table() // 'users'
3760
+ const column = meta.column('id') // 'id'
3761
+ const columnRef = meta.columnReference('id') // `users`.`id`
3762
+ const columnTypeOf = meta.columnTypeOf('id') // number
3763
+ const columnType = meta.columnType('id') // Int
3764
+ const columns = meta.columns() // ['id','uuid',...'updatedAt']
3765
+ const hasColumn = meta.hasColumn('idx') // false
3766
+ const primaryKey = meta.primaryKey() // 'id'
3767
+ const indexes = meta.indexes() // ['users.email@index']
3768
+ const nullable = meta.nullable() // ['uuid','name','createdAt','updatedAt']
3769
+ const defaults = meta.defaults() // { id : null, uuid : null, ..., status : 0, role: 'user' ,..updatedAt : null }
3770
+ const enums = meta.enums('role') // [ 'admin', 'user' ]
3771
+ const enumsObj = meta.enum('role') // { admin: 'admin', user: 'user' }
3772
+ ```
3773
+
3774
+ ## Audit
3775
+ Keeps a complete history of database changes, tracking who made changes and what was changed.
3776
+
3777
+ ```js
3778
+ await new User()
3779
+ // support actions SELECT, INSERT, UPDATE, and DELETE.
3780
+ // so you have a complete history of data changes and queries.
3781
+ .audit(99 , { name : 'userNumber-99' })
3782
+ .create({
3783
+ username : 'hi audit',
3784
+ email : 'tspace-mysql@gmail.com',
3785
+ // ...
3786
+ })
3787
+ .save()
3788
+ ```
3789
+
3790
+ ## Repository
3791
+ ```js
3792
+ Repository is a mechanism that encapsulates all database operations related to a specific model.
3793
+ It provides methods for querying, inserting, updating, and deleting records in the database associated with the model.
3794
+
3795
+ ** The Repository check always type Type Safety if model is used the type of schema
3796
+
3797
+ ```
3798
+ ### Repository Select Statements
3799
+ ```js
3800
+ import { Repository, OP , type T } from 'tspace-mysql'
3801
+ import { User } from '../Models/User'
3802
+
3803
+ // Create repository instance for User entity
3804
+ const userRepository = Repository(User)
3805
+
3806
+ // Or Use static methods for flexible querying
3807
+ // const user = await User.findOne({ ... }) // traditional way without repository
3808
+
3809
+ // Fetch a single user with flexible query options
3810
+ const user = await userRepository.findOne({
3811
+ /**
3812
+ * 🎯 SELECT
3813
+ * Specify which columns should be returned.
3814
+ * Supports nested selection for relations.
3815
+ */
3816
+ select: {
3817
+ id: true,
3818
+ name: true,
3819
+ username: true,
3820
+
3821
+ // Nested relation field selection
3822
+ phone: {
3823
+ id: true,
3824
+ name: true,
3825
+ user_id: true,
3826
+ }
3827
+ },
3828
+
3829
+ /**
3830
+ * 🚫 EXCEPT
3831
+ * Exclude specific columns from the result.
3832
+ * Useful for hiding audit fields or sensitive data.
3833
+ */
3834
+ // except: {
3835
+ // deleted_at: true,
3836
+ // created_at: true,
3837
+ // updated_at: true,
3838
+ // },
3839
+
3840
+ /**
3841
+ * 🧮 SELECT RAW
3842
+ * Add computed/raw SQL fields to the result.
3843
+ * Automatically extends the return type.
3844
+ */
3845
+ // selectRaw: {
3846
+ // fullName: DB.raw('CONCAT(?,"@",?)', ['name', 'id'])
3847
+ // },
3848
+
3849
+ /**
3850
+ * 🧩 EXTEND
3851
+ * Manually extend the result type with additional fields.
3852
+ * Supports nested objects and arrays.
3853
+ */
3854
+ // extend: {
3855
+ // myNumber: Number,
3856
+ // myProfile: {
3857
+ // id: Number,
3858
+ // name: String
3859
+ // },
3860
+ // myOtherData: [
3861
+ // {
3862
+ // id: Number,
3863
+ // name: String
3864
+ // }
3865
+ // ]
3866
+ // },
3867
+
3868
+ /**
3869
+ * 🔎 WHERE
3870
+ * Define filtering conditions.
3871
+ */
3872
+ where: {
3873
+ id: 1
3874
+ },
3875
+
3876
+ /**
3877
+ * ⚡ WHEN
3878
+ * Conditionally modify the query.
3879
+ * Only applied if `condition` is true.
3880
+ */
3881
+ when: {
3882
+ condition: true,
3883
+ query: () => ({
3884
+ relations: {
3885
+ phone: true
3886
+
3887
+ /**
3888
+ * You can also customize relation query:
3889
+ *
3890
+ * phone: {
3891
+ * where: { id: 41 },
3892
+ * select: {
3893
+ * id: true,
3894
+ * user_id: true
3895
+ * }
3896
+ * }
3897
+ */
3898
+ }
3899
+ })
3900
+ },
3901
+
3902
+ /**
3903
+ * 🛠 USING
3904
+ * Direct access to the underlying query builder.
3905
+ * Allows advanced customization using model methods.
3906
+ */
3907
+ using: (query) => {
3908
+ return query
3909
+
3910
+ // Examples:
3911
+ // query.where('id', 1)
3912
+ // query.customMethodWhereUser(1)
3913
+ }
3914
+ })
3915
+
3916
+
3917
+ // Use static methods for flexible querying
3918
+ // const users = await User.findMany({ limit: 3 }) // traditional way without repository
3919
+ const users = await userRepository.findMany({
3920
+ select : {
3921
+ id : true,
3922
+ name : true,
3923
+ username : true,
3924
+ },
3925
+ limit : 3,
3926
+ orderBy : {
3927
+ id : 'ASC',
3928
+ name : 'DESC'
3929
+ },
3930
+ groupBy : {
3931
+ id : true
3932
+ },
3933
+ where : {
3934
+ id: OP.in([1,2,3])
3935
+ }
3936
+ })
3937
+
3938
+ // Use static methods for flexible querying
3939
+ // const userPaginate = await User.pagination() // traditional way without repository
3940
+ const userPaginate = await userRepository.pagination({
3941
+ select : {
3942
+ id : true,
3943
+ name : true,
3944
+ username : true,
3945
+ },
3946
+ page : 1,
3947
+ limit : 3,
3948
+ where : {
3949
+ id: OP.in([1,2,3])
3950
+ }
3951
+ })
3952
+
3953
+ const findFullName = await userRepository.findOne({
3954
+ select : {
3955
+ name : true,
3956
+ [`${DB.raw('CONCAT(firstName," ",lastName) as fullName')}`]: true
3957
+ },
3958
+ whereRaw : [
3959
+ `CONCAT(firstName," ",lastName) LIKE '%${search}%'`
3960
+ ]
3961
+ })
3962
+ ```
3963
+ ### Repository Insert Statements
3964
+ ```js
3965
+ // Use static methods for flexible querying
3966
+ // const created = await User.create({...}) // traditional way without repository
3967
+
3968
+ const userRepository = Repository(User)
3969
+
3970
+ const created = await userRepository.create({
3971
+ data : {
3972
+ name : "repository-name",
3973
+ // ....
3974
+ }
3975
+ })
3976
+
3977
+ const createdMultiple = await u.createMultiple({
3978
+ data : [
3979
+ {
3980
+ name: "tspace4",
3981
+ // ....
3982
+ },
3983
+ {
3984
+ name: "tspace5",
3985
+ // ....
3986
+ },
3987
+ {
3988
+ name: "tspace6",
3989
+ // ....
3990
+ }
3991
+ // ....
3992
+ ]
3993
+ })
3994
+
3995
+ const createdNotExists = await userRepository.createNotExists({
3996
+ data : {
3997
+ name : "repository-name",
3998
+ // ....
3999
+ },
4000
+ where : {
4001
+ id : 1
4002
+ }
4003
+ })
4004
+
4005
+ const createdOrSelected = await userRepository.createOrSelect({
4006
+ data : {
4007
+ name : "repository-name",
4008
+ // ....
4009
+ },
4010
+ where : {
4011
+ id : 1
4012
+ }
4013
+ })
4014
+
4015
+
4016
+ ```
4017
+ ### Repository Update Statements
4018
+ ```js
4019
+
4020
+ // Use static methods for flexible querying
4021
+ // const updated = await User.update({...}) // traditional way without repository
4022
+
4023
+ const userRepository = Repository(User)
4024
+ const updated = await userRepository.update({
4025
+ data : {
4026
+ name : "repository-name",
4027
+ // ....
4028
+ },
4029
+ where : {
4030
+ id : 1
4031
+ }
4032
+ })
4033
+
4034
+ ```
4035
+ ### Repository Delete Statements
4036
+ ```js
4037
+
4038
+ // Use static methods for flexible querying
4039
+ // const deleted = await User.delete({...}) // traditional way without repository
4040
+
4041
+ const userRepository = Repository(User)
4042
+ const deleted = await userRepository.delete({
4043
+ where : {
4044
+ id : 1
4045
+ }
4046
+ })
4047
+
4048
+ ```
4049
+
4050
+ ### Repository Transactions
4051
+
4052
+ ```js
4053
+ import { DB , Repository } from 'tspace-mysql'
4054
+ import { User } from '../Models/User'
4055
+ const userRepository = Repository(User)
4056
+
4057
+ const transaction = await DB.beginTransaction()
4058
+
4059
+ try {
4060
+ await transaction.startTransaction()
4061
+
4062
+ const created = await userRepository.create({
4063
+ data : {
4064
+ name : "repository-name",
4065
+ // ....
4066
+ },
4067
+ transaction // add this for the transaction
4068
+ })
4069
+
4070
+ const updated = await userRepository.update({
4071
+ data : {
4072
+ name : "repository-name",
4073
+ // ....
4074
+ },
4075
+ where : {
4076
+ id : created.id
4077
+ },
4078
+ transaction
4079
+ })
4080
+
4081
+ // after your use commit if use same transction for actions this transction will auto commit
4082
+ await transaction.commit()
4083
+
4084
+ // ensure the nothing with transction just use end of transction
4085
+ await transaction.end();
4086
+
4087
+ } catch (err) {
4088
+
4089
+ await transaction.rollback()
4090
+ }
4091
+
4092
+ ```
4093
+
4094
+ ### Repository Relations
4095
+ ```js
4096
+ import { Repository , OP } from 'tspace-mysql'
4097
+ import { User } from '../Models/User'
4098
+ import { Phone } from '../Models/Phone'
4099
+
4100
+ // Use static methods for flexible querying
4101
+ // const userHasPhones = await User.findOne({...}) // traditional way without repository
4102
+
4103
+ const userRepository = Repository(User)
4104
+ const userHasPhones = await userRepository.findOne({
4105
+ select : {
4106
+ id : true,
4107
+ name : true,
4108
+ username : true,
4109
+ phone : {
4110
+ id : true,
4111
+ user_id : true,
4112
+ name: true
4113
+ }
4114
+ },
4115
+ where : {
4116
+ id: 1
4117
+ },
4118
+ relations: {
4119
+ phone: {
4120
+ user : true
4121
+ }
4122
+ }
4123
+ });
4124
+
4125
+ const phoneRepository = Repository(Phone)
4126
+
4127
+ const phoneBelongUser = await phoneRepository.findOne({
4128
+ select : '*',
4129
+ where : {
4130
+ id: 1
4131
+ },
4132
+ relations : {
4133
+ user : true
4134
+ }
4135
+ })
4136
+
4137
+ ```
4138
+
4139
+ ## Queue
4140
+ A lightweight, high-performance job queue built for ORM-based systems,
4141
+ designed to run on top of database layers with support for concurrency,
4142
+ retries, priorities, and job inspection.
4143
+
4144
+ - Concurrency Each worker can process multiple jobs at the same time.
4145
+
4146
+ - Priority Higher priority jobs are executed first.
4147
+
4148
+ - Retry Failed jobs are automatically retried up to maxAttempts.
4149
+
4150
+ - Delay Jobs can be scheduled for future execution using delayMs.
4151
+
4152
+ - Idle / Wake Workers automatically go idle when no jobs are available, and wake up instantly when new jobs arrive.
4153
+
4154
+ ```js
4155
+ import { Queue, Job } from 'tspace-mysql';
4156
+
4157
+ const fakeSendEmail = async (job: Job) => {
4158
+ if(Math.random() < 0.5) throw new Error(`Failed job ${job.id}`)
4159
+ await new Promise<void>((ok) => setTimeout(ok, 2000));
4160
+ return `Send email Completed job ${job.id}`;
4161
+ }
4162
+
4163
+ // start the Queue
4164
+ await Queue.start({
4165
+ inspect : true,
4166
+ flush : false, // flush = true -> remove all jobs
4167
+ hostname: 'pod1'
4168
+ });
4169
+
4170
+ const worker = 20;
4171
+
4172
+ // register process send email 20
4173
+ for(let i = 1; i <= worker; i++) {
4174
+
4175
+ Queue.process(`send-email-(${i})`, async(job) => {
4176
+ return await fakeSendEmail(job)
4177
+ } , { concurrency : 10 }) // 1 process / 10 concurrency
4178
+
4179
+ }
4180
+
4181
+ // add jobs 10_000 records
4182
+ for(let j = 1; j <= worker * 500; j++) {
4183
+
4184
+ const i = Math.floor((Math.random() * worker) + 1);
4185
+
4186
+ Queue.add(`send-email-(${i})`, {
4187
+ email: `John-${i}@gmail.com`,
4188
+ name: `John-${i}`
4189
+ }, {
4190
+ delayMs : 1000 * Math.random() * 10,
4191
+ priority : i % 2 ? 9999 + Math.floor((Math.random() * 9999) + 1) : 0,
4192
+ maxAttempts : 3,
4193
+ metadata : {
4194
+ userId : j,
4195
+ name : `John-${i}`
4196
+ }
4197
+ })
4198
+ }
4199
+
4200
+ // for view stats Jobs
4201
+ // await Queue.getJobOverallStats()
4202
+ // await Queue.getJobStats()
4203
+
4204
+ // if you want to end the Queue
4205
+ // await Queue.end()
4206
+
4207
+ ```
4208
+
4209
+ ## View
4210
+
4211
+ Your database schema can also use views. These views are represented by classes that behave similarly to models,
4212
+ but they are based on stored SQL queries instead of actual tables.
4213
+ Let's look at a basic view class example:
4214
+ ```js
4215
+
4216
+ import { type T, Blueprint, Model , View , Meta } from 'tspace-mysql'
4217
+
4218
+ const schemaUser = {
4219
+ id: Blueprint.int().notNull().primary().autoIncrement(),
4220
+ uuid: Blueprint.varchar(50).null().index(),
4221
+ name: Blueprint.varchar(191).notNull(),
4222
+ email: Blueprint.varchar(191).notNull()
4223
+ }
4224
+
4225
+ type TUser = T.Schema<typeof schemaUser>
4226
+
4227
+ class User extends Model<TUser> {
4228
+ protected boot(): void {
4229
+ this.useSchema(schemaUser)
4230
+ }
4231
+ }
4232
+
4233
+ const schemaPost = {
4234
+ id: Blueprint.int().notNull().primary().autoIncrement(),
4235
+ uuid: Blueprint.varchar(50).null().index(),
4236
+ user_id :Blueprint.int().notnull(),
4237
+ title: Blueprint.varchar(191).notNull(),
4238
+ content: Blueprint.varchar(191).notNull()
4239
+ }
4240
+
4241
+ type TPost = T.Schema<typeof schemaPost>
4242
+
4243
+ class Post extends Model<TPost> {
4244
+ protected boot(): void {
4245
+ this.useSchema(schemaPost)
4246
+ }
4247
+ }
4248
+
4249
+ const schemaUserPostCountView = {
4250
+ id :Blueprint.int().notNull().primary().autoIncrement(),
4251
+ user_id :Blueprint.int().notnull(),
4252
+ name :Blueprint.varchar(255).null(),
4253
+ post_count : Blueprint.int().notnull()
4254
+ }
4255
+
4256
+ type TSUserPostCountView = T.Schema<typeof schemaUserPostCountView>
4257
+ type TRUserPostCountView = T.Relation<{
4258
+ user: User
4259
+ }>
4260
+
4261
+ class UserPostCountView extends View<TSUserPostCountView,TRUserPostCountView> {
4262
+
4263
+ protected boot(): void {
4264
+ this.useSchema(schemaUserPostCountView)
4265
+ const metaUser = Meta(User)
4266
+ const metaPost = Meta(Post)
4267
+
4268
+ this.createView({
4269
+ synchronize: true,
4270
+ expression : new User()
4271
+ .selectRaw(`ROW_NUMBER() OVER (ORDER BY ${metaUser.columnRef('id')}) AS id`)
4272
+ .selectRaw(`${metaUser.columnRef('id')} AS user_id`)
4273
+ .selectRaw(metaUser.columnRef('name'))
4274
+ .select(metaUser.columnRef('email'))
4275
+ .selectRaw(`COUNT(${metaPost.columnRef('id')}) AS post_count`)
4276
+ .leftJoin(metaUser.columnRef('id'),metaPost.columnRef('user_id'))
4277
+ .groupBy(metaUser.columnRef('id'))
4278
+ .groupBy(metaUser.columnRef('name'))
4279
+ .toString()
4280
+
4281
+ // Look like this
4282
+ // expression :
4283
+ // SELECT
4284
+ // ROW_NUMBER() OVER (ORDER BY `users`.`id`) AS id,
4285
+ // `users`.`id` AS user_id, `users`.`name`, `users`.`email`,
4286
+ // COUNT(`posts`.`id`) AS post_count
4287
+ // FROM `users`
4288
+ // LEFT JOIN `posts` ON `users`.`id` = `posts`.`user_id`
4289
+ // GROUP BY `users`.`id`, `users`.`name`
4290
+ })
4291
+
4292
+ this.belongsTo({ name : 'user' , model : User })
4293
+ }
4294
+ }
4295
+
4296
+ new UserPostCountView()
4297
+ .with('user')
4298
+ .get()
4299
+ .then( v=> {
4300
+ console.log(v)
4301
+ })
4302
+
4303
+ ```
4304
+
4305
+ ## Stored Procedure
4306
+ StoredProcedure is a predefined set of SQL statements stored in the database that you can call (execute) by name.
4307
+ ```js
4308
+
4309
+ import { StoredProcedure } from 'tspace-mysql'
4310
+
4311
+ type T = {
4312
+ AddUser: {
4313
+ params: {
4314
+ name : string;
4315
+ email: string;
4316
+ } | [string,string];
4317
+ result: {
4318
+ fieldCount: number;
4319
+ affectedRows: number;
4320
+ insertId: number;
4321
+ info: string;
4322
+ serverStatus: number;
4323
+ warningStatus: number;
4324
+ changedRows: number;
4325
+ }
4326
+ };
4327
+ GetUser: {
4328
+ params: [number];
4329
+ result: any[]
4330
+ },
4331
+ GetUsers: {
4332
+ params: [];
4333
+ result: any[]
4334
+ }
4335
+ };
4336
+
4337
+ class MyStoreProcedure extends StoredProcedure<T> {
4338
+ protected boot(): void {
4339
+
4340
+ this.createProcedure({
4341
+ name: 'AddUser',
4342
+ expression: `
4343
+ CREATE PROCEDURE AddUser(IN name VARCHAR(255), IN email VARCHAR(255))
4344
+ BEGIN
4345
+ INSERT INTO users (name, email) VALUES (name, email);
4346
+ END;
4347
+ `,
4348
+ synchronize: true
4349
+ });
4350
+
4351
+ this.createProcedure({
4352
+ name: 'GetUsers',
4353
+ expression: `
4354
+ CREATE PROCEDURE GetUsers()
4355
+ BEGIN
4356
+ SELECT * FROM users LIMIT 5;
4357
+ END;
4358
+ `,
4359
+ synchronize: true
4360
+ });
4361
+
4362
+ this.createProcedure({
4363
+ name: 'GetUser',
4364
+ expression: `
4365
+ CREATE PROCEDURE GetUser(IN userId INT)
4366
+ BEGIN
4367
+ SELECT * FROM users WHERE id = userId LIMIT 1;
4368
+ END;
4369
+ `,
4370
+ synchronize: true
4371
+ })
4372
+ }
4373
+ }
4374
+
4375
+ const storeProcedure = new MyStoreProcedure()
4376
+
4377
+ storeProcedure.call('AddUser', { name : 'tspace-mysql' , email : 'tspace-mysql@example.com'})
4378
+ .then(r => console.log(r))
4379
+ .catch(e => console.log(e))
4380
+
4381
+ storeProcedure.call('GetUser',[1])
4382
+ .then(r => console.log(r))
4383
+ .catch(e => console.log(e))
4384
+
4385
+ storeProcedure.call('GetUsers',[])
4386
+ .then(r => console.log(r))
4387
+ .catch(e => console.log(e))
4388
+
4389
+ ```
4390
+ ## Blueprint
4391
+
4392
+ Blueprint is a tool used for defining database schemas programmatically.
4393
+ It allows developers to describe the structure of their database tables using a simple and intuitive syntax rather than writing SQL queries directly., you may use the:
4394
+
4395
+ ```js
4396
+ import { Schema , Blueprint , DB } from 'tspace-mysql'
4397
+ (async () => {
4398
+ await new Schema().table('users', {
4399
+ id : Blueprint.int().notNull().primary().autoIncrement(),
4400
+ // or id : Blueprint.serial().primary(),
4401
+ uuid : Blueprint.varchar(120).null()
4402
+ name : Blueprint.varchar(120).default('name'),
4403
+ email : Blueprint.varchar(255).unique().notNull(),
4404
+ email_verify : Blueprint.tinyInt(),
4405
+ password : Blueprint.varchar(255),
4406
+ json : Blueprint.json(),
4407
+ created_at : Blueprint.null().timestamp(),
4408
+ updated_at : Blueprint.null().timestamp(),
4409
+ deleted_at : Blueprint.null().timestamp()
4410
+ })
4411
+ /**
4412
+ *
4413
+ * @Faker fake data 5 raw
4414
+ * await new DB().table('users').faker(5)
4415
+ */
4416
+ })()
4417
+
4418
+ /**
4419
+ * To add types of the schema to the database
4420
+ * @Types
4421
+ *
4422
+ */
4423
+ int (number)
4424
+ tinyInt (number)
4425
+ bigInt (number)
4426
+ double ()
4427
+ float ()
4428
+ json ()
4429
+ varchar (number)
4430
+ char (number)
4431
+ longText()
4432
+ mediumText()
4433
+ tinyText()
4434
+ text()
4435
+ enum(...n)
4436
+ date()
4437
+ dateTime()
4438
+ timestamp ()
4439
+
4440
+ /**
4441
+ * To add attributes of the schema to the database
4442
+ * @Attrbuites
4443
+ *
4444
+ */
4445
+ unsigned()
4446
+ unique()
4447
+ null()
4448
+ notNull()
4449
+ primary()
4450
+ default(string)
4451
+ defaultTimestamp()
4452
+ autoIncrement()
4453
+
4454
+ /**
4455
+ * To add a foreign key to the column
4456
+ * @ForeginKey
4457
+ */
4458
+ foreign({ references : ${COLUMN} , on : ${TABLE-NAME OR MODEL CLASSES} })
4459
+
4460
+ /**
4461
+ * To add a index key to the column
4462
+ * @indexKey
4463
+ */
4464
+ index()
4465
+ ```
4466
+
4467
+ ## Cli
4468
+
4469
+ To get started, let's install tspace-mysql
4470
+ you may use a basic cli :
4471
+
4472
+ ```sh
4473
+ npm install tspace-mysql -g
4474
+
4475
+ ```
4476
+
4477
+ ## Make Model
4478
+
4479
+ The command will be placed Model in the specific directory.
4480
+
4481
+ ```sh
4482
+
4483
+ /**
4484
+ *
4485
+ * @make Model
4486
+ * @options
4487
+ * @arg --m => created scheme table for migrate. short cut migration table like Make Migration
4488
+ * @arg --dir=directory => created model in directory. default root directory
4489
+ * @arg --type=js // extension js. default ts
4490
+ */
4491
+ tspace-mysql make:model <model name> --m --dir=.... --type=....
4492
+
4493
+ tspace-mysql make:model User --m --dir=app/Models
4494
+ /**
4495
+ *
4496
+ * @Ex directory
4497
+ */
4498
+ - node_modules
4499
+ - app
4500
+ - Models
4501
+ User.ts
4502
+ ```
4503
+
4504
+ ## Make Migration
4505
+
4506
+ The command will be placed Migration in the specific directory.
4507
+
4508
+ ```sh
4509
+ /**
4510
+ *
4511
+ * @make Migration Table
4512
+ * @options
4513
+ * @arg --dir=directory => created scheme table in directory. default root directory
4514
+ * @arg --type=js // extension js default ts
4515
+ */
4516
+ tspace-mysql make:migration <table name> --type=... --dir=....
4517
+
4518
+ tspace-mysql make:migration users --dir=app/Models/Migrations
4519
+ /**
4520
+ *
4521
+ * @Ex directory
4522
+ */
4523
+ - node_modules
4524
+ - app
4525
+ - Models
4526
+ - Migrations
4527
+ create_users_table.ts
4528
+ User.ts
4529
+ ```
4530
+
4531
+ ## Migrate
4532
+
4533
+ ```sh
4534
+ /**
4535
+ *
4536
+ * @run Migrate table
4537
+ * @options
4538
+ * @arg --dir=directory => find migrate in directory. default find in root folder
4539
+ * @arg --type=js // extension js default ts
4540
+ */
4541
+ tspace-mysql migrate <folder> --type=<type file js or ts> --dir=<directory for migrate>
4542
+
4543
+ tspace-mysql migrate --dir=app/Models/Migrations --type=js
4544
+
4545
+ /**
4546
+ *
4547
+ * @Ex directory
4548
+ */
4549
+ - node_modules
4550
+ - app
4551
+ - Models
4552
+ - Migrations
4553
+ create_users_table.ts
4554
+ create_posts_table.ts
4555
+ User.ts
4556
+ Post.ts
4557
+ // => migrate all schemas in folder <Migrations>. created into database
4558
+ ```
4559
+
4560
+ # Query
4561
+
4562
+ The command will execute a query.
4563
+
4564
+ ```sh
4565
+ tspace-mysql query "SELECT * FROM users"
4566
+
4567
+ ```
4568
+
4569
+ # Dump
4570
+
4571
+ The command will dump the database or table into a file.
4572
+
4573
+ ```sh
4574
+ tspace-mysql dump:db --dir=<folder for dump> --values // backup with values in the tables
4575
+
4576
+ tspace-mysql dump:table "table_name" --dir=<folder for dump> --values // backup with values in the table
4577
+
4578
+ ```
4579
+
4580
+ # Generate Models
4581
+
4582
+ The command will generate models from tables in the database.
4583
+
4584
+ ```sh
4585
+ tspace-mysql generate:models --dir=<folder for creating>
4586
+
4587
+ tspace-mysql generate:models --dir=app/Models --env=development --decorators
4588
+
4589
+ ```
4590
+
4591
+ # Migration Models
4592
+
4593
+ The command will generate migrations based on the schema in your models to a .sql file,
4594
+ can also push the migration files to the database.
4595
+
4596
+ ```sh
4597
+ /**
4598
+ *
4599
+ * @arg --push will push the migration files to the database
4600
+ * @arg --generate will generate the migration files
4601
+ */
4602
+ tspace-mysql migrations:models --dir=<path-to-migration> --models=<path to your models> --generate
4603
+ tspace-mysql migrations:models --dir=<path-to-migration> --push
4604
+
4605
+ tspace-mysql migrations:models --models=src/app/models --dir=migrations --generate
4606
+ tspace-mysql migrations:models --dir=migrations --push
4607
+
4608
+ ```
4609
+
4610
+ # Migration DB
4611
+
4612
+ The command will generate migrations based on the schema in your database to a .sql file,
4613
+ can also push the migration files to the database.
4614
+
4615
+ ```sh
4616
+ /**
4617
+ *
4618
+ * @arg --push will push the migration files to the database
4619
+ * @arg --generate will generate the migration files
4620
+ */
4621
+ tspace-mysql migrations:db --dir=<path-to-migration> --generate --env=<YOUR_ENV> -filename=<YOUR_FILENAME>
4622
+ tspace-mysql migrations:db --dir=<path-to-migration> --push
4623
+
4624
+ tspace-mysql migrations:db --dir=migrations --generate --filename=dump.sql --env=development
4625
+ tspace-mysql migrations:db --dir=migrations --push --filename=dump.sql --env=development
57
4626
 
58
- - [Getting Started](https://thanathip41.github.io/tspace-mysql/#/)
59
- - [Install](https://thanathip41.github.io/tspace-mysql/#/?id=install)
60
- - [Configuration](https://thanathip41.github.io/tspace-mysql/#/?id=configuration)
61
- - [Integrations](https://thanathip41.github.io/tspace-mysql/#/integrations)
62
- - [Http](https://thanathip41.github.io/tspace-mysql/#/integrations?id=http)
63
- - [Expressjs](https://thanathip41.github.io/tspace-mysql/#/integrations)
64
- - [Fastify](https://thanathip41.github.io/tspace-mysql/#/integrations?id=expressjs)
65
- - [Nestjs](https://thanathip41.github.io/tspace-mysql/#/integrations?id=nestjs)
66
- - [Bun](https://thanathip41.github.io/tspace-mysql/#/integrations?id=bun-native)
67
- - [Elysia](https://thanathip41.github.io/tspace-mysql/#/integrations?id=elysia)
68
- - [Hono](https://thanathip41.github.io/tspace-mysql/#/integrations?id=hono)
69
- - [SQL Like](https://thanathip41.github.io/tspace-mysql/#/sql-like)
70
- - [Select Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=select-statements)
71
- - [Insert Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=insert-statements)
72
- - [Update Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=update-statements)
73
- - [Delete Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=delete-statements)
74
- - [Query Builder](https://thanathip41.github.io/tspace-mysql/#/query-builder)
75
- - [Table Name & Alias Name](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=table-name--alias-name)
76
- - [Returning Results](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=returning-results)
77
- - [Query Statement](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=query-statements)
78
- - [Select Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=select-statements)
79
- - [Insert Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=insert-statements)
80
- - [Update Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=update-statements)
81
- - [Delete Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=delete-statements)
82
- - [Raw Expressions](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=raw-expressions)
83
- - [Ordering, Grouping, Limit and Offset](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=ordering-grouping-limit-and-offset)
84
- - [Ordering](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=ordering)
85
- - [Grouping](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=grouping)
86
- - [Limit and Offset](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=limit-and-offset)
87
- - [Joins](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=joins)
88
- - [Inner Join Clause](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=inner-join-clause)
89
- - [Left Join, Right Join Clause](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=left-join-right-join-clause)
90
- - [Cross Join Clause](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=cross-join-clause)
91
- - [Basic Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=basic-where-clauses)
92
- - [Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-clauses)
93
- - [Or Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=or-where-clauses)
94
- - [Where cases](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-cases)
95
- - [Where Object Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-object-clauses)
96
- - [JSON Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=json-where-clauses)
97
- - [Additional Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=additional-where-clauses)
98
- - [Logical Grouping](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=logical-grouping)
99
- - [Advanced Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=advanced-where-clauses)
100
- - [Where Exists Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-exists-clauses)
101
- - [Subquery Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=subquery-where-clauses)
102
- - [Conditional Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=conditional-where-clauses)
103
- - [GetGroupBy](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=getgroupby)
104
- - [Paginating](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=paginating)
105
- - [Hook Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=hook-statements)
106
- - [Faker Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=faker-statements)
107
- - [Unset Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=unset-statements)
108
- - [Common Table Expressions](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=common-table-expressions)
109
- - [Union](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=union)
110
- - [More Methods](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=more-methods)
111
- - [Database Transactions](https://thanathip41.github.io/tspace-mysql/#/database-transactions)
112
- - [Race Condition](https://thanathip41.github.io/tspace-mysql/#/race-condition)
113
- - [Connection](https://thanathip41.github.io/tspace-mysql/#/connection)
114
- - [Backup](https://thanathip41.github.io/tspace-mysql/#/backup)
115
- - [Injection](https://thanathip41.github.io/tspace-mysql/#/injection)
116
- - [Model](https://thanathip41.github.io/tspace-mysql/#/model)
117
- - [Basic Model Setup](https://thanathip41.github.io/tspace-mysql/#/model?id=basic-model-setup)
118
- - [Table Name](https://thanathip41.github.io/tspace-mysql/#/model?id=table-name)
119
- - [Pattern](https://thanathip41.github.io/tspace-mysql/#/model?id=pattern)
120
- - [UUID](https://thanathip41.github.io/tspace-mysql/#/model?id=uuid)
121
- - [Timestamp](https://thanathip41.github.io/tspace-mysql/#/model?id=timestamp)
122
- - [Debug](https://thanathip41.github.io/tspace-mysql/#/model?id=debug)
123
- - [Observer](https://thanathip41.github.io/tspace-mysql/#/model?id=observer)
124
- - [Logger](https://thanathip41.github.io/tspace-mysql/#/model?id=logger)
125
- - [Hooks](https://thanathip41.github.io/tspace-mysql/#/model?id=hooks)
126
- - [Global Scope](https://thanathip41.github.io/tspace-mysql/#/model?id=global-scope)
127
- - [Schema](https://thanathip41.github.io/tspace-mysql/#/model?id=schema)
128
- - [Schema Model](https://thanathip41.github.io/tspace-mysql/#/model?id=schema-model)
129
- - [Virtual Column](https://thanathip41.github.io/tspace-mysql/#/model?id=virtual-column)
130
- - [Validation](https://thanathip41.github.io/tspace-mysql/#/model?id=validation)
131
- - [Sync](https://thanathip41.github.io/tspace-mysql/#/model?id=sync)
132
- - [SoftDelete](https://thanathip41.github.io/tspace-mysql/#/model?id=softdelete)
133
- - [Joins Model](https://thanathip41.github.io/tspace-mysql/#/model?id=joins-model)
134
- - [Inner Join Model Clause](https://thanathip41.github.io/tspace-mysql/#/model?id=inner-join-model-clause)
135
- - [Left Join , Right Join Model Clause](https://thanathip41.github.io/tspace-mysql/#/model?id=left-join-right-join-model-clause)
136
- - [Cross Join Model Clause](https://thanathip41.github.io/tspace-mysql/#/model?id=cross-join-model-clause)
137
- - [Relationships](https://thanathip41.github.io/tspace-mysql/#/model?id=relationships)
138
- - [One To One](https://thanathip41.github.io/tspace-mysql/#/model?id=one-to-one)
139
- - [One To Many](https://thanathip41.github.io/tspace-mysql/#/model?id=one-to-many)
140
- - [Belongs To](https://thanathip41.github.io/tspace-mysql/#/model?id=belongs-to)
141
- - [Many To Many](https://thanathip41.github.io/tspace-mysql/#/model?id=many-to-many)
142
- - [Relation](https://thanathip41.github.io/tspace-mysql/#/model?id=relation)
143
- - [Deeply Nested Relations](https://thanathip41.github.io/tspace-mysql/#/model?id=deeply-nested-relations)
144
- - [Relation Exists](https://thanathip41.github.io/tspace-mysql/#/model?id=relation-exists)
145
- - [Relation Count](https://thanathip41.github.io/tspace-mysql/#/model?id=relation-count)
146
- - [Relation Trashed](https://thanathip41.github.io/tspace-mysql/#/model?id=relation-trashed)
147
- - [Built in Relation Functions](https://thanathip41.github.io/tspace-mysql/#/model?id=built-in-relation-functions)
148
- - [Cache](https://thanathip41.github.io/tspace-mysql/#/model?id=cache)
149
- - [Decorator](https://thanathip41.github.io/tspace-mysql/#/model?id=decorator)
150
- - [Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=type-safety)
151
- - [Select Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=select-type-safety-type-safety)
152
- - [OrderBy Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=order-by-type-safety)
153
- - [GroupBy Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=group-by-type-safety)
154
- - [Where Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=where-type-safety)
155
- - [Insert Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=insert-type-safety)
156
- - [Update Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=update-type-safety)
157
- - [Delete Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=delete-type-safety)
158
- - [Relationships Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=relationships-type-safety)
159
- - [Results Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=results-type-safety)
160
- - [Metadata](https://thanathip41.github.io/tspace-mysql/#/model?id=metadata)
161
- - [Audit](https://thanathip41.github.io/tspace-mysql/#/model?id=audit)
162
- - [Repository](https://thanathip41.github.io/tspace-mysql/#/repository)
163
- - [Select Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=select-statements)
164
- - [Insert Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=insert-statements)
165
- - [Update Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=update-statements)
166
- - [Delete Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=delete-statements)
167
- - [Transactions](https://thanathip41.github.io/tspace-mysql/#/repository?id=transactions)
168
- - [Relations](https://thanathip41.github.io/tspace-mysql/#/repository?id=relations)
169
- - [View](https://thanathip41.github.io/tspace-mysql/#/view)
170
- - [Stored Procedure](https://thanathip41.github.io/tspace-mysql/#/stored-procedure)
171
- - [Blueprint](https://thanathip41.github.io/tspace-mysql/#/blueprint)
172
- - [Cli](https://thanathip41.github.io/tspace-mysql/#/cli)
173
- - [Make Model](https://thanathip41.github.io/tspace-mysql/#/cli?id=make-model)
174
- - [Make Migration](https://thanathip41.github.io/tspace-mysql/#/cli?id=make-migration)
175
- - [Migrate](https://thanathip41.github.io/tspace-mysql/#/cli?id=migrate)
176
- - [Query](https://thanathip41.github.io/tspace-mysql/#/cli?id=query)
177
- - [Dump](https://thanathip41.github.io/tspace-mysql/#/cli?id=dump)
178
- - [Generate Models](https://thanathip41.github.io/tspace-mysql/#/cli?id=generate-models)
179
- - [Migration Models](https://thanathip41.github.io/tspace-mysql/#/cli?id=migration-models)
180
- - [Migration DB](https://thanathip41.github.io/tspace-mysql/#/cli?id=migration-db)
4627
+ ```