@bunnykit/orm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +904 -0
- package/dist/bin/bunny.d.ts +2 -0
- package/dist/bin/bunny.js +108 -0
- package/dist/src/connection/Connection.d.ts +13 -0
- package/dist/src/connection/Connection.js +49 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +18 -0
- package/dist/src/migration/Migration.d.ts +4 -0
- package/dist/src/migration/Migration.js +2 -0
- package/dist/src/migration/MigrationCreator.d.ts +5 -0
- package/dist/src/migration/MigrationCreator.js +39 -0
- package/dist/src/migration/Migrator.d.ts +21 -0
- package/dist/src/migration/Migrator.js +137 -0
- package/dist/src/model/BelongsToMany.d.ts +27 -0
- package/dist/src/model/BelongsToMany.js +118 -0
- package/dist/src/model/Model.d.ts +166 -0
- package/dist/src/model/Model.js +763 -0
- package/dist/src/model/MorphMap.d.ts +7 -0
- package/dist/src/model/MorphMap.js +12 -0
- package/dist/src/model/MorphRelations.d.ts +81 -0
- package/dist/src/model/MorphRelations.js +296 -0
- package/dist/src/model/Observer.d.ts +19 -0
- package/dist/src/model/Observer.js +21 -0
- package/dist/src/query/Builder.d.ts +106 -0
- package/dist/src/query/Builder.js +466 -0
- package/dist/src/schema/Blueprint.d.ts +66 -0
- package/dist/src/schema/Blueprint.js +200 -0
- package/dist/src/schema/Schema.d.ts +16 -0
- package/dist/src/schema/Schema.js +135 -0
- package/dist/src/schema/grammars/Grammar.d.ts +26 -0
- package/dist/src/schema/grammars/Grammar.js +95 -0
- package/dist/src/schema/grammars/MySqlGrammar.d.ts +18 -0
- package/dist/src/schema/grammars/MySqlGrammar.js +96 -0
- package/dist/src/schema/grammars/PostgresGrammar.d.ts +16 -0
- package/dist/src/schema/grammars/PostgresGrammar.js +88 -0
- package/dist/src/schema/grammars/SQLiteGrammar.d.ts +16 -0
- package/dist/src/schema/grammars/SQLiteGrammar.js +108 -0
- package/dist/src/typegen/TypeGenerator.d.ts +29 -0
- package/dist/src/typegen/TypeGenerator.js +171 -0
- package/dist/src/typegen/TypeMapper.d.ts +4 -0
- package/dist/src/typegen/TypeMapper.js +27 -0
- package/dist/src/types/index.d.ts +53 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.js +6 -0
- package/package.json +62 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
export class Builder {
|
|
2
|
+
connection;
|
|
3
|
+
tableName;
|
|
4
|
+
columns = ["*"];
|
|
5
|
+
wheres = [];
|
|
6
|
+
orders = [];
|
|
7
|
+
groups = [];
|
|
8
|
+
havings = [];
|
|
9
|
+
limitValue;
|
|
10
|
+
offsetValue;
|
|
11
|
+
joins = [];
|
|
12
|
+
distinctFlag = false;
|
|
13
|
+
model;
|
|
14
|
+
eagerLoads = [];
|
|
15
|
+
constructor(connection, table) {
|
|
16
|
+
this.connection = connection;
|
|
17
|
+
this.tableName = table;
|
|
18
|
+
}
|
|
19
|
+
setModel(model) {
|
|
20
|
+
this.model = model;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
table(table) {
|
|
24
|
+
this.tableName = table;
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
select(...columns) {
|
|
28
|
+
this.columns = columns;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
distinct() {
|
|
32
|
+
this.distinctFlag = true;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
where(column, operator, value, boolean = "and", scope) {
|
|
36
|
+
if (typeof column === "object" && column !== null) {
|
|
37
|
+
for (const [key, val] of Object.entries(column)) {
|
|
38
|
+
this.where(key, "=", val, boolean, scope);
|
|
39
|
+
}
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
if (value === undefined) {
|
|
43
|
+
value = operator;
|
|
44
|
+
operator = "=";
|
|
45
|
+
}
|
|
46
|
+
this.wheres.push({ type: "basic", column, operator, value, boolean, scope });
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
orWhere(column, operator, value) {
|
|
50
|
+
return this.where(column, operator, value, "or");
|
|
51
|
+
}
|
|
52
|
+
whereIn(column, values, boolean = "and", scope) {
|
|
53
|
+
this.wheres.push({ type: "in", column, value: values, boolean, scope });
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
whereNotIn(column, values, boolean = "and", scope) {
|
|
57
|
+
this.wheres.push({ type: "in", column, value: values, boolean, operator: "NOT IN", scope });
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
whereNull(column, boolean = "and", scope) {
|
|
61
|
+
this.wheres.push({ type: "null", column, boolean, scope });
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
whereNotNull(column, boolean = "and", scope) {
|
|
65
|
+
this.wheres.push({ type: "null", column, boolean, operator: "NOT NULL", scope });
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
whereBetween(column, values, boolean = "and", scope) {
|
|
69
|
+
this.wheres.push({ type: "between", column, value: values, boolean, scope });
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
whereNotBetween(column, values, boolean = "and", scope) {
|
|
73
|
+
this.wheres.push({ type: "between", column, value: values, boolean, operator: "NOT BETWEEN", scope });
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
whereRaw(sql, boolean = "and", scope) {
|
|
77
|
+
this.wheres.push({ type: "raw", column: sql, boolean, scope });
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
whereColumn(first, operator, second, boolean = "and") {
|
|
81
|
+
this.wheres.push({ type: "column", column: first, operator, value: second, boolean });
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
whereExists(sql, boolean = "and", not = false) {
|
|
85
|
+
this.wheres.push({ type: "exists", column: sql, boolean, operator: not ? "NOT EXISTS" : "EXISTS" });
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
orderBy(column, direction = "asc") {
|
|
89
|
+
this.orders.push({ column, direction });
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
groupBy(...columns) {
|
|
93
|
+
this.groups.push(...columns);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
having(column, operator, value) {
|
|
97
|
+
this.havings.push(`${this.wrap(column)} ${operator} ${this.escape(value)}`);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
limit(count) {
|
|
101
|
+
this.limitValue = count;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
offset(count) {
|
|
105
|
+
this.offsetValue = count;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
forPage(page, perPage = 15) {
|
|
109
|
+
return this.offset((page - 1) * perPage).limit(perPage);
|
|
110
|
+
}
|
|
111
|
+
join(table, first, operator, second, type = "INNER") {
|
|
112
|
+
const joinSql = `${type} JOIN ${this.wrap(table)} ON ${this.wrap(first)} ${operator} ${this.wrap(second)}`;
|
|
113
|
+
this.joins.push(joinSql);
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
leftJoin(table, first, operator, second) {
|
|
117
|
+
return this.join(table, first, operator, second, "LEFT");
|
|
118
|
+
}
|
|
119
|
+
rightJoin(table, first, operator, second) {
|
|
120
|
+
return this.join(table, first, operator, second, "RIGHT");
|
|
121
|
+
}
|
|
122
|
+
with(...relations) {
|
|
123
|
+
this.eagerLoads.push(...relations);
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
withoutGlobalScope(scope) {
|
|
127
|
+
this.wheres = this.wheres.filter((where) => where.scope !== scope);
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
withoutGlobalScopes() {
|
|
131
|
+
this.wheres = this.wheres.filter((where) => !where.scope);
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
withTrashed() {
|
|
135
|
+
return this.withoutGlobalScope("softDeletes");
|
|
136
|
+
}
|
|
137
|
+
onlyTrashed() {
|
|
138
|
+
this.withTrashed();
|
|
139
|
+
const model = this.model;
|
|
140
|
+
if (model?.softDeletes) {
|
|
141
|
+
this.whereNotNull(model.getQualifiedDeletedAtColumn());
|
|
142
|
+
}
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
scope(name, ...args) {
|
|
146
|
+
if (!this.model) {
|
|
147
|
+
throw new Error(`Cannot apply scope "${name}" without a model`);
|
|
148
|
+
}
|
|
149
|
+
const method = `scope${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
150
|
+
const scope = this.model[method] || this.model.scopes?.[name];
|
|
151
|
+
if (typeof scope !== "function") {
|
|
152
|
+
throw new Error(`Scope "${name}" is not defined on model ${this.model.name}`);
|
|
153
|
+
}
|
|
154
|
+
const result = scope.call(this.model, this, ...args);
|
|
155
|
+
return (result || this);
|
|
156
|
+
}
|
|
157
|
+
has(relationName, operator = ">=", count = 1, callback) {
|
|
158
|
+
if (typeof operator === "function") {
|
|
159
|
+
callback = operator;
|
|
160
|
+
operator = ">=";
|
|
161
|
+
count = 1;
|
|
162
|
+
}
|
|
163
|
+
const relation = this.getModelRelation(relationName);
|
|
164
|
+
if (operator === ">=" && count === 1) {
|
|
165
|
+
return this.whereExists(relation.getRelationExistenceSql(this, callback));
|
|
166
|
+
}
|
|
167
|
+
if ((operator === "<" || operator === "=") && count <= 0) {
|
|
168
|
+
return this.whereExists(relation.getRelationExistenceSql(this, callback), "and", true);
|
|
169
|
+
}
|
|
170
|
+
return this.whereRaw(`(${relation.getRelationCountSql(this, callback)}) ${operator} ${this.escape(count)}`);
|
|
171
|
+
}
|
|
172
|
+
orHas(relationName, operator = ">=", count = 1, callback) {
|
|
173
|
+
if (typeof operator === "function") {
|
|
174
|
+
callback = operator;
|
|
175
|
+
operator = ">=";
|
|
176
|
+
count = 1;
|
|
177
|
+
}
|
|
178
|
+
const relation = this.getModelRelation(relationName);
|
|
179
|
+
if (operator === ">=" && count === 1) {
|
|
180
|
+
return this.whereExists(relation.getRelationExistenceSql(this, callback), "or");
|
|
181
|
+
}
|
|
182
|
+
if ((operator === "<" || operator === "=") && count <= 0) {
|
|
183
|
+
return this.whereExists(relation.getRelationExistenceSql(this, callback), "or", true);
|
|
184
|
+
}
|
|
185
|
+
return this.whereRaw(`(${relation.getRelationCountSql(this, callback)}) ${operator} ${this.escape(count)}`, "or");
|
|
186
|
+
}
|
|
187
|
+
whereHas(relationName, callback, operator = ">=", count = 1) {
|
|
188
|
+
return this.has(relationName, operator, count, callback);
|
|
189
|
+
}
|
|
190
|
+
orWhereHas(relationName, callback, operator = ">=", count = 1) {
|
|
191
|
+
return this.orHas(relationName, operator, count, callback);
|
|
192
|
+
}
|
|
193
|
+
doesntHave(relationName, callback) {
|
|
194
|
+
return this.has(relationName, "<", 1, callback);
|
|
195
|
+
}
|
|
196
|
+
whereDoesntHave(relationName, callback) {
|
|
197
|
+
return this.doesntHave(relationName, callback);
|
|
198
|
+
}
|
|
199
|
+
withCount(relationName, alias) {
|
|
200
|
+
const relation = this.getModelRelation(relationName);
|
|
201
|
+
this.addSelect(`(${relation.getRelationCountSql(this)}) as ${alias || `${relationName}_count`}`);
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
withSum(relationName, column, alias) {
|
|
205
|
+
return this.withAggregate(relationName, column, "SUM", alias);
|
|
206
|
+
}
|
|
207
|
+
withAvg(relationName, column, alias) {
|
|
208
|
+
return this.withAggregate(relationName, column, "AVG", alias);
|
|
209
|
+
}
|
|
210
|
+
withMin(relationName, column, alias) {
|
|
211
|
+
return this.withAggregate(relationName, column, "MIN", alias);
|
|
212
|
+
}
|
|
213
|
+
withMax(relationName, column, alias) {
|
|
214
|
+
return this.withAggregate(relationName, column, "MAX", alias);
|
|
215
|
+
}
|
|
216
|
+
addSelect(...columns) {
|
|
217
|
+
if (this.columns.length === 1 && this.columns[0] === "*") {
|
|
218
|
+
this.columns = [`${this.tableName}.*`];
|
|
219
|
+
}
|
|
220
|
+
this.columns.push(...columns);
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
clone() {
|
|
224
|
+
const cloned = new Builder(this.connection, this.tableName);
|
|
225
|
+
cloned.columns = [...this.columns];
|
|
226
|
+
cloned.wheres = [...this.wheres];
|
|
227
|
+
cloned.orders = [...this.orders];
|
|
228
|
+
cloned.groups = [...this.groups];
|
|
229
|
+
cloned.havings = [...this.havings];
|
|
230
|
+
cloned.limitValue = this.limitValue;
|
|
231
|
+
cloned.offsetValue = this.offsetValue;
|
|
232
|
+
cloned.joins = [...this.joins];
|
|
233
|
+
cloned.distinctFlag = this.distinctFlag;
|
|
234
|
+
cloned.model = this.model;
|
|
235
|
+
cloned.eagerLoads = [...this.eagerLoads];
|
|
236
|
+
return cloned;
|
|
237
|
+
}
|
|
238
|
+
wrapColumn(value) {
|
|
239
|
+
return this.wrap(value);
|
|
240
|
+
}
|
|
241
|
+
escapeValue(value) {
|
|
242
|
+
return this.escape(value);
|
|
243
|
+
}
|
|
244
|
+
wrap(value) {
|
|
245
|
+
if (value.includes(" as ")) {
|
|
246
|
+
const [column, alias] = value.split(/\s+as\s+/i);
|
|
247
|
+
return `${this.wrap(column)} AS ${this.wrapValue(alias)}`;
|
|
248
|
+
}
|
|
249
|
+
if (value.includes(".")) {
|
|
250
|
+
return value.split(".").map((v) => this.wrapValue(v)).join(".");
|
|
251
|
+
}
|
|
252
|
+
return this.wrapValue(value);
|
|
253
|
+
}
|
|
254
|
+
wrapValue(value) {
|
|
255
|
+
const driver = this.connection.getDriverName();
|
|
256
|
+
const char = driver === "mysql" ? "`" : '"';
|
|
257
|
+
if (value === "*")
|
|
258
|
+
return value;
|
|
259
|
+
return `${char}${value}${char}`;
|
|
260
|
+
}
|
|
261
|
+
escape(value) {
|
|
262
|
+
if (value === null)
|
|
263
|
+
return "NULL";
|
|
264
|
+
if (typeof value === "boolean")
|
|
265
|
+
return value ? "1" : "0";
|
|
266
|
+
if (typeof value === "number")
|
|
267
|
+
return String(value);
|
|
268
|
+
if (typeof value === "string" && value.toUpperCase().includes("CURRENT_TIMESTAMP"))
|
|
269
|
+
return value;
|
|
270
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
271
|
+
}
|
|
272
|
+
compileWheres() {
|
|
273
|
+
if (this.wheres.length === 0)
|
|
274
|
+
return "";
|
|
275
|
+
const clauses = this.wheres.map((where, index) => {
|
|
276
|
+
const prefix = index === 0 ? "WHERE" : where.boolean.toUpperCase();
|
|
277
|
+
if (where.type === "basic") {
|
|
278
|
+
return `${prefix} ${this.wrap(where.column)} ${where.operator} ${this.escape(where.value)}`;
|
|
279
|
+
}
|
|
280
|
+
else if (where.type === "in") {
|
|
281
|
+
const op = where.operator === "NOT IN" ? "NOT IN" : "IN";
|
|
282
|
+
return `${prefix} ${this.wrap(where.column)} ${op} (${where.value.map((v) => this.escape(v)).join(", ")})`;
|
|
283
|
+
}
|
|
284
|
+
else if (where.type === "null") {
|
|
285
|
+
const op = where.operator === "NOT NULL" ? "IS NOT NULL" : "IS NULL";
|
|
286
|
+
return `${prefix} ${this.wrap(where.column)} ${op}`;
|
|
287
|
+
}
|
|
288
|
+
else if (where.type === "between") {
|
|
289
|
+
const op = where.operator === "NOT BETWEEN" ? "NOT BETWEEN" : "BETWEEN";
|
|
290
|
+
return `${prefix} ${this.wrap(where.column)} ${op} ${this.escape(where.value[0])} AND ${this.escape(where.value[1])}`;
|
|
291
|
+
}
|
|
292
|
+
else if (where.type === "raw") {
|
|
293
|
+
return `${prefix} ${where.column}`;
|
|
294
|
+
}
|
|
295
|
+
else if (where.type === "column") {
|
|
296
|
+
return `${prefix} ${this.wrap(where.column)} ${where.operator} ${this.wrap(where.value)}`;
|
|
297
|
+
}
|
|
298
|
+
else if (where.type === "exists") {
|
|
299
|
+
return `${prefix} ${where.operator} (${where.column})`;
|
|
300
|
+
}
|
|
301
|
+
return "";
|
|
302
|
+
});
|
|
303
|
+
return clauses.join(" ");
|
|
304
|
+
}
|
|
305
|
+
compileOrders() {
|
|
306
|
+
if (this.orders.length === 0)
|
|
307
|
+
return "";
|
|
308
|
+
return `ORDER BY ${this.orders.map((o) => `${this.wrap(o.column)} ${o.direction.toUpperCase()}`).join(", ")}`;
|
|
309
|
+
}
|
|
310
|
+
compileGroups() {
|
|
311
|
+
if (this.groups.length === 0)
|
|
312
|
+
return "";
|
|
313
|
+
return `GROUP BY ${this.groups.map((c) => this.wrap(c)).join(", ")}`;
|
|
314
|
+
}
|
|
315
|
+
compileHavings() {
|
|
316
|
+
if (this.havings.length === 0)
|
|
317
|
+
return "";
|
|
318
|
+
return `HAVING ${this.havings.join(" AND ")}`;
|
|
319
|
+
}
|
|
320
|
+
compileLimit() {
|
|
321
|
+
if (this.limitValue === undefined)
|
|
322
|
+
return "";
|
|
323
|
+
return `LIMIT ${this.limitValue}`;
|
|
324
|
+
}
|
|
325
|
+
compileOffset() {
|
|
326
|
+
if (this.offsetValue === undefined)
|
|
327
|
+
return "";
|
|
328
|
+
return `OFFSET ${this.offsetValue}`;
|
|
329
|
+
}
|
|
330
|
+
compileColumns() {
|
|
331
|
+
return this.columns.map((c) => (this.isRawColumn(c) ? c : this.wrap(c))).join(", ");
|
|
332
|
+
}
|
|
333
|
+
isRawColumn(column) {
|
|
334
|
+
return column.includes("(") || /\s+as\s+/i.test(column) || /^[0-9]+$/.test(column);
|
|
335
|
+
}
|
|
336
|
+
toSql() {
|
|
337
|
+
const distinct = this.distinctFlag ? "DISTINCT " : "";
|
|
338
|
+
let sql = `SELECT ${distinct}${this.compileColumns()} FROM ${this.wrap(this.tableName)}`;
|
|
339
|
+
if (this.joins.length > 0)
|
|
340
|
+
sql += " " + this.joins.join(" ");
|
|
341
|
+
sql += " " + this.compileWheres();
|
|
342
|
+
sql += " " + this.compileGroups();
|
|
343
|
+
sql += " " + this.compileHavings();
|
|
344
|
+
sql += " " + this.compileOrders();
|
|
345
|
+
sql += " " + this.compileLimit();
|
|
346
|
+
sql += " " + this.compileOffset();
|
|
347
|
+
return sql.replace(/\s+/g, " ").trim();
|
|
348
|
+
}
|
|
349
|
+
async get() {
|
|
350
|
+
const results = await this.connection.query(this.toSql());
|
|
351
|
+
const rows = Array.from(results);
|
|
352
|
+
if (this.model) {
|
|
353
|
+
const models = rows.map((row) => {
|
|
354
|
+
const instance = new this.model(row);
|
|
355
|
+
instance.$exists = true;
|
|
356
|
+
instance.$original = { ...row };
|
|
357
|
+
return instance;
|
|
358
|
+
});
|
|
359
|
+
if (this.eagerLoads.length > 0) {
|
|
360
|
+
await this.model.eagerLoadRelations(models, this.eagerLoads);
|
|
361
|
+
}
|
|
362
|
+
return models;
|
|
363
|
+
}
|
|
364
|
+
return rows;
|
|
365
|
+
}
|
|
366
|
+
async first() {
|
|
367
|
+
const results = await this.limit(1).get();
|
|
368
|
+
return results[0] || null;
|
|
369
|
+
}
|
|
370
|
+
async find(id, column = "id") {
|
|
371
|
+
return this.where(column, id).first();
|
|
372
|
+
}
|
|
373
|
+
async pluck(column) {
|
|
374
|
+
const results = await this.select(column).get();
|
|
375
|
+
return results.map((row) => row[column]);
|
|
376
|
+
}
|
|
377
|
+
async aggregate(sql, alias) {
|
|
378
|
+
const model = this.model;
|
|
379
|
+
this.model = undefined;
|
|
380
|
+
const result = await this.select(`${sql} as ${alias}`).first();
|
|
381
|
+
this.model = model;
|
|
382
|
+
return result ? result[alias] : null;
|
|
383
|
+
}
|
|
384
|
+
async count(column = "*") {
|
|
385
|
+
return Number(await this.aggregate(`COUNT(${column})`, "count") ?? 0);
|
|
386
|
+
}
|
|
387
|
+
async sum(column) {
|
|
388
|
+
return Number(await this.aggregate(`SUM(${column})`, "sum") ?? 0);
|
|
389
|
+
}
|
|
390
|
+
async avg(column) {
|
|
391
|
+
return Number(await this.aggregate(`AVG(${column})`, "avg") ?? 0);
|
|
392
|
+
}
|
|
393
|
+
async min(column) {
|
|
394
|
+
return await this.aggregate(`MIN(${column})`, "min");
|
|
395
|
+
}
|
|
396
|
+
async max(column) {
|
|
397
|
+
return await this.aggregate(`MAX(${column})`, "max");
|
|
398
|
+
}
|
|
399
|
+
async paginate(perPage = 15, page = 1) {
|
|
400
|
+
const total = await this.clone().count();
|
|
401
|
+
const data = await this.clone().forPage(page, perPage).get();
|
|
402
|
+
return {
|
|
403
|
+
data,
|
|
404
|
+
current_page: page,
|
|
405
|
+
per_page: perPage,
|
|
406
|
+
total,
|
|
407
|
+
last_page: Math.max(1, Math.ceil(total / perPage)),
|
|
408
|
+
from: total === 0 ? 0 : (page - 1) * perPage + 1,
|
|
409
|
+
to: total === 0 ? 0 : Math.min(page * perPage, total),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async insert(data) {
|
|
413
|
+
const records = Array.isArray(data) ? data : [data];
|
|
414
|
+
if (records.length === 0)
|
|
415
|
+
return;
|
|
416
|
+
const columns = Object.keys(records[0]);
|
|
417
|
+
const values = records.map((record) => {
|
|
418
|
+
return `(${columns.map((col) => this.escape(record[col])).join(", ")})`;
|
|
419
|
+
});
|
|
420
|
+
const sql = `INSERT INTO ${this.wrap(this.tableName)} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")}`;
|
|
421
|
+
return await this.connection.run(sql);
|
|
422
|
+
}
|
|
423
|
+
async insertGetId(data, idColumn = "id") {
|
|
424
|
+
const result = await this.insert(data);
|
|
425
|
+
return result?.lastInsertRowid ?? result?.insertId ?? null;
|
|
426
|
+
}
|
|
427
|
+
async update(data) {
|
|
428
|
+
const sets = Object.entries(data)
|
|
429
|
+
.map(([key, value]) => `${this.wrap(key)} = ${this.escape(value)}`)
|
|
430
|
+
.join(", ");
|
|
431
|
+
const sql = `UPDATE ${this.wrap(this.tableName)} SET ${sets} ${this.compileWheres()}`;
|
|
432
|
+
return await this.connection.run(sql.trim());
|
|
433
|
+
}
|
|
434
|
+
async delete() {
|
|
435
|
+
const sql = `DELETE FROM ${this.wrap(this.tableName)} ${this.compileWheres()}`;
|
|
436
|
+
return await this.connection.run(sql.trim());
|
|
437
|
+
}
|
|
438
|
+
async restore() {
|
|
439
|
+
const model = this.model;
|
|
440
|
+
if (!model?.softDeletes) {
|
|
441
|
+
throw new Error("restore() is only available for soft deleting models");
|
|
442
|
+
}
|
|
443
|
+
return this.withTrashed().update({ [model.deletedAtColumn]: null });
|
|
444
|
+
}
|
|
445
|
+
async exists() {
|
|
446
|
+
const result = await this.select("1 as exists_check").limit(1).get();
|
|
447
|
+
return result.length > 0;
|
|
448
|
+
}
|
|
449
|
+
getModelRelation(relationName) {
|
|
450
|
+
if (!this.model) {
|
|
451
|
+
throw new Error(`Cannot query relation "${relationName}" without a model`);
|
|
452
|
+
}
|
|
453
|
+
const instance = new this.model();
|
|
454
|
+
const relation = instance[relationName]?.();
|
|
455
|
+
if (!relation) {
|
|
456
|
+
throw new Error(`Relation "${relationName}" is not defined on model ${this.model.name}`);
|
|
457
|
+
}
|
|
458
|
+
return relation;
|
|
459
|
+
}
|
|
460
|
+
withAggregate(relationName, column, fn, alias) {
|
|
461
|
+
const relation = this.getModelRelation(relationName);
|
|
462
|
+
const defaultAlias = `${relationName}_${fn.toLowerCase()}_${column.replace(/\W+/g, "_")}`;
|
|
463
|
+
this.addSelect(`(${relation.getRelationAggregateSql(this, `${fn}(${relation.qualifyRelatedColumn(column)})`)}) as ${alias || defaultAlias}`);
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ColumnDefinition, IndexDefinition } from "../types/index.js";
|
|
2
|
+
export declare class ForeignKeyBuilder {
|
|
3
|
+
fk: {
|
|
4
|
+
name?: string;
|
|
5
|
+
columns: string[];
|
|
6
|
+
references: string[];
|
|
7
|
+
onTable: string;
|
|
8
|
+
onDelete?: string;
|
|
9
|
+
onUpdate?: string;
|
|
10
|
+
};
|
|
11
|
+
blueprint: Blueprint;
|
|
12
|
+
constructor(blueprint: Blueprint, columns: string[], name?: string);
|
|
13
|
+
references(columns: string | string[]): this;
|
|
14
|
+
on(table: string): this;
|
|
15
|
+
onDelete(action: string): this;
|
|
16
|
+
onUpdate(action: string): this;
|
|
17
|
+
}
|
|
18
|
+
export declare class Blueprint {
|
|
19
|
+
readonly table: string;
|
|
20
|
+
columns: ColumnDefinition[];
|
|
21
|
+
indexes: IndexDefinition[];
|
|
22
|
+
foreignKeys: any[];
|
|
23
|
+
commands: {
|
|
24
|
+
name: string;
|
|
25
|
+
parameters?: Record<string, any>;
|
|
26
|
+
}[];
|
|
27
|
+
private currentColumn?;
|
|
28
|
+
constructor(table: string);
|
|
29
|
+
private addColumn;
|
|
30
|
+
increments(name?: string): this;
|
|
31
|
+
bigIncrements(name?: string): this;
|
|
32
|
+
string(name: string, length?: number): this;
|
|
33
|
+
text(name: string): this;
|
|
34
|
+
integer(name: string): this;
|
|
35
|
+
bigInteger(name: string): this;
|
|
36
|
+
smallInteger(name: string): this;
|
|
37
|
+
tinyInteger(name: string): this;
|
|
38
|
+
float(name: string, precision?: number, scale?: number): this;
|
|
39
|
+
double(name: string, precision?: number, scale?: number): this;
|
|
40
|
+
decimal(name: string, precision?: number, scale?: number): this;
|
|
41
|
+
boolean(name: string): this;
|
|
42
|
+
date(name: string): this;
|
|
43
|
+
dateTime(name: string): this;
|
|
44
|
+
time(name: string): this;
|
|
45
|
+
timestamp(name: string): this;
|
|
46
|
+
json(name: string): this;
|
|
47
|
+
jsonb(name: string): this;
|
|
48
|
+
binary(name: string): this;
|
|
49
|
+
uuid(name: string): this;
|
|
50
|
+
enum(name: string, values: string[]): this;
|
|
51
|
+
nullable(): this;
|
|
52
|
+
default(value: any): this;
|
|
53
|
+
unique(): this;
|
|
54
|
+
index(): this;
|
|
55
|
+
primary(): this;
|
|
56
|
+
unsigned(): this;
|
|
57
|
+
comment(text: string): this;
|
|
58
|
+
timestamps(): void;
|
|
59
|
+
softDeletes(): void;
|
|
60
|
+
foreign(columns: string | string[], name?: string): ForeignKeyBuilder;
|
|
61
|
+
dropColumn(column: string | string[]): void;
|
|
62
|
+
renameColumn(from: string, to: string): void;
|
|
63
|
+
dropIndex(name: string): void;
|
|
64
|
+
dropUnique(name: string): void;
|
|
65
|
+
dropForeign(name: string): void;
|
|
66
|
+
}
|