1 | var hide = require('./hideProperty')
|
2 | var async = require('./async')
|
3 | var extend = require('./extend')
|
4 |
|
5 | var Row = module.exports = function Row(data, table) {
|
6 | var self = this
|
7 | if (table && table.Row && typeof table.Row === 'function') {
|
8 | var model = function () {}
|
9 | var rowPrototype = extend({}, Row.prototype)
|
10 | model.prototype = new table.Row(data, table)
|
11 | extend(model.prototype, rowPrototype)
|
12 | var newRow = new model(data, table)
|
13 | newRow.initialize(data, table)
|
14 | return newRow
|
15 | }
|
16 | if (!(self instanceof Row)) {
|
17 | return new Row(data, table)
|
18 | }
|
19 | self.initialize(data, table)
|
20 | }
|
21 |
|
22 |
|
23 | Row.prototype.initialize = function (data, table) {
|
24 | var self = this
|
25 | data = data || {}
|
26 |
|
27 | if (self._table || !table) return
|
28 |
|
29 | hide(self, '_data')
|
30 | hide(self, '_table')
|
31 | hide(self, '_db')
|
32 |
|
33 |
|
34 | self._data = self._data || {}
|
35 | self._table = table
|
36 | self._db = table.db
|
37 |
|
38 | Object.getOwnPropertyNames(data).forEach(function (prop) {
|
39 | var desc = Object.getOwnPropertyDescriptor(data, prop)
|
40 | Object.defineProperty(self, prop, desc)
|
41 | })
|
42 | }
|
43 |
|
44 |
|
45 | Row.prototype.dump = function() {
|
46 | console.log(this._table.name + '(' + this.getPrimaryKey() + '):', this);
|
47 | }
|
48 |
|
49 |
|
50 | Row.prototype.getForeignRows = function(foreignRowsName, cb) {
|
51 | var self = this
|
52 | var db = self._table.db
|
53 | var parts = foreignRowsName.split(':')
|
54 | var fkName = parts[0]
|
55 | var table = parts[1]
|
56 | var fk = db[table].fk[fkName]
|
57 | var where = {}
|
58 | fk.columns.forEach(function (col, i) {
|
59 | where[col] = self[fk.foreignColumns[i]]
|
60 | })
|
61 | db[table].find({
|
62 | where: where
|
63 | }, cb)
|
64 | }
|
65 |
|
66 |
|
67 | Row.prototype.getForeignRowsName = function(fkName) {
|
68 | var self = this
|
69 | var db = self._table.db
|
70 | if (self._table.fk[fkName]) {
|
71 | return false
|
72 | }
|
73 | var colExists = !!self._table.columns.filter(function (col) {
|
74 | return col.name === fkName
|
75 | }).length
|
76 | if (colExists) {
|
77 | return false
|
78 | }
|
79 | var foreignRowsName
|
80 | var parts = fkName.split(':')
|
81 | var table = parts[0]
|
82 | if (parts.length > 1) {
|
83 | foreignRowsName = parts[0]
|
84 | table = parts[1]
|
85 | }
|
86 | if (!db[table]) {
|
87 | return false
|
88 | }
|
89 | var fk = db[table].fk
|
90 | if (foreignRowsName) {
|
91 | if (fk[foreignRowsName].foreignTable === self._table.name) {
|
92 | return fkName
|
93 | }
|
94 | throw new Error('The foreign key specified is not linked to this row.')
|
95 | }
|
96 | var found = 0
|
97 | Object.keys(fk).forEach(function (name) {
|
98 | if (fk[name].foreignTable === self._table.name) {
|
99 | found++
|
100 | foreignRowsName = name + ':' + table
|
101 | }
|
102 | })
|
103 | if (found > 1) {
|
104 | throw new Error("hydrate('" + table + "') is ambiguous. Try something like '" + fkName + "'")
|
105 | }
|
106 | return foreignRowsName || false
|
107 | }
|
108 |
|
109 |
|
110 | Row.prototype.hydrate = function(fkNames, cb) {
|
111 | var self = this
|
112 | var db = self._table.db
|
113 | cb = cb || db._promiseResolver()
|
114 | if (!isArray(fkNames)) {
|
115 | fkNames = [fkNames]
|
116 | }
|
117 | async.each(fkNames, function (fkName, done) {
|
118 | var foreignRowsName = self.getForeignRowsName(fkName)
|
119 | if (foreignRowsName) {
|
120 | self.getForeignRows(foreignRowsName, function (err, rows) {
|
121 | if (err) return done(err)
|
122 | self[fkName] = rows
|
123 | done(null)
|
124 | })
|
125 | return
|
126 | }
|
127 | if (!self._table.fk || !self._table.fk[fkName]) {
|
128 | return cb(new Error(self._table.name + '.' + fkName + ' is not hydratable.'))
|
129 | }
|
130 | var fk = self._table.fk[fkName]
|
131 | var property = fk.constraintName
|
132 | var fkTable = fk.foreignTable
|
133 | var fkPk = {}
|
134 | fk.columns.forEach(function(column, i) {
|
135 | fkPk[fk.foreignColumns[i]] = self[column]
|
136 | })
|
137 | var invalidPk = false
|
138 | Object.keys(fkPk).forEach(function(prop) {
|
139 | var val = fkPk[prop]
|
140 | if (val === null || typeof val === 'undefined') {
|
141 | invalidPk = true
|
142 | }
|
143 | })
|
144 | if (invalidPk) return done(null)
|
145 | self._table.db[fkTable].get(fkPk, function(err, obj) {
|
146 | if (err && !err.notFound) return done(err)
|
147 | self[fkName] = obj
|
148 | done(null)
|
149 | })
|
150 | }, cb)
|
151 | return cb.promise ? cb.promise : this
|
152 | }
|
153 |
|
154 |
|
155 | Row.prototype.getPrimaryKey = function() {
|
156 | var self = this
|
157 | var pk = {}
|
158 | if (!self._table.primaryKey) return pk
|
159 | self._table.primaryKey.forEach(function(field) {
|
160 | pk[field] = self[field]
|
161 | })
|
162 | return pk
|
163 | }
|
164 |
|
165 |
|
166 | Row.prototype.getCacheKey = function() {
|
167 | var self = this
|
168 | var pk = self.getPrimaryKey()
|
169 | var cacheKey = ''
|
170 | Object.keys(pk).forEach(function(k) {
|
171 | var val = pk[k]
|
172 | if (cacheKey) cacheKey = cacheKey + ','
|
173 | cacheKey = cacheKey + val
|
174 | })
|
175 | cacheKey = self._table.name + ':' + cacheKey
|
176 | return cacheKey
|
177 | }
|
178 |
|
179 |
|
180 | Row.prototype.save = require('./save')
|
181 |
|
182 |
|
183 | Row.prototype.set = function(data) {
|
184 | var self = this
|
185 | Object.keys(data).forEach(function(field) {
|
186 | self._data[field] = self[field]
|
187 | self[field] = data[field]
|
188 | })
|
189 | }
|
190 |
|
191 |
|
192 | Row.prototype.update = function(data, cb) {
|
193 | var self = this
|
194 | var db = self._table.db
|
195 | cb = cb || db._promiseResolver()
|
196 | Object.keys(data).forEach(function(key) {
|
197 | self[key] = data[key]
|
198 | })
|
199 | self.save(cb)
|
200 | return cb.promise ? cb.promise : this
|
201 | }
|
202 |
|
203 | Row.prototype.delete = function (opts, cb) {
|
204 | var self = this
|
205 | var table = self._table
|
206 | var db = table.db
|
207 | var iq = db._platform.identifierQuoteChar
|
208 | opts = opts || {}
|
209 | if (typeof opts === 'function') {
|
210 | cb = opts
|
211 | opts = {}
|
212 | }
|
213 | cb = cb || db._promiseResolver()
|
214 | var pk = self.getPrimaryKey()
|
215 | var where = table.getPrimaryKeyWhereClause(pk)
|
216 | var cacheKey = self.getCacheKey()
|
217 | var sql = [
|
218 | 'DELETE FROM ' + iq + table.name + iq,
|
219 | 'WHERE ' + where
|
220 | ]
|
221 | db.execute(sql, function (err) {
|
222 | if (err) return cb(err)
|
223 |
|
224 | self._table.invalidateMemo(pk)
|
225 |
|
226 | Object.keys(self).forEach(function (key) {
|
227 | delete self[key]
|
228 | })
|
229 |
|
230 | var cache = db._opts.cache
|
231 | if (!cache || typeof cache.set !== 'function') {
|
232 | return cb(null)
|
233 | }
|
234 | cache.set(cacheKey, null, function(err) {
|
235 | if (err) return cb(err)
|
236 | cb(null)
|
237 | })
|
238 | })
|
239 | return cb.promise ? cb.promise : this
|
240 | }
|
241 |
|
242 | function isArray(obj) {
|
243 | return Object.prototype.toString.call(obj) === '[object Array]'
|
244 | }
|