tspace-mysql 1.8.9 → 1.9.0-beta.2

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