mevn-orm 2.3.5 → 2.3.6
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/dist/connection.d.ts +2 -0
- package/dist/connection.js +9 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/dist/model.d.ts +171 -0
- package/dist/model.js +399 -0
- package/package.json +8 -4
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/dependabot.yml +0 -30
- package/.github/workflows/node.js.yml +0 -31
- package/initDb +0 -118
- package/test/model.test.js +0 -87
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const import_cwd_1 = tslib_1.__importDefault(require("import-cwd"));
|
|
5
|
+
const config = import_cwd_1.default('./knexfile');
|
|
6
|
+
const knex_1 = tslib_1.__importDefault(require("knex"));
|
|
7
|
+
//@ts-ignore
|
|
8
|
+
const connection = knex_1.default(process.env.NODE_ENV === 'testing' ? config.development : config.production);
|
|
9
|
+
exports.default = connection;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const tslib_1 = require("tslib");
|
|
3
|
+
const dotenv_1 = tslib_1.__importDefault(require("dotenv"));
|
|
4
|
+
dotenv_1.default.config();
|
|
5
|
+
const model_1 = tslib_1.__importDefault(require("./model"));
|
|
6
|
+
const connection_1 = tslib_1.__importDefault(require("./connection"));
|
|
7
|
+
module.exports = {
|
|
8
|
+
Model: model_1.default, DB: connection_1.default
|
|
9
|
+
};
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
declare class Model {
|
|
2
|
+
#private;
|
|
3
|
+
collection: {};
|
|
4
|
+
/**
|
|
5
|
+
* These columns are hidden in collections
|
|
6
|
+
*/
|
|
7
|
+
hidden: any[];
|
|
8
|
+
/**
|
|
9
|
+
* Model ID
|
|
10
|
+
*/
|
|
11
|
+
id: number;
|
|
12
|
+
/**
|
|
13
|
+
* Model name
|
|
14
|
+
*/
|
|
15
|
+
modelName: string;
|
|
16
|
+
/**
|
|
17
|
+
* The current table
|
|
18
|
+
*/
|
|
19
|
+
table: any;
|
|
20
|
+
/**
|
|
21
|
+
* The foreign key
|
|
22
|
+
*/
|
|
23
|
+
foreignKey: string;
|
|
24
|
+
/**
|
|
25
|
+
* Contains the db record
|
|
26
|
+
*/
|
|
27
|
+
currentModel: any;
|
|
28
|
+
static collection: any;
|
|
29
|
+
static currentStaticQuery: any;
|
|
30
|
+
/**
|
|
31
|
+
* New Model instance
|
|
32
|
+
* @param id the database ID of the model
|
|
33
|
+
*/
|
|
34
|
+
constructor(id: number, attributes?: {
|
|
35
|
+
[x: string]: any;
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Fetch the current model from the database
|
|
39
|
+
*/
|
|
40
|
+
fetch(): Promise<any>;
|
|
41
|
+
/**
|
|
42
|
+
* Delete the current model
|
|
43
|
+
* @returns {Promise} deletes item from the database
|
|
44
|
+
*/
|
|
45
|
+
destroy(): Promise<any>;
|
|
46
|
+
/**
|
|
47
|
+
* Update a given model
|
|
48
|
+
* @param {Array} attributes fields
|
|
49
|
+
* @returns {Promise|any} updates a model in the database
|
|
50
|
+
*/
|
|
51
|
+
update(attributes?: Array<any>): Promise<any>;
|
|
52
|
+
/**
|
|
53
|
+
* Return an array rep of a model
|
|
54
|
+
*/
|
|
55
|
+
toArray(): any;
|
|
56
|
+
/**
|
|
57
|
+
* ========================================================================
|
|
58
|
+
* STATIC METHODS
|
|
59
|
+
* ========================================================================
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* Create a collection to return to the user
|
|
63
|
+
* @param { String| Array } elements
|
|
64
|
+
* @returns Array()
|
|
65
|
+
*/
|
|
66
|
+
private static collect;
|
|
67
|
+
/**
|
|
68
|
+
* The models table name
|
|
69
|
+
* eg Movie will automatically be movies
|
|
70
|
+
* @returns String
|
|
71
|
+
*/
|
|
72
|
+
static tableName(): any;
|
|
73
|
+
/**
|
|
74
|
+
* Return the models that match a given condition
|
|
75
|
+
* @param columns The columns to return
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
static get(columns?: string): Promise<Model | Model[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Get the Model count
|
|
81
|
+
*/
|
|
82
|
+
static count(): Promise<{
|
|
83
|
+
[k: string]: string | number;
|
|
84
|
+
}[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Get all rows from the database
|
|
87
|
+
*
|
|
88
|
+
* @returns Promise<>
|
|
89
|
+
*/
|
|
90
|
+
static all(): Promise<Model[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Get rows that match some conditions
|
|
93
|
+
* @param {Object} conditions
|
|
94
|
+
*/
|
|
95
|
+
static where(conditions?: object): typeof Model;
|
|
96
|
+
/**
|
|
97
|
+
* Get rows that match some conditions
|
|
98
|
+
* @param {Object} conditions
|
|
99
|
+
*/
|
|
100
|
+
static whereFirst(conditions?: object): Promise<any>;
|
|
101
|
+
/**
|
|
102
|
+
* Get the first record for the database
|
|
103
|
+
*
|
|
104
|
+
* @returns Promise
|
|
105
|
+
*/
|
|
106
|
+
static first(): Promise<Model>;
|
|
107
|
+
/**
|
|
108
|
+
* Find model by Id
|
|
109
|
+
* @param id
|
|
110
|
+
*/
|
|
111
|
+
static find(id: number): Promise<Model>;
|
|
112
|
+
/**
|
|
113
|
+
* Delete a model from the database
|
|
114
|
+
* @param id
|
|
115
|
+
*/
|
|
116
|
+
static delete(id: number): Promise<number>;
|
|
117
|
+
/**
|
|
118
|
+
* Create a new Model
|
|
119
|
+
* @param attributes
|
|
120
|
+
*/
|
|
121
|
+
static create(attributes?: any[]): Promise<Model>;
|
|
122
|
+
/**
|
|
123
|
+
* -----------------------------------------------
|
|
124
|
+
* Relationships
|
|
125
|
+
* -----------------------------------------------
|
|
126
|
+
*/
|
|
127
|
+
/**
|
|
128
|
+
* Relationships to load this the current model
|
|
129
|
+
* @param {Array} relations
|
|
130
|
+
*/
|
|
131
|
+
load(relations?: Array<any>): this;
|
|
132
|
+
/**
|
|
133
|
+
* Define a HasOne relationship
|
|
134
|
+
* @param {String} related
|
|
135
|
+
* @param {String} primaryKey
|
|
136
|
+
* @param {String} foreignKey
|
|
137
|
+
*/
|
|
138
|
+
hasOne(related: string, primaryKey?: number | any, foreignKey?: number | any): Promise<any>;
|
|
139
|
+
/**
|
|
140
|
+
* Define a reverse has one relationship
|
|
141
|
+
* @param {String} related
|
|
142
|
+
* @param {String} primaryKey
|
|
143
|
+
* @param {String} foreignKey
|
|
144
|
+
*/
|
|
145
|
+
belongsTo(related: string, primaryKey?: string, foreignKey?: string): Promise<any[]>;
|
|
146
|
+
/**
|
|
147
|
+
* Define a Many to many relationship relationship
|
|
148
|
+
* @param {String} related
|
|
149
|
+
*/
|
|
150
|
+
belongsToMany(related: string, primaryKey?: string, foreignKey?: string): string;
|
|
151
|
+
/**
|
|
152
|
+
* Define a HasMany relationship
|
|
153
|
+
* @param {String} related
|
|
154
|
+
* @param {String} primaryKey
|
|
155
|
+
* @param {String} foreignKey
|
|
156
|
+
*/
|
|
157
|
+
hasMany(related: string, primaryKey?: string, foreignKey?: string): Promise<any[]>;
|
|
158
|
+
/**
|
|
159
|
+
* MorphOne relationship
|
|
160
|
+
* @param {String} related the class name of the other model
|
|
161
|
+
* @param {String} reference the reference column in the other model
|
|
162
|
+
*/
|
|
163
|
+
morphOne(related: String, reference: String): Promise<any>;
|
|
164
|
+
/**
|
|
165
|
+
* MorphMany relationship
|
|
166
|
+
* @param {String} related the class name of the other model
|
|
167
|
+
* @param {String} reference the reference column in the other model
|
|
168
|
+
*/
|
|
169
|
+
morphMany(related: String, reference: String): Promise<any[]>;
|
|
170
|
+
}
|
|
171
|
+
export default Model;
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const connection_1 = tslib_1.__importDefault(require("./connection"));
|
|
5
|
+
const pluralize_1 = tslib_1.__importDefault(require("pluralize"));
|
|
6
|
+
class Model {
|
|
7
|
+
/**
|
|
8
|
+
* New Model instance
|
|
9
|
+
* @param id the database ID of the model
|
|
10
|
+
*/
|
|
11
|
+
constructor(id, attributes = {}) {
|
|
12
|
+
this.#collection = {};
|
|
13
|
+
this.#hidden = [];
|
|
14
|
+
this.id = id;
|
|
15
|
+
this.#modelName = this.constructor.name.toLowerCase();
|
|
16
|
+
this.#table = pluralize_1.default(this.#modelName);
|
|
17
|
+
this.#foreignKey = `${this.#modelName}_id`;
|
|
18
|
+
if (attributes) {
|
|
19
|
+
for (const a in attributes) {
|
|
20
|
+
if (Object.prototype.hasOwnProperty.call(attributes, a)) {
|
|
21
|
+
this[a] = attributes[a];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
this.fetch().then((c) => {
|
|
26
|
+
this.#currentModel = c;
|
|
27
|
+
for (const a in c) {
|
|
28
|
+
this[a] = c[a];
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
#foreignKey;
|
|
33
|
+
#collection;
|
|
34
|
+
#hidden;
|
|
35
|
+
#modelName;
|
|
36
|
+
#currentModel;
|
|
37
|
+
#table;
|
|
38
|
+
/**
|
|
39
|
+
* Fetch the current model from the database
|
|
40
|
+
*/
|
|
41
|
+
async fetch() {
|
|
42
|
+
try {
|
|
43
|
+
return connection_1.default
|
|
44
|
+
.table(this.#table)
|
|
45
|
+
.where('id', this.id)
|
|
46
|
+
.first();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new Error(error).stack;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Delete the current model
|
|
54
|
+
* @returns {Promise} deletes item from the database
|
|
55
|
+
*/
|
|
56
|
+
async destroy() {
|
|
57
|
+
try {
|
|
58
|
+
await connection_1.default
|
|
59
|
+
.table(this.#table)
|
|
60
|
+
.where({ id: this.id })
|
|
61
|
+
.delete();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Update a given model
|
|
69
|
+
* @param {Array} attributes fields
|
|
70
|
+
* @returns {Promise|any} updates a model in the database
|
|
71
|
+
*/
|
|
72
|
+
async update(attributes = []) {
|
|
73
|
+
try {
|
|
74
|
+
await connection_1.default
|
|
75
|
+
.table(this.#table)
|
|
76
|
+
.where({ id: this.id })
|
|
77
|
+
.update(attributes);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Return an array rep of a model
|
|
85
|
+
*/
|
|
86
|
+
toArray() {
|
|
87
|
+
return this.#currentModel;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* ========================================================================
|
|
91
|
+
* STATIC METHODS
|
|
92
|
+
* ========================================================================
|
|
93
|
+
*/
|
|
94
|
+
/**
|
|
95
|
+
* Create a collection to return to the user
|
|
96
|
+
* @param { String| Array } elements
|
|
97
|
+
* @returns Array()
|
|
98
|
+
*/
|
|
99
|
+
static collect(elements = '') {
|
|
100
|
+
console.log(this.collection);
|
|
101
|
+
this.collection.push(elements);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The models table name
|
|
105
|
+
* eg Movie will automatically be movies
|
|
106
|
+
* @returns String
|
|
107
|
+
*/
|
|
108
|
+
static tableName() {
|
|
109
|
+
return pluralize_1.default(this.name).toLowerCase();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Return the models that match a given condition
|
|
113
|
+
* @param columns The columns to return
|
|
114
|
+
* @returns
|
|
115
|
+
*/
|
|
116
|
+
static async get(columns = '*') {
|
|
117
|
+
const m = await this.currentStaticQuery.select(columns);
|
|
118
|
+
if (Array.isArray(m)) {
|
|
119
|
+
return m.map((model) => {
|
|
120
|
+
return new this(model.id, model);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return new this(m.id, m);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get the Model count
|
|
127
|
+
*/
|
|
128
|
+
static async count() {
|
|
129
|
+
try {
|
|
130
|
+
return await connection_1.default
|
|
131
|
+
.table(this.tableName())
|
|
132
|
+
.count();
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get all rows from the database
|
|
140
|
+
*
|
|
141
|
+
* @returns Promise<>
|
|
142
|
+
*/
|
|
143
|
+
static async all() {
|
|
144
|
+
try {
|
|
145
|
+
const models = await connection_1.default.table(this.tableName()).select('*');
|
|
146
|
+
return models.map((m) => {
|
|
147
|
+
return new this(m.id, m);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get rows that match some conditions
|
|
156
|
+
* @param {Object} conditions
|
|
157
|
+
*/
|
|
158
|
+
static where(conditions = {}) {
|
|
159
|
+
try {
|
|
160
|
+
this.currentStaticQuery = connection_1.default
|
|
161
|
+
.table(this.tableName())
|
|
162
|
+
.where(conditions)
|
|
163
|
+
.select('*');
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get rows that match some conditions
|
|
172
|
+
* @param {Object} conditions
|
|
173
|
+
*/
|
|
174
|
+
static async whereFirst(conditions = {}) {
|
|
175
|
+
try {
|
|
176
|
+
console.warn('where.first() has been deprecated and will be removed in future please use where().first() instead');
|
|
177
|
+
return await connection_1.default
|
|
178
|
+
.table(this.tableName())
|
|
179
|
+
.where(conditions)
|
|
180
|
+
.first();
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the first record for the database
|
|
188
|
+
*
|
|
189
|
+
* @returns Promise
|
|
190
|
+
*/
|
|
191
|
+
static async first() {
|
|
192
|
+
try {
|
|
193
|
+
if (this.currentStaticQuery) {
|
|
194
|
+
// It means it is chained
|
|
195
|
+
const model = await this.currentStaticQuery.first();
|
|
196
|
+
if (model) {
|
|
197
|
+
return new this(model.id, model);
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
// It is not being chained
|
|
202
|
+
const record = await connection_1.default
|
|
203
|
+
.table(this.tableName())
|
|
204
|
+
.select('*')
|
|
205
|
+
.first();
|
|
206
|
+
return new this(record.id, record);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Find model by Id
|
|
214
|
+
* @param id
|
|
215
|
+
*/
|
|
216
|
+
static async find(id) {
|
|
217
|
+
try {
|
|
218
|
+
const m = await connection_1.default
|
|
219
|
+
.table(this.tableName())
|
|
220
|
+
.where({ id })
|
|
221
|
+
.first();
|
|
222
|
+
if (m) {
|
|
223
|
+
return new this(id, m);
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Delete a model from the database
|
|
233
|
+
* @param id
|
|
234
|
+
*/
|
|
235
|
+
static async delete(id) {
|
|
236
|
+
try {
|
|
237
|
+
return await connection_1.default
|
|
238
|
+
.table(this.tableName())
|
|
239
|
+
.where({ id })
|
|
240
|
+
.del();
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Create a new Model
|
|
248
|
+
* @param attributes
|
|
249
|
+
*/
|
|
250
|
+
static async create(attributes = []) {
|
|
251
|
+
try {
|
|
252
|
+
const ids = await connection_1.default
|
|
253
|
+
.table(this.tableName())
|
|
254
|
+
.insert(attributes);
|
|
255
|
+
const m = new this(ids[0]);
|
|
256
|
+
await m.fetch();
|
|
257
|
+
return m;
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* -----------------------------------------------
|
|
265
|
+
* Relationships
|
|
266
|
+
* -----------------------------------------------
|
|
267
|
+
*/
|
|
268
|
+
/**
|
|
269
|
+
* Relationships to load this the current model
|
|
270
|
+
* @param {Array} relations
|
|
271
|
+
*/
|
|
272
|
+
load(relations = []) {
|
|
273
|
+
this.#collection[this.#modelName] = this.#currentModel;
|
|
274
|
+
relations.forEach(function (relation) {
|
|
275
|
+
const rows = this[relation]();
|
|
276
|
+
this.#collection[this.#modelName][relation] = rows;
|
|
277
|
+
}, this);
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Define a HasOne relationship
|
|
282
|
+
* @param {String} related
|
|
283
|
+
* @param {String} primaryKey
|
|
284
|
+
* @param {String} foreignKey
|
|
285
|
+
*/
|
|
286
|
+
async hasOne(related, primaryKey = null, foreignKey = null) {
|
|
287
|
+
// I should get a query to get related records
|
|
288
|
+
const fk = foreignKey || this.#foreignKey;
|
|
289
|
+
const pk = primaryKey || this.id;
|
|
290
|
+
return await connection_1.default
|
|
291
|
+
.table(pluralize_1.default(related.toLowerCase()))
|
|
292
|
+
// in the form of model_id
|
|
293
|
+
.where(fk, pk)
|
|
294
|
+
.first()
|
|
295
|
+
.asCallback((err, rows) => {
|
|
296
|
+
if (err) {
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
299
|
+
return rows;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Define a reverse has one relationship
|
|
304
|
+
* @param {String} related
|
|
305
|
+
* @param {String} primaryKey
|
|
306
|
+
* @param {String} foreignKey
|
|
307
|
+
*/
|
|
308
|
+
async belongsTo(related, primaryKey = null, foreignKey = null) {
|
|
309
|
+
// I should get a query to get related records
|
|
310
|
+
const fk = foreignKey || this.#foreignKey;
|
|
311
|
+
const pk = primaryKey || this.id;
|
|
312
|
+
return await connection_1.default
|
|
313
|
+
.table(this.#table)
|
|
314
|
+
// in the form of model_id
|
|
315
|
+
.where(fk, pk)
|
|
316
|
+
.asCallback((err, rows) => {
|
|
317
|
+
if (err) {
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
return rows;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Define a Many to many relationship relationship
|
|
325
|
+
* @param {String} related
|
|
326
|
+
*/
|
|
327
|
+
belongsToMany(related, primaryKey = null, foreignKey = null) {
|
|
328
|
+
// look for a pivot table
|
|
329
|
+
// eg Farmer has many Crops crop_farmer is the table
|
|
330
|
+
// We'll use class names ['This Class, Related Class'].sort
|
|
331
|
+
const relatedName = related.toLowerCase();
|
|
332
|
+
const pivotTable = [this.#modelName, relatedName].sort().join('_');
|
|
333
|
+
const firstCol = `${this.#modelName}_id`;
|
|
334
|
+
const secondCol = `${relatedName}_id`;
|
|
335
|
+
// TODO Honestly I believe there is a better way
|
|
336
|
+
// Gets the related IDs from the pivot table
|
|
337
|
+
const firstQuery = connection_1.default
|
|
338
|
+
.table(pivotTable)
|
|
339
|
+
.where(firstCol, this.id)
|
|
340
|
+
.select(secondCol)
|
|
341
|
+
.toQuery();
|
|
342
|
+
// Get the records for the other table
|
|
343
|
+
return firstQuery;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Define a HasMany relationship
|
|
347
|
+
* @param {String} related
|
|
348
|
+
* @param {String} primaryKey
|
|
349
|
+
* @param {String} foreignKey
|
|
350
|
+
*/
|
|
351
|
+
async hasMany(related, primaryKey = null, foreignKey = null) {
|
|
352
|
+
// I should get a query to get related records
|
|
353
|
+
const fk = foreignKey || this.#foreignKey;
|
|
354
|
+
const pk = primaryKey || this.id;
|
|
355
|
+
return await connection_1.default
|
|
356
|
+
.table(pluralize_1.default(related.toLowerCase()))
|
|
357
|
+
.where(fk, pk)
|
|
358
|
+
.select('*')
|
|
359
|
+
.asCallback((err, rows) => {
|
|
360
|
+
if (err) {
|
|
361
|
+
throw err;
|
|
362
|
+
}
|
|
363
|
+
return rows;
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* MorphOne relationship
|
|
368
|
+
* @param {String} related the class name of the other model
|
|
369
|
+
* @param {String} reference the reference column in the other model
|
|
370
|
+
*/
|
|
371
|
+
async morphOne(related, reference) {
|
|
372
|
+
const foreign_id = `${reference}_id`;
|
|
373
|
+
const foreign_type = `${this.#modelName}_type`;
|
|
374
|
+
let condition = {};
|
|
375
|
+
condition[`${reference}_id`] = this.id;
|
|
376
|
+
condition[`${reference}_type`] = this.constructor.name;
|
|
377
|
+
const table = pluralize_1.default(related.toLocaleLowerCase());
|
|
378
|
+
return await connection_1.default.table(table)
|
|
379
|
+
.where(condition)
|
|
380
|
+
.first();
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* MorphMany relationship
|
|
384
|
+
* @param {String} related the class name of the other model
|
|
385
|
+
* @param {String} reference the reference column in the other model
|
|
386
|
+
*/
|
|
387
|
+
async morphMany(related, reference) {
|
|
388
|
+
const foreign_id = `${reference}_id`;
|
|
389
|
+
const foreign_type = `${this.#modelName}_type`;
|
|
390
|
+
let condition = {};
|
|
391
|
+
condition[`${reference}_id`] = this.id;
|
|
392
|
+
condition[`${reference}_type`] = this.constructor.name;
|
|
393
|
+
const table = pluralize_1.default(related.toLocaleLowerCase());
|
|
394
|
+
return await connection_1.default.table(table)
|
|
395
|
+
.where(condition)
|
|
396
|
+
.select();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.default = Model;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mevn-orm",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"description": "simple ORM for express js",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"scripts": {
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"lint": "eslint --ext .js ./"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"dotenv": "^
|
|
15
|
-
"knex": "^1.0.
|
|
14
|
+
"dotenv": "^16.0.0",
|
|
15
|
+
"knex": "^1.0.2",
|
|
16
16
|
"mysql": "^2.18.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
"ORM",
|
|
28
28
|
"express",
|
|
29
29
|
"Mysql",
|
|
30
|
-
"mevn"
|
|
30
|
+
"mevn",
|
|
31
|
+
"database",
|
|
32
|
+
"orm",
|
|
33
|
+
"orm-express",
|
|
34
|
+
"orm-mysql"
|
|
31
35
|
],
|
|
32
36
|
"author": "Stanley Masinde <stanmasinde@gmail.com> (https://stanleymasinde.github.io)",
|
|
33
37
|
"contributors": [
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Bug report
|
|
3
|
-
about: Create a report to help us improve
|
|
4
|
-
title: ''
|
|
5
|
-
labels: ''
|
|
6
|
-
assignees: ''
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
**Describe the bug**
|
|
11
|
-
A clear and concise description of what the bug is.
|
|
12
|
-
|
|
13
|
-
**To Reproduce**
|
|
14
|
-
Steps to reproduce the behavior:
|
|
15
|
-
1. Go to '...'
|
|
16
|
-
2. Click on '....'
|
|
17
|
-
3. Scroll down to '....'
|
|
18
|
-
4. See error
|
|
19
|
-
|
|
20
|
-
**Expected behavior**
|
|
21
|
-
A clear and concise description of what you expected to happen.
|
|
22
|
-
|
|
23
|
-
**Screenshots**
|
|
24
|
-
If applicable, add screenshots to help explain your problem.
|
|
25
|
-
|
|
26
|
-
**Desktop (please complete the following information):**
|
|
27
|
-
- OS: [e.g. iOS]
|
|
28
|
-
- Browser [e.g. chrome, safari]
|
|
29
|
-
- Version [e.g. 22]
|
|
30
|
-
|
|
31
|
-
**Smartphone (please complete the following information):**
|
|
32
|
-
- Device: [e.g. iPhone6]
|
|
33
|
-
- OS: [e.g. iOS8.1]
|
|
34
|
-
- Browser [e.g. stock browser, safari]
|
|
35
|
-
- Version [e.g. 22]
|
|
36
|
-
|
|
37
|
-
**Additional context**
|
|
38
|
-
Add any other context about the problem here.
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Feature request
|
|
3
|
-
about: Suggest an idea for this project
|
|
4
|
-
title: ''
|
|
5
|
-
labels: ''
|
|
6
|
-
assignees: ''
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
**Is your feature request related to a problem? Please describe.**
|
|
11
|
-
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
12
|
-
|
|
13
|
-
**Describe the solution you'd like**
|
|
14
|
-
A clear and concise description of what you want to happen.
|
|
15
|
-
|
|
16
|
-
**Describe alternatives you've considered**
|
|
17
|
-
A clear and concise description of any alternative solutions or features you've considered.
|
|
18
|
-
|
|
19
|
-
**Additional context**
|
|
20
|
-
Add any other context or screenshots about the feature request here.
|
package/.github/dependabot.yml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
updates:
|
|
3
|
-
- package-ecosystem: npm
|
|
4
|
-
directory: '/'
|
|
5
|
-
schedule:
|
|
6
|
-
interval: daily
|
|
7
|
-
time: '00:00'
|
|
8
|
-
open-pull-requests-limit: 10
|
|
9
|
-
reviewers:
|
|
10
|
-
- stanleymasinde
|
|
11
|
-
assignees:
|
|
12
|
-
- stanleymasinde
|
|
13
|
-
commit-message:
|
|
14
|
-
prefix: fix
|
|
15
|
-
prefix-development: chore
|
|
16
|
-
include: scope
|
|
17
|
-
- package-ecosystem: github-actions
|
|
18
|
-
directory: '/'
|
|
19
|
-
schedule:
|
|
20
|
-
interval: daily
|
|
21
|
-
time: '00:00'
|
|
22
|
-
open-pull-requests-limit: 10
|
|
23
|
-
reviewers:
|
|
24
|
-
- stanleymasinde
|
|
25
|
-
assignees:
|
|
26
|
-
- stanleymasinde
|
|
27
|
-
commit-message:
|
|
28
|
-
prefix: fix
|
|
29
|
-
prefix-development: chore
|
|
30
|
-
include: scope
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
-
|
|
4
|
-
name: Node.js CI
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
branches: [ main ]
|
|
9
|
-
pull_request:
|
|
10
|
-
branches: [ main ]
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
build:
|
|
14
|
-
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
node-version: [12.x, 14.x, 15.x]
|
|
20
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- uses: actions/checkout@v2
|
|
24
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
-
uses: actions/setup-node@v2
|
|
26
|
-
with:
|
|
27
|
-
node-version: ${{ matrix.node-version }}
|
|
28
|
-
- run: cp .env.example .env
|
|
29
|
-
- run: npm ci
|
|
30
|
-
- run: npm run build --if-present
|
|
31
|
-
- run: npm test
|
package/initDb
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
#!/bin/env node
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
require('dotenv').config()
|
|
4
|
-
const Knex = require('knex')
|
|
5
|
-
|
|
6
|
-
let knexfilePath
|
|
7
|
-
|
|
8
|
-
if (fs.existsSync(process.cwd() + '/knexfile.js')) {
|
|
9
|
-
knexfilePath = process.cwd() + '/knexfile.js'
|
|
10
|
-
} else {
|
|
11
|
-
knexfilePath = process.cwd() + '/knexfile.cjs'
|
|
12
|
-
}
|
|
13
|
-
const { development, staging, production } = require(knexfilePath)
|
|
14
|
-
let config
|
|
15
|
-
|
|
16
|
-
switch (process.env.NODE_ENV) {
|
|
17
|
-
case 'testing':
|
|
18
|
-
config = development
|
|
19
|
-
break
|
|
20
|
-
case 'development':
|
|
21
|
-
config = development
|
|
22
|
-
break
|
|
23
|
-
case 'staging':
|
|
24
|
-
config = staging
|
|
25
|
-
break
|
|
26
|
-
default:
|
|
27
|
-
config = production
|
|
28
|
-
break
|
|
29
|
-
}
|
|
30
|
-
async function initDatabase() {
|
|
31
|
-
const DB = Knex(config)
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
await DB.schema.dropTableIfExists('farmers')
|
|
35
|
-
await DB.schema.dropTableIfExists('farms')
|
|
36
|
-
await DB.schema.dropTableIfExists('profiles')
|
|
37
|
-
await DB.schema.dropTableIfExists('articles')
|
|
38
|
-
await DB.schema.createTable('farmers', (table) => {
|
|
39
|
-
table.bigIncrements('id')
|
|
40
|
-
table.string('name')
|
|
41
|
-
table.string('email').unique()
|
|
42
|
-
table.string('password')
|
|
43
|
-
})
|
|
44
|
-
await DB.schema.createTable('farms', (table) => {
|
|
45
|
-
table.bigIncrements('id')
|
|
46
|
-
table.bigInteger('farmer_id')
|
|
47
|
-
table.string('name')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
await DB.schema.createTable('profiles', (table) => {
|
|
51
|
-
table.bigIncrements('id')
|
|
52
|
-
table.bigInteger('farmer_id')
|
|
53
|
-
table.string('bio')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
await DB.schema.createTable('articles', (table) => {
|
|
57
|
-
table.bigIncrements('id')
|
|
58
|
-
table.string('title')
|
|
59
|
-
table.text('body')
|
|
60
|
-
table.bigInteger('postable_id')
|
|
61
|
-
table.string('postable_type')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
await DB.table('Farmers').insert([
|
|
65
|
-
{
|
|
66
|
-
name: 'Jane Doe',
|
|
67
|
-
email: 'jane@mail.com',
|
|
68
|
-
password: 'pasword'
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'Ashley Doe',
|
|
72
|
-
email: 'ashley@mail.com',
|
|
73
|
-
password: 'pasword'
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'Alice Doe',
|
|
77
|
-
email: 'alice@mail.com',
|
|
78
|
-
password: 'pasword'
|
|
79
|
-
}
|
|
80
|
-
])
|
|
81
|
-
|
|
82
|
-
await DB.table('farms').insert([
|
|
83
|
-
{
|
|
84
|
-
farmer_id: 1,
|
|
85
|
-
name: 'Awesome Farm'
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
farmer_id: 1,
|
|
89
|
-
name: 'Awesome Farm two'
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
farmer_id: 1,
|
|
93
|
-
name: 'Awesome Farm three'
|
|
94
|
-
}
|
|
95
|
-
])
|
|
96
|
-
|
|
97
|
-
await DB.table('profiles').insert([
|
|
98
|
-
{
|
|
99
|
-
farmer_id: 1,
|
|
100
|
-
bio: 'Profile for farmer one'
|
|
101
|
-
}
|
|
102
|
-
])
|
|
103
|
-
|
|
104
|
-
await DB.table('articles').insert([
|
|
105
|
-
{
|
|
106
|
-
title: 'Awesome Post',
|
|
107
|
-
body: 'fffgjdfjdbdb something #1',
|
|
108
|
-
postable_id: 1,
|
|
109
|
-
postable_type: 'Farmer'
|
|
110
|
-
}
|
|
111
|
-
])
|
|
112
|
-
|
|
113
|
-
process.exit(0)
|
|
114
|
-
} catch (error) {
|
|
115
|
-
process.exit(1)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
initDatabase()
|
package/test/model.test.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-undef */
|
|
2
|
-
const { Model, DB } = require('../index')
|
|
3
|
-
const faker = require('faker')
|
|
4
|
-
const { expect } = require('chai')
|
|
5
|
-
class Profile extends Model {
|
|
6
|
-
fillable = ['farmer_id', 'bio']
|
|
7
|
-
}
|
|
8
|
-
class Farmer extends Model {
|
|
9
|
-
fillable = ['name', 'email', 'password']
|
|
10
|
-
hidden = ['password']
|
|
11
|
-
|
|
12
|
-
profile() {
|
|
13
|
-
return this.hasOne(Profile)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
DB(Farmer)
|
|
18
|
-
|
|
19
|
-
describe('#Model tests', () => {
|
|
20
|
-
it('#Model instance', async () => {
|
|
21
|
-
const farmer = new Farmer({
|
|
22
|
-
name: faker.name.findName(),
|
|
23
|
-
email: faker.internet.email(),
|
|
24
|
-
password: faker.internet.password()
|
|
25
|
-
})
|
|
26
|
-
await farmer.save()
|
|
27
|
-
expect(farmer).to.an('Object')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('#find a model', async () => {
|
|
31
|
-
const farmer = await Farmer.find(1)
|
|
32
|
-
expect(farmer).to.an('Object')
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('#create a model', async () => {
|
|
36
|
-
const farmer = await Farmer.create({
|
|
37
|
-
name: faker.name.findName(),
|
|
38
|
-
email: faker.internet.email(),
|
|
39
|
-
password: faker.internet.password()
|
|
40
|
-
})
|
|
41
|
-
expect(farmer).to.an('Object')
|
|
42
|
-
expect(farmer.id).to.be.a('number')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('#Update a model with a new instance', async () => {
|
|
46
|
-
const farmer = await Farmer.find(1)
|
|
47
|
-
await farmer.update({
|
|
48
|
-
name: 'new name',
|
|
49
|
-
email: faker.internet.email(),
|
|
50
|
-
password: faker.internet.password()
|
|
51
|
-
})
|
|
52
|
-
expect(farmer).to.an('Object')
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('#chain where and first', async () => {
|
|
56
|
-
const farmer = await Farmer.where({ id: 1 }).first()
|
|
57
|
-
expect(farmer).to.an('Object')
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('#Return null when not found', async () => {
|
|
61
|
-
const farmer = await Farmer.where({ id: 'ggggggg' }).first()
|
|
62
|
-
expect(farmer).to.be.null
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('#Delete a model', async () => {
|
|
66
|
-
const farmer = await Farmer.find(1)
|
|
67
|
-
await farmer.delete()
|
|
68
|
-
expect(await Farmer.find(1)).to.be.null
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('#Has one relationship', async () => {
|
|
72
|
-
const farmer = new Farmer({
|
|
73
|
-
name: faker.name.findName(),
|
|
74
|
-
email: faker.internet.email(),
|
|
75
|
-
password: faker.internet.password()
|
|
76
|
-
})
|
|
77
|
-
await farmer.save()
|
|
78
|
-
await new Profile({
|
|
79
|
-
farmer_id: farmer.id,
|
|
80
|
-
bio: faker.lorem.sentence()
|
|
81
|
-
}).save()
|
|
82
|
-
|
|
83
|
-
expect(farmer).to.an('Object')
|
|
84
|
-
const farmerProfile = await farmer.profile()
|
|
85
|
-
expect(farmerProfile).to.haveOwnProperty('farmer_id', farmer.id)
|
|
86
|
-
})
|
|
87
|
-
})
|