1
|
|
|
<?php |
2
|
|
|
require_once(dirname(__FILE__) . '/fwolflib.php'); |
3
|
|
|
require_once(dirname(__FILE__) . '/adodb.php'); |
4
|
|
|
require_once(dirname(__FILE__) . '/cache/cache.php'); |
5
|
|
|
require_once(dirname(__FILE__) . '/../func/request.php'); |
6
|
|
|
require_once(dirname(__FILE__) . '/../func/string.php'); |
7
|
|
|
|
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Module in MVC |
11
|
|
|
* |
12
|
|
|
* Do data compute or database relate operate. |
13
|
|
|
* Usually only carry data, leave data format job to View. |
14
|
|
|
* |
15
|
|
|
* @deprecated Use Fwlib\Mvc\AbstractModel |
16
|
|
|
* @package fwolflib |
17
|
|
|
* @subpackage class.mvc |
18
|
|
|
* @copyright Copyright 2008-2012, Fwolf |
19
|
|
|
* @author Fwolf <[email protected]> |
20
|
|
|
* @since 2008-04-06 |
21
|
|
|
* @see Controler |
22
|
|
|
* @see View |
23
|
|
|
*/ |
24
|
|
|
abstract class Module extends Fwolflib { |
|
|
|
|
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Cache object |
28
|
|
|
* @var object |
29
|
|
|
*/ |
30
|
|
|
public $oCache = NULL; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Use cache or not |
34
|
|
|
* @var boolean |
35
|
|
|
*/ |
36
|
|
|
public $bCacheOn = FALSE; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Database object |
40
|
|
|
* @var object |
41
|
|
|
*/ |
42
|
|
|
public $oDb = NULL; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Number of items in list |
46
|
|
|
* |
47
|
|
|
* In simple idea, this should set in view, |
48
|
|
|
* but pagesize is impleted as limit in select, |
49
|
|
|
* so when generate sql you need to use it. |
50
|
|
|
* @var int |
51
|
|
|
*/ |
52
|
|
|
public $iPageSize = 10; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Caller: view object |
56
|
|
|
* @var object |
57
|
|
|
*/ |
58
|
|
|
public $oView = NULL; |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
// Get db connection, because unknown db & dblib, |
62
|
|
|
// implete it in application module class. |
63
|
|
|
// Also can extend db connect class easily. |
64
|
|
|
abstract protected function DbConn ($dbprofile); |
65
|
|
|
|
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* construct |
69
|
|
|
* @param object $view Caller view object |
70
|
|
|
*/ |
71
|
|
|
public function __construct ($view = null) { |
72
|
|
|
// Unset for auto new |
73
|
|
|
unset($this->oCache); |
74
|
|
|
unset($this->oDb); |
75
|
|
|
|
76
|
|
|
parent::__construct(); |
77
|
|
|
|
78
|
|
|
if (!is_null($view)) |
79
|
|
|
$this->oView = &$view; |
80
|
|
|
|
81
|
|
|
// Temp cache switch |
82
|
|
|
if ('0' == GetGet('cache')) |
|
|
|
|
83
|
|
|
$this->bCacheOn = FALSE; |
84
|
|
|
} // end of func __construct |
85
|
|
|
|
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Overload __call |
89
|
|
|
* Auto call CacheXxx func. |
90
|
|
|
* |
91
|
|
|
* @param string $name Method name |
92
|
|
|
* @param array $arg Method argument |
93
|
|
|
* @return mixed |
94
|
|
|
*/ |
95
|
|
|
public function __call ($name, $arg) { |
96
|
|
|
// Func name start from Cache |
97
|
|
|
if ('Cache' == substr($name, 0, 5)) { |
98
|
|
|
$method = substr($name, 5); |
99
|
|
|
if (method_exists($this, $method)) { |
100
|
|
|
// Cache key prefix |
101
|
|
|
$key = get_class($this) . '/' . $method . '/'; |
102
|
|
|
// Get cache lifetime by key prefix |
103
|
|
|
$lifetime = $this->CacheLifetime($key); |
104
|
|
|
// Real cache key need arg added |
105
|
|
|
if (!empty($arg)) |
106
|
|
|
foreach ($arg as $k => $v) |
107
|
|
|
$key .= $k . '/' . $v . '/'; |
108
|
|
|
|
109
|
|
|
$val = NULL; |
110
|
|
|
// Try read from cache if cache on |
111
|
|
|
if ($this->bCacheOn) |
112
|
|
|
$val = $this->oCache->Get($key, $lifetime); |
113
|
|
|
|
114
|
|
|
// Read cache fail, call real method |
115
|
|
|
if (is_null($val)) { |
116
|
|
|
$val = call_user_func_array(array($this, $method), $arg); |
117
|
|
|
// Update cache |
118
|
|
|
$this->oCache->Set($key, $val, $lifetime); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
return $val; |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
// Method not exists, trigger error. |
126
|
|
|
//$this->Log('Undefined method: ' . $name . '()', 5); |
127
|
|
|
trigger_error('Undefined method: ' . $name . '()', E_USER_ERROR); |
128
|
|
|
return NULL; |
129
|
|
|
} // end of func __call |
130
|
|
|
|
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Get cache lifetime |
134
|
|
|
* Subclass should rewrite this method, |
135
|
|
|
* to get lifetime by assigned key. |
136
|
|
|
* |
137
|
|
|
* @param string $key Key, or key prefix |
138
|
|
|
* @return int Lifetime in seconds |
139
|
|
|
*/ |
140
|
|
|
public function CacheLifetime ($key) { |
|
|
|
|
141
|
|
|
// Use Cache obj default |
142
|
|
|
return NULL; |
143
|
|
|
} // end of func CacheLifetime |
144
|
|
|
|
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Compare new data with data from db, get diff array |
148
|
|
|
* |
149
|
|
|
* New/old array are all assoc, index by table column. |
150
|
|
|
* PK column must exists in new array, value can be NULL. |
151
|
|
|
* |
152
|
|
|
* In new array, empty PK means INSERT. |
153
|
|
|
* In old array, empty or not exists PK means DELETE. |
154
|
|
|
* |
155
|
|
|
* Multi table and row supported, new/old array must match. |
156
|
|
|
* |
157
|
|
|
* After modify db according diff, 'flag' should be set, |
158
|
|
|
* 'code, msg' can also used to store db op message. |
159
|
|
|
* When db query success, 'code' is count for rows changed, |
160
|
|
|
* when db query fail, 'code' is error no and 'msg' is error msg. |
161
|
|
|
* |
162
|
|
|
* New/old array structure: |
163
|
|
|
* array( |
164
|
|
|
* [tbl] => array( |
165
|
|
|
* array( // Single row can admit this level |
166
|
|
|
* // DbDiffRow() use this as param |
167
|
|
|
* [col] => [val], |
168
|
|
|
* ... |
169
|
|
|
* ), |
170
|
|
|
* ... |
171
|
|
|
* ), |
172
|
|
|
* ... |
173
|
|
|
* ) |
174
|
|
|
* |
175
|
|
|
* Result is array, structure: |
176
|
|
|
* array( |
177
|
|
|
* code: >= 0 means success, < 0 means error |
178
|
|
|
* msg: Error msg |
179
|
|
|
* flag: 0=default, 100=committed, -100=rollbacked |
180
|
|
|
* diff = array( |
181
|
|
|
* [tbl] = array( |
182
|
|
|
* array( // DbDiffRow() return this array |
183
|
|
|
* mode: INSERT | DELETE | UPDATE |
184
|
|
|
* // PK new/old is same in UPDATE mode |
185
|
|
|
* pk: array( |
186
|
|
|
* [pk1] = array( |
187
|
|
|
* old => [val], |
188
|
|
|
* new => [val], |
189
|
|
|
* ), |
190
|
|
|
* ... |
191
|
|
|
* ), |
192
|
|
|
* // Other cols change |
193
|
|
|
* col: array( |
194
|
|
|
* [col] = array( |
195
|
|
|
* old => [val], |
196
|
|
|
* new => [val], |
197
|
|
|
* ), |
198
|
|
|
* ... |
199
|
|
|
* ), |
200
|
|
|
* ), |
201
|
|
|
* ... |
202
|
|
|
* ), |
203
|
|
|
* ... |
204
|
|
|
* ) |
205
|
|
|
* ) |
206
|
|
|
* @param array $ar_new New data array |
207
|
|
|
* @param Adodb $db Db object, need to be connected, |
208
|
|
|
* NULL to use $this->oDb. |
209
|
|
|
* @param array $ar_old Old data array, NULL to read from db |
210
|
|
|
* @return array |
211
|
|
|
*/ |
212
|
|
|
public function DbDiff (array $ar_new, Adodb $db = NULL, array $ar_old = NULL) { |
213
|
|
|
// Param check |
214
|
|
|
if (empty($ar_new)) |
215
|
|
|
return array( |
216
|
|
|
'code' => -1, |
217
|
|
|
'msg' => 'New array empty.', |
218
|
|
|
); |
219
|
|
|
if (is_null($db)) |
220
|
|
|
$db = $this->oDb; |
221
|
|
|
|
222
|
|
|
// Loop for table |
223
|
|
|
$ar_diff = array(); |
224
|
|
|
foreach ($ar_new as $tbl => $ar_rows) { |
225
|
|
|
// Convert single array to 2-dim array |
226
|
|
|
if (!isset($ar_rows[0])) |
227
|
|
|
$ar_rows = array($ar_rows); |
228
|
|
|
if (!empty($ar_old[$tbl]) && !isset($ar_old[$tbl][0])) |
229
|
|
|
$ar_old[$tbl] = array($ar_old[$tbl]); |
230
|
|
|
|
231
|
|
|
$ar_col_pk = $db->GetMetaPrimaryKey($tbl); |
232
|
|
|
if (!is_array($ar_col_pk)) |
233
|
|
|
$ar_col_pk = array($ar_col_pk); |
234
|
|
|
|
235
|
|
|
// Loop for rows |
236
|
|
|
foreach ($ar_rows as $i_row => $row) { |
237
|
|
|
// Check all PK exist in row data |
238
|
|
|
foreach ($ar_col_pk as $s_pk) { |
239
|
|
|
// isset($v) assume $v = NULL is false |
240
|
|
|
if (!array_key_exists($s_pk, $row)) |
241
|
|
|
return array( |
242
|
|
|
'code' => -2, |
243
|
|
|
'msg' => 'Table ' . $tbl . ', PK ' . $s_pk |
244
|
|
|
. ' not assigned in new array, index: ' |
245
|
|
|
. $i_row . '.', |
246
|
|
|
); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
$ar_col = array_keys($row); |
250
|
|
|
$ar_val_pk = array_intersect_key($row |
251
|
|
|
, array_fill_keys($ar_col_pk, NULL)); |
252
|
|
|
|
253
|
|
|
// Got old data array |
254
|
|
|
if (!isset($ar_old[$tbl][$i_row])) { |
255
|
|
|
// Query from db by PK from new array |
256
|
|
|
$rs = $db->GetDataByPk($tbl, $ar_val_pk, $ar_col |
257
|
|
|
, $ar_col_pk); |
258
|
|
|
if (is_null($rs)) |
259
|
|
|
$rs = array(); |
260
|
|
|
if (!is_array($rs)) { |
261
|
|
|
// Row only have one column, convert back to array |
262
|
|
|
$rs = array($ar_col[0] => $rs); |
263
|
|
|
} |
264
|
|
|
$ar_old[$tbl][$i_row] = $rs; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
// Do diff for this row |
268
|
|
|
$ar = $this->DbDiffRow($row, $ar_old[$tbl][$i_row] |
269
|
|
|
, $ar_col_pk); |
270
|
|
|
if (!empty($ar)) |
271
|
|
|
$ar_diff['diff'][$tbl][] = $ar; |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
$ar_diff['code'] = 0; |
276
|
|
|
$ar_diff['msg'] = 'Successful.'; |
277
|
|
|
$ar_diff['flag'] = 0; |
278
|
|
|
return $ar_diff; |
279
|
|
|
} // end of func DbDiff |
280
|
|
|
|
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Execute DbDiff()'s result to modify db |
284
|
|
|
* |
285
|
|
|
* @param array $ar_diff Same with DbDiff()'s result |
286
|
|
|
* @param Adodb $db |
287
|
|
|
* @return int Rows modified, < 0 when error. |
288
|
|
|
* @see DbDiff() |
289
|
|
|
*/ |
290
|
|
View Code Duplication |
public function DbDiffCommit (array &$ar_diff, Adodb $db = NULL) { |
|
|
|
|
291
|
|
|
// Condition check |
292
|
|
|
if (empty($ar_diff) || empty($ar_diff['diff'])) |
293
|
|
|
// No diff data |
294
|
|
|
return -2; |
295
|
|
|
if (0 > $ar_diff['code']) |
296
|
|
|
// Previous op not successful |
297
|
|
|
return -3; |
298
|
|
|
if (100 == $ar_diff['flag']) |
299
|
|
|
// Already committed |
300
|
|
|
return -4; |
301
|
|
|
|
302
|
|
|
if (is_null($db)) |
303
|
|
|
$db = $this->oDb; |
304
|
|
|
|
305
|
|
|
// Generate sql |
306
|
|
|
$ar_sql_all = array(); |
307
|
|
|
foreach ($ar_diff['diff'] as $tbl => $ar_rows) { |
308
|
|
|
if (empty($ar_rows)) |
309
|
|
|
continue; |
310
|
|
|
foreach ($ar_rows as $i_row => $row) { |
311
|
|
|
$ar_sql = array(); |
312
|
|
|
switch ($row['mode']) { |
313
|
|
|
case 'INSERT': |
314
|
|
|
$ar_sql['INSERT'] = $tbl; |
315
|
|
|
$ar_col = $row['pk'] + $row['col']; // Sure not empty |
316
|
|
|
foreach ($ar_col as $k => $v) |
317
|
|
|
$ar_sql['VALUES'][$k] = $v['new']; |
318
|
|
|
break; |
319
|
|
|
case 'DELETE': |
320
|
|
|
$ar_sql['DELETE'] = $tbl; |
321
|
|
|
foreach ($row['pk'] as $k => $v) |
322
|
|
|
$ar_sql['WHERE'][] = $k . ' = ' |
323
|
|
|
. $db->QuoteValue($tbl, $k, $v['old']); |
324
|
|
|
// Limit rowcount to 1 for safety |
325
|
|
|
$ar_sql['LIMIT'] = 1; |
326
|
|
|
break; |
327
|
|
|
case 'UPDATE': |
328
|
|
|
$ar_sql['UPDATE'] = $tbl; |
329
|
|
|
foreach ($row['col'] as $k => $v) |
330
|
|
|
$ar_sql['SET'][$k] = $v['new']; |
331
|
|
|
foreach ($row['pk'] as $k => $v) |
332
|
|
|
$ar_sql['WHERE'][] = $k . ' = ' |
333
|
|
|
. $db->QuoteValue($tbl, $k, $v['new']); |
334
|
|
|
// Limit rowcount to 1 for safety |
335
|
|
|
$ar_sql['LIMIT'] = 1; |
336
|
|
|
break; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
if (!empty($ar_sql)) { |
340
|
|
|
$ar_sql_all[] = $db->GenSql($ar_sql); |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
// Execute sql |
346
|
|
|
$i_cnt = 0; |
347
|
|
|
if (!empty($ar_sql_all)) { |
348
|
|
|
$b_error = false; |
349
|
|
|
$db->BeginTrans(); |
350
|
|
|
while (!$b_error && !empty($ar_sql_all)) { |
351
|
|
|
$db->Execute(array_shift($ar_sql_all)); |
352
|
|
|
if (0 != $db->ErrorNo()) { |
353
|
|
|
$b_error = true; |
354
|
|
|
$this->Log('DbDiffCommit error ' . $db->ErrorNo() |
355
|
|
|
. ' : ' . $db->ErrorMsg()); |
356
|
|
|
} |
357
|
|
|
else |
358
|
|
|
$i_cnt += $db->Affected_Rows(); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
if ($b_error) { |
362
|
|
|
$ar_diff['code'] = abs($db->ErrorNo()) * -1; |
363
|
|
|
$ar_diff['msg'] = $db->ErrorMsg(); |
364
|
|
|
$db->RollbackTrans(); |
365
|
|
|
return -1; |
366
|
|
|
} |
367
|
|
|
else { |
368
|
|
|
$db->CommitTrans(); |
369
|
|
|
// Modify diff info |
370
|
|
|
$ar_diff['code'] = $i_cnt; |
371
|
|
|
$ar_diff['flag'] = 100; |
372
|
|
|
return $i_cnt; |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
return $i_cnt; |
376
|
|
|
} // end of func DbDiffCommit |
377
|
|
|
|
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Do DbDiff() and commit diff result |
381
|
|
|
* |
382
|
|
|
* Param and result same with DbDiff() |
383
|
|
|
* |
384
|
|
|
* @param array $ar_new |
385
|
|
|
* @param Adodb $db |
386
|
|
|
* @param array $ar_old |
387
|
|
|
* @return array |
388
|
|
|
* @see DbDiff() |
389
|
|
|
*/ |
390
|
|
|
public function DbDiffExec (array $ar_new, Adodb $db = NULL, array $ar_old = NULL) { |
391
|
|
|
$ar_diff = $this->DbDiff($ar_new, $db, $ar_old); |
392
|
|
|
if (0 == $ar_diff['code']) { |
393
|
|
|
$i = $this->DbDiffCommit($ar_diff); |
394
|
|
|
if (0 > $i) |
395
|
|
|
$this->Log('DbDiffExec error: ' . $i, 4); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
return $ar_diff; |
399
|
|
|
} // end of func DbDiffExec |
400
|
|
|
|
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Rollback committed DbDiff() result |
404
|
|
|
* |
405
|
|
|
* @param array $ar_diff Same with DbDiff()'s result |
406
|
|
|
* @param Adodb $db |
407
|
|
|
* @return int Rows modified, < 0 when error. |
408
|
|
|
* @see DbDiff() |
409
|
|
|
*/ |
410
|
|
View Code Duplication |
public function DbDiffRollback (array &$ar_diff, Adodb $db = NULL) { |
|
|
|
|
411
|
|
|
// Condition check |
412
|
|
|
if (empty($ar_diff) || empty($ar_diff['diff'])) |
413
|
|
|
// No diff data |
414
|
|
|
return -2; |
415
|
|
|
if (0 > $ar_diff['code']) |
416
|
|
|
// Previous op not successful |
417
|
|
|
return -3; |
418
|
|
|
if (100 != $ar_diff['flag']) |
419
|
|
|
// Not committed |
420
|
|
|
return -4; |
421
|
|
|
|
422
|
|
|
if (is_null($db)) |
423
|
|
|
$db = $this->oDb; |
424
|
|
|
|
425
|
|
|
// Generate sql |
426
|
|
|
$ar_sql_all = array(); |
427
|
|
|
foreach ($ar_diff['diff'] as $tbl => $ar_rows) { |
428
|
|
|
if (empty($ar_rows)) |
429
|
|
|
continue; |
430
|
|
|
foreach ($ar_rows as $i_row => $row) { |
431
|
|
|
$ar_sql = array(); |
432
|
|
|
switch ($row['mode']) { |
433
|
|
|
case 'INSERT': |
434
|
|
|
$ar_sql['DELETE'] = $tbl; |
435
|
|
|
foreach ($row['pk'] as $k => $v) |
436
|
|
|
$ar_sql['WHERE'][] = $k . ' = ' |
437
|
|
|
. $db->QuoteValue($tbl, $k, $v['new']); |
438
|
|
|
// Limit rowcount to 1 for safety |
439
|
|
|
$ar_sql['LIMIT'] = 1; |
440
|
|
|
break; |
441
|
|
|
case 'DELETE': |
442
|
|
|
$ar_sql['INSERT'] = $tbl; |
443
|
|
|
$ar_col = $row['pk'] + $row['col']; // Sure not empty |
444
|
|
|
foreach ($ar_col as $k => $v) |
445
|
|
|
$ar_sql['VALUES'][$k] = $v['old']; |
446
|
|
|
break; |
447
|
|
|
case 'UPDATE': |
448
|
|
|
$ar_sql['UPDATE'] = $tbl; |
449
|
|
|
foreach ($row['col'] as $k => $v) |
450
|
|
|
$ar_sql['SET'][$k] = $v['old']; |
451
|
|
|
foreach ($row['pk'] as $k => $v) |
452
|
|
|
$ar_sql['WHERE'][] = $k . ' = ' |
453
|
|
|
. $db->QuoteValue($tbl, $k, $v['old']); |
454
|
|
|
// Limit rowcount to 1 for safety |
455
|
|
|
$ar_sql['LIMIT'] = 1; |
456
|
|
|
break; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
if (!empty($ar_sql)) { |
460
|
|
|
$ar_sql_all[] = $db->GenSql($ar_sql); |
461
|
|
|
} |
462
|
|
|
} |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
// Execute sql |
466
|
|
|
$i_cnt = 0; |
467
|
|
|
if (!empty($ar_sql_all)) { |
468
|
|
|
$b_error = false; |
469
|
|
|
$db->BeginTrans(); |
470
|
|
|
while (!$b_error && !empty($ar_sql_all)) { |
471
|
|
|
$db->Execute(array_shift($ar_sql_all)); |
472
|
|
|
if (0 != $db->ErrorNo()) { |
473
|
|
|
$b_error = true; |
474
|
|
|
$this->Log('DbDiffRollback error ' . $db->ErrorNo() |
475
|
|
|
. ' : ' . $db->ErrorMsg()); |
476
|
|
|
} |
477
|
|
|
else |
478
|
|
|
$i_cnt += $db->Affected_Rows(); |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
if ($b_error) { |
482
|
|
|
$ar_diff['code'] = abs($db->ErrorNo()) * -1; |
483
|
|
|
$ar_diff['msg'] = $db->ErrorMsg(); |
484
|
|
|
$db->RollbackTrans(); |
485
|
|
|
return -1; |
486
|
|
|
} |
487
|
|
|
else { |
488
|
|
|
$db->CommitTrans(); |
489
|
|
|
// Modify diff info |
490
|
|
|
$ar_diff['code'] = $i_cnt; |
491
|
|
|
$ar_diff['flag'] = -100; |
492
|
|
|
return $i_cnt; |
493
|
|
|
} |
494
|
|
|
} |
495
|
|
|
return $i_cnt; |
496
|
|
|
} // end of func DbDiffRollback |
497
|
|
|
|
498
|
|
|
|
499
|
|
|
/** |
500
|
|
|
* Compare a row's for DbDiff() |
501
|
|
|
* |
502
|
|
|
* Param and result structure, see DbDiff() |
503
|
|
|
* |
504
|
|
|
* $ar_new MUST contain all PK columns. |
505
|
|
|
* |
506
|
|
|
* @param array $ar_new |
507
|
|
|
* @param array $ar_old |
508
|
|
|
* @param array $ar_pk NULL to use first col in $ar_new |
509
|
|
|
* @return array |
510
|
|
|
* @see DbDiff() |
511
|
|
|
*/ |
512
|
|
|
public function DbDiffRow (array $ar_new, array $ar_old = NULL |
513
|
|
|
, array $ar_pk = NULL) { |
514
|
|
|
// Check param |
515
|
|
|
if (is_null($ar_pk)) { |
516
|
|
|
$ar_pk = array_keys($ar_new); |
517
|
|
|
$ar_pk = array($ar_pk[0]); |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
$ar_diff = array(); |
521
|
|
|
|
522
|
|
|
// Detect mode: INSERT/UPDATE/DELETE |
523
|
|
|
$b_new_null = false; |
524
|
|
|
$b_old_null = false; |
525
|
|
|
foreach ($ar_pk as $s_pk) { |
526
|
|
|
if (is_null($ar_new[$s_pk])) |
527
|
|
|
$b_new_null = true; |
528
|
|
|
elseif ($b_new_null) |
529
|
|
|
// Mixed NULL with non-NULL PK value |
530
|
|
|
$this->Log('New array PK ' . $s_pk . ' got value and mixed' |
531
|
|
|
. ' with other NULL PK.', 4); |
532
|
|
|
|
533
|
|
|
if (!isset($ar_old[$s_pk]) || is_null($ar_old[$s_pk])) |
534
|
|
|
$b_old_null = true; |
535
|
|
|
elseif ($b_old_null) |
536
|
|
|
// Mixed NULL with non-NULL PK value |
537
|
|
|
$this->Log('Old array PK ' . $s_pk . ' got value and mixed' |
538
|
|
|
. ' with other NULL PK.', 4); |
539
|
|
|
} |
540
|
|
|
if (true == $b_new_null && false == $b_old_null) { |
|
|
|
|
541
|
|
|
$ar_diff['mode'] = 'DELETE'; |
542
|
|
|
} |
543
|
|
|
elseif (false == $b_new_null && true == $b_old_null) { |
|
|
|
|
544
|
|
|
$ar_diff['mode'] = 'INSERT'; |
545
|
|
|
} |
546
|
|
|
elseif (false == $b_new_null && false == $b_old_null) { |
|
|
|
|
547
|
|
|
$ar_diff['mode'] = 'UPDATE'; |
548
|
|
|
} |
549
|
|
|
else { |
550
|
|
|
// New, old are all NULL PK, should not occur |
551
|
|
|
$this->Log('New and old array\'s PK are all NULL, nothing to do.', 4); |
552
|
|
|
return array(); |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
// PK, include even new same with old |
556
|
|
|
foreach ($ar_pk as $s_pk) { |
557
|
|
|
$ar_diff['pk'][$s_pk] = array( |
558
|
|
|
'new' => $ar_new[$s_pk], |
559
|
|
|
'old' => isset($ar_old[$s_pk]) ? $ar_old[$s_pk] : NULL, |
560
|
|
|
); |
561
|
|
|
// Remove it in normal column |
562
|
|
|
unset($ar_new[$s_pk]); |
563
|
|
|
unset($ar_old[$s_pk]); |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
// Other column, skip same value |
567
|
|
|
$ar_diff['col'] = array(); |
568
|
|
|
$ar_col = array(); |
569
|
|
|
if (!empty($ar_new)) |
570
|
|
|
$ar_col += array_keys($ar_new); |
571
|
|
|
if (!empty($ar_old)) |
572
|
|
|
$ar_col += array_keys($ar_old); |
573
|
|
|
if (!empty($ar_col)) { |
574
|
|
|
foreach ($ar_col as $col) { |
575
|
|
|
$v_new = isset($ar_new[$col]) ? $ar_new[$col] : NULL; |
576
|
|
|
$v_old = isset($ar_old[$col]) ? $ar_old[$col] : NULL; |
577
|
|
|
|
578
|
|
|
// Manual set useless column data to NULL, avoid necessary |
579
|
|
|
// column been skipped by equal check later, to keep diff |
580
|
|
|
// result include necessary column, they maybe used in |
581
|
|
|
// rollback. |
582
|
|
|
// Force new value of DELETE mode to NULL |
583
|
|
|
if ('DELETE' == $ar_diff['mode']) |
584
|
|
|
$v_new = NULL; |
585
|
|
|
// Force old value of INSERT mode to NULL |
586
|
|
|
if ('INSERT' == $ar_diff['mode']) |
587
|
|
|
$v_old = NULL; |
588
|
|
|
|
589
|
|
|
if (is_null($v_new) && is_null($v_old)) |
590
|
|
|
continue; |
591
|
|
|
if (!is_null($v_new) && !is_null($v_old) && $v_new == $v_old) |
592
|
|
|
continue; |
593
|
|
|
|
594
|
|
|
$ar_diff['col'][$col] = array( |
595
|
|
|
'new' => $v_new, |
596
|
|
|
'old' => $v_old, |
597
|
|
|
); |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
// Skip UPDATE with no col change |
602
|
|
|
if ('UPDATE' == $ar_diff['mode'] && empty($ar_diff['col'])) |
603
|
|
|
$ar_diff = array(); |
604
|
|
|
|
605
|
|
|
return $ar_diff; |
606
|
|
|
} // end of func DbDiffRow |
607
|
|
|
|
608
|
|
|
|
609
|
|
|
/** |
610
|
|
|
* Define id relation between db and form - action name |
611
|
|
|
* |
612
|
|
|
* Key is id from db, value id id from form. |
613
|
|
|
* So we can easily turn data between from/post and db. |
614
|
|
|
* |
615
|
|
|
* If one side is not directly assign from another side, |
616
|
|
|
* do not define it here, |
617
|
|
|
* they should be specially treated in other method |
618
|
|
|
* after use this to treat all other easy ones. |
619
|
|
|
* |
620
|
|
|
* This is only an example func. |
621
|
|
|
* @return array |
622
|
|
|
*/ |
623
|
|
|
/* |
624
|
|
|
protected function FormActionNameDef () { |
625
|
|
|
$ar = array(); |
626
|
|
|
$this->FormDefSameId($ar, 'field_same_id'); |
627
|
|
|
$ar['id_db'] = 'id_form'; |
628
|
|
|
|
629
|
|
|
return $ar; |
630
|
|
|
} // end of func FormActionNameDef |
631
|
|
|
*/ |
632
|
|
|
|
633
|
|
|
|
634
|
|
|
/** |
635
|
|
|
* Define id relation between db and form, the same id ones |
636
|
|
|
* |
637
|
|
|
* For detail note, see example func FormActionNameDef(). |
638
|
|
|
* @param array &$ar Config array |
639
|
|
|
* @param string $id Field id |
640
|
|
|
*/ |
641
|
|
|
protected function FormDefSameId (&$ar, $id) { |
642
|
|
|
$ar[$id] = $id; |
643
|
|
|
} // end of func FormDefSameId |
644
|
|
|
|
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Get data from form, according setting in FormActionNameDef() |
648
|
|
|
* |
649
|
|
|
* Data source is $_POST. |
650
|
|
|
* @param string $form Form name |
651
|
|
|
* @return array |
652
|
|
|
*/ |
653
|
|
View Code Duplication |
public function FormGet ($form) { |
|
|
|
|
654
|
|
|
$s_form = 'Form' . StrUnderline2Ucfirst($form, true) . 'Def'; |
|
|
|
|
655
|
|
|
|
656
|
|
|
// If define method missing, return empty array |
657
|
|
|
if (false == method_exists($this, $s_form)) |
|
|
|
|
658
|
|
|
return array(); |
659
|
|
|
|
660
|
|
|
// Do data convert |
661
|
|
|
|
662
|
|
|
$ar_conf = $this->{$s_form}(); |
663
|
|
|
// Let key is id from form |
664
|
|
|
$ar_conf = array_flip($ar_conf); |
665
|
|
|
|
666
|
|
|
$ar = array(); |
667
|
|
|
if (!empty($ar_conf)) { |
668
|
|
|
foreach ($ar_conf as $k_form => $k_db) { |
669
|
|
|
$ar[$k_db] = GetPost($k_form); |
|
|
|
|
670
|
|
|
} |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
return $ar; |
674
|
|
|
} // end of func FormGet |
675
|
|
|
|
676
|
|
|
|
677
|
|
|
/** |
678
|
|
|
* Prepare data from db for form display |
679
|
|
|
* |
680
|
|
|
* According setting in FormActionNameDef() |
681
|
|
|
* @param string $form Form name |
682
|
|
|
* @return array Can use in Form::AddElementValue() |
683
|
|
|
* @see Form::AddElementValue() |
684
|
|
|
*/ |
685
|
|
View Code Duplication |
public function FormSet ($form) { |
|
|
|
|
686
|
|
|
$s_form = 'Form' . StrUnderline2Ucfirst($form, true) . 'Def'; |
|
|
|
|
687
|
|
|
|
688
|
|
|
// If define method missing, return empty array |
689
|
|
|
if (false == method_exists($this, $s_form)) |
|
|
|
|
690
|
|
|
return array(); |
691
|
|
|
|
692
|
|
|
// Do data convert |
693
|
|
|
|
694
|
|
|
// Key is id from db |
695
|
|
|
$ar_conf = $this->{$s_form}(); |
696
|
|
|
|
697
|
|
|
$ar = array(); |
698
|
|
|
if (!empty($ar_conf)) { |
699
|
|
|
foreach ($ar_conf as $k_db => $k_form) { |
700
|
|
|
$ar[$k_form] = HtmlEncode($k_db); |
|
|
|
|
701
|
|
|
} |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
return $ar; |
705
|
|
|
} // end of func FormSet |
706
|
|
|
|
707
|
|
|
|
708
|
|
|
/** |
709
|
|
|
* New Cache instance |
710
|
|
|
* Shoud be overwrited by sub class if use cache. |
711
|
|
|
* |
712
|
|
|
* @return object |
713
|
|
|
*/ |
714
|
|
|
protected function NewObjCache () { |
715
|
|
|
return Cache::Create(''); |
|
|
|
|
716
|
|
|
} // end of func NewObjCache |
717
|
|
|
|
718
|
|
|
|
719
|
|
|
/** |
720
|
|
|
* New db object |
721
|
|
|
* |
722
|
|
|
* @return object |
723
|
|
|
*/ |
724
|
|
|
protected function NewObjDb () { |
725
|
|
|
return $this->DbConn($this->aCfg['dbprofile']); |
726
|
|
|
} // end of func NewObjDb |
727
|
|
|
|
728
|
|
|
|
729
|
|
|
/** |
730
|
|
|
* Set default config. |
731
|
|
|
* |
732
|
|
|
* @return object |
733
|
|
|
*/ |
734
|
|
|
protected function SetCfgDefault () { |
735
|
|
|
parent::SetCfgDefault(); |
736
|
|
|
|
737
|
|
|
// Db profile |
738
|
|
|
$this->aCfg['dbprofile'] = array( |
739
|
|
|
'type' => 'mysql', |
740
|
|
|
'host' => 'localhost', |
741
|
|
|
'user' => 'user', |
742
|
|
|
'pass' => 'pass', |
743
|
|
|
'name' => 'name', |
744
|
|
|
'lang' => 'utf-8', |
745
|
|
|
); |
746
|
|
|
|
747
|
|
|
return $this; |
748
|
|
|
} // end of func SetCfgDefault |
749
|
|
|
|
750
|
|
|
|
751
|
|
|
} // end of class Module |
752
|
|
|
?> |
|
|
|
|
753
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.