1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace RedBeanPHP { |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* RedBean Logging interface. |
7
|
|
|
* Provides a uniform and convenient logging |
8
|
|
|
* interface throughout RedBeanPHP. |
9
|
|
|
* |
10
|
|
|
* @file RedBean/Logging.php |
11
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
12
|
|
|
* @license BSD/GPLv2 |
13
|
|
|
* |
14
|
|
|
* @copyright |
15
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
16
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
17
|
|
|
* with this source code in the file license.txt. |
18
|
|
|
*/ |
19
|
|
|
interface Logger |
20
|
|
|
{ |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* A logger (for\PDO or OCI driver) needs to implement the log method. |
24
|
|
|
* The log method will receive logging data. Note that the number of parameters is 0, this means |
25
|
|
|
* all parameters are optional and the number may vary. This way the logger can be used in a very |
26
|
|
|
* flexible way. Sometimes the logger is used to log a simple error message and in other |
27
|
|
|
* situations sql and bindings are passed. |
28
|
|
|
* The log method should be able to accept all kinds of parameters and data by using |
29
|
|
|
* functions like func_num_args/func_get_args. |
30
|
|
|
* |
31
|
|
|
* @return void |
32
|
|
|
*/ |
33
|
|
|
public function log(); |
34
|
|
|
} |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
namespace RedBeanPHP\Logger { |
38
|
|
|
|
39
|
|
|
use RedBeanPHP\Logger as Logger; |
|
|
|
|
40
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
41
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Logger. Provides a basic logging function for RedBeanPHP. |
45
|
|
|
* |
46
|
|
|
* @file RedBeanPHP/Logger.php |
47
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
48
|
|
|
* @license BSD/GPLv2 |
49
|
|
|
* |
50
|
|
|
* @copyright |
51
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij |
52
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
53
|
|
|
* with this source code in the file license.txt. |
54
|
|
|
*/ |
55
|
|
|
class RDefault implements Logger |
|
|
|
|
56
|
|
|
{ |
57
|
|
|
/** |
58
|
|
|
* Logger modes |
59
|
|
|
*/ |
60
|
|
|
const C_LOGGER_ECHO = 0; |
61
|
|
|
const C_LOGGER_ARRAY = 1; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var integer |
65
|
|
|
*/ |
66
|
|
|
protected $mode = 0; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var array |
70
|
|
|
*/ |
71
|
|
|
protected $logs = array(); |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Default logger method logging to STDOUT. |
75
|
|
|
* This is the default/reference implementation of a logger. |
76
|
|
|
* This method will write the message value to STDOUT (screen) unless |
77
|
|
|
* you have changed the mode of operation to C_LOGGER_ARRAY. |
78
|
|
|
* |
79
|
|
|
* @param $message (optional) message to log (might also be data or output) |
80
|
|
|
* |
81
|
|
|
* @return void |
82
|
|
|
*/ |
83
|
|
|
public function log() |
84
|
|
|
{ |
85
|
|
|
if ( func_num_args() < 1 ) return; |
86
|
|
|
|
87
|
|
|
foreach ( func_get_args() as $argument ) { |
88
|
|
|
if ( is_array( $argument ) ) { |
89
|
|
|
$log = print_r( $argument, TRUE ); |
90
|
|
|
if ( $this->mode === self::C_LOGGER_ECHO ) { |
91
|
|
|
echo $log; |
92
|
|
|
} else { |
93
|
|
|
$this->logs[] = $log; |
94
|
|
|
} |
95
|
|
|
} else { |
96
|
|
|
if ( $this->mode === self::C_LOGGER_ECHO ) { |
97
|
|
|
echo $argument; |
98
|
|
|
} else { |
99
|
|
|
$this->logs[] = $argument; |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
if ( $this->mode === self::C_LOGGER_ECHO ) echo "<br>\n"; |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Returns the internal log array. |
109
|
|
|
* The internal log array is where all log messages are stored. |
110
|
|
|
* |
111
|
|
|
* @return array |
112
|
|
|
*/ |
113
|
|
|
public function getLogs() |
114
|
|
|
{ |
115
|
|
|
return $this->logs; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Clears the internal log array, removing all |
120
|
|
|
* previously stored entries. |
121
|
|
|
* |
122
|
|
|
* @return self |
123
|
|
|
*/ |
124
|
|
|
public function clear() |
125
|
|
|
{ |
126
|
|
|
$this->logs = array(); |
127
|
|
|
return $this; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Selects a logging mode. |
132
|
|
|
* There are several options available. |
133
|
|
|
* |
134
|
|
|
* C_LOGGER_ARRAY - log silently, stores entries in internal log array only |
135
|
|
|
* C_LOGGER_ECHO - also forward log messages directly to STDOUT |
136
|
|
|
* |
137
|
|
|
* @param integer $mode mode of operation for logging object |
138
|
|
|
* |
139
|
|
|
* @return self |
140
|
|
|
*/ |
141
|
|
|
public function setMode( $mode ) |
142
|
|
|
{ |
143
|
|
|
if ($mode !== self::C_LOGGER_ARRAY && $mode !== self::C_LOGGER_ECHO ) { |
144
|
|
|
throw new RedException( 'Invalid mode selected for logger, use C_LOGGER_ARRAY or C_LOGGER_ECHO.' ); |
145
|
|
|
} |
146
|
|
|
$this->mode = $mode; |
147
|
|
|
return $this; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Searches for all log entries in internal log array |
152
|
|
|
* for $needle and returns those entries. |
153
|
|
|
* This method will return an array containing all matches for your |
154
|
|
|
* search query. |
155
|
|
|
* |
156
|
|
|
* @param string $needle phrase to look for in internal log array |
157
|
|
|
* |
158
|
|
|
* @return array |
159
|
|
|
*/ |
160
|
|
|
public function grep( $needle ) |
161
|
|
|
{ |
162
|
|
|
$found = array(); |
163
|
|
|
foreach( $this->logs as $logEntry ) { |
164
|
|
|
if ( strpos( $logEntry, $needle ) !== FALSE ) $found[] = $logEntry; |
165
|
|
|
} |
166
|
|
|
return $found; |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
namespace RedBeanPHP\Logger\RDefault { |
172
|
|
|
|
173
|
|
|
use RedBeanPHP\Logger as Logger; |
|
|
|
|
174
|
|
|
use RedBeanPHP\Logger\RDefault as RDefault; |
|
|
|
|
175
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
176
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Debug logger. |
180
|
|
|
* A special logger for debugging purposes. |
181
|
|
|
* Provides debugging logging functions for RedBeanPHP. |
182
|
|
|
* |
183
|
|
|
* @file RedBeanPHP/Logger/RDefault/Debug.php |
184
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
185
|
|
|
* @license BSD/GPLv2 |
186
|
|
|
* |
187
|
|
|
* @copyright |
188
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij |
189
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
190
|
|
|
* with this source code in the file license.txt. |
191
|
|
|
*/ |
192
|
|
|
class Debug extends RDefault implements Logger |
|
|
|
|
193
|
|
|
{ |
194
|
|
|
/** |
195
|
|
|
* @var integer |
196
|
|
|
*/ |
197
|
|
|
private $strLen = 40; |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Writes a query for logging with all bindings / params filled |
201
|
|
|
* in. |
202
|
|
|
* |
203
|
|
|
* @param string $newSql the query |
204
|
|
|
* @param array $bindings the bindings to process (key-value pairs) |
|
|
|
|
205
|
|
|
* |
206
|
|
|
* @return string |
207
|
|
|
*/ |
208
|
|
|
private function writeQuery( $newSql, $newBindings ) |
209
|
|
|
{ |
210
|
|
|
//avoid str_replace collisions: slot1 and slot10 (issue 407). |
211
|
|
|
uksort( $newBindings, function( $a, $b ) { |
212
|
|
|
return ( strlen( $b ) - strlen( $a ) ); |
213
|
|
|
} ); |
214
|
|
|
|
215
|
|
|
$newStr = $newSql; |
216
|
|
|
foreach( $newBindings as $slot => $value ) { |
217
|
|
|
if ( strpos( $slot, ':' ) === 0 ) { |
218
|
|
|
$newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr ); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
return $newStr; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Fills in a value of a binding and truncates the |
226
|
|
|
* resulting string if necessary. |
227
|
|
|
* |
228
|
|
|
* @param mixed $value |
229
|
|
|
* |
230
|
|
|
* @return string |
231
|
|
|
*/ |
232
|
|
|
protected function fillInValue( $value ) |
233
|
|
|
{ |
234
|
|
|
if ( is_null( $value ) ) $value = 'NULL'; |
235
|
|
|
|
236
|
|
|
$value = strval( $value ); |
237
|
|
|
if ( strlen( $value ) > ( $this->strLen ) ) { |
238
|
|
|
$value = substr( $value, 0, ( $this->strLen ) ).'... '; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
if ( !is_numeric( $value ) && $value !== 'NULL') { |
242
|
|
|
$value = '\''.$value.'\''; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
return $value; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Dependending on the current mode of operation, |
250
|
|
|
* this method will either log and output to STDIN or |
251
|
|
|
* just log. |
252
|
|
|
* |
253
|
|
|
* @param string $str string to log or output and log |
254
|
|
|
* |
255
|
|
|
* @return void |
256
|
|
|
*/ |
257
|
|
|
protected function output( $str ) |
258
|
|
|
{ |
259
|
|
|
$this->logs[] = $str; |
260
|
|
|
if ( !$this->mode ) echo $str .'<br />'; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Normalizes the slots in an SQL string. |
265
|
|
|
* Replaces question mark slots with :slot1 :slot2 etc. |
266
|
|
|
* |
267
|
|
|
* @param string $sql sql to normalize |
268
|
|
|
* |
269
|
|
|
* @return string |
270
|
|
|
*/ |
271
|
|
|
protected function normalizeSlots( $sql ) |
272
|
|
|
{ |
273
|
|
|
$i = 0; |
274
|
|
|
$newSql = $sql; |
275
|
|
|
while($i < 20 && strpos($newSql, '?') !== FALSE ){ |
276
|
|
|
$pos = strpos( $newSql, '?' ); |
277
|
|
|
$slot = ':slot'.$i; |
278
|
|
|
$begin = substr( $newSql, 0, $pos ); |
279
|
|
|
$end = substr( $newSql, $pos+1 ); |
280
|
|
|
$newSql = $begin . $slot . $end; |
281
|
|
|
$i++; |
282
|
|
|
} |
283
|
|
|
return $newSql; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Normalizes the bindings. |
288
|
|
|
* Replaces numeric binding keys with :slot1 :slot2 etc. |
289
|
|
|
* |
290
|
|
|
* @param array $bindings bindings to normalize |
291
|
|
|
* |
292
|
|
|
* @return array |
293
|
|
|
*/ |
294
|
|
|
protected function normalizeBindings( $bindings ) |
295
|
|
|
{ |
296
|
|
|
$i = 0; |
297
|
|
|
$newBindings = array(); |
298
|
|
|
foreach( $bindings as $key => $value ) { |
299
|
|
|
if ( is_numeric($key) ) { |
300
|
|
|
$newKey = ':slot'.$i; |
301
|
|
|
$newBindings[$newKey] = $value; |
302
|
|
|
$i++; |
303
|
|
|
} else { |
304
|
|
|
$newBindings[$key] = $value; |
305
|
|
|
} |
306
|
|
|
} |
307
|
|
|
return $newBindings; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Logger method. |
312
|
|
|
* |
313
|
|
|
* Takes a number of arguments tries to create |
314
|
|
|
* a proper debug log based on the available data. |
315
|
|
|
* |
316
|
|
|
* @return void |
317
|
|
|
*/ |
318
|
|
|
public function log() |
319
|
|
|
{ |
320
|
|
|
if ( func_num_args() < 1 ) return; |
321
|
|
|
|
322
|
|
|
$sql = func_get_arg( 0 ); |
323
|
|
|
|
324
|
|
|
if ( func_num_args() < 2) { |
325
|
|
|
$bindings = array(); |
326
|
|
|
} else { |
327
|
|
|
$bindings = func_get_arg( 1 ); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
if ( !is_array( $bindings ) ) { |
331
|
|
|
return $this->output( $sql ); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
$newSql = $this->normalizeSlots( $sql ); |
335
|
|
|
$newBindings = $this->normalizeBindings( $bindings ); |
336
|
|
|
$newStr = $this->writeQuery( $newSql, $newBindings ); |
337
|
|
|
$this->output( $newStr ); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Sets the max string length for the parameter output in |
342
|
|
|
* SQL queries. Set this value to a reasonable number to |
343
|
|
|
* keep you SQL queries readable. |
344
|
|
|
* |
345
|
|
|
* @param integer $len string length |
346
|
|
|
* |
347
|
|
|
* @return self |
348
|
|
|
*/ |
349
|
|
|
public function setParamStringLength( $len = 20 ) |
350
|
|
|
{ |
351
|
|
|
$this->strLen = max(0, $len); |
352
|
|
|
return $this; |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
namespace RedBeanPHP { |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Interface for database drivers. |
361
|
|
|
* The Driver API conforms to the ADODB pseudo standard |
362
|
|
|
* for database drivers. |
363
|
|
|
* |
364
|
|
|
* @file RedBeanPHP/Driver.php |
365
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
366
|
|
|
* @license BSD/GPLv2 |
367
|
|
|
* |
368
|
|
|
* @copyright |
369
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
370
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
371
|
|
|
* with this source code in the file license.txt. |
372
|
|
|
*/ |
373
|
|
|
interface Driver |
|
|
|
|
374
|
|
|
{ |
375
|
|
|
/** |
376
|
|
|
* Runs a query and fetches results as a multi dimensional array. |
377
|
|
|
* |
378
|
|
|
* @param string $sql SQL to be executed |
379
|
|
|
* @param array $bindings list of values to bind to SQL snippet |
380
|
|
|
* |
381
|
|
|
* @return array |
382
|
|
|
*/ |
383
|
|
|
public function GetAll( $sql, $bindings = array() ); |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Runs a query and fetches results as a column. |
387
|
|
|
* |
388
|
|
|
* @param string $sql SQL Code to execute |
389
|
|
|
* @param array $bindings list of values to bind to SQL snippet |
390
|
|
|
* |
391
|
|
|
* @return array |
392
|
|
|
*/ |
393
|
|
|
public function GetCol( $sql, $bindings = array() ); |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Runs a query and returns results as a single cell. |
397
|
|
|
* |
398
|
|
|
* @param string $sql SQL to execute |
399
|
|
|
* @param array $bindings list of values to bind to SQL snippet |
400
|
|
|
* |
401
|
|
|
* @return mixed |
402
|
|
|
*/ |
403
|
|
|
public function GetOne( $sql, $bindings = array() ); |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Runs a query and returns results as an associative array |
407
|
|
|
* indexed by the first column. |
408
|
|
|
* |
409
|
|
|
* @param string $sql SQL to execute |
410
|
|
|
* @param array $bindings list of values to bind to SQL snippet |
411
|
|
|
* |
412
|
|
|
* @return mixed |
413
|
|
|
*/ |
414
|
|
|
public function GetAssocRow( $sql, $bindings = array() ); |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Runs a query and returns a flat array containing the values of |
418
|
|
|
* one row. |
419
|
|
|
* |
420
|
|
|
* @param string $sql SQL to execute |
421
|
|
|
* @param array $bindings list of values to bind to SQL snippet |
422
|
|
|
* |
423
|
|
|
* @return array |
424
|
|
|
*/ |
425
|
|
|
public function GetRow( $sql, $bindings = array() ); |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* Executes SQL code and allows key-value binding. |
429
|
|
|
* This function allows you to provide an array with values to bind |
430
|
|
|
* to query parameters. For instance you can bind values to question |
431
|
|
|
* marks in the query. Each value in the array corresponds to the |
432
|
|
|
* question mark in the query that matches the position of the value in the |
433
|
|
|
* array. You can also bind values using explicit keys, for instance |
434
|
|
|
* array(":key"=>123) will bind the integer 123 to the key :key in the |
435
|
|
|
* SQL. This method has no return value. |
436
|
|
|
* |
437
|
|
|
* @param string $sql SQL Code to execute |
438
|
|
|
* @param array $bindings list of values to bind to SQL snippet |
439
|
|
|
* |
440
|
|
|
* @return array Affected Rows |
441
|
|
|
*/ |
442
|
|
|
public function Execute( $sql, $bindings = array() ); |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* Returns the latest insert ID if driver does support this |
446
|
|
|
* feature. |
447
|
|
|
* |
448
|
|
|
* @return integer |
449
|
|
|
*/ |
450
|
|
|
public function GetInsertID(); |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* Returns the number of rows affected by the most recent query |
454
|
|
|
* if the currently selected driver driver supports this feature. |
455
|
|
|
* |
456
|
|
|
* @return integer |
457
|
|
|
*/ |
458
|
|
|
public function Affected_Rows(); |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Returns a cursor-like object from the database. |
462
|
|
|
* |
463
|
|
|
* @param string $sql SQL code to execute |
464
|
|
|
* @param array $bindings Bindings |
465
|
|
|
* |
466
|
|
|
* @return mixed |
467
|
|
|
*/ |
468
|
|
|
public function GetCursor( $sql, $bindings = array() ); |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Toggles debug mode. In debug mode the driver will print all |
472
|
|
|
* SQL to the screen together with some information about the |
473
|
|
|
* results. All SQL code that passes through the driver will be |
474
|
|
|
* passes on to the screen for inspection. |
475
|
|
|
* This method has no return value. |
476
|
|
|
* |
477
|
|
|
* @param boolean $trueFalse turn on/off |
|
|
|
|
478
|
|
|
* |
479
|
|
|
* @return void |
480
|
|
|
*/ |
481
|
|
|
public function setDebugMode( $tf ); |
482
|
|
|
|
483
|
|
|
/** |
484
|
|
|
* Starts a transaction. |
485
|
|
|
* |
486
|
|
|
* @return void |
487
|
|
|
*/ |
488
|
|
|
public function CommitTrans(); |
489
|
|
|
|
490
|
|
|
/** |
491
|
|
|
* Commits a transaction. |
492
|
|
|
* |
493
|
|
|
* @return void |
494
|
|
|
*/ |
495
|
|
|
public function StartTrans(); |
496
|
|
|
|
497
|
|
|
/** |
498
|
|
|
* Rolls back a transaction. |
499
|
|
|
* |
500
|
|
|
* @return void |
501
|
|
|
*/ |
502
|
|
|
public function FailTrans(); |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* Resets the internal Query Counter. |
506
|
|
|
* |
507
|
|
|
* @return self |
508
|
|
|
*/ |
509
|
|
|
public function resetCounter(); |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Returns the number of SQL queries processed. |
513
|
|
|
* |
514
|
|
|
* @return integer |
515
|
|
|
*/ |
516
|
|
|
public function getQueryCount(); |
517
|
|
|
} |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
namespace RedBeanPHP\Driver { |
521
|
|
|
|
522
|
|
|
use RedBeanPHP\Driver as Driver; |
|
|
|
|
523
|
|
|
use RedBeanPHP\Logger as Logger; |
|
|
|
|
524
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
525
|
|
|
use RedBeanPHP\RedException\SQL as SQL; |
|
|
|
|
526
|
|
|
use RedBeanPHP\Logger\RDefault as RDefault; |
|
|
|
|
527
|
|
|
use RedBeanPHP\PDOCompatible as PDOCompatible; |
|
|
|
|
528
|
|
|
use RedBeanPHP\Cursor\PDOCursor as PDOCursor; |
|
|
|
|
529
|
|
|
|
530
|
|
|
/** |
531
|
|
|
* PDO Driver |
532
|
|
|
* This Driver implements the RedBean Driver API. |
533
|
|
|
* for RedBeanPHP. This is the standard / default database driver |
534
|
|
|
* for RedBeanPHP. |
535
|
|
|
* |
536
|
|
|
* @file RedBeanPHP/PDO.php |
537
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes |
538
|
|
|
* @license BSD/GPLv2 |
539
|
|
|
* |
540
|
|
|
* @copyright |
541
|
|
|
* copyright (c) Desfrenes & Gabor de Mooij and the RedBeanPHP community |
542
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
543
|
|
|
* with this source code in the file license.txt. |
544
|
|
|
*/ |
545
|
|
|
class RPDO implements Driver |
|
|
|
|
546
|
|
|
{ |
547
|
|
|
/** |
548
|
|
|
* @var integer |
549
|
|
|
*/ |
550
|
|
|
protected $max; |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* @var string |
554
|
|
|
*/ |
555
|
|
|
protected $dsn; |
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* @var boolean |
559
|
|
|
*/ |
560
|
|
|
protected $loggingEnabled = FALSE; |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* @var Logger |
564
|
|
|
*/ |
565
|
|
|
protected $logger = NULL; |
566
|
|
|
|
567
|
|
|
/** |
568
|
|
|
* @var PDO |
569
|
|
|
*/ |
570
|
|
|
protected $pdo; |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* @var integer |
574
|
|
|
*/ |
575
|
|
|
protected $affectedRows; |
576
|
|
|
|
577
|
|
|
/** |
578
|
|
|
* @var integer |
579
|
|
|
*/ |
580
|
|
|
protected $resultArray; |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* @var array |
584
|
|
|
*/ |
585
|
|
|
protected $connectInfo = array(); |
586
|
|
|
|
587
|
|
|
/** |
588
|
|
|
* @var boolean |
589
|
|
|
*/ |
590
|
|
|
protected $isConnected = FALSE; |
591
|
|
|
|
592
|
|
|
/** |
593
|
|
|
* @var bool |
594
|
|
|
*/ |
595
|
|
|
protected $flagUseStringOnlyBinding = FALSE; |
596
|
|
|
|
597
|
|
|
/** |
598
|
|
|
* @var integer |
599
|
|
|
*/ |
600
|
|
|
protected $queryCounter = 0; |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* @var string |
604
|
|
|
*/ |
605
|
|
|
protected $mysqlEncoding = ''; |
606
|
|
|
|
607
|
|
|
/** |
608
|
|
|
* Binds parameters. This method binds parameters to a PDOStatement for |
609
|
|
|
* Query Execution. This method binds parameters as NULL, INTEGER or STRING |
610
|
|
|
* and supports both named keys and question mark keys. |
611
|
|
|
* |
612
|
|
|
* @param PDOStatement $statement PDO Statement instance |
613
|
|
|
* @param array $bindings values that need to get bound to the statement |
614
|
|
|
* |
615
|
|
|
* @return void |
616
|
|
|
*/ |
617
|
|
|
protected function bindParams( $statement, $bindings ) |
618
|
|
|
{ |
619
|
|
|
foreach ( $bindings as $key => &$value ) { |
620
|
|
|
if ( is_integer( $key ) ) { |
621
|
|
|
if ( is_null( $value ) ) { |
622
|
|
|
$statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL ); |
623
|
|
View Code Duplication |
} elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) { |
624
|
|
|
$statement->bindParam( $key + 1, $value, \PDO::PARAM_INT ); |
625
|
|
|
} else { |
626
|
|
|
$statement->bindParam( $key + 1, $value, \PDO::PARAM_STR ); |
627
|
|
|
} |
628
|
|
|
} else { |
629
|
|
|
if ( is_null( $value ) ) { |
630
|
|
|
$statement->bindValue( $key, NULL, \PDO::PARAM_NULL ); |
631
|
|
View Code Duplication |
} elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) { |
632
|
|
|
$statement->bindParam( $key, $value, \PDO::PARAM_INT ); |
633
|
|
|
} else { |
634
|
|
|
$statement->bindParam( $key, $value, \PDO::PARAM_STR ); |
635
|
|
|
} |
636
|
|
|
} |
637
|
|
|
} |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* This method runs the actual SQL query and binds a list of parameters to the query. |
642
|
|
|
* slots. The result of the query will be stored in the protected property |
643
|
|
|
* $rs (always array). The number of rows affected (result of rowcount, if supported by database) |
644
|
|
|
* is stored in protected property $affectedRows. If the debug flag is set |
645
|
|
|
* this function will send debugging output to screen buffer. |
646
|
|
|
* |
647
|
|
|
* @param string $sql the SQL string to be send to database server |
648
|
|
|
* @param array $bindings the values that need to get bound to the query slots |
649
|
|
|
* |
650
|
|
|
* @return void |
651
|
|
|
* |
652
|
|
|
* @throws SQL |
653
|
|
|
*/ |
654
|
|
|
protected function runQuery( $sql, $bindings, $options = array() ) |
655
|
|
|
{ |
656
|
|
|
$this->connect(); |
657
|
|
|
if ( $this->loggingEnabled && $this->logger ) { |
658
|
|
|
$this->logger->log( $sql, $bindings ); |
|
|
|
|
659
|
|
|
} |
660
|
|
|
try { |
661
|
|
|
if ( strpos( 'pgsql', $this->dsn ) === 0 ) { |
662
|
|
|
$statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); |
663
|
|
|
} else { |
664
|
|
|
$statement = $this->pdo->prepare( $sql ); |
665
|
|
|
} |
666
|
|
|
$this->bindParams( $statement, $bindings ); |
667
|
|
|
$statement->execute(); |
668
|
|
|
$this->queryCounter ++; |
669
|
|
|
$this->affectedRows = $statement->rowCount(); |
670
|
|
|
if ( $statement->columnCount() ) { |
671
|
|
|
$fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL; |
672
|
|
|
if ( isset( $options['noFetch'] ) && $options['noFetch'] ) { |
673
|
|
|
$this->resultArray = array(); |
|
|
|
|
674
|
|
|
return $statement; |
675
|
|
|
} |
676
|
|
|
$this->resultArray = $statement->fetchAll( $fetchStyle ); |
677
|
|
|
if ( $this->loggingEnabled && $this->logger ) { |
678
|
|
|
$this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' ); |
|
|
|
|
679
|
|
|
} |
680
|
|
|
} else { |
681
|
|
|
$this->resultArray = array(); |
682
|
|
|
} |
683
|
|
|
} catch (\PDOException $e ) { |
684
|
|
|
//Unfortunately the code field is supposed to be int by default (php) |
685
|
|
|
//So we need a property to convey the SQL State code. |
686
|
|
|
$err = $e->getMessage(); |
687
|
|
|
if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err ); |
|
|
|
|
688
|
|
|
$exception = new SQL( $err, 0 ); |
689
|
|
|
$exception->setSQLState( $e->getCode() ); |
690
|
|
|
throw $exception; |
691
|
|
|
} |
692
|
|
|
} |
693
|
|
|
|
694
|
|
|
/** |
695
|
|
|
* Try to fix MySQL character encoding problems. |
696
|
|
|
* MySQL < 5.5 does not support proper 4 byte unicode but they |
697
|
|
|
* seem to have added it with version 5.5 under a different label: utf8mb4. |
698
|
|
|
* We try to select the best possible charset based on your version data. |
699
|
|
|
*/ |
700
|
|
|
protected function setEncoding() |
701
|
|
|
{ |
702
|
|
|
$driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME ); |
703
|
|
|
$version = floatval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION ) ); |
704
|
|
|
if ($driver === 'mysql') { |
705
|
|
|
$encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8'; |
706
|
|
|
$this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect |
707
|
|
|
$this->pdo->exec(' SET NAMES '. $encoding); //also for current connection |
708
|
|
|
$this->mysqlEncoding = $encoding; |
709
|
|
|
} |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
/** |
713
|
|
|
* Constructor. You may either specify dsn, user and password or |
714
|
|
|
* just give an existing PDO connection. |
715
|
|
|
* |
716
|
|
|
* Examples: |
717
|
|
|
* $driver = new RPDO($dsn, $user, $password); |
718
|
|
|
* $driver = new RPDO($existingConnection); |
719
|
|
|
* |
720
|
|
|
* @param string|object $dsn database connection string |
721
|
|
|
* @param string $user optional, usename to sign in |
722
|
|
|
* @param string $pass optional, password for connection login |
723
|
|
|
* |
724
|
|
|
*/ |
725
|
|
|
public function __construct( $dsn, $user = NULL, $pass = NULL ) |
726
|
|
|
{ |
727
|
|
|
if ( is_object( $dsn ) ) { |
728
|
|
|
$this->pdo = $dsn; |
729
|
|
|
$this->isConnected = TRUE; |
730
|
|
|
$this->setEncoding(); |
731
|
|
|
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION ); |
732
|
|
|
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC ); |
733
|
|
|
// make sure that the dsn at least contains the type |
734
|
|
|
$this->dsn = $this->getDatabaseType(); |
735
|
|
|
} else { |
736
|
|
|
$this->dsn = $dsn; |
737
|
|
|
$this->connectInfo = array( 'pass' => $pass, 'user' => $user ); |
738
|
|
|
} |
739
|
|
|
|
740
|
|
|
//PHP 5.3 PDO SQLite has a bug with large numbers: |
741
|
|
|
if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || $this->dsn === 'test-sqlite-53' ) { |
742
|
|
|
$this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis. |
743
|
|
|
} elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) { |
744
|
|
|
$this->max = 2147483647; //bindParam in pdo_cubrid also fails... |
745
|
|
|
} else { |
746
|
|
|
$this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause) |
747
|
|
|
} |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
/** |
751
|
|
|
* Returns the best possible encoding for MySQL based on version data. |
752
|
|
|
* |
753
|
|
|
* @return string |
754
|
|
|
*/ |
755
|
|
|
public function getMysqlEncoding() |
756
|
|
|
{ |
757
|
|
|
return $this->mysqlEncoding; |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
/** |
761
|
|
|
* Whether to bind all parameters as strings. |
762
|
|
|
* If set to TRUE this will cause all integers to be bound as STRINGS. |
763
|
|
|
* This will NOT affect NULL values. |
764
|
|
|
* |
765
|
|
|
* @param boolean $yesNo pass TRUE to bind all parameters as strings. |
766
|
|
|
* |
767
|
|
|
* @return void |
768
|
|
|
*/ |
769
|
|
|
public function setUseStringOnlyBinding( $yesNo ) |
770
|
|
|
{ |
771
|
|
|
$this->flagUseStringOnlyBinding = (boolean) $yesNo; |
772
|
|
|
} |
773
|
|
|
|
774
|
|
|
/** |
775
|
|
|
* Establishes a connection to the database using PHP\PDO |
776
|
|
|
* functionality. If a connection has already been established this |
777
|
|
|
* method will simply return directly. This method also turns on |
778
|
|
|
* UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as |
779
|
|
|
* PDO-FETCH-ASSOC. |
780
|
|
|
* |
781
|
|
|
* @throws\PDOException |
782
|
|
|
* |
783
|
|
|
* @return void |
784
|
|
|
*/ |
785
|
|
|
public function connect() |
786
|
|
|
{ |
787
|
|
|
if ( $this->isConnected ) return; |
788
|
|
|
try { |
789
|
|
|
$user = $this->connectInfo['user']; |
790
|
|
|
$pass = $this->connectInfo['pass']; |
791
|
|
|
$this->pdo = new \PDO( |
|
|
|
|
792
|
|
|
$this->dsn, |
793
|
|
|
$user, |
794
|
|
|
$pass |
795
|
|
|
); |
796
|
|
|
$this->setEncoding(); |
797
|
|
|
$this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE ); |
798
|
|
|
//cant pass these as argument to constructor, CUBRID driver does not understand... |
799
|
|
|
$this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION ); |
800
|
|
|
$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC ); |
801
|
|
|
$this->isConnected = TRUE; |
802
|
|
|
} catch ( \PDOException $exception ) { |
803
|
|
|
$matches = array(); |
804
|
|
|
$dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?'; |
805
|
|
|
throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() ); |
806
|
|
|
} |
807
|
|
|
} |
808
|
|
|
|
809
|
|
|
/** |
810
|
|
|
* Directly sets PDO instance into driver. |
811
|
|
|
* This method might improve performance, however since the driver does |
812
|
|
|
* not configure this instance terrible things may happen... only use |
813
|
|
|
* this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and |
814
|
|
|
* you know your database server VERY WELL. |
815
|
|
|
* |
816
|
|
|
* @param PDO $pdo PDO instance |
817
|
|
|
* |
818
|
|
|
* @return void |
819
|
|
|
*/ |
820
|
|
|
public function setPDO( \PDO $pdo ) { |
821
|
|
|
$this->pdo = $pdo; |
|
|
|
|
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
/** |
825
|
|
|
* @see Driver::GetAll |
826
|
|
|
*/ |
827
|
|
|
public function GetAll( $sql, $bindings = array() ) |
828
|
|
|
{ |
829
|
|
|
$this->runQuery( $sql, $bindings ); |
830
|
|
|
return $this->resultArray; |
|
|
|
|
831
|
|
|
} |
832
|
|
|
|
833
|
|
|
/** |
834
|
|
|
* @see Driver::GetAssocRow |
835
|
|
|
*/ |
836
|
|
|
public function GetAssocRow( $sql, $bindings = array() ) |
837
|
|
|
{ |
838
|
|
|
$this->runQuery( $sql, $bindings, array( |
839
|
|
|
'fetchStyle' => \PDO::FETCH_ASSOC |
840
|
|
|
) |
841
|
|
|
); |
842
|
|
|
return $this->resultArray; |
843
|
|
|
} |
844
|
|
|
|
845
|
|
|
/** |
846
|
|
|
* @see Driver::GetCol |
847
|
|
|
*/ |
848
|
|
|
public function GetCol( $sql, $bindings = array() ) |
849
|
|
|
{ |
850
|
|
|
$rows = $this->GetAll( $sql, $bindings ); |
851
|
|
|
$cols = array(); |
852
|
|
|
if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) { |
853
|
|
|
foreach ( $rows as $row ) { |
854
|
|
|
$cols[] = array_shift( $row ); |
855
|
|
|
} |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
return $cols; |
859
|
|
|
} |
860
|
|
|
|
861
|
|
|
/** |
862
|
|
|
* @see Driver::GetOne |
863
|
|
|
*/ |
864
|
|
|
public function GetOne( $sql, $bindings = array() ) |
865
|
|
|
{ |
866
|
|
|
$arr = $this->GetAll( $sql, $bindings ); |
867
|
|
|
$res = NULL; |
868
|
|
|
if ( !is_array( $arr ) ) return NULL; |
869
|
|
|
if ( count( $arr ) === 0 ) return NULL; |
870
|
|
|
$row1 = array_shift( $arr ); |
871
|
|
|
if ( !is_array( $row1 ) ) return NULL; |
872
|
|
|
if ( count( $row1 ) === 0 ) return NULL; |
873
|
|
|
$col1 = array_shift( $row1 ); |
874
|
|
|
return $col1; |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
/** |
878
|
|
|
* Alias for getOne(). |
879
|
|
|
* Backward compatibility. |
880
|
|
|
* |
881
|
|
|
* @param string $sql SQL |
882
|
|
|
* @param array $bindings bindings |
883
|
|
|
* |
884
|
|
|
* @return mixed |
885
|
|
|
*/ |
886
|
|
|
public function GetCell( $sql, $bindings = array() ) |
887
|
|
|
{ |
888
|
|
|
return $this->GetOne( $sql, $bindings ); |
889
|
|
|
} |
890
|
|
|
|
891
|
|
|
/** |
892
|
|
|
* @see Driver::GetRow |
893
|
|
|
*/ |
894
|
|
|
public function GetRow( $sql, $bindings = array() ) |
895
|
|
|
{ |
896
|
|
|
$arr = $this->GetAll( $sql, $bindings ); |
897
|
|
|
return array_shift( $arr ); |
898
|
|
|
} |
899
|
|
|
|
900
|
|
|
/** |
901
|
|
|
* @see Driver::Excecute |
902
|
|
|
*/ |
903
|
|
|
public function Execute( $sql, $bindings = array() ) |
904
|
|
|
{ |
905
|
|
|
$this->runQuery( $sql, $bindings ); |
906
|
|
|
return $this->affectedRows; |
|
|
|
|
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
/** |
910
|
|
|
* @see Driver::GetInsertID |
911
|
|
|
*/ |
912
|
|
|
public function GetInsertID() |
913
|
|
|
{ |
914
|
|
|
$this->connect(); |
915
|
|
|
|
916
|
|
|
return (int) $this->pdo->lastInsertId(); |
917
|
|
|
} |
918
|
|
|
|
919
|
|
|
/** |
920
|
|
|
* @see Driver::GetCursor |
921
|
|
|
*/ |
922
|
|
|
public function GetCursor( $sql, $bindings = array() ) |
923
|
|
|
{ |
924
|
|
|
$statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) ); |
|
|
|
|
925
|
|
|
$cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC ); |
|
|
|
|
926
|
|
|
return $cursor; |
927
|
|
|
} |
928
|
|
|
|
929
|
|
|
/** |
930
|
|
|
* @see Driver::Affected_Rows |
931
|
|
|
*/ |
932
|
|
|
public function Affected_Rows() |
933
|
|
|
{ |
934
|
|
|
$this->connect(); |
935
|
|
|
return (int) $this->affectedRows; |
936
|
|
|
} |
937
|
|
|
|
938
|
|
|
/** |
939
|
|
|
* Toggles debug mode. In debug mode the driver will print all |
940
|
|
|
* SQL to the screen together with some information about the |
941
|
|
|
* results. |
942
|
|
|
* |
943
|
|
|
* @param boolean $trueFalse turn on/off |
|
|
|
|
944
|
|
|
* @param Logger $logger logger instance |
945
|
|
|
* |
946
|
|
|
* @return void |
947
|
|
|
*/ |
948
|
|
|
public function setDebugMode( $tf, $logger = NULL ) |
949
|
|
|
{ |
950
|
|
|
$this->connect(); |
951
|
|
|
$this->loggingEnabled = (bool) $tf; |
952
|
|
|
if ( $this->loggingEnabled and !$logger ) { |
|
|
|
|
953
|
|
|
$logger = new RDefault(); |
954
|
|
|
} |
955
|
|
|
$this->setLogger( $logger ); |
|
|
|
|
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
/** |
959
|
|
|
* Injects Logger object. |
960
|
|
|
* Sets the logger instance you wish to use. |
961
|
|
|
* |
962
|
|
|
* @param Logger $logger the logger instance to be used for logging |
963
|
|
|
* |
964
|
|
|
* @return void |
965
|
|
|
*/ |
966
|
|
|
public function setLogger( Logger $logger ) |
967
|
|
|
{ |
968
|
|
|
$this->logger = $logger; |
969
|
|
|
} |
970
|
|
|
|
971
|
|
|
/** |
972
|
|
|
* Gets Logger object. |
973
|
|
|
* Returns the currently active Logger instance. |
974
|
|
|
* |
975
|
|
|
* @return Logger |
976
|
|
|
*/ |
977
|
|
|
public function getLogger() |
978
|
|
|
{ |
979
|
|
|
return $this->logger; |
980
|
|
|
} |
981
|
|
|
|
982
|
|
|
/** |
983
|
|
|
* @see Driver::StartTrans |
984
|
|
|
*/ |
985
|
|
|
public function StartTrans() |
986
|
|
|
{ |
987
|
|
|
$this->connect(); |
988
|
|
|
$this->pdo->beginTransaction(); |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
/** |
992
|
|
|
* @see Driver::CommitTrans |
993
|
|
|
*/ |
994
|
|
|
public function CommitTrans() |
995
|
|
|
{ |
996
|
|
|
$this->connect(); |
997
|
|
|
$this->pdo->commit(); |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
/** |
1001
|
|
|
* @see Driver::FailTrans |
1002
|
|
|
*/ |
1003
|
|
|
public function FailTrans() |
1004
|
|
|
{ |
1005
|
|
|
$this->connect(); |
1006
|
|
|
$this->pdo->rollback(); |
1007
|
|
|
} |
1008
|
|
|
|
1009
|
|
|
/** |
1010
|
|
|
* Returns the name of database driver for PDO. |
1011
|
|
|
* Uses the PDO attribute DRIVER NAME to obtain the name of the |
1012
|
|
|
* PDO driver. |
1013
|
|
|
* |
1014
|
|
|
* @return string |
1015
|
|
|
*/ |
1016
|
|
|
public function getDatabaseType() |
1017
|
|
|
{ |
1018
|
|
|
$this->connect(); |
1019
|
|
|
|
1020
|
|
|
return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME ); |
1021
|
|
|
} |
1022
|
|
|
|
1023
|
|
|
/** |
1024
|
|
|
* Returns the version number of the database. |
1025
|
|
|
* |
1026
|
|
|
* @return mixed $version version number of the database |
1027
|
|
|
*/ |
1028
|
|
|
public function getDatabaseVersion() |
1029
|
|
|
{ |
1030
|
|
|
$this->connect(); |
1031
|
|
|
return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION ); |
1032
|
|
|
} |
1033
|
|
|
|
1034
|
|
|
/** |
1035
|
|
|
* Returns the underlying PHP PDO instance. |
1036
|
|
|
* |
1037
|
|
|
* @return PDO |
1038
|
|
|
*/ |
1039
|
|
|
public function getPDO() |
1040
|
|
|
{ |
1041
|
|
|
$this->connect(); |
1042
|
|
|
return $this->pdo; |
1043
|
|
|
} |
1044
|
|
|
|
1045
|
|
|
/** |
1046
|
|
|
* Closes database connection by destructing\PDO. |
1047
|
|
|
* |
1048
|
|
|
* @return void |
1049
|
|
|
*/ |
1050
|
|
|
public function close() |
1051
|
|
|
{ |
1052
|
|
|
$this->pdo = NULL; |
1053
|
|
|
$this->isConnected = FALSE; |
1054
|
|
|
} |
1055
|
|
|
|
1056
|
|
|
/** |
1057
|
|
|
* Returns TRUE if the current\PDO instance is connected. |
1058
|
|
|
* |
1059
|
|
|
* @return boolean |
1060
|
|
|
*/ |
1061
|
|
|
public function isConnected() |
1062
|
|
|
{ |
1063
|
|
|
return $this->isConnected && $this->pdo; |
1064
|
|
|
} |
1065
|
|
|
|
1066
|
|
|
/** |
1067
|
|
|
* Toggles logging, enables or disables logging. |
1068
|
|
|
* |
1069
|
|
|
* @param boolean $enable TRUE to enable logging |
1070
|
|
|
* |
1071
|
|
|
* @return self |
1072
|
|
|
*/ |
1073
|
|
|
public function setEnableLogging( $enable ) |
1074
|
|
|
{ |
1075
|
|
|
$this->loggingEnabled = (boolean) $enable; |
1076
|
|
|
} |
1077
|
|
|
|
1078
|
|
|
/** |
1079
|
|
|
* Resets the internal Query Counter. |
1080
|
|
|
* |
1081
|
|
|
* @return self |
1082
|
|
|
*/ |
1083
|
|
|
public function resetCounter() |
1084
|
|
|
{ |
1085
|
|
|
$this->queryCounter = 0; |
1086
|
|
|
return $this; |
1087
|
|
|
} |
1088
|
|
|
|
1089
|
|
|
/** |
1090
|
|
|
* Returns the number of SQL queries processed. |
1091
|
|
|
* |
1092
|
|
|
* @return integer |
1093
|
|
|
*/ |
1094
|
|
|
public function getQueryCount() |
1095
|
|
|
{ |
1096
|
|
|
return $this->queryCounter; |
1097
|
|
|
} |
1098
|
|
|
|
1099
|
|
|
/** |
1100
|
|
|
* Returns the maximum value treated as integer parameter |
1101
|
|
|
* binding. |
1102
|
|
|
* |
1103
|
|
|
* This method is mainly for testing purposes but it can help |
1104
|
|
|
* you solve some issues relating to integer bindings. |
1105
|
|
|
* |
1106
|
|
|
* @return integer |
1107
|
|
|
*/ |
1108
|
|
|
public function getIntegerBindingMax() |
1109
|
|
|
{ |
1110
|
|
|
return $this->max; |
1111
|
|
|
} |
1112
|
|
|
} |
1113
|
|
|
} |
1114
|
|
|
|
1115
|
|
|
namespace RedBeanPHP { |
1116
|
|
|
|
1117
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
1118
|
|
|
use RedBeanPHP\BeanHelper as BeanHelper; |
|
|
|
|
1119
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
1120
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
1121
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
1122
|
|
|
|
1123
|
|
|
/** |
1124
|
|
|
* OODBBean (Object Oriented DataBase Bean). |
1125
|
|
|
* |
1126
|
|
|
* to exchange information with the database. A bean represents |
1127
|
|
|
* a single table row and offers generic services for interaction |
1128
|
|
|
* with databases systems as well as some meta-data. |
1129
|
|
|
* |
1130
|
|
|
* @file RedBeanPHP/OODBBean.php |
1131
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
1132
|
|
|
* @license BSD/GPLv2 |
1133
|
|
|
* @desc OODBBean represents a bean. RedBeanPHP uses beans |
1134
|
|
|
* |
1135
|
|
|
* @copyright |
1136
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
1137
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
1138
|
|
|
* with this source code in the file license.txt. |
1139
|
|
|
*/ |
1140
|
|
|
class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable |
|
|
|
|
1141
|
|
|
{ |
1142
|
|
|
/** |
1143
|
|
|
* FUSE error modes. |
1144
|
|
|
*/ |
1145
|
|
|
const C_ERR_IGNORE = FALSE; |
1146
|
|
|
const C_ERR_LOG = 1; |
1147
|
|
|
const C_ERR_NOTICE = 2; |
1148
|
|
|
const C_ERR_WARN = 3; |
1149
|
|
|
const C_ERR_EXCEPTION = 4; |
1150
|
|
|
const C_ERR_FUNC = 5; |
1151
|
|
|
const C_ERR_FATAL = 6; |
1152
|
|
|
|
1153
|
|
|
/** |
1154
|
|
|
* @var boolean |
1155
|
|
|
*/ |
1156
|
|
|
protected static $errorHandlingFUSE = FALSE; |
1157
|
|
|
|
1158
|
|
|
/** |
1159
|
|
|
* @var callable|NULL |
1160
|
|
|
*/ |
1161
|
|
|
protected static $errorHandler = NULL; |
1162
|
|
|
|
1163
|
|
|
/** |
1164
|
|
|
* @var array |
1165
|
|
|
*/ |
1166
|
|
|
protected static $aliases = array(); |
1167
|
|
|
|
1168
|
|
|
/** |
1169
|
|
|
* @var boolean |
1170
|
|
|
*/ |
1171
|
|
|
protected static $autoResolve = FALSE; |
1172
|
|
|
|
1173
|
|
|
/** |
1174
|
|
|
* Sets the error mode for FUSE. |
1175
|
|
|
* What to do if a FUSE model method does not exist? |
1176
|
|
|
* You can set the following options: |
1177
|
|
|
* |
1178
|
|
|
* OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL |
1179
|
|
|
* OODBBean::C_ERR_LOG, logs the incident using error_log |
1180
|
|
|
* OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE |
1181
|
|
|
* OODBBean::C_ERR_WARN, triggers a E_USER_WARNING |
1182
|
|
|
* OODBBean::C_ERR_EXCEPTION, throws an exception |
1183
|
|
|
* OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function) |
1184
|
|
|
* OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR |
1185
|
|
|
* |
1186
|
|
|
* Custom handler method signature: handler( array ( |
1187
|
|
|
* 'message' => string |
1188
|
|
|
* 'bean' => OODBBean |
1189
|
|
|
* 'method' => string |
1190
|
|
|
* ) ) |
1191
|
|
|
* |
1192
|
|
|
* This method returns the old mode and handler as an array. |
1193
|
|
|
* |
1194
|
|
|
* @param integer $mode mode |
1195
|
|
|
* @param callable|NULL $func custom handler |
1196
|
|
|
* |
1197
|
|
|
* @return array |
1198
|
|
|
*/ |
1199
|
|
|
public static function setErrorHandlingFUSE($mode, $func = NULL) { |
1200
|
|
|
if ( |
1201
|
|
|
$mode !== self::C_ERR_IGNORE |
1202
|
|
|
&& $mode !== self::C_ERR_LOG |
1203
|
|
|
&& $mode !== self::C_ERR_NOTICE |
1204
|
|
|
&& $mode !== self::C_ERR_WARN |
1205
|
|
|
&& $mode !== self::C_ERR_EXCEPTION |
1206
|
|
|
&& $mode !== self::C_ERR_FUNC |
1207
|
|
|
&& $mode !== self::C_ERR_FATAL |
1208
|
|
|
) throw new \Exception( 'Invalid error mode selected' ); |
1209
|
|
|
|
1210
|
|
|
if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) { |
1211
|
|
|
throw new \Exception( 'Invalid error handler' ); |
1212
|
|
|
} |
1213
|
|
|
|
1214
|
|
|
$old = array( self::$errorHandlingFUSE, self::$errorHandler ); |
1215
|
|
|
self::$errorHandlingFUSE = $mode; |
|
|
|
|
1216
|
|
|
if ( is_callable( $func ) ) { |
1217
|
|
|
self::$errorHandler = $func; |
1218
|
|
|
} else { |
1219
|
|
|
self::$errorHandler = NULL; |
1220
|
|
|
} |
1221
|
|
|
return $old; |
1222
|
|
|
} |
1223
|
|
|
|
1224
|
|
|
/** |
1225
|
|
|
* Sets aliases. |
1226
|
|
|
* |
1227
|
|
|
* @param array $list |
1228
|
|
|
* |
1229
|
|
|
* @return void |
1230
|
|
|
*/ |
1231
|
|
|
public static function aliases( $list ) |
1232
|
|
|
{ |
1233
|
|
|
self::$aliases = $list; |
1234
|
|
|
} |
1235
|
|
|
|
1236
|
|
|
/** |
1237
|
|
|
* Enables or disables auto-resolving fetch types. |
1238
|
|
|
* Auto-resolving aliased parent beans is convenient but can |
1239
|
|
|
* be slower and can create infinite recursion if you |
1240
|
|
|
* used aliases to break cyclic relations in your domain. |
1241
|
|
|
* |
1242
|
|
|
* @param boolean $automatic TRUE to enable automatic resolving aliased parents |
1243
|
|
|
* |
1244
|
|
|
* @return void |
1245
|
|
|
*/ |
1246
|
|
|
public static function setAutoResolve( $automatic = TRUE ) |
1247
|
|
|
{ |
1248
|
|
|
self::$autoResolve = (boolean) $automatic; |
1249
|
|
|
} |
1250
|
|
|
|
1251
|
|
|
/** |
1252
|
|
|
* This is where the real properties of the bean live. They are stored and retrieved |
1253
|
|
|
* by the magic getter and setter (__get and __set). |
1254
|
|
|
* |
1255
|
|
|
* @var array $properties |
1256
|
|
|
*/ |
1257
|
|
|
protected $properties = array(); |
1258
|
|
|
|
1259
|
|
|
/** |
1260
|
|
|
* Here we keep the meta data of a bean. |
1261
|
|
|
* |
1262
|
|
|
* @var array |
1263
|
|
|
*/ |
1264
|
|
|
protected $__info = array(); |
1265
|
|
|
|
1266
|
|
|
/** |
1267
|
|
|
* The BeanHelper allows the bean to access the toolbox objects to implement |
1268
|
|
|
* rich functionality, otherwise you would have to do everything with R or |
1269
|
|
|
* external objects. |
1270
|
|
|
* |
1271
|
|
|
* @var BeanHelper |
1272
|
|
|
*/ |
1273
|
|
|
protected $beanHelper = NULL; |
1274
|
|
|
|
1275
|
|
|
/** |
1276
|
|
|
* @var null |
1277
|
|
|
*/ |
1278
|
|
|
protected $fetchType = NULL; |
1279
|
|
|
|
1280
|
|
|
/** |
1281
|
|
|
* @var string |
1282
|
|
|
*/ |
1283
|
|
|
protected $withSql = ''; |
1284
|
|
|
|
1285
|
|
|
/** |
1286
|
|
|
* @var array |
1287
|
|
|
*/ |
1288
|
|
|
protected $withParams = array(); |
1289
|
|
|
|
1290
|
|
|
/** |
1291
|
|
|
* @var string |
1292
|
|
|
*/ |
1293
|
|
|
protected $aliasName = NULL; |
1294
|
|
|
|
1295
|
|
|
/** |
1296
|
|
|
* @var string |
1297
|
|
|
*/ |
1298
|
|
|
protected $via = NULL; |
1299
|
|
|
|
1300
|
|
|
/** |
1301
|
|
|
* @var boolean |
1302
|
|
|
*/ |
1303
|
|
|
protected $noLoad = FALSE; |
1304
|
|
|
|
1305
|
|
|
/** |
1306
|
|
|
* @var boolean |
1307
|
|
|
*/ |
1308
|
|
|
protected $all = FALSE; |
1309
|
|
|
|
1310
|
|
|
/** |
1311
|
|
|
* Sets a meta property for all beans. This is a quicker way to set |
1312
|
|
|
* the meta properties for a collection of beans because this method |
1313
|
|
|
* can directly access the property arrays of the beans. |
1314
|
|
|
* This method returns the beans. |
1315
|
|
|
* |
1316
|
|
|
* @param array $beans beans to set the meta property of |
1317
|
|
|
* @param string $property property to set |
1318
|
|
|
* @param mixed $value value |
1319
|
|
|
* |
1320
|
|
|
* @return array |
1321
|
|
|
*/ |
1322
|
|
|
public static function setMetaAll( $beans, $property, $value ) |
1323
|
|
|
{ |
1324
|
|
|
foreach( $beans as $bean ) { |
1325
|
|
|
if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value; |
1326
|
|
|
} |
1327
|
|
|
|
1328
|
|
|
return $beans; |
1329
|
|
|
} |
1330
|
|
|
|
1331
|
|
|
/** |
1332
|
|
|
* Parses the join in the with-snippet. |
1333
|
|
|
* For instance: |
1334
|
|
|
* |
1335
|
|
|
* $author |
1336
|
|
|
* ->withCondition(' @joined.detail.title LIKE ? ') |
1337
|
|
|
* ->ownBookList; |
1338
|
|
|
* |
1339
|
|
|
* will automatically join 'detail' on book to |
1340
|
|
|
* access the title field. |
1341
|
|
|
* |
1342
|
|
|
* @note this feature requires Narrow Field Mode and Join Feature |
1343
|
|
|
* to be both activated (default). |
1344
|
|
|
* |
1345
|
|
|
* @param string $type the source type for the join |
1346
|
|
|
* |
1347
|
|
|
* @return string $joinSql |
1348
|
|
|
*/ |
1349
|
|
|
private function parseJoin( $type ) |
1350
|
|
|
{ |
1351
|
|
|
$joinSql = ''; |
1352
|
|
|
$joins = array(); |
1353
|
|
|
if ( strpos($this->withSql, '@joined.' ) !== FALSE ) { |
1354
|
|
|
$writer = $this->beanHelper->getToolBox()->getWriter(); |
1355
|
|
|
$oldParts = $parts = explode( '@joined.', $this->withSql ); |
1356
|
|
|
array_shift( $parts ); |
1357
|
|
|
foreach($parts as $part) { |
1358
|
|
|
$explosion = explode( '.', $part ); |
1359
|
|
|
$joinInfo = array_shift( $explosion ); |
1360
|
|
|
//Dont join more than once.. |
1361
|
|
|
if ( !isset( $joins[$joinInfo] ) ) { |
1362
|
|
|
$joins[ $joinInfo ] = true; |
1363
|
|
|
$joinSql .= $writer->writeJoin( $type, $joinInfo, 'LEFT' ); |
1364
|
|
|
} |
1365
|
|
|
} |
1366
|
|
|
$this->withSql = implode( '', $oldParts ); |
1367
|
|
|
$joinSql .= ' WHERE '; |
1368
|
|
|
} |
1369
|
|
|
return $joinSql; |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
|
|
/** |
1373
|
|
|
* Internal method. |
1374
|
|
|
* Obtains a shared list for a certain type. |
1375
|
|
|
* |
1376
|
|
|
* @param string $type the name of the list you want to retrieve. |
1377
|
|
|
* |
1378
|
|
|
* @return array |
1379
|
|
|
*/ |
1380
|
|
|
private function getSharedList( $type, $redbean, $toolbox ) |
1381
|
|
|
{ |
1382
|
|
|
$writer = $toolbox->getWriter(); |
1383
|
|
|
|
1384
|
|
View Code Duplication |
if ( $this->via ) { |
1385
|
|
|
$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) ); |
1386
|
|
|
if ( $oldName !== $this->via ) { |
1387
|
|
|
//set the new renaming rule |
1388
|
|
|
$writer->renameAssocTable( $oldName, $this->via ); |
1389
|
|
|
} |
1390
|
|
|
$this->via = NULL; |
1391
|
|
|
} |
1392
|
|
|
|
1393
|
|
|
$beans = array(); |
1394
|
|
|
if ($this->getID()) { |
|
|
|
|
1395
|
|
|
$type = $this->beau( $type ); |
1396
|
|
|
$assocManager = $redbean->getAssociationManager(); |
1397
|
|
|
$beans = $assocManager->related( $this, $type, $this->withSql, $this->withParams ); |
1398
|
|
|
} |
1399
|
|
|
|
1400
|
|
|
$this->withSql = ''; |
1401
|
|
|
$this->withParams = array(); |
1402
|
|
|
|
1403
|
|
|
return $beans; |
1404
|
|
|
} |
1405
|
|
|
|
1406
|
|
|
/** |
1407
|
|
|
* Internal method. |
1408
|
|
|
* Obtains the own list of a certain type. |
1409
|
|
|
* |
1410
|
|
|
* @param string $type name of the list you want to retrieve |
1411
|
|
|
* @param OODB $oodb The RB OODB object database instance |
|
|
|
|
1412
|
|
|
* |
1413
|
|
|
* @return array |
1414
|
|
|
*/ |
1415
|
|
|
private function getOwnList( $type, $redbean ) |
1416
|
|
|
{ |
1417
|
|
|
$type = $this->beau( $type ); |
1418
|
|
|
|
1419
|
|
|
if ( $this->aliasName ) { |
1420
|
|
|
$parentField = $this->aliasName; |
1421
|
|
|
$myFieldLink = $parentField . '_id'; |
1422
|
|
|
|
1423
|
|
|
$this->__info['sys.alias.' . $type] = $this->aliasName; |
1424
|
|
|
|
1425
|
|
|
$this->aliasName = NULL; |
1426
|
|
|
} else { |
1427
|
|
|
$parentField = $this->__info['type']; |
1428
|
|
|
$myFieldLink = $parentField . '_id'; |
1429
|
|
|
} |
1430
|
|
|
|
1431
|
|
|
$beans = array(); |
1432
|
|
|
|
1433
|
|
|
if ( $this->getID() ) { |
|
|
|
|
1434
|
|
|
|
1435
|
|
|
$firstKey = NULL; |
1436
|
|
View Code Duplication |
if ( count( $this->withParams ) > 0 ) { |
1437
|
|
|
reset( $this->withParams ); |
1438
|
|
|
|
1439
|
|
|
$firstKey = key( $this->withParams ); |
1440
|
|
|
} |
1441
|
|
|
|
1442
|
|
|
$joinSql = $this->parseJoin( $type ); |
1443
|
|
|
|
1444
|
|
|
if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { |
1445
|
|
|
$bindings = $this->withParams; |
1446
|
|
|
$bindings[':slot0'] = $this->getID(); |
1447
|
|
|
|
1448
|
|
|
$beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); |
1449
|
|
|
} else { |
1450
|
|
|
$bindings = array_merge( array( $this->getID() ), $this->withParams ); |
1451
|
|
|
|
1452
|
|
|
$beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); |
1453
|
|
|
} |
1454
|
|
|
} |
1455
|
|
|
|
1456
|
|
|
$this->withSql = ''; |
1457
|
|
|
$this->withParams = array(); |
1458
|
|
|
|
1459
|
|
|
foreach ( $beans as $beanFromList ) { |
1460
|
|
|
$beanFromList->__info['sys.parentcache.' . $parentField] = $this; |
1461
|
|
|
} |
1462
|
|
|
|
1463
|
|
|
return $beans; |
1464
|
|
|
} |
1465
|
|
|
|
1466
|
|
|
/** |
1467
|
|
|
* Initializes a bean. Used by OODB for dispensing beans. |
1468
|
|
|
* It is not recommended to use this method to initialize beans. Instead |
1469
|
|
|
* use the OODB object to dispense new beans. You can use this method |
1470
|
|
|
* if you build your own bean dispensing mechanism. |
1471
|
|
|
* |
1472
|
|
|
* @param string $type type of the new bean |
1473
|
|
|
* @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model |
1474
|
|
|
* |
1475
|
|
|
* @return void |
1476
|
|
|
*/ |
1477
|
|
|
public function initializeForDispense( $type, BeanHelper $beanhelper ) |
1478
|
|
|
{ |
1479
|
|
|
$this->beanHelper = $beanhelper; |
1480
|
|
|
$this->__info['type'] = $type; |
1481
|
|
|
$this->__info['sys.id'] = 'id'; |
1482
|
|
|
$this->__info['sys.orig'] = array( 'id' => 0 ); |
1483
|
|
|
$this->__info['tainted'] = TRUE; |
1484
|
|
|
$this->__info['changed'] = TRUE; |
1485
|
|
|
$this->properties['id'] = 0; |
1486
|
|
|
} |
1487
|
|
|
|
1488
|
|
|
/** |
1489
|
|
|
* Sets the Bean Helper. Normally the Bean Helper is set by OODB. |
1490
|
|
|
* Here you can change the Bean Helper. The Bean Helper is an object |
1491
|
|
|
* providing access to a toolbox for the bean necessary to retrieve |
1492
|
|
|
* nested beans (bean lists: ownBean, sharedBean) without the need to |
1493
|
|
|
* rely on static calls to the facade (or make this class dep. on OODB). |
1494
|
|
|
* |
1495
|
|
|
* @param BeanHelper $helper |
1496
|
|
|
* |
1497
|
|
|
* @return void |
1498
|
|
|
*/ |
1499
|
|
|
public function setBeanHelper( BeanHelper $helper ) |
1500
|
|
|
{ |
1501
|
|
|
$this->beanHelper = $helper; |
1502
|
|
|
} |
1503
|
|
|
|
1504
|
|
|
/** |
1505
|
|
|
* Returns an\ArrayIterator so you can treat the bean like |
1506
|
|
|
* an array with the properties container as its contents. |
1507
|
|
|
* This method is meant for PHP and allows you to access beans as if |
1508
|
|
|
* they were arrays, i.e. using array notation: |
1509
|
|
|
* |
1510
|
|
|
* $bean[$key] = $value; |
1511
|
|
|
* |
1512
|
|
|
* Note that not all PHP functions work with the array interface. |
1513
|
|
|
* |
1514
|
|
|
* @return \ArrayIterator |
1515
|
|
|
*/ |
1516
|
|
|
public function getIterator() |
1517
|
|
|
{ |
1518
|
|
|
return new\ArrayIterator( $this->properties ); |
1519
|
|
|
} |
1520
|
|
|
|
1521
|
|
|
/** |
1522
|
|
|
* Imports all values from an associative array $array. Chainable. |
1523
|
|
|
* This method imports the values in the first argument as bean |
1524
|
|
|
* propery and value pairs. Use the second parameter to provide a |
1525
|
|
|
* selection. If a selection array is passed, only the entries |
1526
|
|
|
* having keys mentioned in the selection array will be imported. |
1527
|
|
|
* Set the third parameter to TRUE to preserve spaces in selection keys. |
1528
|
|
|
* |
1529
|
|
|
* @param array $array what you want to import |
1530
|
|
|
* @param string|array $selection selection of values |
1531
|
|
|
* @param boolean $notrim if TRUE selection keys will NOT be trimmed |
1532
|
|
|
* |
1533
|
|
|
* @return OODBBean |
1534
|
|
|
*/ |
1535
|
|
|
public function import( $array, $selection = FALSE, $notrim = FALSE ) |
1536
|
|
|
{ |
1537
|
|
|
if ( is_string( $selection ) ) { |
1538
|
|
|
$selection = explode( ',', $selection ); |
1539
|
|
|
} |
1540
|
|
|
|
1541
|
|
|
if ( !$notrim && is_array( $selection ) ) { |
1542
|
|
|
foreach ( $selection as $key => $selected ) { |
1543
|
|
|
$selection[$key] = trim( $selected ); |
1544
|
|
|
} |
1545
|
|
|
} |
1546
|
|
|
|
1547
|
|
|
foreach ( $array as $key => $value ) { |
1548
|
|
|
if ( $key != '__info' ) { |
1549
|
|
|
if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) { |
|
|
|
|
1550
|
|
|
if ( is_array($value ) ) { |
1551
|
|
|
if ( isset( $value['_type'] ) ) { |
1552
|
|
|
$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] ); |
1553
|
|
|
unset( $value['_type'] ); |
1554
|
|
|
$bean->import($value); |
1555
|
|
|
$this->$key = $bean; |
1556
|
|
|
} else { |
1557
|
|
|
$listBeans = array(); |
1558
|
|
|
foreach( $value as $listKey => $listItem ) { |
1559
|
|
|
$bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] ); |
1560
|
|
|
unset( $listItem['_type'] ); |
1561
|
|
|
$bean->import($listItem); |
1562
|
|
|
$list = &$this->$key; |
1563
|
|
|
$list[ $listKey ] = $bean; |
1564
|
|
|
} |
1565
|
|
|
} |
1566
|
|
|
} else { |
1567
|
|
|
$this->$key = $value; |
1568
|
|
|
} |
1569
|
|
|
} |
1570
|
|
|
} |
1571
|
|
|
} |
1572
|
|
|
|
1573
|
|
|
return $this; |
1574
|
|
|
} |
1575
|
|
|
|
1576
|
|
|
/** |
1577
|
|
|
* Fast way to import a row. |
1578
|
|
|
* Does not perform any checks. |
1579
|
|
|
* |
1580
|
|
|
* @param array $row a database row |
1581
|
|
|
* |
1582
|
|
|
* @return self |
1583
|
|
|
*/ |
1584
|
|
|
public function importRow( $row ) |
1585
|
|
|
{ |
1586
|
|
|
$this->properties = $row; |
1587
|
|
|
$this->__info['sys.orig'] = $row; |
1588
|
|
|
$this->__info['changed'] = FALSE; |
1589
|
|
|
return $this; |
1590
|
|
|
} |
1591
|
|
|
|
1592
|
|
|
/** |
1593
|
|
|
* Imports data from another bean. Chainable. |
1594
|
|
|
* Copies the properties from the source bean to the internal |
1595
|
|
|
* property list. |
1596
|
|
|
* |
1597
|
|
|
* @param OODBBean $sourceBean the source bean to take properties from |
1598
|
|
|
* |
1599
|
|
|
* @return OODBBean |
1600
|
|
|
*/ |
1601
|
|
|
public function importFrom( OODBBean $sourceBean ) |
1602
|
|
|
{ |
1603
|
|
|
$this->__info['tainted'] = TRUE; |
1604
|
|
|
$this->__info['changed'] = TRUE; |
1605
|
|
|
$this->properties = $sourceBean->properties; |
1606
|
|
|
|
1607
|
|
|
return $this; |
1608
|
|
|
} |
1609
|
|
|
|
1610
|
|
|
/** |
1611
|
|
|
* Injects the properties of another bean but keeps the original ID. |
1612
|
|
|
* Just like import() but keeps the original ID. |
1613
|
|
|
* Chainable. |
1614
|
|
|
* |
1615
|
|
|
* @param OODBBean $otherBean the bean whose properties you would like to copy |
1616
|
|
|
* |
1617
|
|
|
* @return OODBBean |
1618
|
|
|
*/ |
1619
|
|
|
public function inject( OODBBean $otherBean ) |
1620
|
|
|
{ |
1621
|
|
|
$myID = $this->properties['id']; |
1622
|
|
|
|
1623
|
|
|
$this->import( $otherBean->export() ); |
1624
|
|
|
|
1625
|
|
|
$this->id = $myID; |
|
|
|
|
1626
|
|
|
|
1627
|
|
|
return $this; |
1628
|
|
|
} |
1629
|
|
|
|
1630
|
|
|
/** |
1631
|
|
|
* Exports the bean as an array. |
1632
|
|
|
* This function exports the contents of a bean to an array and returns |
1633
|
|
|
* the resulting array. |
1634
|
|
|
* |
1635
|
|
|
* @param boolean $meta set to TRUE if you want to export meta data as well |
1636
|
|
|
* @param boolean $parents set to TRUE if you want to export parents as well |
1637
|
|
|
* @param boolean $onlyMe set to TRUE if you want to export only this bean |
1638
|
|
|
* @param array $filters optional whitelist for export |
1639
|
|
|
* |
1640
|
|
|
* @return array |
1641
|
|
|
*/ |
1642
|
|
|
public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() ) |
1643
|
|
|
{ |
1644
|
|
|
$arr = array(); |
1645
|
|
|
|
1646
|
|
|
if ( $parents ) { |
1647
|
|
|
foreach ( $this as $key => $value ) { |
1648
|
|
|
if ( substr( $key, -3 ) != '_id' ) continue; |
1649
|
|
|
|
1650
|
|
|
$prop = substr( $key, 0, strlen( $key ) - 3 ); |
1651
|
|
|
$this->$prop; |
1652
|
|
|
} |
1653
|
|
|
} |
1654
|
|
|
|
1655
|
|
|
$hasFilters = is_array( $filters ) && count( $filters ); |
1656
|
|
|
|
1657
|
|
|
foreach ( $this as $key => $value ) { |
1658
|
|
|
if ( !$onlyMe && is_array( $value ) ) { |
1659
|
|
|
$vn = array(); |
1660
|
|
|
|
1661
|
|
|
foreach ( $value as $i => $b ) { |
1662
|
|
|
if ( !( $b instanceof OODBBean ) ) continue; |
1663
|
|
|
$vn[] = $b->export( $meta, FALSE, FALSE, $filters ); |
1664
|
|
|
$value = $vn; |
1665
|
|
|
} |
1666
|
|
|
} elseif ( $value instanceof OODBBean ) { |
1667
|
|
|
if ( $hasFilters ) { |
1668
|
|
|
if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue; |
1669
|
|
|
} |
1670
|
|
|
|
1671
|
|
|
$value = $value->export( $meta, $parents, FALSE, $filters ); |
1672
|
|
|
} |
1673
|
|
|
|
1674
|
|
|
$arr[$key] = $value; |
1675
|
|
|
} |
1676
|
|
|
|
1677
|
|
|
if ( $meta ) { |
1678
|
|
|
$arr['__info'] = $this->__info; |
1679
|
|
|
} |
1680
|
|
|
|
1681
|
|
|
return $arr; |
1682
|
|
|
} |
1683
|
|
|
|
1684
|
|
|
/** |
1685
|
|
|
* Implements isset() function for use as an array. |
1686
|
|
|
* |
1687
|
|
|
* @param string $property name of the property you want to check |
1688
|
|
|
* |
1689
|
|
|
* @return boolean |
1690
|
|
|
*/ |
1691
|
|
|
public function __isset( $property ) |
1692
|
|
|
{ |
1693
|
|
|
$property = $this->beau( $property ); |
1694
|
|
|
|
1695
|
|
|
if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { |
1696
|
|
|
$property = substr($property, 1); |
1697
|
|
|
} |
1698
|
|
|
return isset( $this->properties[$property] ); |
1699
|
|
|
} |
1700
|
|
|
|
1701
|
|
|
/** |
1702
|
|
|
* Returns the ID of the bean no matter what the ID field is. |
1703
|
|
|
* |
1704
|
|
|
* @return string|null |
1705
|
|
|
*/ |
1706
|
|
|
public function getID() |
1707
|
|
|
{ |
1708
|
|
|
return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL; |
1709
|
|
|
} |
1710
|
|
|
|
1711
|
|
|
/** |
1712
|
|
|
* Unsets a property of a bean. |
1713
|
|
|
* Magic method, gets called implicitly when performing the unset() operation |
1714
|
|
|
* on a bean property. |
1715
|
|
|
* |
1716
|
|
|
* @param string $property property to unset |
1717
|
|
|
* |
1718
|
|
|
* @return void |
1719
|
|
|
*/ |
1720
|
|
|
public function __unset( $property ) |
1721
|
|
|
{ |
1722
|
|
|
$property = $this->beau( $property ); |
1723
|
|
|
|
1724
|
|
|
if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { |
1725
|
|
|
$property = substr($property, 1); |
1726
|
|
|
} |
1727
|
|
|
|
1728
|
|
|
unset( $this->properties[$property] ); |
1729
|
|
|
|
1730
|
|
|
$shadowKey = 'sys.shadow.'.$property; |
1731
|
|
|
if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] ); |
1732
|
|
|
|
1733
|
|
|
//also clear modifiers |
1734
|
|
|
$this->withSql = ''; |
1735
|
|
|
$this->withParams = array(); |
1736
|
|
|
$this->aliasName = NULL; |
1737
|
|
|
$this->fetchType = NULL; |
1738
|
|
|
$this->noLoad = FALSE; |
1739
|
|
|
$this->all = FALSE; |
1740
|
|
|
$this->via = NULL; |
1741
|
|
|
|
1742
|
|
|
return; |
1743
|
|
|
} |
1744
|
|
|
|
1745
|
|
|
/** |
1746
|
|
|
* Adds WHERE clause conditions to ownList retrieval. |
1747
|
|
|
* For instance to get the pages that belong to a book you would |
1748
|
|
|
* issue the following command: $book->ownPage |
1749
|
|
|
* However, to order these pages by number use: |
1750
|
|
|
* |
1751
|
|
|
* $book->with(' ORDER BY `number` ASC ')->ownPage |
1752
|
|
|
* |
1753
|
|
|
* the additional SQL snippet will be merged into the final |
1754
|
|
|
* query. |
1755
|
|
|
* |
1756
|
|
|
* @param string $sql SQL to be added to retrieval query. |
1757
|
|
|
* @param array $bindings array with parameters to bind to SQL snippet |
1758
|
|
|
* |
1759
|
|
|
* @return OODBBean |
1760
|
|
|
*/ |
1761
|
|
|
public function with( $sql, $bindings = array() ) |
1762
|
|
|
{ |
1763
|
|
|
$this->withSql = $sql; |
1764
|
|
|
$this->withParams = $bindings; |
1765
|
|
|
return $this; |
1766
|
|
|
} |
1767
|
|
|
|
1768
|
|
|
/** |
1769
|
|
|
* Just like with(). Except that this method prepends the SQL query snippet |
1770
|
|
|
* with AND which makes it slightly more comfortable to use a conditional |
1771
|
|
|
* SQL snippet. For instance to filter an own-list with pages (belonging to |
1772
|
|
|
* a book) on specific chapters you can use: |
1773
|
|
|
* |
1774
|
|
|
* $book->withCondition(' chapter = 3 ')->ownPage |
1775
|
|
|
* |
1776
|
|
|
* This will return in the own list only the pages having 'chapter == 3'. |
1777
|
|
|
* |
1778
|
|
|
* @param string $sql SQL to be added to retrieval query (prefixed by AND) |
1779
|
|
|
* @param array $bindings array with parameters to bind to SQL snippet |
1780
|
|
|
* |
1781
|
|
|
* @return OODBBean |
1782
|
|
|
*/ |
1783
|
|
|
public function withCondition( $sql, $bindings = array() ) |
1784
|
|
|
{ |
1785
|
|
|
$this->withSql = ' AND ' . $sql; |
1786
|
|
|
$this->withParams = $bindings; |
1787
|
|
|
return $this; |
1788
|
|
|
} |
1789
|
|
|
|
1790
|
|
|
/** |
1791
|
|
|
* Tells the bean to (re)load the following list without any |
1792
|
|
|
* conditions. If you have an ownList or sharedList with a |
1793
|
|
|
* condition you can use this method to reload the entire list. |
1794
|
|
|
* |
1795
|
|
|
* Usage: |
1796
|
|
|
* |
1797
|
|
|
* $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3 |
1798
|
|
|
* $bean->all()->ownPage; //Reload all pages |
1799
|
|
|
* |
1800
|
|
|
* @return self |
1801
|
|
|
*/ |
1802
|
|
|
public function all() |
1803
|
|
|
{ |
1804
|
|
|
$this->all = TRUE; |
1805
|
|
|
return $this; |
1806
|
|
|
} |
1807
|
|
|
|
1808
|
|
|
/** |
1809
|
|
|
* Tells the bean to only access the list but not load |
1810
|
|
|
* its contents. Use this if you only want to add something to a list |
1811
|
|
|
* and you have no interest in retrieving its contents from the database. |
1812
|
|
|
* |
1813
|
|
|
* @return self |
1814
|
|
|
*/ |
1815
|
|
|
public function noLoad() |
1816
|
|
|
{ |
1817
|
|
|
$this->noLoad = TRUE; |
1818
|
|
|
return $this; |
1819
|
|
|
} |
1820
|
|
|
|
1821
|
|
|
/** |
1822
|
|
|
* Prepares an own-list to use an alias. This is best explained using |
1823
|
|
|
* an example. Imagine a project and a person. The project always involves |
1824
|
|
|
* two persons: a teacher and a student. The person beans have been aliased in this |
1825
|
|
|
* case, so to the project has a teacher_id pointing to a person, and a student_id |
1826
|
|
|
* also pointing to a person. Given a project, we obtain the teacher like this: |
1827
|
|
|
* |
1828
|
|
|
* $project->fetchAs('person')->teacher; |
1829
|
|
|
* |
1830
|
|
|
* Now, if we want all projects of a teacher we cant say: |
1831
|
|
|
* |
1832
|
|
|
* $teacher->ownProject |
1833
|
|
|
* |
1834
|
|
|
* because the $teacher is a bean of type 'person' and no project has been |
1835
|
|
|
* assigned to a person. Instead we use the alias() method like this: |
1836
|
|
|
* |
1837
|
|
|
* $teacher->alias('teacher')->ownProject |
1838
|
|
|
* |
1839
|
|
|
* now we get the projects associated with the person bean aliased as |
1840
|
|
|
* a teacher. |
1841
|
|
|
* |
1842
|
|
|
* @param string $aliasName the alias name to use |
1843
|
|
|
* |
1844
|
|
|
* @return OODBBean |
1845
|
|
|
*/ |
1846
|
|
|
public function alias( $aliasName ) |
1847
|
|
|
{ |
1848
|
|
|
$this->aliasName = $this->beau( $aliasName ); |
1849
|
|
|
|
1850
|
|
|
return $this; |
1851
|
|
|
} |
1852
|
|
|
|
1853
|
|
|
/** |
1854
|
|
|
* Returns properties of bean as an array. |
1855
|
|
|
* This method returns the raw internal property list of the |
1856
|
|
|
* bean. Only use this method for optimization purposes. Otherwise |
1857
|
|
|
* use the export() method to export bean data to arrays. |
1858
|
|
|
* |
1859
|
|
|
* @return array |
1860
|
|
|
*/ |
1861
|
|
|
public function getProperties() |
1862
|
|
|
{ |
1863
|
|
|
return $this->properties; |
1864
|
|
|
} |
1865
|
|
|
|
1866
|
|
|
/** |
1867
|
|
|
* Returns properties of bean as an array. |
1868
|
|
|
* This method returns the raw internal property list of the |
1869
|
|
|
* bean. Only use this method for optimization purposes. Otherwise |
1870
|
|
|
* use the export() method to export bean data to arrays. |
1871
|
|
|
* This method returns an array with the properties array and |
1872
|
|
|
* the type (string). |
1873
|
|
|
* |
1874
|
|
|
* @return array |
1875
|
|
|
*/ |
1876
|
|
|
public function getPropertiesAndType() |
1877
|
|
|
{ |
1878
|
|
|
return array( $this->properties, $this->__info['type'] ); |
1879
|
|
|
} |
1880
|
|
|
|
1881
|
|
|
/** |
1882
|
|
|
* Turns a camelcase property name into an underscored property name. |
1883
|
|
|
* |
1884
|
|
|
* Examples: |
1885
|
|
|
* oneACLRoute -> one_acl_route |
1886
|
|
|
* camelCase -> camel_case |
1887
|
|
|
* |
1888
|
|
|
* Also caches the result to improve performance. |
1889
|
|
|
* |
1890
|
|
|
* @param string $property |
1891
|
|
|
* |
1892
|
|
|
* @return string |
1893
|
|
|
*/ |
1894
|
|
|
public function beau( $property ) |
1895
|
|
|
{ |
1896
|
|
|
static $beautifulColumns = array(); |
1897
|
|
|
|
1898
|
|
|
if ( ctype_lower( $property ) ) return $property; |
1899
|
|
|
|
1900
|
|
|
if ( |
1901
|
|
|
( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) |
1902
|
|
|
|| ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) |
1903
|
|
|
|| ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) |
1904
|
|
|
) { |
1905
|
|
|
|
1906
|
|
|
$property = preg_replace( '/List$/', '', $property ); |
1907
|
|
|
return $property; |
1908
|
|
|
} |
1909
|
|
|
|
1910
|
|
|
if ( !isset( $beautifulColumns[$property] ) ) { |
1911
|
|
|
$beautifulColumns[$property] = AQueryWriter::camelsSnake( $property ); |
1912
|
|
|
} |
1913
|
|
|
|
1914
|
|
|
return $beautifulColumns[$property]; |
1915
|
|
|
} |
1916
|
|
|
|
1917
|
|
|
/** |
1918
|
|
|
* Clears all modifiers. |
1919
|
|
|
* |
1920
|
|
|
* @return self |
1921
|
|
|
*/ |
1922
|
|
|
public function clearModifiers() |
1923
|
|
|
{ |
1924
|
|
|
$this->withSql = ''; |
1925
|
|
|
$this->withParams = array(); |
1926
|
|
|
$this->aliasName = NULL; |
1927
|
|
|
$this->fetchType = NULL; |
1928
|
|
|
$this->noLoad = FALSE; |
1929
|
|
|
$this->all = FALSE; |
1930
|
|
|
$this->via = NULL; |
1931
|
|
|
return $this; |
1932
|
|
|
} |
1933
|
|
|
|
1934
|
|
|
/** |
1935
|
|
|
* Determines whether a list is opened in exclusive mode or not. |
1936
|
|
|
* If a list has been opened in exclusive mode this method will return TRUE, |
1937
|
|
|
* othwerwise it will return FALSE. |
1938
|
|
|
* |
1939
|
|
|
* @param string $listName name of the list to check |
1940
|
|
|
* |
1941
|
|
|
* @return boolean |
1942
|
|
|
*/ |
1943
|
|
|
public function isListInExclusiveMode( $listName ) |
1944
|
|
|
{ |
1945
|
|
|
$listName = $this->beau( $listName ); |
1946
|
|
|
|
1947
|
|
|
if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) { |
1948
|
|
|
$listName = substr($listName, 1); |
1949
|
|
|
} |
1950
|
|
|
|
1951
|
|
|
$listName = lcfirst( substr( $listName, 3 ) ); |
1952
|
|
|
|
1953
|
|
|
return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] ); |
1954
|
|
|
} |
1955
|
|
|
|
1956
|
|
|
/** |
1957
|
|
|
* Magic Getter. Gets the value for a specific property in the bean. |
1958
|
|
|
* If the property does not exist this getter will make sure no error |
1959
|
|
|
* occurs. This is because RedBean allows you to query (probe) for |
1960
|
|
|
* properties. If the property can not be found this method will |
1961
|
|
|
* return NULL instead. |
1962
|
|
|
* |
1963
|
|
|
* @param string $property name of the property you wish to obtain the value of |
1964
|
|
|
* |
1965
|
|
|
* @return mixed |
1966
|
|
|
*/ |
1967
|
|
|
public function &__get( $property ) |
1968
|
|
|
{ |
1969
|
|
|
$isEx = FALSE; |
1970
|
|
|
$isOwn = FALSE; |
1971
|
|
|
$isShared = FALSE; |
1972
|
|
|
|
1973
|
|
View Code Duplication |
if ( !ctype_lower( $property ) ) { |
1974
|
|
|
$property = $this->beau( $property ); |
1975
|
|
|
if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { |
1976
|
|
|
$property = substr($property, 1); |
1977
|
|
|
$listName = lcfirst( substr( $property, 3 ) ); |
1978
|
|
|
$isEx = TRUE; |
1979
|
|
|
$isOwn = TRUE; |
1980
|
|
|
$this->__info['sys.exclusive-'.$listName] = TRUE; |
1981
|
|
|
} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { |
1982
|
|
|
$isOwn = TRUE; |
1983
|
|
|
$listName = lcfirst( substr( $property, 3 ) ); |
1984
|
|
|
} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { |
1985
|
|
|
$isShared = TRUE; |
1986
|
|
|
} |
1987
|
|
|
} |
1988
|
|
|
|
1989
|
|
|
$fieldLink = $property . '_id'; |
1990
|
|
|
$exists = isset( $this->properties[$property] ); |
1991
|
|
|
|
1992
|
|
|
//If not exists and no field link and no list, bail out. |
1993
|
|
View Code Duplication |
if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) { |
1994
|
|
|
|
1995
|
|
|
$this->withSql = ''; |
1996
|
|
|
$this->withParams = array(); |
1997
|
|
|
$this->aliasName = NULL; |
1998
|
|
|
$this->fetchType = NULL; |
1999
|
|
|
$this->noLoad = FALSE; |
2000
|
|
|
$this->all = FALSE; |
2001
|
|
|
$this->via = NULL; |
2002
|
|
|
|
2003
|
|
|
$NULL = NULL; |
2004
|
|
|
return $NULL; |
2005
|
|
|
} |
2006
|
|
|
|
2007
|
|
|
$hasAlias = (!is_null($this->aliasName)); |
2008
|
|
|
$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? |
|
|
|
|
2009
|
|
|
($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; |
2010
|
|
|
$hasSQL = ($this->withSql !== '' || $this->via !== NULL); |
2011
|
|
|
$hasAll = (boolean) ($this->all); |
2012
|
|
|
|
2013
|
|
|
//If exists and no list or exits and list not changed, bail out. |
2014
|
|
View Code Duplication |
if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) { |
2015
|
|
|
|
2016
|
|
|
$this->withSql = ''; |
2017
|
|
|
$this->withParams = array(); |
2018
|
|
|
$this->aliasName = NULL; |
2019
|
|
|
$this->fetchType = NULL; |
2020
|
|
|
$this->noLoad = FALSE; |
2021
|
|
|
$this->all = FALSE; |
2022
|
|
|
$this->via = NULL; |
2023
|
|
|
return $this->properties[$property]; |
2024
|
|
|
} |
2025
|
|
|
|
2026
|
|
|
|
2027
|
|
|
list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); |
2028
|
|
|
|
2029
|
|
|
if ( isset( $this->$fieldLink ) ) { |
2030
|
|
|
$this->__info['tainted'] = TRUE; |
2031
|
|
|
|
2032
|
|
|
if ( isset( $this->__info["sys.parentcache.$property"] ) ) { |
2033
|
|
|
$bean = $this->__info["sys.parentcache.$property"]; |
2034
|
|
|
} else { |
2035
|
|
|
if ( isset( self::$aliases[$property] ) ) { |
2036
|
|
|
$type = self::$aliases[$property]; |
2037
|
|
|
} elseif ( $this->fetchType ) { |
2038
|
|
|
$type = $this->fetchType; |
2039
|
|
|
$this->fetchType = NULL; |
2040
|
|
|
} else { |
2041
|
|
|
$type = $property; |
2042
|
|
|
} |
2043
|
|
|
$bean = NULL; |
2044
|
|
|
if ( !is_null( $this->properties[$fieldLink] ) ) { |
2045
|
|
|
$bean = $redbean->load( $type, $this->properties[$fieldLink] ); |
2046
|
|
|
//If the IDs dont match, we failed to load, so try autoresolv in that case... |
2047
|
|
|
if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) { |
2048
|
|
|
$type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property ); |
2049
|
|
|
if ( !is_null( $type) ) { |
2050
|
|
|
$bean = $redbean->load( $type, $this->properties[$fieldLink] ); |
2051
|
|
|
$this->__info["sys.autoresolved.{$property}"] = $type; |
2052
|
|
|
} |
2053
|
|
|
} |
2054
|
|
|
} |
2055
|
|
|
} |
2056
|
|
|
|
2057
|
|
|
$this->properties[$property] = $bean; |
2058
|
|
|
$this->withSql = ''; |
2059
|
|
|
$this->withParams = array(); |
2060
|
|
|
$this->aliasName = NULL; |
2061
|
|
|
$this->fetchType = NULL; |
2062
|
|
|
$this->noLoad = FALSE; |
2063
|
|
|
$this->all = FALSE; |
2064
|
|
|
$this->via = NULL; |
2065
|
|
|
|
2066
|
|
|
return $this->properties[$property]; |
2067
|
|
|
|
2068
|
|
|
} |
2069
|
|
|
//Implicit: elseif ( $isOwn || $isShared ) { |
2070
|
|
|
if ( $this->noLoad ) { |
2071
|
|
|
$beans = array(); |
2072
|
|
|
} elseif ( $isOwn ) { |
2073
|
|
|
$beans = $this->getOwnList( $listName, $redbean ); |
2074
|
|
|
} else { |
2075
|
|
|
$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); |
2076
|
|
|
} |
2077
|
|
|
|
2078
|
|
|
$this->properties[$property] = $beans; |
2079
|
|
|
$this->__info["sys.shadow.$property"] = $beans; |
2080
|
|
|
$this->__info['tainted'] = TRUE; |
2081
|
|
|
|
2082
|
|
|
$this->withSql = ''; |
2083
|
|
|
$this->withParams = array(); |
2084
|
|
|
$this->aliasName = NULL; |
2085
|
|
|
$this->fetchType = NULL; |
2086
|
|
|
$this->noLoad = FALSE; |
2087
|
|
|
$this->all = FALSE; |
2088
|
|
|
$this->via = NULL; |
2089
|
|
|
|
2090
|
|
|
return $this->properties[$property]; |
2091
|
|
|
} |
2092
|
|
|
|
2093
|
|
|
/** |
2094
|
|
|
* Magic Setter. Sets the value for a specific property. |
2095
|
|
|
* This setter acts as a hook for OODB to mark beans as tainted. |
2096
|
|
|
* The tainted meta property can be retrieved using getMeta("tainted"). |
2097
|
|
|
* The tainted meta property indicates whether a bean has been modified and |
2098
|
|
|
* can be used in various caching mechanisms. |
2099
|
|
|
* |
2100
|
|
|
* @param string $property name of the property you wish to assign a value to |
2101
|
|
|
* @param mixed $value the value you want to assign |
2102
|
|
|
* |
2103
|
|
|
* @return void |
2104
|
|
|
* |
2105
|
|
|
* @throws Security |
2106
|
|
|
*/ |
2107
|
|
|
public function __set( $property, $value ) |
2108
|
|
|
{ |
2109
|
|
|
$isEx = FALSE; |
2110
|
|
|
$isOwn = FALSE; |
2111
|
|
|
$isShared = FALSE; |
2112
|
|
|
|
2113
|
|
View Code Duplication |
if ( !ctype_lower( $property ) ) { |
2114
|
|
|
$property = $this->beau( $property ); |
2115
|
|
|
if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { |
2116
|
|
|
$property = substr($property, 1); |
2117
|
|
|
$listName = lcfirst( substr( $property, 3 ) ); |
2118
|
|
|
$isEx = TRUE; |
2119
|
|
|
$isOwn = TRUE; |
2120
|
|
|
$this->__info['sys.exclusive-'.$listName] = TRUE; |
2121
|
|
|
} elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { |
2122
|
|
|
$isOwn = TRUE; |
2123
|
|
|
$listName = lcfirst( substr( $property, 3 ) ); |
2124
|
|
|
} elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { |
2125
|
|
|
$isShared = TRUE; |
2126
|
|
|
} |
2127
|
|
|
} |
2128
|
|
|
|
2129
|
|
|
$hasAlias = (!is_null($this->aliasName)); |
2130
|
|
|
$differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? |
|
|
|
|
2131
|
|
|
($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; |
2132
|
|
|
$hasSQL = ($this->withSql !== '' || $this->via !== NULL); |
2133
|
|
|
$exists = isset( $this->properties[$property] ); |
2134
|
|
|
$fieldLink = $property . '_id'; |
2135
|
|
|
|
2136
|
|
|
if ( ($isOwn || $isShared) && (!$exists || $hasSQL || $differentAlias) ) { |
2137
|
|
|
|
2138
|
|
|
if ( !$this->noLoad ) { |
2139
|
|
|
list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); |
2140
|
|
|
if ( $isOwn ) { |
2141
|
|
|
$beans = $this->getOwnList( $listName, $redbean ); |
2142
|
|
|
} else { |
2143
|
|
|
$beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); |
2144
|
|
|
} |
2145
|
|
|
$this->__info["sys.shadow.$property"] = $beans; |
2146
|
|
|
} |
2147
|
|
|
} |
2148
|
|
|
|
2149
|
|
|
$this->withSql = ''; |
2150
|
|
|
$this->withParams = array(); |
2151
|
|
|
$this->aliasName = NULL; |
2152
|
|
|
$this->fetchType = NULL; |
2153
|
|
|
$this->noLoad = FALSE; |
2154
|
|
|
$this->all = FALSE; |
2155
|
|
|
$this->via = NULL; |
2156
|
|
|
|
2157
|
|
|
$this->__info['tainted'] = TRUE; |
2158
|
|
|
$this->__info['changed'] = TRUE; |
2159
|
|
|
|
2160
|
|
|
if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) { |
2161
|
|
|
if ( is_null( $value ) || $value === FALSE ) { |
2162
|
|
|
|
2163
|
|
|
unset( $this->properties[ $property ]); |
2164
|
|
|
$this->properties[ $fieldLink ] = NULL; |
2165
|
|
|
|
2166
|
|
|
return; |
2167
|
|
|
} else { |
2168
|
|
|
throw new RedException( 'Cannot cast to bean.' ); |
2169
|
|
|
} |
2170
|
|
|
} |
2171
|
|
|
|
2172
|
|
|
if ( $value === FALSE ) { |
2173
|
|
|
$value = '0'; |
2174
|
|
|
} elseif ( $value === TRUE ) { |
2175
|
|
|
$value = '1'; |
2176
|
|
|
} elseif ( $value instanceof \DateTime ) { |
2177
|
|
|
$value = $value->format( 'Y-m-d H:i:s' ); |
2178
|
|
|
} |
2179
|
|
|
|
2180
|
|
|
$this->properties[$property] = $value; |
2181
|
|
|
} |
2182
|
|
|
|
2183
|
|
|
/** |
2184
|
|
|
* Sets a property directly, for internal use only. |
2185
|
|
|
* |
2186
|
|
|
* @param string $property property |
2187
|
|
|
* @param mixed $value value |
2188
|
|
|
* @param boolean $updateShadow whether you want to update the shadow |
2189
|
|
|
* @param boolean $taint whether you want to mark the bean as tainted |
2190
|
|
|
* |
2191
|
|
|
* @return void |
2192
|
|
|
*/ |
2193
|
|
|
public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE ) |
2194
|
|
|
{ |
2195
|
|
|
$this->properties[$property] = $value; |
2196
|
|
|
|
2197
|
|
|
if ( $updateShadow ) { |
2198
|
|
|
$this->__info['sys.shadow.' . $property] = $value; |
2199
|
|
|
} |
2200
|
|
|
|
2201
|
|
|
if ( $taint ) { |
2202
|
|
|
$this->__info['tainted'] = TRUE; |
2203
|
|
|
$this->__info['changed'] = TRUE; |
2204
|
|
|
} |
2205
|
|
|
} |
2206
|
|
|
|
2207
|
|
|
/** |
2208
|
|
|
* Returns the value of a meta property. A meta property |
2209
|
|
|
* contains additional information about the bean object that will not |
2210
|
|
|
* be stored in the database. Meta information is used to instruct |
2211
|
|
|
* RedBeanPHP as well as other systems how to deal with the bean. |
2212
|
|
|
* If the property cannot be found this getter will return NULL instead. |
2213
|
|
|
* |
2214
|
|
|
* Example: |
2215
|
|
|
* |
2216
|
|
|
* $bean->setMeta( 'flush-cache', TRUE ); |
2217
|
|
|
* |
2218
|
|
|
* RedBeanPHP also stores meta data in beans, this meta data uses |
2219
|
|
|
* keys prefixed with 'sys.' (system). |
2220
|
|
|
* |
2221
|
|
|
* @param string $path path |
2222
|
|
|
* @param mixed $default default value |
2223
|
|
|
* |
2224
|
|
|
* @return mixed |
2225
|
|
|
*/ |
2226
|
|
|
public function getMeta( $path, $default = NULL ) |
2227
|
|
|
{ |
2228
|
|
|
return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default; |
2229
|
|
|
} |
2230
|
|
|
|
2231
|
|
|
/** |
2232
|
|
|
* Gets and unsets a meta property. |
2233
|
|
|
* Moves a meta property out of the bean. |
2234
|
|
|
* This is a short-cut method that can be used instead |
2235
|
|
|
* of combining a get/unset. |
2236
|
|
|
* |
2237
|
|
|
* @param string $path path |
2238
|
|
|
* @param mixed $default default value |
|
|
|
|
2239
|
|
|
* |
2240
|
|
|
* @return mixed |
2241
|
|
|
*/ |
2242
|
|
|
public function moveMeta( $path, $value = NULL ) |
2243
|
|
|
{ |
2244
|
|
|
if ( isset( $this->__info[$path] ) ) { |
2245
|
|
|
$value = $this->__info[ $path ]; |
2246
|
|
|
unset( $this->__info[ $path ] ); |
2247
|
|
|
} |
2248
|
|
|
return $value; |
2249
|
|
|
} |
2250
|
|
|
|
2251
|
|
|
/** |
2252
|
|
|
* Stores a value in the specified Meta information property. |
2253
|
|
|
* The first argument should be the key to store the value under, |
2254
|
|
|
* the second argument should be the value. It is common to use |
2255
|
|
|
* a path-like notation for meta data in RedBeanPHP like: |
2256
|
|
|
* 'my.meta.data', however the dots are purely for readability, the |
2257
|
|
|
* meta data methods do not store nested structures or hierarchies. |
2258
|
|
|
* |
2259
|
|
|
* @param string $path path / key to store value under |
2260
|
|
|
* @param mixed $value value to store in bean (not in database) as meta data |
2261
|
|
|
* |
2262
|
|
|
* @return OODBBean |
2263
|
|
|
*/ |
2264
|
|
|
public function setMeta( $path, $value ) |
2265
|
|
|
{ |
2266
|
|
|
$this->__info[$path] = $value; |
2267
|
|
|
|
2268
|
|
|
return $this; |
2269
|
|
|
} |
2270
|
|
|
|
2271
|
|
|
/** |
2272
|
|
|
* Copies the meta information of the specified bean |
2273
|
|
|
* This is a convenience method to enable you to |
2274
|
|
|
* exchange meta information easily. |
2275
|
|
|
* |
2276
|
|
|
* @param OODBBean $bean |
2277
|
|
|
* |
2278
|
|
|
* @return OODBBean |
2279
|
|
|
*/ |
2280
|
|
|
public function copyMetaFrom( OODBBean $bean ) |
2281
|
|
|
{ |
2282
|
|
|
$this->__info = $bean->__info; |
2283
|
|
|
|
2284
|
|
|
return $this; |
2285
|
|
|
} |
2286
|
|
|
|
2287
|
|
|
/** |
2288
|
|
|
* Sends the call to the registered model. |
2289
|
|
|
* |
2290
|
|
|
* @param string $method name of the method |
2291
|
|
|
* @param array $args argument list |
2292
|
|
|
* |
2293
|
|
|
* @return mixed |
2294
|
|
|
*/ |
2295
|
|
|
public function __call( $method, $args ) |
2296
|
|
|
{ |
2297
|
|
|
if ( !isset( $this->__info['model'] ) ) { |
2298
|
|
|
$model = $this->beanHelper->getModelForBean( $this ); |
2299
|
|
|
|
2300
|
|
|
if ( !$model ) { |
2301
|
|
|
return NULL; |
2302
|
|
|
} |
2303
|
|
|
|
2304
|
|
|
$this->__info['model'] = $model; |
2305
|
|
|
} |
2306
|
|
|
if ( !method_exists( $this->__info['model'], $method ) ) { |
2307
|
|
|
|
2308
|
|
|
if ( self::$errorHandlingFUSE === FALSE ) { |
2309
|
|
|
return NULL; |
2310
|
|
|
} |
2311
|
|
|
|
2312
|
|
|
if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) { |
2313
|
|
|
return NULL; |
2314
|
|
|
} |
2315
|
|
|
|
2316
|
|
|
$message = "FUSE: method does not exist in model: $method"; |
2317
|
|
|
if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) { |
2318
|
|
|
error_log( $message ); |
2319
|
|
|
return NULL; |
2320
|
|
|
} elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) { |
2321
|
|
|
trigger_error( $message, E_USER_NOTICE ); |
2322
|
|
|
return NULL; |
2323
|
|
|
} elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) { |
2324
|
|
|
trigger_error( $message, E_USER_WARNING ); |
2325
|
|
|
return NULL; |
2326
|
|
|
} elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) { |
2327
|
|
|
throw new \Exception( $message ); |
2328
|
|
|
} elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) { |
2329
|
|
|
$func = self::$errorHandler; |
2330
|
|
|
return $func(array( |
2331
|
|
|
'message' => $message, |
2332
|
|
|
'method' => $method, |
2333
|
|
|
'args' => $args, |
2334
|
|
|
'bean' => $this |
2335
|
|
|
)); |
2336
|
|
|
} |
2337
|
|
|
trigger_error( $message, E_USER_ERROR ); |
2338
|
|
|
return NULL; |
2339
|
|
|
} |
2340
|
|
|
|
2341
|
|
|
return call_user_func_array( array( $this->__info['model'], $method ), $args ); |
2342
|
|
|
} |
2343
|
|
|
|
2344
|
|
|
/** |
2345
|
|
|
* Implementation of __toString Method |
2346
|
|
|
* Routes call to Model. If the model implements a __toString() method this |
2347
|
|
|
* method will be called and the result will be returned. In case of an |
2348
|
|
|
* echo-statement this result will be printed. If the model does not |
2349
|
|
|
* implement a __toString method, this method will return a JSON |
2350
|
|
|
* representation of the current bean. |
2351
|
|
|
* |
2352
|
|
|
* @return string |
2353
|
|
|
*/ |
2354
|
|
|
public function __toString() |
2355
|
|
|
{ |
2356
|
|
|
$string = $this->__call( '__toString', array() ); |
2357
|
|
|
|
2358
|
|
|
if ( $string === NULL ) { |
2359
|
|
|
return json_encode( $this->properties ); |
2360
|
|
|
} else { |
2361
|
|
|
return $string; |
2362
|
|
|
} |
2363
|
|
|
} |
2364
|
|
|
|
2365
|
|
|
/** |
2366
|
|
|
* Implementation of Array Access Interface, you can access bean objects |
2367
|
|
|
* like an array. |
2368
|
|
|
* Call gets routed to __set. |
2369
|
|
|
* |
2370
|
|
|
* @param mixed $offset offset string |
2371
|
|
|
* @param mixed $value value |
2372
|
|
|
* |
2373
|
|
|
* @return void |
2374
|
|
|
*/ |
2375
|
|
|
public function offsetSet( $offset, $value ) |
2376
|
|
|
{ |
2377
|
|
|
$this->__set( $offset, $value ); |
2378
|
|
|
} |
2379
|
|
|
|
2380
|
|
|
/** |
2381
|
|
|
* Implementation of Array Access Interface, you can access bean objects |
2382
|
|
|
* like an array. |
2383
|
|
|
* |
2384
|
|
|
* Array functions do not reveal x-own-lists and list-alias because |
2385
|
|
|
* you dont want duplicate entries in foreach-loops. |
2386
|
|
|
* Also offers a slight performance improvement for array access. |
2387
|
|
|
* |
2388
|
|
|
* @param mixed $offset property |
2389
|
|
|
* |
2390
|
|
|
* @return boolean |
2391
|
|
|
*/ |
2392
|
|
|
public function offsetExists( $offset ) |
2393
|
|
|
{ |
2394
|
|
|
return $this->__isset( $offset ); |
2395
|
|
|
} |
2396
|
|
|
|
2397
|
|
|
/** |
2398
|
|
|
* Implementation of Array Access Interface, you can access bean objects |
2399
|
|
|
* like an array. |
2400
|
|
|
* Unsets a value from the array/bean. |
2401
|
|
|
* |
2402
|
|
|
* Array functions do not reveal x-own-lists and list-alias because |
2403
|
|
|
* you dont want duplicate entries in foreach-loops. |
2404
|
|
|
* Also offers a slight performance improvement for array access. |
2405
|
|
|
* |
2406
|
|
|
* @param mixed $offset property |
2407
|
|
|
* |
2408
|
|
|
* @return void |
2409
|
|
|
*/ |
2410
|
|
|
public function offsetUnset( $offset ) |
2411
|
|
|
{ |
2412
|
|
|
$this->__unset( $offset ); |
2413
|
|
|
} |
2414
|
|
|
|
2415
|
|
|
/** |
2416
|
|
|
* Implementation of Array Access Interface, you can access bean objects |
2417
|
|
|
* like an array. |
2418
|
|
|
* Returns value of a property. |
2419
|
|
|
* |
2420
|
|
|
* Array functions do not reveal x-own-lists and list-alias because |
2421
|
|
|
* you dont want duplicate entries in foreach-loops. |
2422
|
|
|
* Also offers a slight performance improvement for array access. |
2423
|
|
|
* |
2424
|
|
|
* @param mixed $offset property |
2425
|
|
|
* |
2426
|
|
|
* @return mixed |
2427
|
|
|
*/ |
2428
|
|
|
public function &offsetGet( $offset ) |
2429
|
|
|
{ |
2430
|
|
|
return $this->__get( $offset ); |
2431
|
|
|
} |
2432
|
|
|
|
2433
|
|
|
/** |
2434
|
|
|
* Chainable method to cast a certain ID to a bean; for instance: |
2435
|
|
|
* $person = $club->fetchAs('person')->member; |
2436
|
|
|
* This will load a bean of type person using member_id as ID. |
2437
|
|
|
* |
2438
|
|
|
* @param string $type preferred fetch type |
2439
|
|
|
* |
2440
|
|
|
* @return OODBBean |
2441
|
|
|
*/ |
2442
|
|
|
public function fetchAs( $type ) |
2443
|
|
|
{ |
2444
|
|
|
$this->fetchType = $type; |
|
|
|
|
2445
|
|
|
|
2446
|
|
|
return $this; |
2447
|
|
|
} |
2448
|
|
|
|
2449
|
|
|
/** |
2450
|
|
|
* For polymorphic bean relations. |
2451
|
|
|
* Same as fetchAs but uses a column instead of a direct value. |
2452
|
|
|
* |
2453
|
|
|
* @param string $column |
|
|
|
|
2454
|
|
|
* |
2455
|
|
|
* @return OODBBean |
2456
|
|
|
*/ |
2457
|
|
|
public function poly( $field ) |
2458
|
|
|
{ |
2459
|
|
|
return $this->fetchAs( $this->$field ); |
2460
|
|
|
} |
2461
|
|
|
|
2462
|
|
|
/** |
2463
|
|
|
* Traverses a bean property with the specified function. |
2464
|
|
|
* Recursively iterates through the property invoking the |
2465
|
|
|
* function for each bean along the way passing the bean to it. |
2466
|
|
|
* |
2467
|
|
|
* Can be used together with with, withCondition, alias and fetchAs. |
2468
|
|
|
* |
2469
|
|
|
* @param string $property property |
2470
|
|
|
* @param closure $function function |
2471
|
|
|
* |
2472
|
|
|
* @return OODBBean |
2473
|
|
|
*/ |
2474
|
|
|
public function traverse( $property, $function, $maxDepth = NULL ) |
2475
|
|
|
{ |
2476
|
|
|
$this->via = NULL; |
2477
|
|
|
if ( strpos( $property, 'shared' ) !== FALSE ) { |
2478
|
|
|
throw new RedException( 'Traverse only works with (x)own-lists.' ); |
2479
|
|
|
} |
2480
|
|
|
|
2481
|
|
|
if ( !is_null( $maxDepth ) ) { |
2482
|
|
|
if ( !$maxDepth-- ) return $this; |
2483
|
|
|
} |
2484
|
|
|
|
2485
|
|
|
$oldFetchType = $this->fetchType; |
2486
|
|
|
$oldAliasName = $this->aliasName; |
2487
|
|
|
$oldWith = $this->withSql; |
2488
|
|
|
$oldBindings = $this->withParams; |
2489
|
|
|
|
2490
|
|
|
$beans = $this->$property; |
2491
|
|
|
|
2492
|
|
|
if ( $beans === NULL ) return $this; |
2493
|
|
|
|
2494
|
|
|
if ( !is_array( $beans ) ) $beans = array( $beans ); |
2495
|
|
|
|
2496
|
|
|
foreach( $beans as $bean ) { |
2497
|
|
|
|
2498
|
|
|
$function( $bean ); |
2499
|
|
|
|
2500
|
|
|
$bean->fetchType = $oldFetchType; |
2501
|
|
|
$bean->aliasName = $oldAliasName; |
2502
|
|
|
$bean->withSql = $oldWith; |
2503
|
|
|
$bean->withParams = $oldBindings; |
2504
|
|
|
|
2505
|
|
|
$bean->traverse( $property, $function, $maxDepth ); |
2506
|
|
|
} |
2507
|
|
|
|
2508
|
|
|
return $this; |
2509
|
|
|
} |
2510
|
|
|
|
2511
|
|
|
/** |
2512
|
|
|
* Implementation of\Countable interface. Makes it possible to use |
2513
|
|
|
* count() function on a bean. |
2514
|
|
|
* |
2515
|
|
|
* @return integer |
2516
|
|
|
*/ |
2517
|
|
|
public function count() |
2518
|
|
|
{ |
2519
|
|
|
return count( $this->properties ); |
2520
|
|
|
} |
2521
|
|
|
|
2522
|
|
|
/** |
2523
|
|
|
* Checks whether a bean is empty or not. |
2524
|
|
|
* A bean is empty if it has no other properties than the id field OR |
2525
|
|
|
* if all the other property are empty(). |
2526
|
|
|
* |
2527
|
|
|
* @return boolean |
2528
|
|
|
*/ |
2529
|
|
|
public function isEmpty() |
2530
|
|
|
{ |
2531
|
|
|
$empty = TRUE; |
2532
|
|
|
foreach ( $this->properties as $key => $value ) { |
2533
|
|
|
if ( $key == 'id' ) { |
2534
|
|
|
continue; |
2535
|
|
|
} |
2536
|
|
|
if ( !empty( $value ) ) { |
2537
|
|
|
$empty = FALSE; |
2538
|
|
|
} |
2539
|
|
|
} |
2540
|
|
|
|
2541
|
|
|
return $empty; |
2542
|
|
|
} |
2543
|
|
|
|
2544
|
|
|
/** |
2545
|
|
|
* Chainable setter. |
2546
|
|
|
* |
2547
|
|
|
* @param string $property the property of the bean |
2548
|
|
|
* @param mixed $value the value you want to set |
2549
|
|
|
* |
2550
|
|
|
* @return OODBBean |
2551
|
|
|
*/ |
2552
|
|
|
public function setAttr( $property, $value ) |
2553
|
|
|
{ |
2554
|
|
|
$this->$property = $value; |
2555
|
|
|
|
2556
|
|
|
return $this; |
2557
|
|
|
} |
2558
|
|
|
|
2559
|
|
|
/** |
2560
|
|
|
* Comfort method. |
2561
|
|
|
* Unsets all properties in array. |
2562
|
|
|
* |
2563
|
|
|
* @param array $properties properties you want to unset. |
2564
|
|
|
* |
2565
|
|
|
* @return OODBBean |
2566
|
|
|
*/ |
2567
|
|
|
public function unsetAll( $properties ) |
2568
|
|
|
{ |
2569
|
|
|
foreach ( $properties as $prop ) { |
2570
|
|
|
if ( isset( $this->properties[$prop] ) ) { |
2571
|
|
|
unset( $this->properties[$prop] ); |
2572
|
|
|
} |
2573
|
|
|
} |
2574
|
|
|
|
2575
|
|
|
return $this; |
2576
|
|
|
} |
2577
|
|
|
|
2578
|
|
|
/** |
2579
|
|
|
* Returns original (old) value of a property. |
2580
|
|
|
* You can use this method to see what has changed in a |
2581
|
|
|
* bean. |
2582
|
|
|
* |
2583
|
|
|
* @param string $property name of the property you want the old value of |
2584
|
|
|
* |
2585
|
|
|
* @return mixed |
2586
|
|
|
*/ |
2587
|
|
|
public function old( $property ) |
2588
|
|
|
{ |
2589
|
|
|
$old = $this->getMeta( 'sys.orig', array() ); |
2590
|
|
|
|
2591
|
|
|
if ( array_key_exists( $property, $old ) ) { |
2592
|
|
|
return $old[$property]; |
2593
|
|
|
} |
2594
|
|
|
|
2595
|
|
|
return NULL; |
2596
|
|
|
} |
2597
|
|
|
|
2598
|
|
|
/** |
2599
|
|
|
* Convenience method. |
2600
|
|
|
* Returns TRUE if the bean has been changed, or FALSE otherwise. |
2601
|
|
|
* Same as $bean->getMeta('tainted'); |
2602
|
|
|
* Note that a bean becomes tainted as soon as you retrieve a list from |
2603
|
|
|
* the bean. This is because the bean lists are arrays and the bean cannot |
2604
|
|
|
* determine whether you have made modifications to a list so RedBeanPHP |
2605
|
|
|
* will mark the whole bean as tainted. |
2606
|
|
|
* |
2607
|
|
|
* @return boolean |
2608
|
|
|
*/ |
2609
|
|
|
public function isTainted() |
2610
|
|
|
{ |
2611
|
|
|
return $this->getMeta( 'tainted' ); |
2612
|
|
|
} |
2613
|
|
|
|
2614
|
|
|
/** |
2615
|
|
|
* Returns TRUE if the value of a certain property of the bean has been changed and |
2616
|
|
|
* FALSE otherwise. |
2617
|
|
|
* |
2618
|
|
|
* Note that this method will return TRUE if applied to a loaded list. |
2619
|
|
|
* Also note that this method keeps track of the bean's history regardless whether |
2620
|
|
|
* it has been stored or not. Storing a bean does not undo it's history, |
2621
|
|
|
* to clean the history of a bean use: clearHistory(). |
2622
|
|
|
* |
2623
|
|
|
* @param string $property name of the property you want the change-status of |
2624
|
|
|
* |
2625
|
|
|
* @return boolean |
2626
|
|
|
*/ |
2627
|
|
|
public function hasChanged( $property ) |
2628
|
|
|
{ |
2629
|
|
|
return ( array_key_exists( $property, $this->properties ) ) ? |
2630
|
|
|
$this->old( $property ) != $this->properties[$property] : FALSE; |
2631
|
|
|
} |
2632
|
|
|
|
2633
|
|
|
/** |
2634
|
|
|
* Returns TRUE if the specified list exists, has been loaded and has been changed: |
2635
|
|
|
* beans have been added or deleted. This method will not tell you anything about |
2636
|
|
|
* the state of the beans in the list. |
2637
|
|
|
* |
2638
|
|
|
* @param string $property name of the list to check |
2639
|
|
|
* |
2640
|
|
|
* @return boolean |
2641
|
|
|
*/ |
2642
|
|
|
public function hasListChanged( $property ) |
2643
|
|
|
{ |
2644
|
|
|
if ( !array_key_exists( $property, $this->properties ) ) return FALSE; |
2645
|
|
|
$diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] ); |
2646
|
|
|
if ( count( $diffAdded ) ) return TRUE; |
2647
|
|
|
$diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] ); |
2648
|
|
|
if ( count( $diffMissing ) ) return TRUE; |
2649
|
|
|
return FALSE; |
2650
|
|
|
} |
2651
|
|
|
|
2652
|
|
|
/** |
2653
|
|
|
* Clears (syncs) the history of the bean. |
2654
|
|
|
* Resets all shadow values of the bean to their current value. |
2655
|
|
|
* |
2656
|
|
|
* @return self |
2657
|
|
|
*/ |
2658
|
|
|
public function clearHistory() |
2659
|
|
|
{ |
2660
|
|
|
$this->__info['sys.orig'] = array(); |
2661
|
|
|
foreach( $this->properties as $key => $value ) { |
2662
|
|
|
if ( is_scalar($value) ) { |
2663
|
|
|
$this->__info['sys.orig'][$key] = $value; |
2664
|
|
|
} else { |
2665
|
|
|
$this->__info['sys.shadow.'.$key] = $value; |
2666
|
|
|
} |
2667
|
|
|
} |
2668
|
|
|
return $this; |
2669
|
|
|
} |
2670
|
|
|
|
2671
|
|
|
/** |
2672
|
|
|
* Creates a N-M relation by linking an intermediate bean. |
2673
|
|
|
* This method can be used to quickly connect beans using indirect |
2674
|
|
|
* relations. For instance, given an album and a song you can connect the two |
2675
|
|
|
* using a track with a number like this: |
2676
|
|
|
* |
2677
|
|
|
* Usage: |
2678
|
|
|
* |
2679
|
|
|
* $album->link('track', array('number'=>1))->song = $song; |
2680
|
|
|
* |
2681
|
|
|
* or: |
2682
|
|
|
* |
2683
|
|
|
* $album->link($trackBean)->song = $song; |
2684
|
|
|
* |
2685
|
|
|
* What this method does is adding the link bean to the own-list, in this case |
2686
|
|
|
* ownTrack. If the first argument is a string and the second is an array or |
2687
|
|
|
* a JSON string then the linking bean gets dispensed on-the-fly as seen in |
2688
|
|
|
* example #1. After preparing the linking bean, the bean is returned thus |
2689
|
|
|
* allowing the chained setter: ->song = $song. |
2690
|
|
|
* |
2691
|
|
|
* @param string|OODBBean $type type of bean to dispense or the full bean |
|
|
|
|
2692
|
|
|
* @param string|array $qualification JSON string or array (optional) |
2693
|
|
|
* |
2694
|
|
|
* @return OODBBean |
2695
|
|
|
*/ |
2696
|
|
|
public function link( $typeOrBean, $qualification = array() ) |
2697
|
|
|
{ |
2698
|
|
|
if ( is_string( $typeOrBean ) ) { |
2699
|
|
|
|
2700
|
|
|
$typeOrBean = AQueryWriter::camelsSnake( $typeOrBean ); |
2701
|
|
|
|
2702
|
|
|
$bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean ); |
2703
|
|
|
|
2704
|
|
|
if ( is_string( $qualification ) ) { |
2705
|
|
|
$data = json_decode( $qualification, TRUE ); |
2706
|
|
|
} else { |
2707
|
|
|
$data = $qualification; |
2708
|
|
|
} |
2709
|
|
|
|
2710
|
|
|
foreach ( $data as $key => $value ) { |
2711
|
|
|
$bean->$key = $value; |
2712
|
|
|
} |
2713
|
|
|
} else { |
2714
|
|
|
$bean = $typeOrBean; |
2715
|
|
|
} |
2716
|
|
|
|
2717
|
|
|
$list = 'own' . ucfirst( $bean->getMeta( 'type' ) ); |
2718
|
|
|
|
2719
|
|
|
array_push( $this->$list, $bean ); |
2720
|
|
|
|
2721
|
|
|
return $bean; |
2722
|
|
|
} |
2723
|
|
|
|
2724
|
|
|
/** |
2725
|
|
|
* Returns the same bean freshly loaded from the database. |
2726
|
|
|
* |
2727
|
|
|
* @return OODBBean |
2728
|
|
|
*/ |
2729
|
|
|
public function fresh() |
2730
|
|
|
{ |
2731
|
|
|
return $this->beanHelper->getToolbox()->getRedBean()->load( $this->getMeta( 'type' ), $this->properties['id'] ); |
2732
|
|
|
} |
2733
|
|
|
|
2734
|
|
|
/** |
2735
|
|
|
* Registers a association renaming globally. |
2736
|
|
|
* |
2737
|
|
|
* @param string $via type you wish to use for shared lists |
2738
|
|
|
* |
2739
|
|
|
* @return OODBBean |
2740
|
|
|
*/ |
2741
|
|
|
public function via( $via ) |
2742
|
|
|
{ |
2743
|
|
|
$this->via = AQueryWriter::camelsSnake( $via ); |
2744
|
|
|
|
2745
|
|
|
return $this; |
2746
|
|
|
} |
2747
|
|
|
|
2748
|
|
|
/** |
2749
|
|
|
* Counts all own beans of type $type. |
2750
|
|
|
* Also works with alias(), with() and withCondition(). |
2751
|
|
|
* |
2752
|
|
|
* @param string $type the type of bean you want to count |
2753
|
|
|
* |
2754
|
|
|
* @return integer |
2755
|
|
|
*/ |
2756
|
|
|
public function countOwn( $type ) |
2757
|
|
|
{ |
2758
|
|
|
$type = $this->beau( $type ); |
2759
|
|
|
|
2760
|
|
|
if ( $this->aliasName ) { |
2761
|
|
|
$myFieldLink = $this->aliasName . '_id'; |
2762
|
|
|
|
2763
|
|
|
$this->aliasName = NULL; |
2764
|
|
|
} else { |
2765
|
|
|
$myFieldLink = $this->__info['type'] . '_id'; |
2766
|
|
|
} |
2767
|
|
|
|
2768
|
|
|
$count = 0; |
2769
|
|
|
|
2770
|
|
|
if ( $this->getID() ) { |
|
|
|
|
2771
|
|
|
|
2772
|
|
|
$firstKey = NULL; |
2773
|
|
View Code Duplication |
if ( count( $this->withParams ) > 0 ) { |
2774
|
|
|
reset( $this->withParams ); |
2775
|
|
|
$firstKey = key( $this->withParams ); |
2776
|
|
|
} |
2777
|
|
|
|
2778
|
|
|
$joinSql = $this->parseJoin( $type ); |
2779
|
|
|
|
2780
|
|
|
if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { |
2781
|
|
|
$bindings = $this->withParams; |
2782
|
|
|
$bindings[':slot0'] = $this->getID(); |
2783
|
|
|
$count = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); |
2784
|
|
|
} else { |
2785
|
|
|
$bindings = array_merge( array( $this->getID() ), $this->withParams ); |
2786
|
|
|
$count = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); |
2787
|
|
|
} |
2788
|
|
|
|
2789
|
|
|
} |
2790
|
|
|
|
2791
|
|
|
$this->clearModifiers(); |
2792
|
|
|
return (int) $count; |
2793
|
|
|
} |
2794
|
|
|
|
2795
|
|
|
/** |
2796
|
|
|
* Counts all shared beans of type $type. |
2797
|
|
|
* Also works with via(), with() and withCondition(). |
2798
|
|
|
* |
2799
|
|
|
* @param string $type type of bean you wish to count |
2800
|
|
|
* |
2801
|
|
|
* @return integer |
2802
|
|
|
*/ |
2803
|
|
|
public function countShared( $type ) |
2804
|
|
|
{ |
2805
|
|
|
$toolbox = $this->beanHelper->getToolbox(); |
2806
|
|
|
$redbean = $toolbox->getRedBean(); |
2807
|
|
|
$writer = $toolbox->getWriter(); |
2808
|
|
|
|
2809
|
|
View Code Duplication |
if ( $this->via ) { |
2810
|
|
|
$oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) ); |
2811
|
|
|
|
2812
|
|
|
if ( $oldName !== $this->via ) { |
2813
|
|
|
//set the new renaming rule |
2814
|
|
|
$writer->renameAssocTable( $oldName, $this->via ); |
2815
|
|
|
$this->via = NULL; |
2816
|
|
|
} |
2817
|
|
|
} |
2818
|
|
|
|
2819
|
|
|
$type = $this->beau( $type ); |
2820
|
|
|
$count = 0; |
2821
|
|
|
|
2822
|
|
|
if ( $this->getID() ) { |
|
|
|
|
2823
|
|
|
$count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams, TRUE ); |
|
|
|
|
2824
|
|
|
} |
2825
|
|
|
|
2826
|
|
|
$this->clearModifiers(); |
2827
|
|
|
return (integer) $count; |
2828
|
|
|
} |
2829
|
|
|
|
2830
|
|
|
/** |
2831
|
|
|
* Iterates through the specified own-list and |
2832
|
|
|
* fetches all properties (with their type) and |
2833
|
|
|
* returns the references. |
2834
|
|
|
* Use this method to quickly load indirectly related |
2835
|
|
|
* beans in an own-list. Whenever you cannot use a |
2836
|
|
|
* shared-list this method offers the same convenience |
2837
|
|
|
* by aggregating the parent beans of all children in |
2838
|
|
|
* the specified own-list. |
2839
|
|
|
* |
2840
|
|
|
* Example: |
2841
|
|
|
* |
2842
|
|
|
* $quest->aggr( 'xownQuestTarget', 'target', 'quest' ); |
2843
|
|
|
* |
2844
|
|
|
* Loads (in batch) and returns references to all |
2845
|
|
|
* quest beans residing in the $questTarget->target properties |
2846
|
|
|
* of each element in the xownQuestTargetList. |
2847
|
|
|
* |
2848
|
|
|
* @param string $list the list you wish to process |
2849
|
|
|
* @param string $property the property to load |
2850
|
|
|
* @param string $type the type of bean residing in this property (optional) |
2851
|
|
|
* |
2852
|
|
|
* @return array |
2853
|
|
|
*/ |
2854
|
|
|
public function &aggr( $list, $property, $type = NULL ) |
2855
|
|
|
{ |
2856
|
|
|
$this->via = NULL; |
2857
|
|
|
$ids = $beanIndex = $references = array(); |
2858
|
|
|
|
2859
|
|
|
if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.'); |
2860
|
|
|
if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.'); |
2861
|
|
|
if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.'); |
2862
|
|
|
|
2863
|
|
|
if ( is_null( $type ) ) $type = $property; |
2864
|
|
|
|
2865
|
|
|
foreach( $this->$list as $bean ) { |
2866
|
|
|
$field = $property . '_id'; |
2867
|
|
|
if ( isset( $bean->$field) ) { |
2868
|
|
|
$ids[] = $bean->$field; |
2869
|
|
|
$beanIndex[$bean->$field] = $bean; |
2870
|
|
|
} |
2871
|
|
|
} |
2872
|
|
|
|
2873
|
|
|
$beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids ); |
2874
|
|
|
|
2875
|
|
|
//now preload the beans as well |
2876
|
|
|
foreach( $beans as $bean ) { |
2877
|
|
|
$beanIndex[$bean->id]->setProperty( $property, $bean ); |
2878
|
|
|
} |
2879
|
|
|
|
2880
|
|
|
foreach( $beanIndex as $indexedBean ) { |
2881
|
|
|
$references[] = $indexedBean->$property; |
2882
|
|
|
} |
2883
|
|
|
|
2884
|
|
|
return $references; |
2885
|
|
|
} |
2886
|
|
|
|
2887
|
|
|
/** |
2888
|
|
|
* Tests whether the database identities of two beans are equal. |
2889
|
|
|
* |
2890
|
|
|
* @param OODBBean $bean other bean |
2891
|
|
|
* |
2892
|
|
|
* @return boolean |
2893
|
|
|
*/ |
2894
|
|
|
public function equals(OODBBean $bean) |
2895
|
|
|
{ |
2896
|
|
|
return (bool) ( |
2897
|
|
|
( (string) $this->properties['id'] === (string) $bean->properties['id'] ) |
2898
|
|
|
&& ( (string) $this->__info['type'] === (string) $bean->__info['type'] ) |
2899
|
|
|
); |
2900
|
|
|
} |
2901
|
|
|
} |
2902
|
|
|
} |
2903
|
|
|
|
2904
|
|
|
namespace RedBeanPHP { |
2905
|
|
|
|
2906
|
|
|
use RedBeanPHP\Observer as Observer; |
|
|
|
|
2907
|
|
|
|
2908
|
|
|
/** |
2909
|
|
|
* Observable |
2910
|
|
|
* Base class for Observables |
2911
|
|
|
* |
2912
|
|
|
* @file RedBeanPHP/Observable.php |
2913
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
2914
|
|
|
* @license BSD/GPLv2 |
2915
|
|
|
* |
2916
|
|
|
* @copyright |
2917
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
2918
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
2919
|
|
|
* with this source code in the file license.txt. |
2920
|
|
|
*/ |
2921
|
|
|
abstract class Observable { //bracket must be here - otherwise coverage software does not understand. |
|
|
|
|
2922
|
|
|
|
2923
|
|
|
/** |
2924
|
|
|
* @var array |
2925
|
|
|
*/ |
2926
|
|
|
private $observers = array(); |
2927
|
|
|
|
2928
|
|
|
/** |
2929
|
|
|
* Implementation of the Observer Pattern. |
2930
|
|
|
* Adds an event listener to the observable object. |
2931
|
|
|
* First argument should be the name of the event you wish to listen for. |
2932
|
|
|
* Second argument should be the object that wants to be notified in case |
2933
|
|
|
* the event occurs. |
2934
|
|
|
* |
2935
|
|
|
* @param string $eventname event identifier |
2936
|
|
|
* @param Observer $observer observer instance |
2937
|
|
|
* |
2938
|
|
|
* @return void |
2939
|
|
|
*/ |
2940
|
|
|
public function addEventListener( $eventname, Observer $observer ) |
2941
|
|
|
{ |
2942
|
|
|
if ( !isset( $this->observers[$eventname] ) ) { |
2943
|
|
|
$this->observers[$eventname] = array(); |
2944
|
|
|
} |
2945
|
|
|
|
2946
|
|
|
foreach ( $this->observers[$eventname] as $o ) { |
2947
|
|
|
if ( $o == $observer ) { |
2948
|
|
|
return; |
2949
|
|
|
} |
2950
|
|
|
} |
2951
|
|
|
|
2952
|
|
|
$this->observers[$eventname][] = $observer; |
2953
|
|
|
} |
2954
|
|
|
|
2955
|
|
|
/** |
2956
|
|
|
* Notifies listeners. |
2957
|
|
|
* Sends the signal $eventname, the event identifier and a message object |
2958
|
|
|
* to all observers that have been registered to receive notification for |
2959
|
|
|
* this event. Part of the observer pattern implementation in RedBeanPHP. |
2960
|
|
|
* |
2961
|
|
|
* @param string $eventname event you want signal |
2962
|
|
|
* @param mixed $info message object to send along |
2963
|
|
|
* |
2964
|
|
|
* @return void |
2965
|
|
|
*/ |
2966
|
|
|
public function signal( $eventname, $info ) |
2967
|
|
|
{ |
2968
|
|
|
if ( !isset( $this->observers[$eventname] ) ) { |
2969
|
|
|
$this->observers[$eventname] = array(); |
2970
|
|
|
} |
2971
|
|
|
|
2972
|
|
|
foreach ( $this->observers[$eventname] as $observer ) { |
2973
|
|
|
$observer->onEvent( $eventname, $info ); |
2974
|
|
|
} |
2975
|
|
|
} |
2976
|
|
|
} |
2977
|
|
|
} |
2978
|
|
|
|
2979
|
|
|
namespace RedBeanPHP { |
2980
|
|
|
|
2981
|
|
|
/** |
2982
|
|
|
* Observer. |
2983
|
|
|
* |
2984
|
|
|
* Interface for Observer object. Implementation of the |
2985
|
|
|
* observer pattern. |
2986
|
|
|
* |
2987
|
|
|
* @file RedBeanPHP/Observer.php |
2988
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
2989
|
|
|
* @license BSD/GPLv2 |
2990
|
|
|
* @desc Part of the observer pattern in RedBean |
2991
|
|
|
* |
2992
|
|
|
* @copyright |
2993
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
2994
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
2995
|
|
|
* with this source code in the file license.txt. |
2996
|
|
|
*/ |
2997
|
|
|
interface Observer |
|
|
|
|
2998
|
|
|
{ |
2999
|
|
|
/** |
3000
|
|
|
* An observer object needs to be capable of receiving |
3001
|
|
|
* notifications. Therefore the observer needs to implement the |
3002
|
|
|
* onEvent method with two parameters: the event identifier specifying the |
3003
|
|
|
* current event and a message object (in RedBeanPHP this can also be a bean). |
3004
|
|
|
* |
3005
|
|
|
* @param string $eventname event identifier |
3006
|
|
|
* @param mixed $bean a message sent along with the notification |
3007
|
|
|
* |
3008
|
|
|
* @return void |
3009
|
|
|
*/ |
3010
|
|
|
public function onEvent( $eventname, $bean ); |
3011
|
|
|
} |
3012
|
|
|
} |
3013
|
|
|
|
3014
|
|
|
namespace RedBeanPHP { |
3015
|
|
|
|
3016
|
|
|
/** |
3017
|
|
|
* Adapter Interface. |
3018
|
|
|
* Describes the API for a RedBeanPHP Database Adapter. |
3019
|
|
|
* This interface defines the API contract for |
3020
|
|
|
* a RedBeanPHP Database Adapter. |
3021
|
|
|
* |
3022
|
|
|
* @file RedBeanPHP/Adapter.php |
3023
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
3024
|
|
|
* @license BSD/GPLv2 |
3025
|
|
|
* |
3026
|
|
|
* @copyright |
3027
|
|
|
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
3028
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3029
|
|
|
* with this source code in the file license.txt. |
3030
|
|
|
*/ |
3031
|
|
|
interface Adapter |
|
|
|
|
3032
|
|
|
{ |
3033
|
|
|
/** |
3034
|
|
|
* Returns the latest SQL statement |
3035
|
|
|
* |
3036
|
|
|
* @return string |
3037
|
|
|
*/ |
3038
|
|
|
public function getSQL(); |
3039
|
|
|
|
3040
|
|
|
/** |
3041
|
|
|
* Executes an SQL Statement using an array of values to bind |
3042
|
|
|
* If $noevent is TRUE then this function will not signal its |
3043
|
|
|
* observers to notify about the SQL execution; this to prevent |
3044
|
|
|
* infinite recursion when using observers. |
3045
|
|
|
* |
3046
|
|
|
* @param string $sql SQL |
3047
|
|
|
* @param array $bindings values |
3048
|
|
|
* @param boolean $noevent no event firing |
3049
|
|
|
*/ |
3050
|
|
|
public function exec( $sql, $bindings = array(), $noevent = FALSE ); |
3051
|
|
|
|
3052
|
|
|
/** |
3053
|
|
|
* Executes an SQL Query and returns a resultset. |
3054
|
|
|
* This method returns a multi dimensional resultset similar to getAll |
3055
|
|
|
* The values array can be used to bind values to the place holders in the |
3056
|
|
|
* SQL query. |
3057
|
|
|
* |
3058
|
|
|
* @param string $sql SQL |
3059
|
|
|
* @param array $bindings values |
3060
|
|
|
* |
3061
|
|
|
* @return array |
3062
|
|
|
*/ |
3063
|
|
|
public function get( $sql, $bindings = array() ); |
3064
|
|
|
|
3065
|
|
|
/** |
3066
|
|
|
* Executes an SQL Query and returns a resultset. |
3067
|
|
|
* This method returns a single row (one array) resultset. |
3068
|
|
|
* The values array can be used to bind values to the place holders in the |
3069
|
|
|
* SQL query. |
3070
|
|
|
* |
3071
|
|
|
* @param string $sql SQL |
3072
|
|
|
* @param array $bindings values to bind |
3073
|
|
|
* |
3074
|
|
|
* @return array |
3075
|
|
|
*/ |
3076
|
|
|
public function getRow( $sql, $bindings = array() ); |
3077
|
|
|
|
3078
|
|
|
/** |
3079
|
|
|
* Executes an SQL Query and returns a resultset. |
3080
|
|
|
* This method returns a single column (one array) resultset. |
3081
|
|
|
* The values array can be used to bind values to the place holders in the |
3082
|
|
|
* SQL query. |
3083
|
|
|
* |
3084
|
|
|
* @param string $sql SQL |
3085
|
|
|
* @param array $bindings values to bind |
3086
|
|
|
* |
3087
|
|
|
* @return array |
3088
|
|
|
*/ |
3089
|
|
|
public function getCol( $sql, $bindings = array() ); |
3090
|
|
|
|
3091
|
|
|
/** |
3092
|
|
|
* Executes an SQL Query and returns a resultset. |
3093
|
|
|
* This method returns a single cell, a scalar value as the resultset. |
3094
|
|
|
* The values array can be used to bind values to the place holders in the |
3095
|
|
|
* SQL query. |
3096
|
|
|
* |
3097
|
|
|
* @param string $sql SQL |
3098
|
|
|
* @param array $bindings values to bind |
3099
|
|
|
* |
3100
|
|
|
* @return string |
3101
|
|
|
*/ |
3102
|
|
|
public function getCell( $sql, $bindings = array() ); |
3103
|
|
|
|
3104
|
|
|
/** |
3105
|
|
|
* Executes the SQL query specified in $sql and takes |
3106
|
|
|
* the first two columns of the resultset. This function transforms the |
3107
|
|
|
* resultset into an associative array. Values from the the first column will |
3108
|
|
|
* serve as keys while the values of the second column will be used as values. |
3109
|
|
|
* The values array can be used to bind values to the place holders in the |
3110
|
|
|
* SQL query. |
3111
|
|
|
* |
3112
|
|
|
* @param string $sql SQL |
3113
|
|
|
* @param array $bindings values to bind |
3114
|
|
|
* |
3115
|
|
|
* @return array |
3116
|
|
|
*/ |
3117
|
|
|
public function getAssoc( $sql, $bindings = array() ); |
3118
|
|
|
|
3119
|
|
|
/** |
3120
|
|
|
* Executes the SQL query specified in $sql and indexes |
3121
|
|
|
* the row by the first column. |
3122
|
|
|
* |
3123
|
|
|
* @param string $sql SQL |
3124
|
|
|
* @param array $bindings values to bind |
3125
|
|
|
* |
3126
|
|
|
* @return array |
3127
|
|
|
*/ |
3128
|
|
|
public function getAssocRow( $sql, $bindings = array() ); |
3129
|
|
|
|
3130
|
|
|
/** |
3131
|
|
|
* Returns the latest insert ID. |
3132
|
|
|
* |
3133
|
|
|
* @return integer |
3134
|
|
|
*/ |
3135
|
|
|
public function getInsertID(); |
3136
|
|
|
|
3137
|
|
|
/** |
3138
|
|
|
* Returns the number of rows that have been |
3139
|
|
|
* affected by the last update statement. |
3140
|
|
|
* |
3141
|
|
|
* @return integer |
3142
|
|
|
*/ |
3143
|
|
|
public function getAffectedRows(); |
3144
|
|
|
|
3145
|
|
|
/** |
3146
|
|
|
* Returns a database agnostic Cursor object. |
3147
|
|
|
* |
3148
|
|
|
* @param string $sql SQL |
3149
|
|
|
* @param array $bindings bindings |
3150
|
|
|
* |
3151
|
|
|
* @return Cursor |
3152
|
|
|
*/ |
3153
|
|
|
public function getCursor( $sql, $bindings = array() ); |
3154
|
|
|
|
3155
|
|
|
/** |
3156
|
|
|
* Returns the original database resource. This is useful if you want to |
3157
|
|
|
* perform operations on the driver directly instead of working with the |
3158
|
|
|
* adapter. RedBean will only access the adapter and never to talk |
3159
|
|
|
* directly to the driver though. |
3160
|
|
|
* |
3161
|
|
|
* @return object |
3162
|
|
|
*/ |
3163
|
|
|
public function getDatabase(); |
3164
|
|
|
|
3165
|
|
|
/** |
3166
|
|
|
* This method is part of the RedBean Transaction Management |
3167
|
|
|
* mechanisms. |
3168
|
|
|
* Starts a transaction. |
3169
|
|
|
* |
3170
|
|
|
* @return void |
3171
|
|
|
*/ |
3172
|
|
|
public function startTransaction(); |
3173
|
|
|
|
3174
|
|
|
/** |
3175
|
|
|
* This method is part of the RedBean Transaction Management |
3176
|
|
|
* mechanisms. |
3177
|
|
|
* Commits the transaction. |
3178
|
|
|
* |
3179
|
|
|
* @return void |
3180
|
|
|
*/ |
3181
|
|
|
public function commit(); |
3182
|
|
|
|
3183
|
|
|
/** |
3184
|
|
|
* This method is part of the RedBean Transaction Management |
3185
|
|
|
* mechanisms. |
3186
|
|
|
* Rolls back the transaction. |
3187
|
|
|
* |
3188
|
|
|
* @return void |
3189
|
|
|
*/ |
3190
|
|
|
public function rollback(); |
3191
|
|
|
|
3192
|
|
|
/** |
3193
|
|
|
* Closes database connection. |
3194
|
|
|
* |
3195
|
|
|
* @return void |
3196
|
|
|
*/ |
3197
|
|
|
public function close(); |
3198
|
|
|
} |
3199
|
|
|
} |
3200
|
|
|
|
3201
|
|
|
namespace RedBeanPHP\Adapter { |
3202
|
|
|
|
3203
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
3204
|
|
|
use RedBeanPHP\Adapter as Adapter; |
|
|
|
|
3205
|
|
|
use RedBeanPHP\Driver as Driver; |
|
|
|
|
3206
|
|
|
|
3207
|
|
|
/** |
3208
|
|
|
* DBAdapter (Database Adapter) |
3209
|
|
|
* |
3210
|
|
|
* An adapter class to connect various database systems to RedBean |
3211
|
|
|
* Database Adapter Class. The task of the database adapter class is to |
3212
|
|
|
* communicate with the database driver. You can use all sorts of database |
3213
|
|
|
* drivers with RedBeanPHP. The default database drivers that ships with |
3214
|
|
|
* the RedBeanPHP library is the RPDO driver ( which uses the PHP Data Objects |
3215
|
|
|
* Architecture aka PDO ). |
3216
|
|
|
* |
3217
|
|
|
* @file RedBeanPHP/Adapter/DBAdapter.php |
3218
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community. |
3219
|
|
|
* @license BSD/GPLv2 |
3220
|
|
|
* |
3221
|
|
|
* @copyright |
3222
|
|
|
* (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP community. |
3223
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3224
|
|
|
* with this source code in the file license.txt. |
3225
|
|
|
*/ |
3226
|
|
|
class DBAdapter extends Observable implements Adapter |
|
|
|
|
3227
|
|
|
{ |
3228
|
|
|
/** |
3229
|
|
|
* @var Driver |
3230
|
|
|
*/ |
3231
|
|
|
private $db = NULL; |
3232
|
|
|
|
3233
|
|
|
/** |
3234
|
|
|
* @var string |
3235
|
|
|
*/ |
3236
|
|
|
private $sql = ''; |
3237
|
|
|
|
3238
|
|
|
/** |
3239
|
|
|
* Constructor. |
3240
|
|
|
* |
3241
|
|
|
* Creates an instance of the RedBean Adapter Class. |
3242
|
|
|
* This class provides an interface for RedBean to work |
3243
|
|
|
* with ADO compatible DB instances. |
3244
|
|
|
* |
3245
|
|
|
* @param Driver $database ADO Compatible DB Instance |
3246
|
|
|
*/ |
3247
|
|
|
public function __construct( $database ) |
3248
|
|
|
{ |
3249
|
|
|
$this->db = $database; |
3250
|
|
|
} |
3251
|
|
|
|
3252
|
|
|
/** |
3253
|
|
|
* @see Adapter::getSQL |
3254
|
|
|
*/ |
3255
|
|
|
public function getSQL() |
3256
|
|
|
{ |
3257
|
|
|
return $this->sql; |
3258
|
|
|
} |
3259
|
|
|
|
3260
|
|
|
/** |
3261
|
|
|
* @see Adapter::exec |
3262
|
|
|
*/ |
3263
|
|
|
public function exec( $sql, $bindings = array(), $noevent = FALSE ) |
3264
|
|
|
{ |
3265
|
|
|
if ( !$noevent ) { |
3266
|
|
|
$this->sql = $sql; |
3267
|
|
|
$this->signal( 'sql_exec', $this ); |
3268
|
|
|
} |
3269
|
|
|
|
3270
|
|
|
return $this->db->Execute( $sql, $bindings ); |
3271
|
|
|
} |
3272
|
|
|
|
3273
|
|
|
/** |
3274
|
|
|
* @see Adapter::get |
3275
|
|
|
*/ |
3276
|
|
|
public function get( $sql, $bindings = array() ) |
3277
|
|
|
{ |
3278
|
|
|
$this->sql = $sql; |
3279
|
|
|
$this->signal( 'sql_exec', $this ); |
3280
|
|
|
|
3281
|
|
|
return $this->db->GetAll( $sql, $bindings ); |
3282
|
|
|
} |
3283
|
|
|
|
3284
|
|
|
/** |
3285
|
|
|
* @see Adapter::getRow |
3286
|
|
|
*/ |
3287
|
|
|
public function getRow( $sql, $bindings = array() ) |
3288
|
|
|
{ |
3289
|
|
|
$this->sql = $sql; |
3290
|
|
|
$this->signal( 'sql_exec', $this ); |
3291
|
|
|
|
3292
|
|
|
return $this->db->GetRow( $sql, $bindings ); |
3293
|
|
|
} |
3294
|
|
|
|
3295
|
|
|
/** |
3296
|
|
|
* @see Adapter::getCol |
3297
|
|
|
*/ |
3298
|
|
|
public function getCol( $sql, $bindings = array() ) |
3299
|
|
|
{ |
3300
|
|
|
$this->sql = $sql; |
3301
|
|
|
$this->signal( 'sql_exec', $this ); |
3302
|
|
|
|
3303
|
|
|
return $this->db->GetCol( $sql, $bindings ); |
3304
|
|
|
} |
3305
|
|
|
|
3306
|
|
|
/** |
3307
|
|
|
* @see Adapter::getAssoc |
3308
|
|
|
*/ |
3309
|
|
|
public function getAssoc( $sql, $bindings = array() ) |
3310
|
|
|
{ |
3311
|
|
|
$this->sql = $sql; |
3312
|
|
|
|
3313
|
|
|
$this->signal( 'sql_exec', $this ); |
3314
|
|
|
|
3315
|
|
|
$rows = $this->db->GetAll( $sql, $bindings ); |
3316
|
|
|
|
3317
|
|
|
$assoc = array(); |
3318
|
|
|
if ( !$rows ) { |
|
|
|
|
3319
|
|
|
return $assoc; |
3320
|
|
|
} |
3321
|
|
|
|
3322
|
|
|
foreach ( $rows as $row ) { |
3323
|
|
|
if ( empty( $row ) ) continue; |
3324
|
|
|
|
3325
|
|
|
if ( count( $row ) > 2 ) { |
3326
|
|
|
$key = array_shift( $row ); |
3327
|
|
|
$value = $row; |
3328
|
|
|
} elseif ( count( $row ) > 1 ) { |
3329
|
|
|
$key = array_shift( $row ); |
3330
|
|
|
$value = array_shift( $row ); |
3331
|
|
|
} else { |
3332
|
|
|
$key = array_shift( $row ); |
3333
|
|
|
$value = $key; |
3334
|
|
|
} |
3335
|
|
|
|
3336
|
|
|
$assoc[$key] = $value; |
3337
|
|
|
} |
3338
|
|
|
|
3339
|
|
|
return $assoc; |
3340
|
|
|
} |
3341
|
|
|
|
3342
|
|
|
/** |
3343
|
|
|
* @see Adapter::getAssocRow |
3344
|
|
|
*/ |
3345
|
|
|
public function getAssocRow($sql, $bindings = array()) |
3346
|
|
|
{ |
3347
|
|
|
$this->sql = $sql; |
3348
|
|
|
$this->signal( 'sql_exec', $this ); |
3349
|
|
|
|
3350
|
|
|
return $this->db->GetAssocRow( $sql, $bindings ); |
3351
|
|
|
} |
3352
|
|
|
|
3353
|
|
|
/** |
3354
|
|
|
* @see Adapter::getCell |
3355
|
|
|
*/ |
3356
|
|
|
public function getCell( $sql, $bindings = array(), $noSignal = NULL ) |
3357
|
|
|
{ |
3358
|
|
|
$this->sql = $sql; |
3359
|
|
|
|
3360
|
|
|
if ( !$noSignal ) $this->signal( 'sql_exec', $this ); |
3361
|
|
|
|
3362
|
|
|
return $this->db->GetOne( $sql, $bindings ); |
3363
|
|
|
} |
3364
|
|
|
|
3365
|
|
|
/** |
3366
|
|
|
* @see Adapter::getCursor |
3367
|
|
|
*/ |
3368
|
|
|
public function getCursor( $sql, $bindings = array() ) |
3369
|
|
|
{ |
3370
|
|
|
return $this->db->GetCursor( $sql, $bindings ); |
3371
|
|
|
} |
3372
|
|
|
|
3373
|
|
|
/** |
3374
|
|
|
* @see Adapter::getInsertID |
3375
|
|
|
*/ |
3376
|
|
|
public function getInsertID() |
3377
|
|
|
{ |
3378
|
|
|
return $this->db->getInsertID(); |
3379
|
|
|
} |
3380
|
|
|
|
3381
|
|
|
/** |
3382
|
|
|
* @see Adapter::getAffectedRows |
3383
|
|
|
*/ |
3384
|
|
|
public function getAffectedRows() |
3385
|
|
|
{ |
3386
|
|
|
return $this->db->Affected_Rows(); |
3387
|
|
|
} |
3388
|
|
|
|
3389
|
|
|
/** |
3390
|
|
|
* @see Adapter::getDatabase |
3391
|
|
|
*/ |
3392
|
|
|
public function getDatabase() |
3393
|
|
|
{ |
3394
|
|
|
return $this->db; |
3395
|
|
|
} |
3396
|
|
|
|
3397
|
|
|
/** |
3398
|
|
|
* @see Adapter::startTransaction |
3399
|
|
|
*/ |
3400
|
|
|
public function startTransaction() |
3401
|
|
|
{ |
3402
|
|
|
$this->db->StartTrans(); |
3403
|
|
|
} |
3404
|
|
|
|
3405
|
|
|
/** |
3406
|
|
|
* @see Adapter::commit |
3407
|
|
|
*/ |
3408
|
|
|
public function commit() |
3409
|
|
|
{ |
3410
|
|
|
$this->db->CommitTrans(); |
3411
|
|
|
} |
3412
|
|
|
|
3413
|
|
|
/** |
3414
|
|
|
* @see Adapter::rollback |
3415
|
|
|
*/ |
3416
|
|
|
public function rollback() |
3417
|
|
|
{ |
3418
|
|
|
$this->db->FailTrans(); |
3419
|
|
|
} |
3420
|
|
|
|
3421
|
|
|
/** |
3422
|
|
|
* @see Adapter::close. |
3423
|
|
|
*/ |
3424
|
|
|
public function close() |
3425
|
|
|
{ |
3426
|
|
|
$this->db->close(); |
3427
|
|
|
} |
3428
|
|
|
} |
3429
|
|
|
} |
3430
|
|
|
|
3431
|
|
|
namespace RedBeanPHP { |
3432
|
|
|
|
3433
|
|
|
/** |
3434
|
|
|
* Database Cursor Interface. |
3435
|
|
|
* Represents a simple database cursor. |
3436
|
|
|
* Cursors make it possible to create lightweight BeanCollections. |
3437
|
|
|
* |
3438
|
|
|
* @file RedBeanPHP/Cursor.php |
3439
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
3440
|
|
|
* @license BSD/GPLv2 |
3441
|
|
|
* |
3442
|
|
|
* @copyright |
3443
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
3444
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3445
|
|
|
* with this source code in the file license.txt. |
3446
|
|
|
*/ |
3447
|
|
|
interface Cursor |
|
|
|
|
3448
|
|
|
{ |
3449
|
|
|
/** |
3450
|
|
|
* Retrieves the next row from the result set. |
3451
|
|
|
* |
3452
|
|
|
* @return array |
3453
|
|
|
*/ |
3454
|
|
|
public function getNextItem(); |
3455
|
|
|
|
3456
|
|
|
/** |
3457
|
|
|
* Closes the database cursor. |
3458
|
|
|
* Some databases require a cursor to be closed before executing |
3459
|
|
|
* another statement/opening a new cursor. |
3460
|
|
|
* |
3461
|
|
|
* @return void |
3462
|
|
|
*/ |
3463
|
|
|
public function close(); |
3464
|
|
|
} |
3465
|
|
|
} |
3466
|
|
|
|
3467
|
|
|
namespace RedBeanPHP\Cursor { |
3468
|
|
|
|
3469
|
|
|
use RedBeanPHP\Cursor as Cursor; |
|
|
|
|
3470
|
|
|
|
3471
|
|
|
/** |
3472
|
|
|
* PDO Database Cursor |
3473
|
|
|
* Implementation of PDO Database Cursor. |
3474
|
|
|
* Used by the BeanCollection to fetch one bean at a time. |
3475
|
|
|
* |
3476
|
|
|
* @file RedBeanPHP/Cursor/PDOCursor.php |
3477
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
3478
|
|
|
* @license BSD/GPLv2 |
3479
|
|
|
* |
3480
|
|
|
* @copyright |
3481
|
|
|
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
3482
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3483
|
|
|
* with this source code in the file license.txt. |
3484
|
|
|
*/ |
3485
|
|
|
class PDOCursor implements Cursor |
|
|
|
|
3486
|
|
|
{ |
3487
|
|
|
/** |
3488
|
|
|
* @var PDOStatement |
3489
|
|
|
*/ |
3490
|
|
|
protected $res; |
3491
|
|
|
|
3492
|
|
|
/** |
3493
|
|
|
* @var string |
3494
|
|
|
*/ |
3495
|
|
|
protected $fetchStyle; |
3496
|
|
|
|
3497
|
|
|
/** |
3498
|
|
|
* Constructor, creates a new instance of a PDO Database Cursor. |
3499
|
|
|
* |
3500
|
|
|
* @param PDOStatement $res the PDO statement |
3501
|
|
|
* @param string $fetchStyle fetch style constant to use |
3502
|
|
|
* |
3503
|
|
|
* @return void |
|
|
|
|
3504
|
|
|
*/ |
3505
|
|
|
public function __construct( \PDOStatement $res, $fetchStyle ) |
3506
|
|
|
{ |
3507
|
|
|
$this->res = $res; |
|
|
|
|
3508
|
|
|
$this->fetchStyle = $fetchStyle; |
3509
|
|
|
} |
3510
|
|
|
|
3511
|
|
|
/** |
3512
|
|
|
* @see Cursor::getNextItem |
3513
|
|
|
*/ |
3514
|
|
|
public function getNextItem() |
3515
|
|
|
{ |
3516
|
|
|
return $this->res->fetch(); |
3517
|
|
|
} |
3518
|
|
|
|
3519
|
|
|
/** |
3520
|
|
|
* @see Cursor::close |
3521
|
|
|
*/ |
3522
|
|
|
public function close() |
3523
|
|
|
{ |
3524
|
|
|
$this->res->closeCursor(); |
3525
|
|
|
} |
3526
|
|
|
} |
3527
|
|
|
} |
3528
|
|
|
|
3529
|
|
|
namespace RedBeanPHP\Cursor { |
3530
|
|
|
|
3531
|
|
|
use RedBeanPHP\Cursor as Cursor; |
|
|
|
|
3532
|
|
|
|
3533
|
|
|
/** |
3534
|
|
|
* NULL Database Cursor |
3535
|
|
|
* Implementation of the NULL Cursor. |
3536
|
|
|
* Used for an empty BeanCollection. |
3537
|
|
|
* |
3538
|
|
|
* @file RedBeanPHP/Cursor/NULLCursor.php |
3539
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
3540
|
|
|
* @license BSD/GPLv2 |
3541
|
|
|
* |
3542
|
|
|
* @copyright |
3543
|
|
|
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
3544
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3545
|
|
|
* with this source code in the file license.txt. |
3546
|
|
|
*/ |
3547
|
|
|
class NullCursor implements Cursor |
|
|
|
|
3548
|
|
|
{ |
3549
|
|
|
/** |
3550
|
|
|
* @see Cursor::getNextItem |
3551
|
|
|
*/ |
3552
|
|
|
public function getNextItem() |
3553
|
|
|
{ |
3554
|
|
|
return NULL; |
3555
|
|
|
} |
3556
|
|
|
|
3557
|
|
|
/** |
3558
|
|
|
* @see Cursor::close |
3559
|
|
|
*/ |
3560
|
|
|
public function close() |
3561
|
|
|
{ |
3562
|
|
|
return NULL; |
3563
|
|
|
} |
3564
|
|
|
} |
3565
|
|
|
} |
3566
|
|
|
|
3567
|
|
|
namespace RedBeanPHP { |
3568
|
|
|
|
3569
|
|
|
use RedBeanPHP\Cursor as Cursor; |
|
|
|
|
3570
|
|
|
use RedBeanPHP\Repository as Repository; |
|
|
|
|
3571
|
|
|
|
3572
|
|
|
/** |
3573
|
|
|
* BeanCollection. |
3574
|
|
|
* |
3575
|
|
|
* The BeanCollection represents a collection of beans and |
3576
|
|
|
* makes it possible to use database cursors. The BeanCollection |
3577
|
|
|
* has a method next() to obtain the first, next and last bean |
3578
|
|
|
* in the collection. The BeanCollection does not implement the array |
3579
|
|
|
* interface nor does it try to act like an array because it cannot go |
3580
|
|
|
* backward or rewind itself. |
3581
|
|
|
* |
3582
|
|
|
* Use the BeanCollection for large datasets where skip/limit is not an |
3583
|
|
|
* option. Keep in mind that ID-marking (querying a start ID) is a decent |
3584
|
|
|
* alternative though. |
3585
|
|
|
* |
3586
|
|
|
* @file RedBeanPHP/BeanCollection.php |
3587
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
3588
|
|
|
* @license BSD/GPLv2 |
3589
|
|
|
* |
3590
|
|
|
* @copyright |
3591
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
3592
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3593
|
|
|
* with this source code in the file license.txt. |
3594
|
|
|
*/ |
3595
|
|
|
class BeanCollection |
|
|
|
|
3596
|
|
|
{ |
3597
|
|
|
/** |
3598
|
|
|
* @var Cursor |
3599
|
|
|
*/ |
3600
|
|
|
protected $cursor = NULL; |
3601
|
|
|
|
3602
|
|
|
/** |
3603
|
|
|
* @var Repository |
3604
|
|
|
*/ |
3605
|
|
|
protected $repository = NULL; |
3606
|
|
|
|
3607
|
|
|
/** |
3608
|
|
|
* @var string |
3609
|
|
|
*/ |
3610
|
|
|
protected $type = NULL; |
3611
|
|
|
|
3612
|
|
|
/** |
3613
|
|
|
* Constructor, creates a new instance of the BeanCollection. |
3614
|
|
|
* |
3615
|
|
|
* @param string $type type of beans in this collection |
3616
|
|
|
* @param Repository $repository repository to use to generate bean objects |
3617
|
|
|
* @param Cursor $cursor cursor object to use |
3618
|
|
|
* |
3619
|
|
|
* @return void |
|
|
|
|
3620
|
|
|
*/ |
3621
|
|
|
public function __construct( $type, Repository $repository, Cursor $cursor ) |
3622
|
|
|
{ |
3623
|
|
|
$this->type = $type; |
3624
|
|
|
$this->cursor = $cursor; |
3625
|
|
|
$this->repository = $repository; |
3626
|
|
|
} |
3627
|
|
|
|
3628
|
|
|
/** |
3629
|
|
|
* Returns the next bean in the collection. |
3630
|
|
|
* If called the first time, this will return the first bean in the collection. |
3631
|
|
|
* If there are no more beans left in the collection, this method |
3632
|
|
|
* will return NULL. |
3633
|
|
|
* |
3634
|
|
|
* @return OODBBean|NULL |
3635
|
|
|
*/ |
3636
|
|
|
public function next() |
3637
|
|
|
{ |
3638
|
|
|
$row = $this->cursor->getNextItem(); |
3639
|
|
|
if ( $row ) { |
|
|
|
|
3640
|
|
|
$beans = $this->repository->convertToBeans( $this->type, array( $row ) ); |
3641
|
|
|
$bean = array_shift( $beans ); |
3642
|
|
|
return $bean; |
3643
|
|
|
} |
3644
|
|
|
return NULL; |
3645
|
|
|
} |
3646
|
|
|
|
3647
|
|
|
/** |
3648
|
|
|
* Closes the underlying cursor (needed for some databases). |
3649
|
|
|
* |
3650
|
|
|
* @return void |
3651
|
|
|
*/ |
3652
|
|
|
public function close() |
3653
|
|
|
{ |
3654
|
|
|
$this->cursor->close(); |
3655
|
|
|
} |
3656
|
|
|
} |
3657
|
|
|
} |
3658
|
|
|
|
3659
|
|
|
namespace RedBeanPHP { |
3660
|
|
|
|
3661
|
|
|
/** |
3662
|
|
|
* QueryWriter |
3663
|
|
|
* Interface for QueryWriters. |
3664
|
|
|
* Describes the API for a QueryWriter. |
3665
|
|
|
* |
3666
|
|
|
* Terminology: |
3667
|
|
|
* |
3668
|
|
|
* - beautified property (a camelCased property, has to be converted first) |
3669
|
|
|
* - beautified type (a camelCased type, has to be converted first) |
3670
|
|
|
* - type (a bean type, corresponds directly to a table) |
3671
|
|
|
* - property (a bean property, corresponds directly to a column) |
3672
|
|
|
* - table (a checked and quoted type, ready for use in a query) |
3673
|
|
|
* - column (a checked and quoted property, ready for use in query) |
3674
|
|
|
* - tableNoQ (same as type, but in context of a database operation) |
3675
|
|
|
* - columnNoQ (same as property, but in context of a database operation) |
3676
|
|
|
* |
3677
|
|
|
* @file RedBeanPHP/QueryWriter.php |
3678
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
3679
|
|
|
* @license BSD/GPLv2 |
3680
|
|
|
* |
3681
|
|
|
* @copyright |
3682
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
3683
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
3684
|
|
|
* with this source code in the file license.txt. |
3685
|
|
|
*/ |
3686
|
|
|
interface QueryWriter |
|
|
|
|
3687
|
|
|
{ |
3688
|
|
|
/** |
3689
|
|
|
* SQL filter constants |
3690
|
|
|
*/ |
3691
|
|
|
const C_SQLFILTER_READ = 'r'; |
3692
|
|
|
const C_SQLFILTER_WRITE = 'w'; |
3693
|
|
|
|
3694
|
|
|
/** |
3695
|
|
|
* Query Writer constants. |
3696
|
|
|
*/ |
3697
|
|
|
const C_SQLSTATE_NO_SUCH_TABLE = 1; |
3698
|
|
|
const C_SQLSTATE_NO_SUCH_COLUMN = 2; |
3699
|
|
|
const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3; |
3700
|
|
|
|
3701
|
|
|
/** |
3702
|
|
|
* Define data type regions |
3703
|
|
|
* |
3704
|
|
|
* 00 - 80: normal data types |
3705
|
|
|
* 80 - 99: special data types, only scan/code if requested |
3706
|
|
|
* 99 : specified by user, don't change |
3707
|
|
|
*/ |
3708
|
|
|
const C_DATATYPE_RANGE_SPECIAL = 80; |
3709
|
|
|
const C_DATATYPE_RANGE_SPECIFIED = 99; |
3710
|
|
|
|
3711
|
|
|
/** |
3712
|
|
|
* Define GLUE types for use with glueSQLCondition methods. |
3713
|
|
|
* Determines how to prefix a snippet of SQL before appending it |
3714
|
|
|
* to other SQL (or integrating it, mixing it otherwise). |
3715
|
|
|
* |
3716
|
|
|
* WHERE - glue as WHERE condition |
3717
|
|
|
* AND - glue as AND condition |
3718
|
|
|
*/ |
3719
|
|
|
const C_GLUE_WHERE = 1; |
3720
|
|
|
const C_GLUE_AND = 2; |
3721
|
|
|
|
3722
|
|
|
/** |
3723
|
|
|
* Writes an SQL Snippet for a JOIN, returns the |
3724
|
|
|
* SQL snippet string. |
3725
|
|
|
* |
3726
|
|
|
* @note A default implementation is available in AQueryWriter |
3727
|
|
|
* unless a database uses very different SQL this should suffice. |
3728
|
|
|
* |
3729
|
|
|
* @param string $type source type |
3730
|
|
|
* @param string $targetType target type (type to join) |
3731
|
|
|
* @param string $leftRight type of join (possible: 'LEFT', 'RIGHT' or 'INNER'). |
|
|
|
|
3732
|
|
|
* |
3733
|
|
|
* @return string $joinSQLSnippet |
3734
|
|
|
*/ |
3735
|
|
|
public function writeJoin( $type, $targetType, $joinType ); |
3736
|
|
|
|
3737
|
|
|
/** |
3738
|
|
|
* Glues an SQL snippet to the beginning of a WHERE clause. |
3739
|
|
|
* This ensures users don't have to add WHERE to their query snippets. |
3740
|
|
|
* |
3741
|
|
|
* The snippet gets prefixed with WHERE or AND |
3742
|
|
|
* if it starts with a condition. |
3743
|
|
|
* |
3744
|
|
|
* If the snippet does NOT start with a condition (or this function thinks so) |
3745
|
|
|
* the snippet is returned as-is. |
3746
|
|
|
* |
3747
|
|
|
* The GLUE type determines the prefix: |
3748
|
|
|
* |
3749
|
|
|
* - NONE prefixes with WHERE |
3750
|
|
|
* - WHERE prefixes with WHERE and replaces AND if snippets starts with AND |
3751
|
|
|
* - AND prefixes with AND |
3752
|
|
|
* |
3753
|
|
|
* This method will never replace WHERE with AND since a snippet should never |
3754
|
|
|
* begin with WHERE in the first place. OR is not supported. |
3755
|
|
|
* |
3756
|
|
|
* Only a limited set of clauses will be recognized as non-conditions. |
3757
|
|
|
* For instance beginning a snippet with complex statements like JOIN or UNION |
3758
|
|
|
* will not work. This is too complex for use in a snippet. |
3759
|
|
|
* |
3760
|
|
|
* @note A default implementation is available in AQueryWriter |
3761
|
|
|
* unless a database uses very different SQL this should suffice. |
3762
|
|
|
* |
3763
|
|
|
* @param string $sql SQL Snippet |
3764
|
|
|
* @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND) |
3765
|
|
|
* |
3766
|
|
|
* @return string |
3767
|
|
|
*/ |
3768
|
|
|
public function glueSQLCondition( $sql, $glue = NULL ); |
3769
|
|
|
|
3770
|
|
|
/** |
3771
|
|
|
* Determines if there is a LIMIT 1 clause in the SQL. |
3772
|
|
|
* If not, it will add a LIMIT 1. (used for findOne). |
3773
|
|
|
* |
3774
|
|
|
* @note A default implementation is available in AQueryWriter |
3775
|
|
|
* unless a database uses very different SQL this should suffice. |
3776
|
|
|
* |
3777
|
|
|
* @param string $sql query to scan and adjust |
3778
|
|
|
* |
3779
|
|
|
* @return string |
3780
|
|
|
*/ |
3781
|
|
|
public function glueLimitOne( $sql ); |
3782
|
|
|
|
3783
|
|
|
/** |
3784
|
|
|
* Returns the tables that are in the database. |
3785
|
|
|
* |
3786
|
|
|
* @return array |
3787
|
|
|
*/ |
3788
|
|
|
public function getTables(); |
3789
|
|
|
|
3790
|
|
|
/** |
3791
|
|
|
* This method will create a table for the bean. |
3792
|
|
|
* This methods accepts a type and infers the corresponding table name. |
3793
|
|
|
* |
3794
|
|
|
* @param string $type type of bean you want to create a table for |
3795
|
|
|
* |
3796
|
|
|
* @return void |
3797
|
|
|
*/ |
3798
|
|
|
public function createTable( $type ); |
3799
|
|
|
|
3800
|
|
|
/** |
3801
|
|
|
* Returns an array containing all the columns of the specified type. |
3802
|
|
|
* The format of the return array looks like this: |
3803
|
|
|
* $field => $type where $field is the name of the column and $type |
3804
|
|
|
* is a database specific description of the datatype. |
3805
|
|
|
* |
3806
|
|
|
* This methods accepts a type and infers the corresponding table name. |
3807
|
|
|
* |
3808
|
|
|
* @param string $type type of bean you want to obtain a column list of |
3809
|
|
|
* |
3810
|
|
|
* @return array |
3811
|
|
|
*/ |
3812
|
|
|
public function getColumns( $type ); |
3813
|
|
|
|
3814
|
|
|
/** |
3815
|
|
|
* Returns the Column Type Code (integer) that corresponds |
3816
|
|
|
* to the given value type. This method is used to determine the minimum |
3817
|
|
|
* column type required to represent the given value. |
3818
|
|
|
* |
3819
|
|
|
* @param string $value value |
3820
|
|
|
* |
3821
|
|
|
* @return integer |
3822
|
|
|
*/ |
3823
|
|
|
public function scanType( $value, $alsoScanSpecialForTypes = FALSE ); |
3824
|
|
|
|
3825
|
|
|
/** |
3826
|
|
|
* This method will add a column to a table. |
3827
|
|
|
* This methods accepts a type and infers the corresponding table name. |
3828
|
|
|
* |
3829
|
|
|
* @param string $type name of the table |
3830
|
|
|
* @param string $column name of the column |
3831
|
|
|
* @param integer $field data type for field |
3832
|
|
|
* |
3833
|
|
|
* @return void |
3834
|
|
|
*/ |
3835
|
|
|
public function addColumn( $type, $column, $field ); |
3836
|
|
|
|
3837
|
|
|
/** |
3838
|
|
|
* Returns the Type Code for a Column Description. |
3839
|
|
|
* Given an SQL column description this method will return the corresponding |
3840
|
|
|
* code for the writer. If the include specials flag is set it will also |
3841
|
|
|
* return codes for special columns. Otherwise special columns will be identified |
3842
|
|
|
* as specified columns. |
3843
|
|
|
* |
3844
|
|
|
* @param string $typedescription description |
3845
|
|
|
* @param boolean $includeSpecials whether you want to get codes for special columns as well |
3846
|
|
|
* |
3847
|
|
|
* @return integer |
3848
|
|
|
*/ |
3849
|
|
|
public function code( $typedescription, $includeSpecials = FALSE ); |
3850
|
|
|
|
3851
|
|
|
/** |
3852
|
|
|
* This method will widen the column to the specified data type. |
3853
|
|
|
* This methods accepts a type and infers the corresponding table name. |
3854
|
|
|
* |
3855
|
|
|
* @param string $type type / table that needs to be adjusted |
3856
|
|
|
* @param string $column column that needs to be altered |
3857
|
|
|
* @param integer $datatype target data type |
3858
|
|
|
* |
3859
|
|
|
* @return void |
3860
|
|
|
*/ |
3861
|
|
|
public function widenColumn( $type, $column, $datatype ); |
3862
|
|
|
|
3863
|
|
|
/** |
3864
|
|
|
* Selects records from the database. |
3865
|
|
|
* This methods selects the records from the database that match the specified |
3866
|
|
|
* type, conditions (optional) and additional SQL snippet (optional). |
3867
|
|
|
* |
3868
|
|
|
* @param string $type name of the table you want to query |
3869
|
|
|
* @param array $conditions criteria ( $column => array( $values ) ) |
3870
|
|
|
* @param string $addSQL additional SQL snippet |
|
|
|
|
3871
|
|
|
* @param array $bindings bindings for SQL snippet |
3872
|
|
|
* |
3873
|
|
|
* @return array |
3874
|
|
|
*/ |
3875
|
|
|
public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ); |
3876
|
|
|
|
3877
|
|
|
/** |
3878
|
|
|
* Selects records from the database and returns a cursor. |
3879
|
|
|
* This methods selects the records from the database that match the specified |
3880
|
|
|
* type, conditions (optional) and additional SQL snippet (optional). |
3881
|
|
|
* |
3882
|
|
|
* @param string $type name of the table you want to query |
3883
|
|
|
* @param array $conditions criteria ( $column => array( $values ) ) |
|
|
|
|
3884
|
|
|
* @param string $addSQL additional SQL snippet |
|
|
|
|
3885
|
|
|
* @param array $bindings bindings for SQL snippet |
3886
|
|
|
* |
3887
|
|
|
* @return Cursor |
3888
|
|
|
*/ |
3889
|
|
|
public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() ); |
3890
|
|
|
|
3891
|
|
|
/** |
3892
|
|
|
* Returns records through an intermediate type. This method is used to obtain records using a link table and |
3893
|
|
|
* allows the SQL snippets to reference columns in the link table for additional filtering or ordering. |
3894
|
|
|
* |
3895
|
|
|
* @param string $sourceType source type, the reference type you want to use to fetch related items on the other side |
3896
|
|
|
* @param string $destType destination type, the target type you want to get beans of |
3897
|
|
|
* @param mixed $linkID ID to use for the link table |
3898
|
|
|
* @param string $addSql Additional SQL snippet |
3899
|
|
|
* @param array $bindings Bindings for SQL snippet |
3900
|
|
|
* |
3901
|
|
|
* @return array |
3902
|
|
|
*/ |
3903
|
|
|
public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ); |
3904
|
|
|
|
3905
|
|
|
/** |
3906
|
|
|
* Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation. |
3907
|
|
|
* |
3908
|
|
|
* @param string $sourceType source type, the first part of the link you're looking for |
3909
|
|
|
* @param string $destType destination type, the second part of the link you're looking for |
3910
|
|
|
* @param string $sourceID ID for the source |
3911
|
|
|
* @param string $destID ID for the destination |
3912
|
|
|
* |
3913
|
|
|
* @return array|null |
3914
|
|
|
*/ |
3915
|
|
|
public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ); |
3916
|
|
|
|
3917
|
|
|
/** |
3918
|
|
|
* Counts the number of records in the database that match the |
3919
|
|
|
* conditions and additional SQL. |
3920
|
|
|
* |
3921
|
|
|
* @param string $type name of the table you want to query |
3922
|
|
|
* @param array $conditions criteria ( $column => array( $values ) ) |
3923
|
|
|
* @param string $addSQL additional SQL snippet |
|
|
|
|
3924
|
|
|
* @param array $bindings bindings for SQL snippet |
3925
|
|
|
* |
3926
|
|
|
* @return integer |
3927
|
|
|
*/ |
3928
|
|
|
public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ); |
3929
|
|
|
|
3930
|
|
|
/** |
3931
|
|
|
* Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings. |
3932
|
|
|
* |
3933
|
|
|
* @param string $sourceType source type |
3934
|
|
|
* @param string $targetType the thing you want to count |
3935
|
|
|
* @param mixed $linkID the of the source type |
3936
|
|
|
* @param string $addSQL additional SQL snippet |
3937
|
|
|
* @param array $bindings bindings for SQL snippet |
3938
|
|
|
* |
3939
|
|
|
* @return integer |
3940
|
|
|
*/ |
3941
|
|
|
public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() ); |
3942
|
|
|
|
3943
|
|
|
/** |
3944
|
|
|
* Returns all rows of specified type that have been tagged with one of the |
3945
|
|
|
* strings in the specified tag list array. |
3946
|
|
|
* |
3947
|
|
|
* Note that the additional SQL snippet can only be used for pagination, |
3948
|
|
|
* the SQL snippet will be appended to the end of the query. |
3949
|
|
|
* |
3950
|
|
|
* @param string $type the bean type you want to query |
3951
|
|
|
* @param array $tagList an array of strings, each string containing a tag title |
3952
|
|
|
* @param boolean $all if TRUE only return records that have been associated with ALL the tags in the list |
3953
|
|
|
* @param string $addSql addition SQL snippet, for pagination |
3954
|
|
|
* @param array $bindings parameter bindings for additional SQL snippet |
3955
|
|
|
* |
3956
|
|
|
* @return array |
3957
|
|
|
*/ |
3958
|
|
|
public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ); |
3959
|
|
|
|
3960
|
|
|
/** |
3961
|
|
|
* This method should update (or insert a record), it takes |
3962
|
|
|
* a table name, a list of update values ( $field => $value ) and an |
3963
|
|
|
* primary key ID (optional). If no primary key ID is provided, an |
3964
|
|
|
* INSERT will take place. |
3965
|
|
|
* Returns the new ID. |
3966
|
|
|
* This methods accepts a type and infers the corresponding table name. |
3967
|
|
|
* |
3968
|
|
|
* @param string $type name of the table to update |
3969
|
|
|
* @param array $updatevalues list of update values |
3970
|
|
|
* @param integer $id optional primary key ID value |
3971
|
|
|
* |
3972
|
|
|
* @return integer |
3973
|
|
|
*/ |
3974
|
|
|
public function updateRecord( $type, $updatevalues, $id = NULL ); |
3975
|
|
|
|
3976
|
|
|
/** |
3977
|
|
|
* Deletes records from the database. |
3978
|
|
|
* @note $addSql is always prefixed with ' WHERE ' or ' AND .' |
3979
|
|
|
* |
3980
|
|
|
* @param string $type name of the table you want to query |
3981
|
|
|
* @param array $conditions criteria ( $column => array( $values ) ) |
3982
|
|
|
* @param string $sql additional SQL |
|
|
|
|
3983
|
|
|
* @param array $bindings bindings |
3984
|
|
|
* |
3985
|
|
|
* @return void |
3986
|
|
|
*/ |
3987
|
|
|
public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() ); |
3988
|
|
|
|
3989
|
|
|
/** |
3990
|
|
|
* Deletes all links between $sourceType and $destType in an N-M relation. |
3991
|
|
|
* |
3992
|
|
|
* @param string $sourceType source type |
3993
|
|
|
* @param string $destType destination type |
3994
|
|
|
* @param string $sourceID source ID |
3995
|
|
|
* |
3996
|
|
|
* @return void |
3997
|
|
|
*/ |
3998
|
|
|
public function deleteRelations( $sourceType, $destType, $sourceID ); |
3999
|
|
|
|
4000
|
|
|
/** |
4001
|
|
|
* @see QueryWriter::addUniqueConstaint |
4002
|
|
|
*/ |
4003
|
|
|
public function addUniqueIndex( $type, $columns ); |
4004
|
|
|
|
4005
|
|
|
/** |
4006
|
|
|
* This method will add a UNIQUE constraint index to a table on columns $columns. |
4007
|
|
|
* This methods accepts a type and infers the corresponding table name. |
4008
|
|
|
* |
4009
|
|
|
* @param string $type type |
4010
|
|
|
* @param array $columnsPartOfIndex columns to include in index |
|
|
|
|
4011
|
|
|
* |
4012
|
|
|
* @return void |
4013
|
|
|
*/ |
4014
|
|
|
public function addUniqueConstraint( $type, $columns ); |
4015
|
|
|
|
4016
|
|
|
/** |
4017
|
|
|
* This method will check whether the SQL state is in the list of specified states |
4018
|
|
|
* and returns TRUE if it does appear in this list or FALSE if it |
4019
|
|
|
* does not. The purpose of this method is to translate the database specific state to |
4020
|
|
|
* a one of the constants defined in this class and then check whether it is in the list |
4021
|
|
|
* of standard states provided. |
4022
|
|
|
* |
4023
|
|
|
* @param string $state sql state |
4024
|
|
|
* @param array $list list |
4025
|
|
|
* |
4026
|
|
|
* @return boolean |
4027
|
|
|
*/ |
4028
|
|
|
public function sqlStateIn( $state, $list ); |
4029
|
|
|
|
4030
|
|
|
/** |
4031
|
|
|
* This method will remove all beans of a certain type. |
4032
|
|
|
* This methods accepts a type and infers the corresponding table name. |
4033
|
|
|
* |
4034
|
|
|
* @param string $type bean type |
4035
|
|
|
* |
4036
|
|
|
* @return void |
4037
|
|
|
*/ |
4038
|
|
|
public function wipe( $type ); |
4039
|
|
|
|
4040
|
|
|
/** |
4041
|
|
|
* This method will add a foreign key from type and field to |
4042
|
|
|
* target type and target field. |
4043
|
|
|
* The foreign key is created without an action. On delete/update |
4044
|
|
|
* no action will be triggered. The FK is only used to allow database |
4045
|
|
|
* tools to generate pretty diagrams and to make it easy to add actions |
4046
|
|
|
* later on. |
4047
|
|
|
* This methods accepts a type and infers the corresponding table name. |
4048
|
|
|
* |
4049
|
|
|
* |
4050
|
|
|
* @param string $type type that will have a foreign key field |
4051
|
|
|
* @param string $targetType points to this type |
4052
|
|
|
* @param string $property field that contains the foreign key value |
4053
|
|
|
* @param string $targetProperty field where the fk points to |
4054
|
|
|
* @param string $isDep whether target is dependent and should cascade on update/delete |
4055
|
|
|
* |
4056
|
|
|
* @return void |
4057
|
|
|
*/ |
4058
|
|
|
public function addFK( $type, $targetType, $property, $targetProperty, $isDep = false ); |
4059
|
|
|
|
4060
|
|
|
/** |
4061
|
|
|
* This method will add an index to a type and field with name |
4062
|
|
|
* $name. |
4063
|
|
|
* This methods accepts a type and infers the corresponding table name. |
4064
|
|
|
* |
4065
|
|
|
* @param string $type type to add index to |
4066
|
|
|
* @param string $name name of the new index |
4067
|
|
|
* @param string $property field to index |
4068
|
|
|
* |
4069
|
|
|
* @return void |
4070
|
|
|
*/ |
4071
|
|
|
public function addIndex( $type, $name, $property ); |
4072
|
|
|
|
4073
|
|
|
/** |
4074
|
|
|
* Checks and filters a database structure element like a table of column |
4075
|
|
|
* for safe use in a query. A database structure has to conform to the |
4076
|
|
|
* RedBeanPHP DB security policy which basically means only alphanumeric |
4077
|
|
|
* symbols are allowed. This security policy is more strict than conventional |
4078
|
|
|
* SQL policies and does therefore not require database specific escaping rules. |
4079
|
|
|
* |
4080
|
|
|
* @param string $databaseStructure name of the column/table to check |
4081
|
|
|
* @param boolean $noQuotes TRUE to NOT put backticks or quotes around the string |
|
|
|
|
4082
|
|
|
* |
4083
|
|
|
* @return string |
4084
|
|
|
*/ |
4085
|
|
|
public function esc( $databaseStructure, $dontQuote = FALSE ); |
4086
|
|
|
|
4087
|
|
|
/** |
4088
|
|
|
* Removes all tables and views from the database. |
4089
|
|
|
* |
4090
|
|
|
* @return void |
4091
|
|
|
*/ |
4092
|
|
|
public function wipeAll(); |
4093
|
|
|
|
4094
|
|
|
/** |
4095
|
|
|
* Renames an association. For instance if you would like to refer to |
4096
|
|
|
* album_song as: track you can specify this by calling this method like: |
4097
|
|
|
* |
4098
|
|
|
* renameAssociation('album_song','track') |
4099
|
|
|
* |
4100
|
|
|
* This allows: |
4101
|
|
|
* |
4102
|
|
|
* $album->sharedSong |
4103
|
|
|
* |
4104
|
|
|
* to add/retrieve beans from track instead of album_song. |
4105
|
|
|
* Also works for exportAll(). |
4106
|
|
|
* |
4107
|
|
|
* This method also accepts a single associative array as |
4108
|
|
|
* its first argument. |
4109
|
|
|
* |
4110
|
|
|
* @param string|array $fromType |
4111
|
|
|
* @param string $toType (optional) |
4112
|
|
|
* |
4113
|
|
|
* @return void |
4114
|
|
|
*/ |
4115
|
|
|
public function renameAssocTable( $fromType, $toType = NULL ); |
4116
|
|
|
|
4117
|
|
|
/** |
4118
|
|
|
* Returns the format for link tables. |
4119
|
|
|
* Given an array containing two type names this method returns the |
4120
|
|
|
* name of the link table to be used to store and retrieve |
4121
|
|
|
* association records. For instance, given two types: person and |
4122
|
|
|
* project, the corresponding link table might be: 'person_project'. |
4123
|
|
|
* |
4124
|
|
|
* @param array $types two types array($type1, $type2) |
4125
|
|
|
* |
4126
|
|
|
* @return string |
4127
|
|
|
*/ |
4128
|
|
|
public function getAssocTable( $types ); |
4129
|
|
|
|
4130
|
|
|
/** |
4131
|
|
|
* Given a bean type and a property, this method |
4132
|
|
|
* tries to infer the fetch type using the foreign key |
4133
|
|
|
* definitions in the database. |
4134
|
|
|
* For instance: project, student -> person. |
4135
|
|
|
* If no fetchType can be inferred, this method will return NULL. |
4136
|
|
|
* |
4137
|
|
|
* @note QueryWriters do not have to implement this method, |
4138
|
|
|
* it's optional. A default version is available in AQueryWriter. |
4139
|
|
|
* |
4140
|
|
|
* @param $type the source type to fetch a target type for |
4141
|
|
|
* @param $property the property to fetch the type of |
4142
|
|
|
* |
4143
|
|
|
* @return string|NULL |
4144
|
|
|
*/ |
4145
|
|
|
public function inferFetchType( $type, $property ); |
4146
|
|
|
} |
4147
|
|
|
} |
4148
|
|
|
|
4149
|
|
|
namespace RedBeanPHP\QueryWriter { |
4150
|
|
|
|
4151
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
4152
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
4153
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
4154
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
4155
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
4156
|
|
|
|
4157
|
|
|
/** |
4158
|
|
|
* RedBeanPHP Abstract Query Writer. |
4159
|
|
|
* Represents an abstract Database to RedBean |
4160
|
|
|
* To write a driver for a different database for RedBean |
4161
|
|
|
* Contains a number of functions all implementors can |
4162
|
|
|
* inherit or override. |
4163
|
|
|
* |
4164
|
|
|
* @file RedBeanPHP/QueryWriter/AQueryWriter.php |
4165
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
4166
|
|
|
* @license BSD/GPLv2 |
4167
|
|
|
* |
4168
|
|
|
* @copyright |
4169
|
|
|
* (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
4170
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
4171
|
|
|
* with this source code in the file license.txt. |
4172
|
|
|
*/ |
4173
|
|
|
abstract class AQueryWriter |
|
|
|
|
4174
|
|
|
{ |
4175
|
|
|
/** |
4176
|
|
|
* @var array |
4177
|
|
|
*/ |
4178
|
|
|
private static $sqlFilters = array(); |
4179
|
|
|
|
4180
|
|
|
/** |
4181
|
|
|
* @var boolean |
4182
|
|
|
*/ |
4183
|
|
|
private static $flagSQLFilterSafeMode = false; |
4184
|
|
|
|
4185
|
|
|
/** |
4186
|
|
|
* @var boolean |
4187
|
|
|
*/ |
4188
|
|
|
private static $flagNarrowFieldMode = true; |
4189
|
|
|
|
4190
|
|
|
/** |
4191
|
|
|
* @var array |
4192
|
|
|
*/ |
4193
|
|
|
public static $renames = array(); |
4194
|
|
|
|
4195
|
|
|
/** |
4196
|
|
|
* @var DBAdapter |
4197
|
|
|
*/ |
4198
|
|
|
protected $adapter; |
4199
|
|
|
|
4200
|
|
|
/** |
4201
|
|
|
* @var string |
4202
|
|
|
*/ |
4203
|
|
|
protected $defaultValue = 'NULL'; |
4204
|
|
|
|
4205
|
|
|
/** |
4206
|
|
|
* @var string |
4207
|
|
|
*/ |
4208
|
|
|
protected $quoteCharacter = ''; |
4209
|
|
|
|
4210
|
|
|
/** |
4211
|
|
|
* @var boolean |
4212
|
|
|
*/ |
4213
|
|
|
protected $flagUseCache = TRUE; |
4214
|
|
|
|
4215
|
|
|
/** |
4216
|
|
|
* @var array |
4217
|
|
|
*/ |
4218
|
|
|
protected $cache = array(); |
4219
|
|
|
|
4220
|
|
|
/** |
4221
|
|
|
* @var integer |
4222
|
|
|
*/ |
4223
|
|
|
protected $maxCacheSizePerType = 20; |
4224
|
|
|
|
4225
|
|
|
/** |
4226
|
|
|
* @var array |
4227
|
|
|
*/ |
4228
|
|
|
public $typeno_sqltype = array(); |
4229
|
|
|
|
4230
|
|
|
/** |
4231
|
|
|
* Checks whether a number can be treated like an int. |
4232
|
|
|
* |
4233
|
|
|
* @param string $value string representation of a certain value |
4234
|
|
|
* |
4235
|
|
|
* @return boolean |
4236
|
|
|
*/ |
4237
|
|
|
public static function canBeTreatedAsInt( $value ) |
4238
|
|
|
{ |
4239
|
|
|
return (bool) ( strval( $value ) === strval( intval( $value ) ) ); |
4240
|
|
|
} |
4241
|
|
|
|
4242
|
|
|
/** |
4243
|
|
|
* @see QueryWriter::getAssocTableFormat |
4244
|
|
|
*/ |
4245
|
|
|
public static function getAssocTableFormat( $types ) |
4246
|
|
|
{ |
4247
|
|
|
sort( $types ); |
4248
|
|
|
|
4249
|
|
|
$assoc = implode( '_', $types ); |
4250
|
|
|
|
4251
|
|
|
return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc; |
4252
|
|
|
} |
4253
|
|
|
|
4254
|
|
|
/** |
4255
|
|
|
* @see QueryWriter::renameAssociation |
4256
|
|
|
*/ |
4257
|
|
|
public static function renameAssociation( $from, $to = NULL ) |
4258
|
|
|
{ |
4259
|
|
|
if ( is_array( $from ) ) { |
4260
|
|
|
foreach ( $from as $key => $value ) self::$renames[$key] = $value; |
4261
|
|
|
|
4262
|
|
|
return; |
4263
|
|
|
} |
4264
|
|
|
|
4265
|
|
|
self::$renames[$from] = $to; |
4266
|
|
|
} |
4267
|
|
|
|
4268
|
|
|
/** |
4269
|
|
|
* Globally available service method for RedBeanPHP. |
4270
|
|
|
* Converts a camel cased string to a snake cased string. |
4271
|
|
|
* |
4272
|
|
|
* @param string $camel a camelCased string |
4273
|
|
|
* |
4274
|
|
|
* @return string |
4275
|
|
|
*/ |
4276
|
|
|
public static function camelsSnake( $camel ) |
4277
|
|
|
{ |
4278
|
|
|
return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) ); |
4279
|
|
|
} |
4280
|
|
|
|
4281
|
|
|
/** |
4282
|
|
|
* Clears renames. |
4283
|
|
|
* |
4284
|
|
|
* @return void |
4285
|
|
|
*/ |
4286
|
|
|
public static function clearRenames() |
4287
|
|
|
{ |
4288
|
|
|
self::$renames = array(); |
4289
|
|
|
} |
4290
|
|
|
|
4291
|
|
|
/** |
4292
|
|
|
* Toggles 'Narrow Field Mode'. |
4293
|
|
|
* In Narrow Field mode the queryRecord method will |
4294
|
|
|
* narrow its selection field to |
4295
|
|
|
* |
4296
|
|
|
* SELECT {table}.* |
4297
|
|
|
* |
4298
|
|
|
* instead of |
4299
|
|
|
* |
4300
|
|
|
* SELECT * |
4301
|
|
|
* |
4302
|
|
|
* This is a better way of querying because it allows |
4303
|
|
|
* more flexibility (for instance joins). However if you need |
4304
|
|
|
* the wide selector for backward compatibility; use this method |
4305
|
|
|
* to turn OFF Narrow Field Mode by passing FALSE. |
4306
|
|
|
* |
4307
|
|
|
* @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field |
4308
|
|
|
* |
4309
|
|
|
* @return void |
4310
|
|
|
*/ |
4311
|
|
|
public static function setNarrowFieldMode( $narrowField ) |
4312
|
|
|
{ |
4313
|
|
|
self::$flagNarrowFieldMode = (boolean) $narrowField; |
4314
|
|
|
} |
4315
|
|
|
|
4316
|
|
|
/** |
4317
|
|
|
* Sets SQL filters. |
4318
|
|
|
* This is a lowlevel method to set the SQL filter array. |
4319
|
|
|
* The format of this array is: |
4320
|
|
|
* |
4321
|
|
|
* array( |
4322
|
|
|
* '<MODE, i.e. 'r' for read, 'w' for write>' => array( |
4323
|
|
|
* '<TABLE NAME>' => array( |
4324
|
|
|
* '<COLUMN NAME>' => '<SQL>' |
4325
|
|
|
* ) |
4326
|
|
|
* ) |
4327
|
|
|
* ) |
4328
|
|
|
* |
4329
|
|
|
* Example: |
4330
|
|
|
* |
4331
|
|
|
* array( |
4332
|
|
|
* QueryWriter::C_SQLFILTER_READ => array( |
4333
|
|
|
* 'book' => array( |
4334
|
|
|
* 'title' => ' LOWER(book.title) ' |
4335
|
|
|
* ) |
4336
|
|
|
* ) |
4337
|
|
|
* |
4338
|
|
|
* Note that you can use constants instead of magical chars |
4339
|
|
|
* as keys for the uppermost array. |
4340
|
|
|
* This is a lowlevel method. For a more friendly method |
4341
|
|
|
* please take a look at the facade: R::bindFunc(). |
4342
|
|
|
* |
4343
|
|
|
* @param array |
4344
|
|
|
*/ |
4345
|
|
|
public static function setSQLFilters( $sqlFilters, $safeMode = false ) |
4346
|
|
|
{ |
4347
|
|
|
self::$flagSQLFilterSafeMode = (boolean) $safeMode; |
4348
|
|
|
self::$sqlFilters = $sqlFilters; |
4349
|
|
|
} |
4350
|
|
|
|
4351
|
|
|
/** |
4352
|
|
|
* Returns current SQL Filters. |
4353
|
|
|
* This method returns the raw SQL filter array. |
4354
|
|
|
* This is a lowlevel method. For a more friendly method |
4355
|
|
|
* please take a look at the facade: R::bindFunc(). |
4356
|
|
|
* |
4357
|
|
|
* @return array |
4358
|
|
|
*/ |
4359
|
|
|
public static function getSQLFilters() |
4360
|
|
|
{ |
4361
|
|
|
return self::$sqlFilters; |
4362
|
|
|
} |
4363
|
|
|
|
4364
|
|
|
/** |
4365
|
|
|
* Returns a cache key for the cache values passed. |
4366
|
|
|
* This method returns a fingerprint string to be used as a key to store |
4367
|
|
|
* data in the writer cache. |
4368
|
|
|
* |
4369
|
|
|
* @param array $keyValues key-value to generate key for |
4370
|
|
|
* |
4371
|
|
|
* @return string |
4372
|
|
|
*/ |
4373
|
|
|
private function getCacheKey( $keyValues ) |
4374
|
|
|
{ |
4375
|
|
|
return json_encode( $keyValues ); |
4376
|
|
|
} |
4377
|
|
|
|
4378
|
|
|
/** |
4379
|
|
|
* Returns the values associated with the provided cache tag and key. |
4380
|
|
|
* |
4381
|
|
|
* @param string $cacheTag cache tag to use for lookup |
4382
|
|
|
* @param string $key key to use for lookup |
4383
|
|
|
* |
4384
|
|
|
* @return mixed |
4385
|
|
|
*/ |
4386
|
|
|
private function getCached( $cacheTag, $key ) |
4387
|
|
|
{ |
4388
|
|
|
$sql = $this->adapter->getSQL(); |
4389
|
|
|
|
4390
|
|
|
if ($this->updateCache()) { |
4391
|
|
|
if ( isset( $this->cache[$cacheTag][$key] ) ) { |
4392
|
|
|
return $this->cache[$cacheTag][$key]; |
4393
|
|
|
} |
4394
|
|
|
} |
4395
|
|
|
|
4396
|
|
|
return NULL; |
4397
|
|
|
} |
4398
|
|
|
|
4399
|
|
|
/** |
4400
|
|
|
* Checks if the previous query had a keep-cache tag. |
4401
|
|
|
* If so, the cache will persist, otherwise the cache will be flushed. |
4402
|
|
|
* |
4403
|
|
|
* Returns TRUE if the cache will remain and FALSE if a flush has |
4404
|
|
|
* been performed. |
4405
|
|
|
* |
4406
|
|
|
* @return boolean |
4407
|
|
|
*/ |
4408
|
|
|
private function updateCache() |
4409
|
|
|
{ |
4410
|
|
|
$sql = $this->adapter->getSQL(); |
4411
|
|
|
if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) { |
4412
|
|
|
// If SQL has been taken place outside of this method then something else then |
4413
|
|
|
// a select query might have happened! (or instruct to keep cache) |
4414
|
|
|
$this->cache = array(); |
4415
|
|
|
return FALSE; |
4416
|
|
|
} |
4417
|
|
|
return TRUE; |
4418
|
|
|
} |
4419
|
|
|
|
4420
|
|
|
/** |
4421
|
|
|
* Stores data from the writer in the cache under a specific key and cache tag. |
4422
|
|
|
* A cache tag is used to make sure the cache remains consistent. In most cases the cache tag |
4423
|
|
|
* will be the bean type, this makes sure queries associated with a certain reference type will |
4424
|
|
|
* never contain conflicting data. |
4425
|
|
|
* Why not use the cache tag as a key? Well |
4426
|
|
|
* we need to make sure the cache contents fits the key (and key is based on the cache values). |
4427
|
|
|
* Otherwise it would be possible to store two different result sets under the same key (the cache tag). |
4428
|
|
|
* |
4429
|
|
|
* In previous versions you could only store one key-entry, I have changed this to |
4430
|
|
|
* improve caching efficiency (issue #400). |
4431
|
|
|
* |
4432
|
|
|
* @param string $cacheTag cache tag (secondary key) |
4433
|
|
|
* @param string $key key |
4434
|
|
|
* @param array $values content to be stored |
4435
|
|
|
* |
4436
|
|
|
* @return void |
4437
|
|
|
*/ |
4438
|
|
|
private function putResultInCache( $cacheTag, $key, $values ) |
4439
|
|
|
{ |
4440
|
|
|
if ( isset( $this->cache[$cacheTag] ) ) { |
4441
|
|
|
if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] ); |
4442
|
|
|
} else { |
4443
|
|
|
$this->cache[$cacheTag] = array(); |
4444
|
|
|
} |
4445
|
|
|
|
4446
|
|
|
$this->cache[$cacheTag][$key] = $values; |
4447
|
|
|
} |
4448
|
|
|
|
4449
|
|
|
/** |
4450
|
|
|
* Creates an SQL snippet from a list of conditions of format: |
4451
|
|
|
* |
4452
|
|
|
* array( |
4453
|
|
|
* key => array( |
4454
|
|
|
* value1, value2, value3 .... |
4455
|
|
|
* ) |
4456
|
|
|
* ) |
4457
|
|
|
* |
4458
|
|
|
* @param array $conditions list of conditions |
4459
|
|
|
* @param array $bindings parameter bindings for SQL snippet |
4460
|
|
|
* @param string $addSql SQL snippet |
4461
|
|
|
* |
4462
|
|
|
* @return string |
4463
|
|
|
*/ |
4464
|
|
|
private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' ) |
4465
|
|
|
{ |
4466
|
|
|
reset( $bindings ); |
4467
|
|
|
$firstKey = key( $bindings ); |
4468
|
|
|
$paramTypeIsNum = ( is_numeric( $firstKey ) ); |
4469
|
|
|
$counter = 0; |
4470
|
|
|
|
4471
|
|
|
$sqlConditions = array(); |
4472
|
|
|
foreach ( $conditions as $column => $values ) { |
4473
|
|
|
if ( !count( $values ) ) continue; |
4474
|
|
|
|
4475
|
|
|
$sql = $this->esc( $column ); |
4476
|
|
|
$sql .= ' IN ( '; |
4477
|
|
|
|
4478
|
|
|
if ( !is_array( $values ) ) $values = array( $values ); |
4479
|
|
|
|
4480
|
|
|
// If it's safe to skip bindings, do so... |
4481
|
|
|
if ( ctype_digit( implode( '', $values ) ) ) { |
4482
|
|
|
$sql .= implode( ',', $values ) . ' ) '; |
4483
|
|
|
|
4484
|
|
|
// only numeric, cant do much harm |
4485
|
|
|
$sqlConditions[] = $sql; |
4486
|
|
|
} else { |
4487
|
|
|
|
4488
|
|
|
if ( $paramTypeIsNum ) { |
4489
|
|
|
$sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) '; |
4490
|
|
|
|
4491
|
|
|
array_unshift($sqlConditions, $sql); |
4492
|
|
|
|
4493
|
|
|
foreach ( $values as $k => $v ) { |
4494
|
|
|
$values[$k] = strval( $v ); |
4495
|
|
|
|
4496
|
|
|
array_unshift( $bindings, $v ); |
4497
|
|
|
} |
4498
|
|
|
} else { |
4499
|
|
|
|
4500
|
|
|
$slots = array(); |
4501
|
|
|
|
4502
|
|
|
foreach( $values as $k => $v ) { |
4503
|
|
|
$slot = ':slot'.$counter++; |
4504
|
|
|
$slots[] = $slot; |
4505
|
|
|
$bindings[$slot] = strval( $v ); |
4506
|
|
|
} |
4507
|
|
|
|
4508
|
|
|
$sql .= implode( ',', $slots ).' ) '; |
4509
|
|
|
$sqlConditions[] = $sql; |
4510
|
|
|
} |
4511
|
|
|
} |
4512
|
|
|
} |
4513
|
|
|
|
4514
|
|
|
$sql = ''; |
4515
|
|
|
if ( is_array( $sqlConditions ) && count( $sqlConditions ) > 0 ) { |
4516
|
|
|
$sql = implode( ' AND ', $sqlConditions ); |
4517
|
|
|
$sql = " WHERE ( $sql ) "; |
4518
|
|
|
|
4519
|
|
|
if ( $addSql ) $sql .= $addSql; |
4520
|
|
|
} elseif ( $addSql ) { |
4521
|
|
|
$sql = $addSql; |
4522
|
|
|
} |
4523
|
|
|
|
4524
|
|
|
return $sql; |
4525
|
|
|
} |
4526
|
|
|
|
4527
|
|
|
/** |
4528
|
|
|
* Returns the table names and column names for a relational query. |
4529
|
|
|
* |
4530
|
|
|
* @param string $sourceType type of the source bean |
4531
|
|
|
* @param string $destType type of the bean you want to obtain using the relation |
4532
|
|
|
* @param boolean $noQuote TRUE if you want to omit quotes |
4533
|
|
|
* |
4534
|
|
|
* @return array |
4535
|
|
|
*/ |
4536
|
|
|
private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE ) |
4537
|
|
|
{ |
4538
|
|
|
$linkTable = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote ); |
4539
|
|
|
$sourceCol = $this->esc( $sourceType . '_id', $noQuote ); |
4540
|
|
|
|
4541
|
|
|
if ( $sourceType === $destType ) { |
4542
|
|
|
$destCol = $this->esc( $destType . '2_id', $noQuote ); |
4543
|
|
|
} else { |
4544
|
|
|
$destCol = $this->esc( $destType . '_id', $noQuote ); |
4545
|
|
|
} |
4546
|
|
|
|
4547
|
|
|
$sourceTable = $this->esc( $sourceType, $noQuote ); |
4548
|
|
|
$destTable = $this->esc( $destType, $noQuote ); |
4549
|
|
|
|
4550
|
|
|
return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ); |
4551
|
|
|
} |
4552
|
|
|
|
4553
|
|
|
/** |
4554
|
|
|
* Given a type and a property name this method |
4555
|
|
|
* returns the foreign key map section associated with this pair. |
4556
|
|
|
* |
4557
|
|
|
* @param string $type name of the type |
4558
|
|
|
* @param string $property name of the property |
4559
|
|
|
* |
4560
|
|
|
* @return array|NULL |
4561
|
|
|
*/ |
4562
|
|
|
protected function getForeignKeyForTypeProperty( $type, $property ) |
4563
|
|
|
{ |
4564
|
|
|
$property = $this->esc( $property, TRUE ); |
4565
|
|
|
|
4566
|
|
|
try { |
4567
|
|
|
$map = $this->getKeyMapForType( $type ); |
4568
|
|
|
} catch ( SQLException $e ) { |
4569
|
|
|
return NULL; |
4570
|
|
|
} |
4571
|
|
|
|
4572
|
|
|
foreach( $map as $key ) { |
4573
|
|
|
if ( $key['from'] === $property ) return $key; |
4574
|
|
|
} |
4575
|
|
|
return NULL; |
4576
|
|
|
} |
4577
|
|
|
|
4578
|
|
|
/** |
4579
|
|
|
* Returns the foreign key map (FKM) for a type. |
4580
|
|
|
* A foreign key map describes the foreign keys in a table. |
4581
|
|
|
* A FKM always has the same structure: |
4582
|
|
|
* |
4583
|
|
|
* array( |
4584
|
|
|
* 'name' => <name of the foreign key> |
4585
|
|
|
* 'from' => <name of the column on the source table> |
4586
|
|
|
* 'table' => <name of the target table> |
4587
|
|
|
* 'to' => <name of the target column> (most of the time 'id') |
4588
|
|
|
* 'on_update' => <update rule: 'SET NULL','CASCADE' or 'RESTRICT'> |
4589
|
|
|
* 'on_delete' => <delete rule: 'SET NULL','CASCADE' or 'RESTRICT'> |
4590
|
|
|
* ) |
4591
|
|
|
* |
4592
|
|
|
* @note the keys in the result array are FKDLs, i.e. descriptive unique |
4593
|
|
|
* keys per source table. Also see: AQueryWriter::makeFKLabel for details. |
4594
|
|
|
* |
4595
|
|
|
* @param string $type the bean type you wish to obtain a key map of |
4596
|
|
|
* |
4597
|
|
|
* @return array |
4598
|
|
|
*/ |
4599
|
|
|
protected function getKeyMapForType( $type ) |
4600
|
|
|
{ |
4601
|
|
|
return array(); |
4602
|
|
|
} |
4603
|
|
|
|
4604
|
|
|
/** |
4605
|
|
|
* This method makes a key for a foreign key description array. |
4606
|
|
|
* This key is a readable string unique for every source table. |
4607
|
|
|
* This uniform key is called the FKDL Foreign Key Description Label. |
4608
|
|
|
* Note that the source table is not part of the FKDL because |
4609
|
|
|
* this key is supposed to be 'per source table'. If you wish to |
4610
|
|
|
* include a source table, prefix the key with 'on_table_<SOURCE>_'. |
4611
|
|
|
* |
4612
|
|
|
* @param string $from the column of the key in the source table |
4613
|
|
|
* @param string $type the type (table) where the key points to |
4614
|
|
|
* @param string $to the target column of the foreign key (mostly just 'id') |
4615
|
|
|
* |
4616
|
|
|
* @return string |
4617
|
|
|
*/ |
4618
|
|
|
protected function makeFKLabel($from, $type, $to) |
4619
|
|
|
{ |
4620
|
|
|
return "from_{$from}_to_table_{$type}_col_{$to}"; |
4621
|
|
|
} |
4622
|
|
|
|
4623
|
|
|
/** |
4624
|
|
|
* Returns an SQL Filter snippet for reading. |
4625
|
|
|
* |
4626
|
|
|
* @param string $type type of bean |
4627
|
|
|
* |
4628
|
|
|
* @return string |
4629
|
|
|
*/ |
4630
|
|
|
protected function getSQLFilterSnippet( $type ) |
4631
|
|
|
{ |
4632
|
|
|
$existingCols = array(); |
4633
|
|
|
if (self::$flagSQLFilterSafeMode) { |
4634
|
|
|
$existingCols = $this->getColumns( $type ); |
4635
|
|
|
} |
4636
|
|
|
|
4637
|
|
|
$sqlFilters = array(); |
4638
|
|
|
if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) { |
4639
|
|
|
foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) { |
4640
|
|
|
if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) { |
4641
|
|
|
$sqlFilters[] = $sqlFilter.' AS '.$property.' '; |
4642
|
|
|
} |
4643
|
|
|
} |
4644
|
|
|
} |
4645
|
|
|
$sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : ''; |
4646
|
|
|
return $sqlFilterStr; |
4647
|
|
|
} |
4648
|
|
|
|
4649
|
|
|
/** |
4650
|
|
|
* Generates a list of parameters (slots) for an SQL snippet. |
4651
|
|
|
* This method calculates the correct number of slots to insert in the |
4652
|
|
|
* SQL snippet and determines the correct type of slot. If the bindings |
4653
|
|
|
* array contains named parameters this method will return named ones and |
4654
|
|
|
* update the keys in the value list accordingly (that's why we use the &). |
4655
|
|
|
* |
4656
|
|
|
* If you pass an offset the bindings will be re-added to the value list. |
4657
|
|
|
* Some databases cant handle duplicate parameter names in queries. |
4658
|
|
|
* |
4659
|
|
|
* @param array &$valueList list of values to generate slots for (gets modified if needed) |
4660
|
|
|
* @param array $otherBindings list of additional bindings |
4661
|
|
|
* @param integer $offset start counter at... |
4662
|
|
|
* |
4663
|
|
|
* @return string |
4664
|
|
|
*/ |
4665
|
|
|
protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 ) |
4666
|
|
|
{ |
4667
|
|
|
if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) { |
4668
|
|
|
reset( $otherBindings ); |
4669
|
|
|
|
4670
|
|
|
$key = key( $otherBindings ); |
4671
|
|
|
|
4672
|
|
|
if ( !is_numeric($key) ) { |
4673
|
|
|
$filler = array(); |
4674
|
|
|
$newList = (!$offset) ? array() : $valueList; |
4675
|
|
|
$counter = $offset; |
4676
|
|
|
|
4677
|
|
|
foreach( $valueList as $value ) { |
4678
|
|
|
$slot = ':slot' . ( $counter++ ); |
4679
|
|
|
$filler[] = $slot; |
4680
|
|
|
$newList[$slot] = $value; |
4681
|
|
|
} |
4682
|
|
|
|
4683
|
|
|
// Change the keys! |
4684
|
|
|
$valueList = $newList; |
4685
|
|
|
|
4686
|
|
|
return implode( ',', $filler ); |
4687
|
|
|
} |
4688
|
|
|
} |
4689
|
|
|
|
4690
|
|
|
return implode( ',', array_fill( 0, count( $valueList ), '?' ) ); |
4691
|
|
|
} |
4692
|
|
|
|
4693
|
|
|
/** |
4694
|
|
|
* Adds a data type to the list of data types. |
4695
|
|
|
* Use this method to add a new column type definition to the writer. |
4696
|
|
|
* Used for UUID support. |
4697
|
|
|
* |
4698
|
|
|
* @param integer $dataTypeID magic number constant assigned to this data type |
4699
|
|
|
* @param string $SQLDefinition SQL column definition (i.e. INT(11)) |
4700
|
|
|
* |
4701
|
|
|
* @return self |
4702
|
|
|
*/ |
4703
|
|
|
protected function addDataType( $dataTypeID, $SQLDefinition ) |
4704
|
|
|
{ |
4705
|
|
|
$this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition; |
4706
|
|
|
$this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID; |
|
|
|
|
4707
|
|
|
return $this; |
4708
|
|
|
} |
4709
|
|
|
|
4710
|
|
|
/** |
4711
|
|
|
* Returns the sql that should follow an insert statement. |
4712
|
|
|
* |
4713
|
|
|
* @param string $table name |
4714
|
|
|
* |
4715
|
|
|
* @return string |
4716
|
|
|
*/ |
4717
|
|
|
protected function getInsertSuffix( $table ) |
|
|
|
|
4718
|
|
|
{ |
4719
|
|
|
return ''; |
4720
|
|
|
} |
4721
|
|
|
|
4722
|
|
|
/** |
4723
|
|
|
* Checks whether a value starts with zeros. In this case |
4724
|
|
|
* the value should probably be stored using a text datatype instead of a |
4725
|
|
|
* numerical type in order to preserve the zeros. |
4726
|
|
|
* |
4727
|
|
|
* @param string $value value to be checked. |
4728
|
|
|
* |
4729
|
|
|
* @return boolean |
4730
|
|
|
*/ |
4731
|
|
|
protected function startsWithZeros( $value ) |
4732
|
|
|
{ |
4733
|
|
|
$value = strval( $value ); |
4734
|
|
|
|
4735
|
|
|
if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) { |
4736
|
|
|
return TRUE; |
4737
|
|
|
} else { |
4738
|
|
|
return FALSE; |
4739
|
|
|
} |
4740
|
|
|
} |
4741
|
|
|
|
4742
|
|
|
/** |
4743
|
|
|
* Inserts a record into the database using a series of insert columns |
4744
|
|
|
* and corresponding insertvalues. Returns the insert id. |
4745
|
|
|
* |
4746
|
|
|
* @param string $table table to perform query on |
|
|
|
|
4747
|
|
|
* @param array $insertcolumns columns to be inserted |
4748
|
|
|
* @param array $insertvalues values to be inserted |
4749
|
|
|
* |
4750
|
|
|
* @return integer |
4751
|
|
|
*/ |
4752
|
|
|
protected function insertRecord( $type, $insertcolumns, $insertvalues ) |
4753
|
|
|
{ |
4754
|
|
|
$default = $this->defaultValue; |
4755
|
|
|
$suffix = $this->getInsertSuffix( $type ); |
4756
|
|
|
$table = $this->esc( $type ); |
4757
|
|
|
|
4758
|
|
|
if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) { |
4759
|
|
|
|
4760
|
|
|
$insertSlots = array(); |
4761
|
|
|
foreach ( $insertcolumns as $k => $v ) { |
4762
|
|
|
$insertcolumns[$k] = $this->esc( $v ); |
4763
|
|
|
|
4764
|
|
|
if (isset(self::$sqlFilters['w'][$type][$v])) { |
4765
|
|
|
$insertSlots[] = self::$sqlFilters['w'][$type][$v]; |
4766
|
|
|
} else { |
4767
|
|
|
$insertSlots[] = '?'; |
4768
|
|
|
} |
4769
|
|
|
} |
4770
|
|
|
|
4771
|
|
|
$insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES |
4772
|
|
|
( $default, " . implode( ',', $insertSlots ) . " ) $suffix"; |
4773
|
|
|
|
4774
|
|
|
$ids = array(); |
4775
|
|
|
foreach ( $insertvalues as $i => $insertvalue ) { |
4776
|
|
|
$ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i ); |
4777
|
|
|
} |
4778
|
|
|
|
4779
|
|
|
$result = count( $ids ) === 1 ? array_pop( $ids ) : $ids; |
4780
|
|
|
} else { |
4781
|
|
|
$result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" ); |
4782
|
|
|
} |
4783
|
|
|
|
4784
|
|
|
if ( $suffix ) return $result; |
4785
|
|
|
|
4786
|
|
|
$last_id = $this->adapter->getInsertID(); |
4787
|
|
|
|
4788
|
|
|
return $last_id; |
4789
|
|
|
} |
4790
|
|
|
|
4791
|
|
|
/** |
4792
|
|
|
* Checks table name or column name. |
4793
|
|
|
* |
4794
|
|
|
* @param string $table table string |
|
|
|
|
4795
|
|
|
* |
4796
|
|
|
* @return string |
4797
|
|
|
* |
4798
|
|
|
* @throws Security |
4799
|
|
|
*/ |
4800
|
|
|
protected function check( $struct ) |
4801
|
|
|
{ |
4802
|
|
|
if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) { |
4803
|
|
|
throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' ); |
4804
|
|
|
} |
4805
|
|
|
|
4806
|
|
|
return $struct; |
4807
|
|
|
} |
4808
|
|
|
|
4809
|
|
|
/** |
4810
|
|
|
* Checks whether the specified type (i.e. table) already exists in the database. |
4811
|
|
|
* Not part of the Object Database interface! |
4812
|
|
|
* |
4813
|
|
|
* @param string $table table name |
4814
|
|
|
* |
4815
|
|
|
* @return boolean |
4816
|
|
|
*/ |
4817
|
|
|
public function tableExists( $table ) |
4818
|
|
|
{ |
4819
|
|
|
$tables = $this->getTables(); |
4820
|
|
|
|
4821
|
|
|
return in_array( $table, $tables ); |
4822
|
|
|
} |
4823
|
|
|
|
4824
|
|
|
/** |
4825
|
|
|
* @see QueryWriter::glueSQLCondition |
4826
|
|
|
*/ |
4827
|
|
|
public function glueSQLCondition( $sql, $glue = NULL ) |
4828
|
|
|
{ |
4829
|
|
|
static $snippetCache = array(); |
4830
|
|
|
|
4831
|
|
|
if ( trim( $sql ) === '' ) { |
4832
|
|
|
return $sql; |
4833
|
|
|
} |
4834
|
|
|
|
4835
|
|
|
$key = $glue . '|' . $sql; |
4836
|
|
|
|
4837
|
|
|
if ( isset( $snippetCache[$key] ) ) { |
4838
|
|
|
return $snippetCache[$key]; |
4839
|
|
|
} |
4840
|
|
|
|
4841
|
|
|
$lsql = ltrim( $sql ); |
4842
|
|
|
|
4843
|
|
|
if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) { |
4844
|
|
|
if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) { |
4845
|
|
|
$snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 ); |
4846
|
|
|
} else { |
4847
|
|
|
$snippetCache[$key] = $sql; |
4848
|
|
|
} |
4849
|
|
|
} else { |
4850
|
|
|
$snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql; |
4851
|
|
|
} |
4852
|
|
|
|
4853
|
|
|
return $snippetCache[$key]; |
4854
|
|
|
} |
4855
|
|
|
|
4856
|
|
|
/** |
4857
|
|
|
* @see QueryWriter::glueLimitOne |
4858
|
|
|
*/ |
4859
|
|
|
public function glueLimitOne( $sql = '') |
4860
|
|
|
{ |
4861
|
|
|
return ( strpos( $sql, 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql; |
4862
|
|
|
} |
4863
|
|
|
|
4864
|
|
|
/** |
4865
|
|
|
* @see QueryWriter::esc |
4866
|
|
|
*/ |
4867
|
|
|
public function esc( $dbStructure, $dontQuote = FALSE ) |
4868
|
|
|
{ |
4869
|
|
|
$this->check( $dbStructure ); |
4870
|
|
|
|
4871
|
|
|
return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter; |
4872
|
|
|
} |
4873
|
|
|
|
4874
|
|
|
/** |
4875
|
|
|
* @see QueryWriter::addColumn |
4876
|
|
|
*/ |
4877
|
|
View Code Duplication |
public function addColumn( $type, $column, $field ) |
4878
|
|
|
{ |
4879
|
|
|
$table = $type; |
4880
|
|
|
$type = $field; |
4881
|
|
|
$table = $this->esc( $table ); |
4882
|
|
|
$column = $this->esc( $column ); |
4883
|
|
|
|
4884
|
|
|
$type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : ''; |
4885
|
|
|
|
4886
|
|
|
$this->adapter->exec( "ALTER TABLE $table ADD $column $type " ); |
4887
|
|
|
} |
4888
|
|
|
|
4889
|
|
|
/** |
4890
|
|
|
* @see QueryWriter::updateRecord |
4891
|
|
|
*/ |
4892
|
|
|
public function updateRecord( $type, $updatevalues, $id = NULL ) |
4893
|
|
|
{ |
4894
|
|
|
$table = $type; |
4895
|
|
|
|
4896
|
|
|
if ( !$id ) { |
4897
|
|
|
$insertcolumns = $insertvalues = array(); |
4898
|
|
|
|
4899
|
|
|
foreach ( $updatevalues as $pair ) { |
4900
|
|
|
$insertcolumns[] = $pair['property']; |
4901
|
|
|
$insertvalues[] = $pair['value']; |
4902
|
|
|
} |
4903
|
|
|
|
4904
|
|
|
//Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff) |
4905
|
|
|
return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) ); |
4906
|
|
|
} |
4907
|
|
|
|
4908
|
|
|
if ( $id && !count( $updatevalues ) ) { |
4909
|
|
|
return $id; |
4910
|
|
|
} |
4911
|
|
|
|
4912
|
|
|
$table = $this->esc( $table ); |
4913
|
|
|
$sql = "UPDATE $table SET "; |
4914
|
|
|
|
4915
|
|
|
$p = $v = array(); |
4916
|
|
|
|
4917
|
|
|
foreach ( $updatevalues as $uv ) { |
4918
|
|
|
|
4919
|
|
|
if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) { |
4920
|
|
|
$p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']]; |
4921
|
|
|
} else { |
4922
|
|
|
$p[] = " {$this->esc( $uv["property"] )} = ? "; |
4923
|
|
|
} |
4924
|
|
|
|
4925
|
|
|
$v[] = $uv['value']; |
4926
|
|
|
} |
4927
|
|
|
|
4928
|
|
|
$sql .= implode( ',', $p ) . ' WHERE id = ? '; |
4929
|
|
|
|
4930
|
|
|
$v[] = $id; |
4931
|
|
|
|
4932
|
|
|
$this->adapter->exec( $sql, $v ); |
4933
|
|
|
|
4934
|
|
|
return $id; |
4935
|
|
|
} |
4936
|
|
|
|
4937
|
|
|
/** |
4938
|
|
|
* @see QueryWriter::writeJoin |
4939
|
|
|
*/ |
4940
|
|
|
public function writeJoin( $type, $targetType, $leftRight = 'LEFT' ) |
4941
|
|
|
{ |
4942
|
|
|
if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' ) |
4943
|
|
|
throw new RedException( 'Invalid JOIN.' ); |
4944
|
|
|
|
4945
|
|
|
$table = $this->esc( $type ); |
4946
|
|
|
$targetTable = $this->esc( $targetType ); |
4947
|
|
|
$field = $this->esc( $targetType, TRUE ); |
4948
|
|
|
return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id "; |
4949
|
|
|
} |
4950
|
|
|
|
4951
|
|
|
/** |
4952
|
|
|
* @see QueryWriter::queryRecord |
4953
|
|
|
*/ |
4954
|
|
|
public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) |
4955
|
|
|
{ |
4956
|
|
|
$addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL ); |
4957
|
|
|
|
4958
|
|
|
$key = NULL; |
4959
|
|
|
if ( $this->flagUseCache ) { |
4960
|
|
|
$key = $this->getCacheKey( array( $conditions, $addSql, $bindings, 'select' ) ); |
4961
|
|
|
|
4962
|
|
|
if ( $cached = $this->getCached( $type, $key ) ) { |
4963
|
|
|
return $cached; |
4964
|
|
|
} |
4965
|
|
|
} |
4966
|
|
|
|
4967
|
|
|
$table = $this->esc( $type ); |
4968
|
|
|
|
4969
|
|
|
$sqlFilterStr = ''; |
4970
|
|
|
if ( count( self::$sqlFilters ) ) { |
4971
|
|
|
$sqlFilterStr = $this->getSQLFilterSnippet( $type ); |
4972
|
|
|
} |
4973
|
|
|
|
4974
|
|
|
$sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); |
4975
|
|
|
|
4976
|
|
|
$fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*'; |
4977
|
|
|
$sql = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache"; |
4978
|
|
|
|
4979
|
|
|
$rows = $this->adapter->get( $sql, $bindings ); |
4980
|
|
|
|
4981
|
|
|
|
4982
|
|
|
if ( $this->flagUseCache && $key ) { |
|
|
|
|
4983
|
|
|
$this->putResultInCache( $type, $key, $rows ); |
4984
|
|
|
} |
4985
|
|
|
|
4986
|
|
|
return $rows; |
4987
|
|
|
} |
4988
|
|
|
|
4989
|
|
|
/** |
4990
|
|
|
* @see QueryWriter::queryRecordWithCursor |
4991
|
|
|
*/ |
4992
|
|
|
public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() ) |
4993
|
|
|
{ |
4994
|
|
|
$sql = $this->glueSQLCondition( $addSql, NULL ); |
4995
|
|
|
$table = $this->esc( $type ); |
4996
|
|
|
$sql = "SELECT {$table}.* FROM {$table} {$sql}"; |
4997
|
|
|
return $this->adapter->getCursor( $sql, $bindings ); |
4998
|
|
|
} |
4999
|
|
|
|
5000
|
|
|
/** |
5001
|
|
|
* @see QueryWriter::queryRecordRelated |
5002
|
|
|
*/ |
5003
|
|
|
public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() ) |
5004
|
|
|
{ |
5005
|
|
|
$addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE ); |
5006
|
|
|
|
5007
|
|
|
list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); |
|
|
|
|
5008
|
|
|
|
5009
|
|
|
$key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) ); |
5010
|
|
|
|
5011
|
|
|
if ( $this->flagUseCache && $cached = $this->getCached( $destType, $key ) ) { |
5012
|
|
|
return $cached; |
5013
|
|
|
} |
5014
|
|
|
|
5015
|
|
|
$inClause = $this->getParametersForInClause( $linkIDs, $bindings ); |
5016
|
|
|
|
5017
|
|
|
$sqlFilterStr = ''; |
5018
|
|
|
if ( count( self::$sqlFilters ) ) { |
5019
|
|
|
$sqlFilterStr = $this->getSQLFilterSnippet( $destType ); |
5020
|
|
|
} |
5021
|
|
|
|
5022
|
|
|
if ( $sourceType === $destType ) { |
5023
|
|
|
$inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases |
5024
|
|
|
$sql = " |
5025
|
|
|
SELECT |
5026
|
|
|
{$destTable}.* {$sqlFilterStr} , |
5027
|
|
|
COALESCE( |
5028
|
|
|
NULLIF({$linkTable}.{$sourceCol}, {$destTable}.id), |
5029
|
|
|
NULLIF({$linkTable}.{$destCol}, {$destTable}.id)) AS linked_by |
5030
|
|
|
FROM {$linkTable} |
5031
|
|
|
INNER JOIN {$destTable} ON |
5032
|
|
|
( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) OR |
5033
|
|
|
( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} IN ($inClause2) ) |
5034
|
|
|
{$addSql} |
5035
|
|
|
-- keep-cache"; |
5036
|
|
|
|
5037
|
|
|
$linkIDs = array_merge( $linkIDs, $linkIDs ); |
5038
|
|
|
} else { |
5039
|
|
|
$sql = " |
5040
|
|
|
SELECT |
5041
|
|
|
{$destTable}.* {$sqlFilterStr}, |
5042
|
|
|
{$linkTable}.{$sourceCol} AS linked_by |
5043
|
|
|
FROM {$linkTable} |
5044
|
|
|
INNER JOIN {$destTable} ON |
5045
|
|
|
( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) |
5046
|
|
|
{$addSql} |
5047
|
|
|
-- keep-cache"; |
5048
|
|
|
} |
5049
|
|
|
|
5050
|
|
|
$bindings = array_merge( $linkIDs, $bindings ); |
5051
|
|
|
|
5052
|
|
|
$rows = $this->adapter->get( $sql, $bindings ); |
5053
|
|
|
|
5054
|
|
|
$this->putResultInCache( $destType, $key, $rows ); |
5055
|
|
|
|
5056
|
|
|
return $rows; |
5057
|
|
|
} |
5058
|
|
|
|
5059
|
|
|
/** |
5060
|
|
|
* @see QueryWriter::queryRecordLink |
5061
|
|
|
*/ |
5062
|
|
|
public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ) |
5063
|
|
|
{ |
5064
|
|
|
list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); |
5065
|
|
|
|
5066
|
|
|
$key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID ) ); |
5067
|
|
|
|
5068
|
|
|
if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) { |
5069
|
|
|
return $cached; |
5070
|
|
|
} |
5071
|
|
|
|
5072
|
|
|
$sqlFilterStr = ''; |
5073
|
|
|
if ( count( self::$sqlFilters ) ) { |
5074
|
|
|
$sqlFilterStr = $this->getSQLFilterSnippet( $destType ); |
5075
|
|
|
} |
5076
|
|
|
|
5077
|
|
|
if ( $sourceTable === $destTable ) { |
5078
|
|
|
$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} |
5079
|
|
|
WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR |
5080
|
|
|
( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache"; |
5081
|
|
|
$row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) ); |
5082
|
|
|
} else { |
5083
|
|
|
$sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} |
5084
|
|
|
WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache"; |
5085
|
|
|
$row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) ); |
5086
|
|
|
} |
5087
|
|
|
|
5088
|
|
|
$this->putResultInCache( $linkTable, $key, $row ); |
5089
|
|
|
|
5090
|
|
|
return $row; |
5091
|
|
|
} |
5092
|
|
|
|
5093
|
|
|
/** |
5094
|
|
|
* @see QueryWriter::queryTagged |
5095
|
|
|
*/ |
5096
|
|
|
public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ) |
5097
|
|
|
{ |
5098
|
|
|
$assocType = $this->getAssocTable( array( $type, 'tag' ) ); |
5099
|
|
|
$assocTable = $this->esc( $assocType ); |
5100
|
|
|
$assocField = $type . '_id'; |
5101
|
|
|
$table = $this->esc( $type ); |
5102
|
|
|
$slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) ); |
5103
|
|
|
$score = ( $all ) ? count( $tagList ) : 1; |
5104
|
|
|
|
5105
|
|
|
$sql = " |
5106
|
|
|
SELECT {$table}.*, count({$table}.id) FROM {$table} |
5107
|
|
|
INNER JOIN {$assocTable} ON {$assocField} = {$table}.id |
5108
|
|
|
INNER JOIN tag ON {$assocTable}.tag_id = tag.id |
5109
|
|
|
WHERE tag.title IN ({$slots}) |
5110
|
|
|
GROUP BY {$table}.id |
5111
|
|
|
HAVING count({$table}.id) >= ? |
5112
|
|
|
{$addSql} |
5113
|
|
|
"; |
5114
|
|
|
|
5115
|
|
|
$bindings = array_merge( $tagList, array( $score ), $bindings ); |
5116
|
|
|
$rows = $this->adapter->get( $sql, $bindings ); |
5117
|
|
|
return $rows; |
5118
|
|
|
} |
5119
|
|
|
|
5120
|
|
|
/** |
5121
|
|
|
* @see QueryWriter::queryRecordCount |
5122
|
|
|
*/ |
5123
|
|
View Code Duplication |
public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) |
5124
|
|
|
{ |
5125
|
|
|
$addSql = $this->glueSQLCondition( $addSql ); |
5126
|
|
|
|
5127
|
|
|
$table = $this->esc( $type ); |
5128
|
|
|
|
5129
|
|
|
$this->updateCache(); //check if cache chain has been broken |
5130
|
|
|
|
5131
|
|
|
$sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); |
5132
|
|
|
$sql = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache"; |
5133
|
|
|
|
5134
|
|
|
return (int) $this->adapter->getCell( $sql, $bindings ); |
5135
|
|
|
} |
5136
|
|
|
|
5137
|
|
|
/** |
5138
|
|
|
* @see QueryWriter::queryRecordCountRelated |
5139
|
|
|
*/ |
5140
|
|
|
public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ) |
5141
|
|
|
{ |
5142
|
|
|
list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); |
|
|
|
|
5143
|
|
|
|
5144
|
|
|
$this->updateCache(); //check if cache chain has been broken |
5145
|
|
|
|
5146
|
|
|
if ( $sourceType === $destType ) { |
5147
|
|
|
$sql = " |
5148
|
|
|
SELECT COUNT(*) FROM {$linkTable} |
5149
|
|
|
INNER JOIN {$destTable} ON |
5150
|
|
|
( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR |
5151
|
|
|
( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} = ? ) |
5152
|
|
|
{$addSql} |
5153
|
|
|
-- keep-cache"; |
5154
|
|
|
|
5155
|
|
|
$bindings = array_merge( array( $linkID, $linkID ), $bindings ); |
5156
|
|
|
} else { |
5157
|
|
|
$sql = " |
5158
|
|
|
SELECT COUNT(*) FROM {$linkTable} |
5159
|
|
|
INNER JOIN {$destTable} ON |
5160
|
|
|
( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) |
5161
|
|
|
{$addSql} |
5162
|
|
|
-- keep-cache"; |
5163
|
|
|
|
5164
|
|
|
$bindings = array_merge( array( $linkID ), $bindings ); |
5165
|
|
|
} |
5166
|
|
|
|
5167
|
|
|
return (int) $this->adapter->getCell( $sql, $bindings ); |
5168
|
|
|
} |
5169
|
|
|
|
5170
|
|
|
/** |
5171
|
|
|
* @see QueryWriter::deleteRecord |
5172
|
|
|
*/ |
5173
|
|
View Code Duplication |
public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) |
5174
|
|
|
{ |
5175
|
|
|
$addSql = $this->glueSQLCondition( $addSql ); |
5176
|
|
|
|
5177
|
|
|
$table = $this->esc( $type ); |
5178
|
|
|
|
5179
|
|
|
$sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); |
5180
|
|
|
$sql = "DELETE FROM {$table} {$sql}"; |
5181
|
|
|
|
5182
|
|
|
$this->adapter->exec( $sql, $bindings ); |
5183
|
|
|
} |
5184
|
|
|
|
5185
|
|
|
/** |
5186
|
|
|
* @see QueryWriter::deleteRelations |
5187
|
|
|
*/ |
5188
|
|
|
public function deleteRelations( $sourceType, $destType, $sourceID ) |
5189
|
|
|
{ |
5190
|
|
|
list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); |
5191
|
|
|
|
5192
|
|
|
if ( $sourceTable === $destTable ) { |
5193
|
|
|
$sql = "DELETE FROM {$linkTable} |
5194
|
|
|
WHERE ( {$sourceCol} = ? ) OR |
5195
|
|
|
( {$destCol} = ? ) |
5196
|
|
|
"; |
5197
|
|
|
|
5198
|
|
|
$this->adapter->exec( $sql, array( $sourceID, $sourceID ) ); |
5199
|
|
|
} else { |
5200
|
|
|
$sql = "DELETE FROM {$linkTable} |
5201
|
|
|
WHERE {$sourceCol} = ? "; |
5202
|
|
|
|
5203
|
|
|
$this->adapter->exec( $sql, array( $sourceID ) ); |
5204
|
|
|
} |
5205
|
|
|
} |
5206
|
|
|
|
5207
|
|
|
/** |
5208
|
|
|
* @see QueryWriter::widenColumn |
5209
|
|
|
*/ |
5210
|
|
|
public function widenColumn( $type, $property, $dataType ) |
5211
|
|
|
{ |
5212
|
|
|
if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE; |
5213
|
|
|
|
5214
|
|
|
$table = $this->esc( $type ); |
5215
|
|
|
$column = $this->esc( $property ); |
5216
|
|
|
|
5217
|
|
|
$newType = $this->typeno_sqltype[$dataType]; |
5218
|
|
|
|
5219
|
|
|
$this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " ); |
5220
|
|
|
|
5221
|
|
|
return TRUE; |
5222
|
|
|
} |
5223
|
|
|
|
5224
|
|
|
/** |
5225
|
|
|
* @see QueryWriter::wipe |
5226
|
|
|
*/ |
5227
|
|
|
public function wipe( $type ) |
5228
|
|
|
{ |
5229
|
|
|
$table = $this->esc( $type ); |
5230
|
|
|
|
5231
|
|
|
$this->adapter->exec( "TRUNCATE $table " ); |
5232
|
|
|
} |
5233
|
|
|
|
5234
|
|
|
/** |
5235
|
|
|
* @see QueryWriter::renameAssocTable |
5236
|
|
|
*/ |
5237
|
|
|
public function renameAssocTable( $from, $to = NULL ) |
5238
|
|
|
{ |
5239
|
|
|
self::renameAssociation( $from, $to ); |
5240
|
|
|
} |
5241
|
|
|
|
5242
|
|
|
/** |
5243
|
|
|
* @see QueryWriter::getAssocTable |
5244
|
|
|
*/ |
5245
|
|
|
public function getAssocTable( $types ) |
5246
|
|
|
{ |
5247
|
|
|
return self::getAssocTableFormat( $types ); |
5248
|
|
|
} |
5249
|
|
|
|
5250
|
|
|
/** |
5251
|
|
|
* Turns caching on or off. Default: off. |
5252
|
|
|
* If caching is turned on retrieval queries fired after eachother will |
5253
|
|
|
* use a result row cache. |
5254
|
|
|
* |
5255
|
|
|
* @param boolean |
5256
|
|
|
*/ |
5257
|
|
|
public function setUseCache( $yesNo ) |
5258
|
|
|
{ |
5259
|
|
|
$this->flushCache(); |
5260
|
|
|
|
5261
|
|
|
$this->flagUseCache = (bool) $yesNo; |
5262
|
|
|
} |
5263
|
|
|
|
5264
|
|
|
/** |
5265
|
|
|
* Flushes the Query Writer Cache. |
5266
|
|
|
* Clears the internal query cache array and returns its overall |
5267
|
|
|
* size. |
5268
|
|
|
* |
5269
|
|
|
* @return integer |
5270
|
|
|
*/ |
5271
|
|
|
public function flushCache( $newMaxCacheSizePerType = NULL ) |
5272
|
|
|
{ |
5273
|
|
|
if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) { |
5274
|
|
|
$this->maxCacheSizePerType = $newMaxCacheSizePerType; |
5275
|
|
|
} |
5276
|
|
|
$count = count( $this->cache, COUNT_RECURSIVE ); |
5277
|
|
|
$this->cache = array(); |
5278
|
|
|
return $count; |
5279
|
|
|
} |
5280
|
|
|
|
5281
|
|
|
/** |
5282
|
|
|
* @deprecated Use esc() instead. |
5283
|
|
|
* |
5284
|
|
|
* @param string $column column to be escaped |
5285
|
|
|
* @param boolean $noQuotes omit quotes |
5286
|
|
|
* |
5287
|
|
|
* @return string |
5288
|
|
|
*/ |
5289
|
|
|
public function safeColumn( $column, $noQuotes = FALSE ) |
5290
|
|
|
{ |
5291
|
|
|
return $this->esc( $column, $noQuotes ); |
5292
|
|
|
} |
5293
|
|
|
|
5294
|
|
|
/** |
5295
|
|
|
* @deprecated Use esc() instead. |
5296
|
|
|
* |
5297
|
|
|
* @param string $table table to be escaped |
5298
|
|
|
* @param boolean $noQuotes omit quotes |
5299
|
|
|
* |
5300
|
|
|
* @return string |
5301
|
|
|
*/ |
5302
|
|
|
public function safeTable( $table, $noQuotes = FALSE ) |
5303
|
|
|
{ |
5304
|
|
|
return $this->esc( $table, $noQuotes ); |
5305
|
|
|
} |
5306
|
|
|
|
5307
|
|
|
/** |
5308
|
|
|
* @see QueryWriter::inferFetchType |
5309
|
|
|
*/ |
5310
|
|
|
public function inferFetchType( $type, $property ) |
5311
|
|
|
{ |
5312
|
|
|
$type = $this->esc( $type, TRUE ); |
5313
|
|
|
$field = $this->esc( $property, TRUE ) . '_id'; |
5314
|
|
|
$keys = $this->getKeyMapForType( $type ); |
5315
|
|
|
|
5316
|
|
|
foreach( $keys as $key ) { |
5317
|
|
|
if ( |
5318
|
|
|
$key['from'] === $field |
5319
|
|
|
) return $key['table']; |
5320
|
|
|
} |
5321
|
|
|
return NULL; |
5322
|
|
|
} |
5323
|
|
|
|
5324
|
|
|
/** |
5325
|
|
|
* @see QueryWriter::addUniqueConstraint |
5326
|
|
|
*/ |
5327
|
|
|
public function addUniqueIndex( $type, $properties ) |
5328
|
|
|
{ |
5329
|
|
|
return $this->addUniqueConstraint( $type, $properties ); |
5330
|
|
|
} |
5331
|
|
|
} |
5332
|
|
|
} |
5333
|
|
|
|
5334
|
|
|
namespace RedBeanPHP\QueryWriter { |
5335
|
|
|
|
5336
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
5337
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
5338
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
5339
|
|
|
use RedBeanPHP\Adapter as Adapter; |
|
|
|
|
5340
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
5341
|
|
|
|
5342
|
|
|
/** |
5343
|
|
|
* RedBeanPHP MySQLWriter. |
5344
|
|
|
* This is a QueryWriter class for RedBeanPHP. |
5345
|
|
|
* This QueryWriter provides support for the MySQL/MariaDB database platform. |
5346
|
|
|
* |
5347
|
|
|
* @file RedBeanPHP/QueryWriter/MySQL.php |
5348
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
5349
|
|
|
* @license BSD/GPLv2 |
5350
|
|
|
* |
5351
|
|
|
* @copyright |
5352
|
|
|
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
5353
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
5354
|
|
|
* with this source code in the file license.txt. |
5355
|
|
|
*/ |
5356
|
|
|
class MySQL extends AQueryWriter implements QueryWriter |
|
|
|
|
5357
|
|
|
{ |
5358
|
|
|
/** |
5359
|
|
|
* Data types |
5360
|
|
|
*/ |
5361
|
|
|
const C_DATATYPE_BOOL = 0; |
5362
|
|
|
const C_DATATYPE_UINT32 = 2; |
5363
|
|
|
const C_DATATYPE_DOUBLE = 3; |
5364
|
|
|
const C_DATATYPE_TEXT7 = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible |
5365
|
|
|
const C_DATATYPE_TEXT8 = 5; |
5366
|
|
|
const C_DATATYPE_TEXT16 = 6; |
5367
|
|
|
const C_DATATYPE_TEXT32 = 7; |
5368
|
|
|
const C_DATATYPE_SPECIAL_DATE = 80; |
5369
|
|
|
const C_DATATYPE_SPECIAL_DATETIME = 81; |
5370
|
|
|
const C_DATATYPE_SPECIAL_POINT = 90; |
5371
|
|
|
const C_DATATYPE_SPECIAL_LINESTRING = 91; |
5372
|
|
|
const C_DATATYPE_SPECIAL_POLYGON = 92; |
5373
|
|
|
|
5374
|
|
|
const C_DATATYPE_SPECIFIED = 99; |
5375
|
|
|
|
5376
|
|
|
/** |
5377
|
|
|
* @var DBAdapter |
5378
|
|
|
*/ |
5379
|
|
|
protected $adapter; |
5380
|
|
|
|
5381
|
|
|
/** |
5382
|
|
|
* @var string |
5383
|
|
|
*/ |
5384
|
|
|
protected $quoteCharacter = '`'; |
5385
|
|
|
|
5386
|
|
|
/** |
5387
|
|
|
* @see AQueryWriter::getKeyMapForType |
5388
|
|
|
*/ |
5389
|
|
View Code Duplication |
protected function getKeyMapForType( $type ) |
5390
|
|
|
{ |
5391
|
|
|
$table = $this->esc( $type, TRUE ); |
5392
|
|
|
$keys = $this->adapter->get(' |
5393
|
|
|
SELECT |
5394
|
|
|
information_schema.key_column_usage.constraint_name AS `name`, |
5395
|
|
|
information_schema.key_column_usage.referenced_table_name AS `table`, |
5396
|
|
|
information_schema.key_column_usage.column_name AS `from`, |
5397
|
|
|
information_schema.key_column_usage.referenced_column_name AS `to`, |
5398
|
|
|
information_schema.referential_constraints.update_rule AS `on_update`, |
5399
|
|
|
information_schema.referential_constraints.delete_rule AS `on_delete` |
5400
|
|
|
FROM information_schema.key_column_usage |
5401
|
|
|
INNER JOIN information_schema.referential_constraints |
5402
|
|
|
ON ( |
5403
|
|
|
information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name |
5404
|
|
|
AND information_schema.referential_constraints.constraint_schema = information_schema.key_column_usage.constraint_schema |
5405
|
|
|
AND information_schema.referential_constraints.constraint_catalog = information_schema.key_column_usage.constraint_catalog |
5406
|
|
|
) |
5407
|
|
|
WHERE |
5408
|
|
|
information_schema.key_column_usage.table_schema IN ( SELECT DATABASE() ) |
5409
|
|
|
AND information_schema.key_column_usage.table_name = ? |
5410
|
|
|
AND information_schema.key_column_usage.constraint_name != \'PRIMARY\' |
5411
|
|
|
AND information_schema.key_column_usage.referenced_table_name IS NOT NULL |
5412
|
|
|
', array($table)); |
5413
|
|
|
$keyInfoList = array(); |
5414
|
|
|
foreach ( $keys as $k ) { |
5415
|
|
|
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); |
5416
|
|
|
$keyInfoList[$label] = array( |
5417
|
|
|
'name' => $k['name'], |
5418
|
|
|
'from' => $k['from'], |
5419
|
|
|
'table' => $k['table'], |
5420
|
|
|
'to' => $k['to'], |
5421
|
|
|
'on_update' => $k['on_update'], |
5422
|
|
|
'on_delete' => $k['on_delete'] |
5423
|
|
|
); |
5424
|
|
|
} |
5425
|
|
|
return $keyInfoList; |
5426
|
|
|
} |
5427
|
|
|
|
5428
|
|
|
/** |
5429
|
|
|
* Constructor |
5430
|
|
|
* |
5431
|
|
|
* @param Adapter $adapter Database Adapter |
5432
|
|
|
*/ |
5433
|
|
|
public function __construct( Adapter $adapter ) |
5434
|
|
|
{ |
5435
|
|
|
$this->typeno_sqltype = array( |
5436
|
|
|
MySQL::C_DATATYPE_BOOL => ' TINYINT(1) UNSIGNED ', |
5437
|
|
|
MySQL::C_DATATYPE_UINT32 => ' INT(11) UNSIGNED ', |
5438
|
|
|
MySQL::C_DATATYPE_DOUBLE => ' DOUBLE ', |
5439
|
|
|
MySQL::C_DATATYPE_TEXT7 => ' VARCHAR(191) ', |
5440
|
|
|
MYSQL::C_DATATYPE_TEXT8 => ' VARCHAR(255) ', |
5441
|
|
|
MySQL::C_DATATYPE_TEXT16 => ' TEXT ', |
5442
|
|
|
MySQL::C_DATATYPE_TEXT32 => ' LONGTEXT ', |
5443
|
|
|
MySQL::C_DATATYPE_SPECIAL_DATE => ' DATE ', |
5444
|
|
|
MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ', |
5445
|
|
|
MySQL::C_DATATYPE_SPECIAL_POINT => ' POINT ', |
5446
|
|
|
MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ', |
5447
|
|
|
MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ', |
5448
|
|
|
); |
5449
|
|
|
|
5450
|
|
|
$this->sqltype_typeno = array(); |
|
|
|
|
5451
|
|
|
|
5452
|
|
|
foreach ( $this->typeno_sqltype as $k => $v ) { |
5453
|
|
|
$this->sqltype_typeno[trim( strtolower( $v ) )] = $k; |
5454
|
|
|
} |
5455
|
|
|
|
5456
|
|
|
$this->adapter = $adapter; |
|
|
|
|
5457
|
|
|
|
5458
|
|
|
$this->encoding = $this->adapter->getDatabase()->getMysqlEncoding(); |
|
|
|
|
5459
|
|
|
} |
5460
|
|
|
|
5461
|
|
|
/** |
5462
|
|
|
* This method returns the datatype to be used for primary key IDS and |
5463
|
|
|
* foreign keys. Returns one if the data type constants. |
5464
|
|
|
* |
5465
|
|
|
* @return integer $const data type to be used for IDS. |
5466
|
|
|
*/ |
5467
|
|
|
public function getTypeForID() |
5468
|
|
|
{ |
5469
|
|
|
return self::C_DATATYPE_UINT32; |
5470
|
|
|
} |
5471
|
|
|
|
5472
|
|
|
/** |
5473
|
|
|
* @see QueryWriter::getTables |
5474
|
|
|
*/ |
5475
|
|
|
public function getTables() |
5476
|
|
|
{ |
5477
|
|
|
return $this->adapter->getCol( 'show tables' ); |
5478
|
|
|
} |
5479
|
|
|
|
5480
|
|
|
/** |
5481
|
|
|
* @see QueryWriter::createTable |
5482
|
|
|
*/ |
5483
|
|
|
public function createTable( $table ) |
5484
|
|
|
{ |
5485
|
|
|
$table = $this->esc( $table ); |
5486
|
|
|
|
5487
|
|
|
$encoding = $this->adapter->getDatabase()->getMysqlEncoding(); |
5488
|
|
|
$sql = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci "; |
5489
|
|
|
|
5490
|
|
|
$this->adapter->exec( $sql ); |
5491
|
|
|
} |
5492
|
|
|
|
5493
|
|
|
/** |
5494
|
|
|
* @see QueryWriter::getColumns |
5495
|
|
|
*/ |
5496
|
|
|
public function getColumns( $table ) |
5497
|
|
|
{ |
5498
|
|
|
$columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) ); |
5499
|
|
|
|
5500
|
|
|
$columns = array(); |
5501
|
|
|
foreach ( $columnsRaw as $r ) { |
5502
|
|
|
$columns[$r['Field']] = $r['Type']; |
5503
|
|
|
} |
5504
|
|
|
|
5505
|
|
|
return $columns; |
5506
|
|
|
} |
5507
|
|
|
|
5508
|
|
|
/** |
5509
|
|
|
* @see QueryWriter::scanType |
5510
|
|
|
*/ |
5511
|
|
|
public function scanType( $value, $flagSpecial = FALSE ) |
5512
|
|
|
{ |
5513
|
|
|
$this->svalue = $value; |
|
|
|
|
5514
|
|
|
|
5515
|
|
|
if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL; |
5516
|
|
|
if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7; |
5517
|
|
|
|
5518
|
|
|
if ( $flagSpecial ) { |
5519
|
|
|
if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { |
5520
|
|
|
return MySQL::C_DATATYPE_SPECIAL_DATE; |
5521
|
|
|
} |
5522
|
|
|
if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) { |
5523
|
|
|
return MySQL::C_DATATYPE_SPECIAL_DATETIME; |
5524
|
|
|
} |
5525
|
|
|
if ( preg_match( '/^POINT\(/', $value ) ) { |
5526
|
|
|
return MySQL::C_DATATYPE_SPECIAL_POINT; |
5527
|
|
|
} |
5528
|
|
|
if ( preg_match( '/^LINESTRING\(/', $value ) ) { |
5529
|
|
|
return MySQL::C_DATATYPE_SPECIAL_LINESTRING; |
5530
|
|
|
} |
5531
|
|
|
if ( preg_match( '/^POLYGON\(/', $value ) ) { |
5532
|
|
|
return MySQL::C_DATATYPE_SPECIAL_POLYGON; |
5533
|
|
|
} |
5534
|
|
|
} |
5535
|
|
|
|
5536
|
|
|
//setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?). |
5537
|
|
|
if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) { |
5538
|
|
|
return MySQL::C_DATATYPE_BOOL; |
5539
|
|
|
} |
5540
|
|
|
|
5541
|
|
|
if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE; |
5542
|
|
|
|
5543
|
|
|
if ( !$this->startsWithZeros( $value ) ) { |
5544
|
|
|
|
5545
|
|
|
if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) { |
5546
|
|
|
return MySQL::C_DATATYPE_UINT32; |
5547
|
|
|
} |
5548
|
|
|
|
5549
|
|
|
if ( is_numeric( $value ) ) { |
5550
|
|
|
return MySQL::C_DATATYPE_DOUBLE; |
5551
|
|
|
} |
5552
|
|
|
} |
5553
|
|
|
|
5554
|
|
|
if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) { |
5555
|
|
|
return MySQL::C_DATATYPE_TEXT7; |
5556
|
|
|
} |
5557
|
|
|
|
5558
|
|
|
if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) { |
5559
|
|
|
return MySQL::C_DATATYPE_TEXT8; |
5560
|
|
|
} |
5561
|
|
|
|
5562
|
|
|
if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) { |
5563
|
|
|
return MySQL::C_DATATYPE_TEXT16; |
5564
|
|
|
} |
5565
|
|
|
|
5566
|
|
|
return MySQL::C_DATATYPE_TEXT32; |
5567
|
|
|
} |
5568
|
|
|
|
5569
|
|
|
/** |
5570
|
|
|
* @see QueryWriter::code |
5571
|
|
|
*/ |
5572
|
|
View Code Duplication |
public function code( $typedescription, $includeSpecials = FALSE ) |
5573
|
|
|
{ |
5574
|
|
|
if ( isset( $this->sqltype_typeno[$typedescription] ) ) { |
5575
|
|
|
$r = $this->sqltype_typeno[$typedescription]; |
5576
|
|
|
} else { |
5577
|
|
|
$r = self::C_DATATYPE_SPECIFIED; |
5578
|
|
|
} |
5579
|
|
|
|
5580
|
|
|
if ( $includeSpecials ) { |
5581
|
|
|
return $r; |
5582
|
|
|
} |
5583
|
|
|
|
5584
|
|
|
if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { |
5585
|
|
|
return self::C_DATATYPE_SPECIFIED; |
5586
|
|
|
} |
5587
|
|
|
|
5588
|
|
|
return $r; |
5589
|
|
|
} |
5590
|
|
|
|
5591
|
|
|
/** |
5592
|
|
|
* @see QueryWriter::addUniqueIndex |
5593
|
|
|
*/ |
5594
|
|
View Code Duplication |
public function addUniqueConstraint( $type, $properties ) |
5595
|
|
|
{ |
5596
|
|
|
$tableNoQ = $this->esc( $type, TRUE ); |
5597
|
|
|
$columns = array(); |
5598
|
|
|
foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column ); |
5599
|
|
|
$table = $this->esc( $type ); |
5600
|
|
|
sort( $columns ); // Else we get multiple indexes due to order-effects |
5601
|
|
|
$name = 'UQ_' . sha1( implode( ',', $columns ) ); |
5602
|
|
|
try { |
5603
|
|
|
$sql = "ALTER TABLE $table |
5604
|
|
|
ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")"; |
5605
|
|
|
$this->adapter->exec( $sql ); |
5606
|
|
|
} catch ( SQLException $e ) { |
5607
|
|
|
//do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways! |
5608
|
|
|
return FALSE; |
5609
|
|
|
} |
5610
|
|
|
return TRUE; |
5611
|
|
|
} |
5612
|
|
|
|
5613
|
|
|
/** |
5614
|
|
|
* @see QueryWriter::addIndex |
5615
|
|
|
*/ |
5616
|
|
View Code Duplication |
public function addIndex( $type, $name, $property ) |
5617
|
|
|
{ |
5618
|
|
|
try { |
5619
|
|
|
$table = $this->esc( $type ); |
5620
|
|
|
$name = preg_replace( '/\W/', '', $name ); |
5621
|
|
|
$column = $this->esc( $property ); |
5622
|
|
|
$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " ); |
5623
|
|
|
return TRUE; |
5624
|
|
|
} catch ( SQLException $e ) { |
5625
|
|
|
return FALSE; |
5626
|
|
|
} |
5627
|
|
|
} |
5628
|
|
|
|
5629
|
|
|
/** |
5630
|
|
|
* @see QueryWriter::addFK |
5631
|
|
|
*/ |
5632
|
|
|
public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE ) |
5633
|
|
|
{ |
5634
|
|
|
$table = $this->esc( $type ); |
5635
|
|
|
$targetTable = $this->esc( $targetType ); |
5636
|
|
|
$targetTableNoQ = $this->esc( $targetType, TRUE ); |
5637
|
|
|
$field = $this->esc( $property ); |
5638
|
|
|
$fieldNoQ = $this->esc( $property, TRUE ); |
5639
|
|
|
$targetField = $this->esc( $targetProperty ); |
5640
|
|
|
$targetFieldNoQ = $this->esc( $targetProperty, TRUE ); |
5641
|
|
|
$tableNoQ = $this->esc( $type, TRUE ); |
5642
|
|
|
$fieldNoQ = $this->esc( $property, TRUE ); |
5643
|
|
|
if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE; |
5644
|
|
|
|
5645
|
|
|
//Widen the column if it's incapable of representing a foreign key (at least INT). |
5646
|
|
|
$columns = $this->getColumns( $tableNoQ ); |
5647
|
|
|
$idType = $this->getTypeForID(); |
5648
|
|
|
if ( $this->code( $columns[$fieldNoQ] ) !== $idType ) { |
5649
|
|
|
$this->widenColumn( $type, $property, $idType ); |
5650
|
|
|
} |
5651
|
|
|
|
5652
|
|
|
$fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ); |
5653
|
|
|
$cName = 'c_'.$fkName; |
5654
|
|
|
try { |
5655
|
|
|
$this->adapter->exec( " |
5656
|
|
|
ALTER TABLE {$table} |
5657
|
|
|
ADD CONSTRAINT $cName |
5658
|
|
|
FOREIGN KEY $fkName ( {$fieldNoQ} ) REFERENCES {$targetTableNoQ} |
5659
|
|
|
({$targetFieldNoQ}) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';'); |
5660
|
|
|
} catch ( SQLException $e ) { |
5661
|
|
|
// Failure of fk-constraints is not a problem |
5662
|
|
|
} |
5663
|
|
|
} |
5664
|
|
|
|
5665
|
|
|
/** |
5666
|
|
|
* @see QueryWriter::sqlStateIn |
5667
|
|
|
*/ |
5668
|
|
|
public function sqlStateIn( $state, $list ) |
5669
|
|
|
{ |
5670
|
|
|
$stateMap = array( |
5671
|
|
|
'42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, |
5672
|
|
|
'42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, |
5673
|
|
|
'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION |
5674
|
|
|
); |
5675
|
|
|
|
5676
|
|
|
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); |
5677
|
|
|
} |
5678
|
|
|
|
5679
|
|
|
/** |
5680
|
|
|
* @see QueryWriter::wipeAll |
5681
|
|
|
*/ |
5682
|
|
View Code Duplication |
public function wipeAll() |
5683
|
|
|
{ |
5684
|
|
|
$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' ); |
5685
|
|
|
|
5686
|
|
|
foreach ( $this->getTables() as $t ) { |
5687
|
|
|
try { |
5688
|
|
|
$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); |
5689
|
|
|
} catch ( SQLException $e ) { |
|
|
|
|
5690
|
|
|
} |
5691
|
|
|
|
5692
|
|
|
try { |
5693
|
|
|
$this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); |
5694
|
|
|
} catch ( SQLException $e ) { |
|
|
|
|
5695
|
|
|
} |
5696
|
|
|
} |
5697
|
|
|
|
5698
|
|
|
$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' ); |
5699
|
|
|
} |
5700
|
|
|
} |
5701
|
|
|
} |
5702
|
|
|
|
5703
|
|
|
namespace RedBeanPHP\QueryWriter { |
5704
|
|
|
|
5705
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
5706
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
5707
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
5708
|
|
|
use RedBeanPHP\Adapter as Adapter; |
|
|
|
|
5709
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
5710
|
|
|
|
5711
|
|
|
/** |
5712
|
|
|
* RedBeanPHP SQLiteWriter with support for SQLite types |
5713
|
|
|
* This is a QueryWriter class for RedBeanPHP. |
5714
|
|
|
* This QueryWriter provides support for the SQLite database platform. |
5715
|
|
|
* |
5716
|
|
|
* @file RedBeanPHP/QueryWriter/SQLiteT.php |
5717
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
5718
|
|
|
* @license BSD/GPLv2 |
5719
|
|
|
* |
5720
|
|
|
* @copyright |
5721
|
|
|
* (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
5722
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
5723
|
|
|
* with this source code in the file license.txt. |
5724
|
|
|
*/ |
5725
|
|
|
class SQLiteT extends AQueryWriter implements QueryWriter |
|
|
|
|
5726
|
|
|
{ |
5727
|
|
|
/** |
5728
|
|
|
* Data types |
5729
|
|
|
*/ |
5730
|
|
|
const C_DATATYPE_INTEGER = 0; |
5731
|
|
|
const C_DATATYPE_NUMERIC = 1; |
5732
|
|
|
const C_DATATYPE_TEXT = 2; |
5733
|
|
|
const C_DATATYPE_SPECIFIED = 99; |
5734
|
|
|
|
5735
|
|
|
/** |
5736
|
|
|
* @var DBAdapter |
5737
|
|
|
*/ |
5738
|
|
|
protected $adapter; |
5739
|
|
|
|
5740
|
|
|
/** |
5741
|
|
|
* @var string |
5742
|
|
|
*/ |
5743
|
|
|
protected $quoteCharacter = '`'; |
5744
|
|
|
|
5745
|
|
|
/** |
5746
|
|
|
* Gets all information about a table (from a type). |
5747
|
|
|
* |
5748
|
|
|
* Format: |
5749
|
|
|
* array( |
5750
|
|
|
* name => name of the table |
5751
|
|
|
* columns => array( name => datatype ) |
5752
|
|
|
* indexes => array() raw index information rows from PRAGMA query |
5753
|
|
|
* keys => array() raw key information rows from PRAGMA query |
5754
|
|
|
* ) |
5755
|
|
|
* |
5756
|
|
|
* @param string $type type you want to get info of |
5757
|
|
|
* |
5758
|
|
|
* @return array $info |
5759
|
|
|
*/ |
5760
|
|
|
protected function getTable( $type ) |
5761
|
|
|
{ |
5762
|
|
|
$tableName = $this->esc( $type, TRUE ); |
5763
|
|
|
$columns = $this->getColumns( $type ); |
5764
|
|
|
$indexes = $this->getIndexes( $type ); |
5765
|
|
|
$keys = $this->getKeyMapForType( $type ); |
5766
|
|
|
|
5767
|
|
|
$table = array( |
5768
|
|
|
'columns' => $columns, |
5769
|
|
|
'indexes' => $indexes, |
5770
|
|
|
'keys' => $keys, |
5771
|
|
|
'name' => $tableName |
5772
|
|
|
); |
5773
|
|
|
|
5774
|
|
|
$this->tableArchive[$tableName] = $table; |
|
|
|
|
5775
|
|
|
|
5776
|
|
|
return $table; |
5777
|
|
|
} |
5778
|
|
|
|
5779
|
|
|
/** |
5780
|
|
|
* Puts a table. Updates the table structure. |
5781
|
|
|
* In SQLite we can't change columns, drop columns, change or add foreign keys so we |
5782
|
|
|
* have a table-rebuild function. You simply load your table with getTable(), modify it and |
5783
|
|
|
* then store it with putTable()... |
5784
|
|
|
* |
5785
|
|
|
* @param array $tableMap information array |
5786
|
|
|
*/ |
5787
|
|
|
protected function putTable( $tableMap ) |
5788
|
|
|
{ |
5789
|
|
|
$table = $tableMap['name']; |
5790
|
|
|
$q = array(); |
5791
|
|
|
$q[] = "DROP TABLE IF EXISTS tmp_backup;"; |
5792
|
|
|
|
5793
|
|
|
$oldColumnNames = array_keys( $this->getColumns( $table ) ); |
5794
|
|
|
|
5795
|
|
|
foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`"; |
5796
|
|
|
|
5797
|
|
|
$q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");"; |
5798
|
|
|
$q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;"; |
5799
|
|
|
$q[] = "PRAGMA foreign_keys = 0 "; |
5800
|
|
|
$q[] = "DROP TABLE `$table`;"; |
5801
|
|
|
|
5802
|
|
|
$newTableDefStr = ''; |
5803
|
|
|
foreach ( $tableMap['columns'] as $column => $type ) { |
5804
|
|
|
if ( $column != 'id' ) { |
5805
|
|
|
$newTableDefStr .= ",`$column` $type"; |
5806
|
|
|
} |
5807
|
|
|
} |
5808
|
|
|
|
5809
|
|
|
$fkDef = ''; |
5810
|
|
|
foreach ( $tableMap['keys'] as $key ) { |
5811
|
|
|
$fkDef .= ", FOREIGN KEY(`{$key['from']}`) |
5812
|
|
|
REFERENCES `{$key['table']}`(`{$key['to']}`) |
5813
|
|
|
ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}"; |
5814
|
|
|
} |
5815
|
|
|
|
5816
|
|
|
$q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr $fkDef );"; |
5817
|
|
|
|
5818
|
|
|
foreach ( $tableMap['indexes'] as $name => $index ) { |
5819
|
|
|
if ( strpos( $name, 'UQ_' ) === 0 ) { |
5820
|
|
|
$cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) ); |
5821
|
|
|
foreach ( $cols as $k => $v ) $cols[$k] = "`$v`"; |
5822
|
|
|
$q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")"; |
5823
|
|
|
} else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) "; |
5824
|
|
|
} |
5825
|
|
|
|
5826
|
|
|
$q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;"; |
5827
|
|
|
$q[] = "DROP TABLE tmp_backup;"; |
5828
|
|
|
$q[] = "PRAGMA foreign_keys = 1 "; |
5829
|
|
|
|
5830
|
|
|
foreach ( $q as $sq ) $this->adapter->exec( $sq ); |
5831
|
|
|
} |
5832
|
|
|
|
5833
|
|
|
/** |
5834
|
|
|
* Returns the indexes for type $type. |
5835
|
|
|
* |
5836
|
|
|
* @param string $type |
5837
|
|
|
* |
5838
|
|
|
* @return array $indexInfo index information |
5839
|
|
|
*/ |
5840
|
|
|
protected function getIndexes( $type ) |
5841
|
|
|
{ |
5842
|
|
|
$table = $this->esc( $type, TRUE ); |
5843
|
|
|
$indexes = $this->adapter->get( "PRAGMA index_list('$table')" ); |
5844
|
|
|
|
5845
|
|
|
$indexInfoList = array(); |
5846
|
|
|
foreach ( $indexes as $i ) { |
5847
|
|
|
$indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " ); |
5848
|
|
|
|
5849
|
|
|
$indexInfoList[$i['name']]['unique'] = $i['unique']; |
5850
|
|
|
} |
5851
|
|
|
|
5852
|
|
|
return $indexInfoList; |
5853
|
|
|
} |
5854
|
|
|
|
5855
|
|
|
/** |
5856
|
|
|
* Adds a foreign key to a type |
5857
|
|
|
* |
5858
|
|
|
* @param string $type type you want to modify table of |
5859
|
|
|
* @param string $targetType target type |
5860
|
|
|
* @param string $field field of the type that needs to get the fk |
|
|
|
|
5861
|
|
|
* @param string $targetField field where the fk needs to point to |
|
|
|
|
5862
|
|
|
* @param integer $buildopt 0 = NO ACTION, 1 = ON DELETE CASCADE |
|
|
|
|
5863
|
|
|
* |
5864
|
|
|
* @return boolean $didIt |
5865
|
|
|
* |
5866
|
|
|
* @note: cant put this in try-catch because that can hide the fact |
5867
|
|
|
* that database has been damaged. |
5868
|
|
|
*/ |
5869
|
|
|
protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE ) |
5870
|
|
|
{ |
5871
|
|
|
$table = $this->esc( $type, TRUE ); |
5872
|
|
|
$targetTable = $this->esc( $targetType, TRUE ); |
5873
|
|
|
$column = $this->esc( $property, TRUE ); |
5874
|
|
|
$targetColumn = $this->esc( $targetProperty, TRUE ); |
5875
|
|
|
|
5876
|
|
|
$tables = $this->getTables(); |
5877
|
|
|
if ( !in_array( $targetTable, $tables ) ) return FALSE; |
5878
|
|
|
|
5879
|
|
|
if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE; |
5880
|
|
|
$t = $this->getTable( $table ); |
5881
|
|
|
$consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' ); |
5882
|
|
|
$label = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn; |
5883
|
|
|
$t['keys'][$label] = array( |
5884
|
|
|
'table' => $targetTable, |
5885
|
|
|
'from' => $column, |
5886
|
|
|
'to' => $targetColumn, |
5887
|
|
|
'on_update' => $consSQL, |
5888
|
|
|
'on_delete' => $consSQL |
5889
|
|
|
); |
5890
|
|
|
$this->putTable( $t ); |
5891
|
|
|
return TRUE; |
5892
|
|
|
} |
5893
|
|
|
|
5894
|
|
|
/** |
5895
|
|
|
* @see AQueryWriter::getKeyMapForType |
5896
|
|
|
*/ |
5897
|
|
View Code Duplication |
protected function getKeyMapForType( $type ) |
5898
|
|
|
{ |
5899
|
|
|
$table = $this->esc( $type, TRUE ); |
5900
|
|
|
$keys = $this->adapter->get( "PRAGMA foreign_key_list('$table')" ); |
5901
|
|
|
$keyInfoList = array(); |
5902
|
|
|
foreach ( $keys as $k ) { |
5903
|
|
|
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); |
5904
|
|
|
$keyInfoList[$label] = array( |
5905
|
|
|
'name' => $label, |
5906
|
|
|
'from' => $k['from'], |
5907
|
|
|
'table' => $k['table'], |
5908
|
|
|
'to' => $k['to'], |
5909
|
|
|
'on_update' => $k['on_update'], |
5910
|
|
|
'on_delete' => $k['on_delete'] |
5911
|
|
|
); |
5912
|
|
|
} |
5913
|
|
|
return $keyInfoList; |
5914
|
|
|
} |
5915
|
|
|
|
5916
|
|
|
/** |
5917
|
|
|
* Constructor |
5918
|
|
|
* |
5919
|
|
|
* @param Adapter $adapter Database Adapter |
5920
|
|
|
*/ |
5921
|
|
|
public function __construct( Adapter $adapter ) |
5922
|
|
|
{ |
5923
|
|
|
$this->typeno_sqltype = array( |
5924
|
|
|
SQLiteT::C_DATATYPE_INTEGER => 'INTEGER', |
5925
|
|
|
SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC', |
5926
|
|
|
SQLiteT::C_DATATYPE_TEXT => 'TEXT', |
5927
|
|
|
); |
5928
|
|
|
|
5929
|
|
|
$this->sqltype_typeno = array(); |
|
|
|
|
5930
|
|
|
|
5931
|
|
|
foreach ( $this->typeno_sqltype as $k => $v ) { |
5932
|
|
|
$this->sqltype_typeno[$v] = $k; |
5933
|
|
|
} |
5934
|
|
|
|
5935
|
|
|
$this->adapter = $adapter; |
|
|
|
|
5936
|
|
|
} |
5937
|
|
|
|
5938
|
|
|
/** |
5939
|
|
|
* This method returns the datatype to be used for primary key IDS and |
5940
|
|
|
* foreign keys. Returns one if the data type constants. |
5941
|
|
|
* |
5942
|
|
|
* @return integer $const data type to be used for IDS. |
5943
|
|
|
*/ |
5944
|
|
|
public function getTypeForID() |
5945
|
|
|
{ |
5946
|
|
|
return self::C_DATATYPE_INTEGER; |
5947
|
|
|
} |
5948
|
|
|
|
5949
|
|
|
/** |
5950
|
|
|
* @see QueryWriter::scanType |
5951
|
|
|
*/ |
5952
|
|
|
public function scanType( $value, $flagSpecial = FALSE ) |
5953
|
|
|
{ |
5954
|
|
|
$this->svalue = $value; |
|
|
|
|
5955
|
|
|
|
5956
|
|
|
if ( $value === NULL ) return self::C_DATATYPE_INTEGER; |
5957
|
|
|
if ( $value === INF ) return self::C_DATATYPE_TEXT; |
5958
|
|
|
|
5959
|
|
|
if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; |
5960
|
|
|
|
5961
|
|
|
if ( $value === TRUE || $value === FALSE ) return self::C_DATATYPE_INTEGER; |
5962
|
|
|
|
5963
|
|
|
if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER; |
5964
|
|
|
|
5965
|
|
|
if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648) |
5966
|
|
|
|| preg_match( '/\d{4}\-\d\d\-\d\d/', $value ) |
5967
|
|
|
|| preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value ) |
5968
|
|
|
) { |
5969
|
|
|
return self::C_DATATYPE_NUMERIC; |
5970
|
|
|
} |
5971
|
|
|
|
5972
|
|
|
return self::C_DATATYPE_TEXT; |
5973
|
|
|
} |
5974
|
|
|
|
5975
|
|
|
/** |
5976
|
|
|
* @see QueryWriter::addColumn |
5977
|
|
|
*/ |
5978
|
|
|
public function addColumn( $table, $column, $type ) |
5979
|
|
|
{ |
5980
|
|
|
$column = $this->check( $column ); |
5981
|
|
|
$table = $this->check( $table ); |
5982
|
|
|
$type = $this->typeno_sqltype[$type]; |
5983
|
|
|
|
5984
|
|
|
$this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " ); |
5985
|
|
|
} |
5986
|
|
|
|
5987
|
|
|
/** |
5988
|
|
|
* @see QueryWriter::code |
5989
|
|
|
*/ |
5990
|
|
|
public function code( $typedescription, $includeSpecials = FALSE ) |
5991
|
|
|
{ |
5992
|
|
|
$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 ); |
5993
|
|
|
|
5994
|
|
|
return $r; |
5995
|
|
|
} |
5996
|
|
|
|
5997
|
|
|
/** |
5998
|
|
|
* @see QueryWriter::widenColumn |
5999
|
|
|
*/ |
6000
|
|
|
public function widenColumn( $type, $column, $datatype ) |
6001
|
|
|
{ |
6002
|
|
|
$t = $this->getTable( $type ); |
6003
|
|
|
|
6004
|
|
|
$t['columns'][$column] = $this->typeno_sqltype[$datatype]; |
6005
|
|
|
|
6006
|
|
|
$this->putTable( $t ); |
6007
|
|
|
} |
6008
|
|
|
|
6009
|
|
|
/** |
6010
|
|
|
* @see QueryWriter::getTables(); |
6011
|
|
|
*/ |
6012
|
|
|
public function getTables() |
6013
|
|
|
{ |
6014
|
|
|
return $this->adapter->getCol( "SELECT name FROM sqlite_master |
6015
|
|
|
WHERE type='table' AND name!='sqlite_sequence';" ); |
6016
|
|
|
} |
6017
|
|
|
|
6018
|
|
|
/** |
6019
|
|
|
* @see QueryWriter::createTable |
6020
|
|
|
*/ |
6021
|
|
|
public function createTable( $table ) |
6022
|
|
|
{ |
6023
|
|
|
$table = $this->esc( $table ); |
6024
|
|
|
|
6025
|
|
|
$sql = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) "; |
6026
|
|
|
|
6027
|
|
|
$this->adapter->exec( $sql ); |
6028
|
|
|
} |
6029
|
|
|
|
6030
|
|
|
/** |
6031
|
|
|
* @see QueryWriter::getColumns |
6032
|
|
|
*/ |
6033
|
|
View Code Duplication |
public function getColumns( $table ) |
6034
|
|
|
{ |
6035
|
|
|
$table = $this->esc( $table, TRUE ); |
6036
|
|
|
|
6037
|
|
|
$columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" ); |
6038
|
|
|
|
6039
|
|
|
$columns = array(); |
6040
|
|
|
foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type']; |
6041
|
|
|
|
6042
|
|
|
return $columns; |
6043
|
|
|
} |
6044
|
|
|
|
6045
|
|
|
/** |
6046
|
|
|
* @see QueryWriter::addUniqueIndex |
6047
|
|
|
*/ |
6048
|
|
|
public function addUniqueConstraint( $type, $properties ) |
6049
|
|
|
{ |
6050
|
|
|
$tableNoQ = $this->esc( $type, TRUE ); |
6051
|
|
|
$name = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties ); |
6052
|
|
|
$t = $this->getTable( $type ); |
6053
|
|
|
$t['indexes'][$name] = array( 'name' => $name ); |
6054
|
|
|
try { |
6055
|
|
|
$this->putTable( $t ); |
6056
|
|
|
} catch( SQLException $e ) { |
6057
|
|
|
return FALSE; |
6058
|
|
|
} |
6059
|
|
|
return TRUE; |
6060
|
|
|
} |
6061
|
|
|
|
6062
|
|
|
/** |
6063
|
|
|
* @see QueryWriter::sqlStateIn |
6064
|
|
|
*/ |
6065
|
|
View Code Duplication |
public function sqlStateIn( $state, $list ) |
6066
|
|
|
{ |
6067
|
|
|
$stateMap = array( |
6068
|
|
|
'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, |
6069
|
|
|
'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION |
6070
|
|
|
); |
6071
|
|
|
|
6072
|
|
|
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); |
6073
|
|
|
} |
6074
|
|
|
|
6075
|
|
|
/** |
6076
|
|
|
* @see QueryWriter::addIndex |
6077
|
|
|
*/ |
6078
|
|
|
public function addIndex( $type, $name, $column ) |
6079
|
|
|
{ |
6080
|
|
|
$columns = $this->getColumns( $type ); |
6081
|
|
|
if ( !isset( $columns[$column] ) ) return FALSE; |
6082
|
|
|
|
6083
|
|
|
$table = $this->esc( $type ); |
6084
|
|
|
$name = preg_replace( '/\W/', '', $name ); |
6085
|
|
|
$column = $this->esc( $column, TRUE ); |
6086
|
|
|
|
6087
|
|
|
try { |
6088
|
|
|
$t = $this->getTable( $type ); |
6089
|
|
|
$t['indexes'][$name] = array( 'name' => $column ); |
6090
|
|
|
$this->putTable( $t ); |
6091
|
|
|
return TRUE; |
6092
|
|
|
} catch( SQLException $exception ) { |
6093
|
|
|
return FALSE; |
6094
|
|
|
} |
6095
|
|
|
} |
6096
|
|
|
|
6097
|
|
|
/** |
6098
|
|
|
* @see QueryWriter::wipe |
6099
|
|
|
*/ |
6100
|
|
|
public function wipe( $type ) |
6101
|
|
|
{ |
6102
|
|
|
$table = $this->esc( $type ); |
6103
|
|
|
|
6104
|
|
|
$this->adapter->exec( "DELETE FROM $table " ); |
6105
|
|
|
} |
6106
|
|
|
|
6107
|
|
|
/** |
6108
|
|
|
* @see QueryWriter::addFK |
6109
|
|
|
*/ |
6110
|
|
|
public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE ) |
6111
|
|
|
{ |
6112
|
|
|
return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep ); |
6113
|
|
|
} |
6114
|
|
|
|
6115
|
|
|
/** |
6116
|
|
|
* @see QueryWriter::wipeAll |
6117
|
|
|
*/ |
6118
|
|
View Code Duplication |
public function wipeAll() |
6119
|
|
|
{ |
6120
|
|
|
$this->adapter->exec( 'PRAGMA foreign_keys = 0 ' ); |
6121
|
|
|
|
6122
|
|
|
foreach ( $this->getTables() as $t ) { |
6123
|
|
|
try { |
6124
|
|
|
$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); |
6125
|
|
|
} catch ( SQLException $e ) { |
|
|
|
|
6126
|
|
|
} |
6127
|
|
|
|
6128
|
|
|
try { |
6129
|
|
|
$this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); |
6130
|
|
|
} catch ( SQLException $e ) { |
|
|
|
|
6131
|
|
|
} |
6132
|
|
|
} |
6133
|
|
|
|
6134
|
|
|
$this->adapter->exec( 'PRAGMA foreign_keys = 1 ' ); |
6135
|
|
|
} |
6136
|
|
|
} |
6137
|
|
|
} |
6138
|
|
|
|
6139
|
|
|
namespace RedBeanPHP\QueryWriter { |
6140
|
|
|
|
6141
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
6142
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
6143
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
6144
|
|
|
use RedBeanPHP\Adapter as Adapter; |
|
|
|
|
6145
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
6146
|
|
|
|
6147
|
|
|
/** |
6148
|
|
|
* RedBeanPHP PostgreSQL Query Writer. |
6149
|
|
|
* This is a QueryWriter class for RedBeanPHP. |
6150
|
|
|
* This QueryWriter provides support for the PostgreSQL database platform. |
6151
|
|
|
* |
6152
|
|
|
* @file RedBeanPHP/QueryWriter/PostgreSQL.php |
6153
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
6154
|
|
|
* @license BSD/GPLv2 |
6155
|
|
|
* |
6156
|
|
|
* @copyright |
6157
|
|
|
* (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
6158
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
6159
|
|
|
* with this source code in the file license.txt. |
6160
|
|
|
*/ |
6161
|
|
|
class PostgreSQL extends AQueryWriter implements QueryWriter |
|
|
|
|
6162
|
|
|
{ |
6163
|
|
|
/** |
6164
|
|
|
* Data types |
6165
|
|
|
*/ |
6166
|
|
|
const C_DATATYPE_INTEGER = 0; |
6167
|
|
|
const C_DATATYPE_DOUBLE = 1; |
6168
|
|
|
const C_DATATYPE_TEXT = 3; |
6169
|
|
|
const C_DATATYPE_SPECIAL_DATE = 80; |
6170
|
|
|
const C_DATATYPE_SPECIAL_DATETIME = 81; |
6171
|
|
|
const C_DATATYPE_SPECIAL_POINT = 90; |
6172
|
|
|
const C_DATATYPE_SPECIAL_LSEG = 91; |
6173
|
|
|
const C_DATATYPE_SPECIAL_CIRCLE = 92; |
6174
|
|
|
const C_DATATYPE_SPECIAL_MONEY = 93; |
6175
|
|
|
const C_DATATYPE_SPECIAL_POLYGON = 94; |
6176
|
|
|
const C_DATATYPE_SPECIFIED = 99; |
6177
|
|
|
|
6178
|
|
|
/** |
6179
|
|
|
* @var DBAdapter |
6180
|
|
|
*/ |
6181
|
|
|
protected $adapter; |
6182
|
|
|
|
6183
|
|
|
/** |
6184
|
|
|
* @var string |
6185
|
|
|
*/ |
6186
|
|
|
protected $quoteCharacter = '"'; |
6187
|
|
|
|
6188
|
|
|
/** |
6189
|
|
|
* @var string |
6190
|
|
|
*/ |
6191
|
|
|
protected $defaultValue = 'DEFAULT'; |
6192
|
|
|
|
6193
|
|
|
/** |
6194
|
|
|
* Returns the insert suffix SQL Snippet |
6195
|
|
|
* |
6196
|
|
|
* @param string $table table |
6197
|
|
|
* |
6198
|
|
|
* @return string $sql SQL Snippet |
6199
|
|
|
*/ |
6200
|
|
|
protected function getInsertSuffix( $table ) |
6201
|
|
|
{ |
6202
|
|
|
return 'RETURNING id '; |
6203
|
|
|
} |
6204
|
|
|
|
6205
|
|
|
/** |
6206
|
|
|
* @see AQueryWriter::getKeyMapForType |
6207
|
|
|
*/ |
6208
|
|
View Code Duplication |
protected function getKeyMapForType( $type ) |
6209
|
|
|
{ |
6210
|
|
|
$table = $this->esc( $type, TRUE ); |
6211
|
|
|
$keys = $this->adapter->get( ' |
6212
|
|
|
SELECT |
6213
|
|
|
information_schema.key_column_usage.constraint_name AS "name", |
6214
|
|
|
information_schema.key_column_usage.column_name AS "from", |
6215
|
|
|
information_schema.constraint_table_usage.table_name AS "table", |
6216
|
|
|
information_schema.constraint_column_usage.column_name AS "to", |
6217
|
|
|
information_schema.referential_constraints.update_rule AS "on_update", |
6218
|
|
|
information_schema.referential_constraints.delete_rule AS "on_delete" |
6219
|
|
|
FROM information_schema.key_column_usage |
6220
|
|
|
INNER JOIN information_schema.constraint_table_usage |
6221
|
|
|
ON ( |
6222
|
|
|
information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name |
6223
|
|
|
AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema |
6224
|
|
|
AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog |
6225
|
|
|
) |
6226
|
|
|
INNER JOIN information_schema.constraint_column_usage |
6227
|
|
|
ON ( |
6228
|
|
|
information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name |
6229
|
|
|
AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema |
6230
|
|
|
AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog |
6231
|
|
|
) |
6232
|
|
|
INNER JOIN information_schema.referential_constraints |
6233
|
|
|
ON ( |
6234
|
|
|
information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name |
6235
|
|
|
AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema |
6236
|
|
|
AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog |
6237
|
|
|
) |
6238
|
|
|
WHERE |
6239
|
|
|
information_schema.key_column_usage.table_catalog = current_database() |
6240
|
|
|
AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) ) |
6241
|
|
|
AND information_schema.key_column_usage.table_name = ? |
6242
|
|
|
', array( $type ) ); |
6243
|
|
|
$keyInfoList = array(); |
6244
|
|
|
foreach ( $keys as $k ) { |
6245
|
|
|
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); |
6246
|
|
|
$keyInfoList[$label] = array( |
6247
|
|
|
'name' => $k['name'], |
6248
|
|
|
'from' => $k['from'], |
6249
|
|
|
'table' => $k['table'], |
6250
|
|
|
'to' => $k['to'], |
6251
|
|
|
'on_update' => $k['on_update'], |
6252
|
|
|
'on_delete' => $k['on_delete'] |
6253
|
|
|
); |
6254
|
|
|
} |
6255
|
|
|
return $keyInfoList; |
6256
|
|
|
} |
6257
|
|
|
|
6258
|
|
|
/** |
6259
|
|
|
* Constructor |
6260
|
|
|
* |
6261
|
|
|
* @param Adapter $adapter Database Adapter |
6262
|
|
|
*/ |
6263
|
|
|
public function __construct( Adapter $adapter ) |
6264
|
|
|
{ |
6265
|
|
|
$this->typeno_sqltype = array( |
6266
|
|
|
self::C_DATATYPE_INTEGER => ' integer ', |
6267
|
|
|
self::C_DATATYPE_DOUBLE => ' double precision ', |
6268
|
|
|
self::C_DATATYPE_TEXT => ' text ', |
6269
|
|
|
self::C_DATATYPE_SPECIAL_DATE => ' date ', |
6270
|
|
|
self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ', |
6271
|
|
|
self::C_DATATYPE_SPECIAL_POINT => ' point ', |
6272
|
|
|
self::C_DATATYPE_SPECIAL_LSEG => ' lseg ', |
6273
|
|
|
self::C_DATATYPE_SPECIAL_CIRCLE => ' circle ', |
6274
|
|
|
self::C_DATATYPE_SPECIAL_MONEY => ' money ', |
6275
|
|
|
self::C_DATATYPE_SPECIAL_POLYGON => ' polygon ', |
6276
|
|
|
); |
6277
|
|
|
|
6278
|
|
|
$this->sqltype_typeno = array(); |
|
|
|
|
6279
|
|
|
|
6280
|
|
|
foreach ( $this->typeno_sqltype as $k => $v ) { |
6281
|
|
|
$this->sqltype_typeno[trim( strtolower( $v ) )] = $k; |
6282
|
|
|
} |
6283
|
|
|
|
6284
|
|
|
$this->adapter = $adapter; |
|
|
|
|
6285
|
|
|
} |
6286
|
|
|
|
6287
|
|
|
/** |
6288
|
|
|
* This method returns the datatype to be used for primary key IDS and |
6289
|
|
|
* foreign keys. Returns one if the data type constants. |
6290
|
|
|
* |
6291
|
|
|
* @return integer $const data type to be used for IDS. |
6292
|
|
|
*/ |
6293
|
|
|
public function getTypeForID() |
6294
|
|
|
{ |
6295
|
|
|
return self::C_DATATYPE_INTEGER; |
6296
|
|
|
} |
6297
|
|
|
|
6298
|
|
|
/** |
6299
|
|
|
* @see QueryWriter::getTables |
6300
|
|
|
*/ |
6301
|
|
|
public function getTables() |
6302
|
|
|
{ |
6303
|
|
|
return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' ); |
6304
|
|
|
} |
6305
|
|
|
|
6306
|
|
|
/** |
6307
|
|
|
* @see QueryWriter::createTable |
6308
|
|
|
*/ |
6309
|
|
|
public function createTable( $table ) |
6310
|
|
|
{ |
6311
|
|
|
$table = $this->esc( $table ); |
6312
|
|
|
|
6313
|
|
|
$this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " ); |
6314
|
|
|
} |
6315
|
|
|
|
6316
|
|
|
/** |
6317
|
|
|
* @see QueryWriter::getColumns |
6318
|
|
|
*/ |
6319
|
|
View Code Duplication |
public function getColumns( $table ) |
6320
|
|
|
{ |
6321
|
|
|
$table = $this->esc( $table, TRUE ); |
6322
|
|
|
|
6323
|
|
|
$columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table'" ); |
6324
|
|
|
|
6325
|
|
|
$columns = array(); |
6326
|
|
|
foreach ( $columnsRaw as $r ) { |
6327
|
|
|
$columns[$r['column_name']] = $r['data_type']; |
6328
|
|
|
} |
6329
|
|
|
|
6330
|
|
|
return $columns; |
6331
|
|
|
} |
6332
|
|
|
|
6333
|
|
|
/** |
6334
|
|
|
* @see QueryWriter::scanType |
6335
|
|
|
*/ |
6336
|
|
|
public function scanType( $value, $flagSpecial = FALSE ) |
6337
|
|
|
{ |
6338
|
|
|
$this->svalue = $value; |
|
|
|
|
6339
|
|
|
|
6340
|
|
|
if ( $value === INF ) return self::C_DATATYPE_TEXT; |
6341
|
|
|
|
6342
|
|
|
if ( $flagSpecial && $value ) { |
6343
|
|
|
if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { |
6344
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_DATE; |
6345
|
|
|
} |
6346
|
|
|
|
6347
|
|
|
if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) { |
6348
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME; |
6349
|
|
|
} |
6350
|
|
|
|
6351
|
|
|
if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) { |
6352
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_POINT; |
6353
|
|
|
} |
6354
|
|
|
|
6355
|
|
|
if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) { |
6356
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_LSEG; |
6357
|
|
|
} |
6358
|
|
|
|
6359
|
|
|
if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) { |
6360
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE; |
6361
|
|
|
} |
6362
|
|
|
|
6363
|
|
|
if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) { |
6364
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON; |
6365
|
|
|
} |
6366
|
|
|
|
6367
|
|
|
if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) { |
6368
|
|
|
return PostgreSQL::C_DATATYPE_SPECIAL_MONEY; |
6369
|
|
|
} |
6370
|
|
|
} |
6371
|
|
|
|
6372
|
|
|
if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE; |
6373
|
|
|
|
6374
|
|
|
if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; |
6375
|
|
|
|
6376
|
|
|
if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value ) |
6377
|
|
|
&& AQueryWriter::canBeTreatedAsInt( $value ) |
6378
|
|
|
&& $value < 2147483648 |
6379
|
|
|
&& $value > -2147483648 ) |
6380
|
|
|
) { |
6381
|
|
|
return self::C_DATATYPE_INTEGER; |
6382
|
|
|
} elseif ( is_numeric( $value ) ) { |
6383
|
|
|
return self::C_DATATYPE_DOUBLE; |
6384
|
|
|
} else { |
6385
|
|
|
return self::C_DATATYPE_TEXT; |
6386
|
|
|
} |
6387
|
|
|
} |
6388
|
|
|
|
6389
|
|
|
/** |
6390
|
|
|
* @see QueryWriter::code |
6391
|
|
|
*/ |
6392
|
|
View Code Duplication |
public function code( $typedescription, $includeSpecials = FALSE ) |
6393
|
|
|
{ |
6394
|
|
|
$r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99; |
6395
|
|
|
|
6396
|
|
|
if ( $includeSpecials ) return $r; |
6397
|
|
|
|
6398
|
|
|
if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { |
6399
|
|
|
return self::C_DATATYPE_SPECIFIED; |
6400
|
|
|
} |
6401
|
|
|
|
6402
|
|
|
return $r; |
6403
|
|
|
} |
6404
|
|
|
|
6405
|
|
|
/** |
6406
|
|
|
* @see QueryWriter::widenColumn |
6407
|
|
|
*/ |
6408
|
|
View Code Duplication |
public function widenColumn( $type, $column, $datatype ) |
6409
|
|
|
{ |
6410
|
|
|
$table = $type; |
6411
|
|
|
$type = $datatype; |
6412
|
|
|
|
6413
|
|
|
$table = $this->esc( $table ); |
6414
|
|
|
$column = $this->esc( $column ); |
6415
|
|
|
|
6416
|
|
|
$newtype = $this->typeno_sqltype[$type]; |
6417
|
|
|
|
6418
|
|
|
$this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " ); |
6419
|
|
|
} |
6420
|
|
|
|
6421
|
|
|
/** |
6422
|
|
|
* @see QueryWriter::addUniqueIndex |
6423
|
|
|
*/ |
6424
|
|
View Code Duplication |
public function addUniqueConstraint( $type, $properties ) |
6425
|
|
|
{ |
6426
|
|
|
$tableNoQ = $this->esc( $type, TRUE ); |
6427
|
|
|
$columns = array(); |
6428
|
|
|
foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column ); |
6429
|
|
|
$table = $this->esc( $type ); |
6430
|
|
|
sort( $columns ); //else we get multiple indexes due to order-effects |
6431
|
|
|
$name = "UQ_" . sha1( $table . implode( ',', $columns ) ); |
6432
|
|
|
$sql = "ALTER TABLE {$table} |
6433
|
|
|
ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")"; |
6434
|
|
|
try { |
6435
|
|
|
$this->adapter->exec( $sql ); |
6436
|
|
|
} catch( SQLException $e ) { |
6437
|
|
|
return FALSE; |
6438
|
|
|
} |
6439
|
|
|
return TRUE; |
6440
|
|
|
} |
6441
|
|
|
|
6442
|
|
|
/** |
6443
|
|
|
* @see QueryWriter::sqlStateIn |
6444
|
|
|
*/ |
6445
|
|
View Code Duplication |
public function sqlStateIn( $state, $list ) |
6446
|
|
|
{ |
6447
|
|
|
$stateMap = array( |
6448
|
|
|
'42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, |
6449
|
|
|
'42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, |
6450
|
|
|
'23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION |
6451
|
|
|
); |
6452
|
|
|
|
6453
|
|
|
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); |
6454
|
|
|
} |
6455
|
|
|
|
6456
|
|
|
/** |
6457
|
|
|
* @see QueryWriter::addIndex |
6458
|
|
|
*/ |
6459
|
|
View Code Duplication |
public function addIndex( $type, $name, $property ) |
6460
|
|
|
{ |
6461
|
|
|
$table = $this->esc( $type ); |
6462
|
|
|
$name = preg_replace( '/\W/', '', $name ); |
6463
|
|
|
$column = $this->esc( $property ); |
6464
|
|
|
|
6465
|
|
|
try { |
6466
|
|
|
$this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " ); |
6467
|
|
|
return TRUE; |
6468
|
|
|
} catch ( SQLException $e ) { |
6469
|
|
|
return FALSE; |
6470
|
|
|
} |
6471
|
|
|
} |
6472
|
|
|
|
6473
|
|
|
/** |
6474
|
|
|
* @see QueryWriter::addFK |
6475
|
|
|
*/ |
6476
|
|
|
public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE ) |
6477
|
|
|
{ |
6478
|
|
|
$table = $this->esc( $type ); |
6479
|
|
|
$targetTable = $this->esc( $targetType ); |
6480
|
|
|
$field = $this->esc( $property ); |
6481
|
|
|
$targetField = $this->esc( $targetProperty ); |
6482
|
|
|
$tableNoQ = $this->esc( $type, TRUE ); |
6483
|
|
|
$fieldNoQ = $this->esc( $property, TRUE ); |
6484
|
|
|
if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE; |
6485
|
|
|
try{ |
6486
|
|
|
$delRule = ( $isDep ? 'CASCADE' : 'SET NULL' ); |
6487
|
|
|
$this->adapter->exec( "ALTER TABLE {$table} |
6488
|
|
|
ADD FOREIGN KEY ( {$field} ) REFERENCES {$targetTable} |
6489
|
|
|
({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" ); |
6490
|
|
|
return TRUE; |
6491
|
|
|
} catch ( SQLException $e ) { |
6492
|
|
|
return FALSE; |
6493
|
|
|
} |
6494
|
|
|
} |
6495
|
|
|
|
6496
|
|
|
/** |
6497
|
|
|
* @see QueryWriter::wipeAll |
6498
|
|
|
*/ |
6499
|
|
|
public function wipeAll() |
6500
|
|
|
{ |
6501
|
|
|
$this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' ); |
6502
|
|
|
|
6503
|
|
|
foreach ( $this->getTables() as $t ) { |
6504
|
|
|
$t = $this->esc( $t ); |
6505
|
|
|
|
6506
|
|
|
$this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " ); |
6507
|
|
|
} |
6508
|
|
|
|
6509
|
|
|
$this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' ); |
6510
|
|
|
} |
6511
|
|
|
} |
6512
|
|
|
} |
6513
|
|
|
|
6514
|
|
|
namespace RedBeanPHP { |
6515
|
|
|
|
6516
|
|
|
/** |
6517
|
|
|
* RedBean\Exception Base. |
6518
|
|
|
* Represents the base class for RedBeanPHP\Exceptions. |
6519
|
|
|
* |
6520
|
|
|
* @file RedBeanPHP/Exception.php |
6521
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
6522
|
|
|
* @license BSD/GPLv2 |
6523
|
|
|
* |
6524
|
|
|
* @copyright |
6525
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
6526
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
6527
|
|
|
* with this source code in the file license.txt. |
6528
|
|
|
*/ |
6529
|
|
|
class RedException extends \Exception |
|
|
|
|
6530
|
|
|
{ |
6531
|
|
|
} |
6532
|
|
|
} |
6533
|
|
|
|
6534
|
|
|
namespace RedBeanPHP\RedException { |
6535
|
|
|
|
6536
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
6537
|
|
|
|
6538
|
|
|
/** |
6539
|
|
|
* SQL Exception. |
6540
|
|
|
* Represents a generic database exception independent of the underlying driver. |
6541
|
|
|
* |
6542
|
|
|
* @file RedBeanPHP/RedException/SQL.php |
6543
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
6544
|
|
|
* @license BSD/GPLv2 |
6545
|
|
|
* |
6546
|
|
|
* @copyright |
6547
|
|
|
* (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
6548
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
6549
|
|
|
* with this source code in the file license.txt. |
6550
|
|
|
*/ |
6551
|
|
|
class SQL extends RedException |
|
|
|
|
6552
|
|
|
{ |
6553
|
|
|
/** |
6554
|
|
|
* @var string |
6555
|
|
|
*/ |
6556
|
|
|
private $sqlState; |
6557
|
|
|
|
6558
|
|
|
/** |
6559
|
|
|
* Returns an ANSI-92 compliant SQL state. |
6560
|
|
|
* |
6561
|
|
|
* @return string $state ANSI state code |
6562
|
|
|
*/ |
6563
|
|
|
public function getSQLState() |
6564
|
|
|
{ |
6565
|
|
|
return $this->sqlState; |
6566
|
|
|
} |
6567
|
|
|
|
6568
|
|
|
/** |
6569
|
|
|
* @todo parse state to verify valid ANSI92! |
6570
|
|
|
* Stores ANSI-92 compliant SQL state. |
6571
|
|
|
* |
6572
|
|
|
* @param string $sqlState code |
6573
|
|
|
* |
6574
|
|
|
* @return void |
6575
|
|
|
*/ |
6576
|
|
|
public function setSQLState( $sqlState ) |
6577
|
|
|
{ |
6578
|
|
|
$this->sqlState = $sqlState; |
6579
|
|
|
} |
6580
|
|
|
|
6581
|
|
|
/** |
6582
|
|
|
* To String prints both code and SQL state. |
6583
|
|
|
* |
6584
|
|
|
* @return string $message prints this exception instance as a string |
6585
|
|
|
*/ |
6586
|
|
|
public function __toString() |
6587
|
|
|
{ |
6588
|
|
|
return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n". |
6589
|
|
|
'trace: ' . $this->getTraceAsString(); |
6590
|
|
|
} |
6591
|
|
|
} |
6592
|
|
|
} |
6593
|
|
|
|
6594
|
|
|
namespace RedBeanPHP { |
6595
|
|
|
|
6596
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
6597
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
6598
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
6599
|
|
|
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; |
|
|
|
|
6600
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
6601
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
6602
|
|
|
use RedBeanPHP\SimpleModel as SimpleModel; |
|
|
|
|
6603
|
|
|
use RedBeanPHP\BeanHelper as BeanHelper; |
|
|
|
|
6604
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
6605
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
6606
|
|
|
use RedBeanPHP\OODB as OODB; |
|
|
|
|
6607
|
|
|
use RedBeanPHP\Cursor as Cursor; |
|
|
|
|
6608
|
|
|
use RedBeanPHP\Cursor\NullCursor as NullCursor; |
|
|
|
|
6609
|
|
|
|
6610
|
|
|
/** |
6611
|
|
|
* Abstract Repository. |
6612
|
|
|
* |
6613
|
|
|
* OODB manages two repositories, a fluid one that |
6614
|
|
|
* adjust the database schema on-the-fly to accomodate for |
6615
|
|
|
* new bean types (tables) and new properties (columns) and |
6616
|
|
|
* a frozen one for use in a production environment. OODB |
6617
|
|
|
* allows you to swap the repository instances using the freeze() |
6618
|
|
|
* method. |
6619
|
|
|
* |
6620
|
|
|
* @file RedBeanPHP/Repository.php |
6621
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
6622
|
|
|
* @license BSD/GPLv2 |
6623
|
|
|
* |
6624
|
|
|
* @copyright |
6625
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
6626
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
6627
|
|
|
* with this source code in the file license.txt. |
6628
|
|
|
*/ |
6629
|
|
|
abstract class Repository |
|
|
|
|
6630
|
|
|
{ |
6631
|
|
|
/** |
6632
|
|
|
* @var array |
6633
|
|
|
*/ |
6634
|
|
|
protected $stash = NULL; |
6635
|
|
|
|
6636
|
|
|
/* |
6637
|
|
|
* @var integer |
6638
|
|
|
*/ |
6639
|
|
|
protected $nesting = 0; |
6640
|
|
|
|
6641
|
|
|
/** |
6642
|
|
|
* @var DBAdapter |
6643
|
|
|
*/ |
6644
|
|
|
protected $writer; |
6645
|
|
|
|
6646
|
|
|
/** |
6647
|
|
|
* Stores a bean and its lists in one run. |
6648
|
|
|
* |
6649
|
|
|
* @param OODBBean $bean |
6650
|
|
|
* |
6651
|
|
|
* @return void |
6652
|
|
|
*/ |
6653
|
|
|
protected function storeBeanWithLists( OODBBean $bean ) |
6654
|
|
|
{ |
6655
|
|
|
$sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups |
6656
|
|
|
foreach ( $bean as $property => $value ) { |
6657
|
|
|
$value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value; |
6658
|
|
|
if ( $value instanceof OODBBean ) { |
6659
|
|
|
$this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value ); |
6660
|
|
|
$bean->setMeta("sys.typeof.{$property}", $value->getMeta('type')); |
6661
|
|
|
} elseif ( is_array( $value ) ) { |
6662
|
|
|
$originals = $bean->moveMeta( 'sys.shadow.' . $property, array() ); |
6663
|
|
|
if ( strpos( $property, 'own' ) === 0 ) { |
6664
|
|
|
list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue ); |
6665
|
|
|
$listName = lcfirst( substr( $property, 3 ) ); |
6666
|
|
|
if ($bean->moveMeta( 'sys.exclusive-'. $listName ) ) { |
6667
|
|
|
OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE ); |
6668
|
|
|
OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) ); |
6669
|
|
|
} |
6670
|
|
|
unset( $bean->$property ); |
6671
|
|
|
} elseif ( strpos( $property, 'shared' ) === 0 ) { |
6672
|
|
|
list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue ); |
6673
|
|
|
unset( $bean->$property ); |
6674
|
|
|
} |
6675
|
|
|
} |
6676
|
|
|
} |
6677
|
|
|
$this->storeBean( $bean ); |
|
|
|
|
6678
|
|
|
$this->processTrashcan( $bean, $ownTrashcan ); |
6679
|
|
|
$this->processAdditions( $bean, $ownAdditions ); |
6680
|
|
|
$this->processResidue( $ownresidue ); |
6681
|
|
|
$this->processSharedTrashcan( $bean, $sharedTrashcan ); |
6682
|
|
|
$this->processSharedAdditions( $bean, $sharedAdditions ); |
6683
|
|
|
$this->processSharedResidue( $bean, $sharedresidue ); |
6684
|
|
|
} |
6685
|
|
|
|
6686
|
|
|
/** |
6687
|
|
|
* Process groups. Internal function. Processes different kind of groups for |
6688
|
|
|
* storage function. Given a list of original beans and a list of current beans, |
6689
|
|
|
* this function calculates which beans remain in the list (residue), which |
6690
|
|
|
* have been deleted (are in the trashcan) and which beans have been added |
6691
|
|
|
* (additions). |
6692
|
|
|
* |
6693
|
|
|
* @param array $originals originals |
6694
|
|
|
* @param array $current the current beans |
6695
|
|
|
* @param array $additions beans that have been added |
6696
|
|
|
* @param array $trashcan beans that have been deleted |
6697
|
|
|
* @param array $residue beans that have been left untouched |
6698
|
|
|
* |
6699
|
|
|
* @return array |
6700
|
|
|
*/ |
6701
|
|
|
protected function processGroups( $originals, $current, $additions, $trashcan, $residue ) |
6702
|
|
|
{ |
6703
|
|
|
return array( |
6704
|
|
|
array_merge( $additions, array_diff( $current, $originals ) ), |
6705
|
|
|
array_merge( $trashcan, array_diff( $originals, $current ) ), |
6706
|
|
|
array_merge( $residue, array_intersect( $current, $originals ) ) |
6707
|
|
|
); |
6708
|
|
|
} |
6709
|
|
|
|
6710
|
|
|
/** |
6711
|
|
|
* Processes an embedded bean. |
6712
|
|
|
* |
6713
|
|
|
* @param OODBBean|SimpleModel $embeddedBean the bean or model |
6714
|
|
|
* |
6715
|
|
|
* @return integer |
6716
|
|
|
*/ |
6717
|
|
|
protected function prepareEmbeddedBean( $embeddedBean ) |
6718
|
|
|
{ |
6719
|
|
|
if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) { |
|
|
|
|
6720
|
|
|
$this->store( $embeddedBean ); |
6721
|
|
|
} |
6722
|
|
|
|
6723
|
|
|
return $embeddedBean->id; |
6724
|
|
|
} |
6725
|
|
|
|
6726
|
|
|
/** |
6727
|
|
|
* Processes a list of beans from a bean. A bean may contain lists. This |
6728
|
|
|
* method handles shared addition lists; i.e. the $bean->sharedObject properties. |
6729
|
|
|
* |
6730
|
|
|
* @param OODBBean $bean the bean |
6731
|
|
|
* @param array $sharedAdditions list with shared additions |
6732
|
|
|
* |
6733
|
|
|
* @return void |
6734
|
|
|
* |
6735
|
|
|
* @throws Security |
6736
|
|
|
*/ |
6737
|
|
|
protected function processSharedAdditions( $bean, $sharedAdditions ) |
6738
|
|
|
{ |
6739
|
|
|
foreach ( $sharedAdditions as $addition ) { |
6740
|
|
|
if ( $addition instanceof OODBBean ) { |
6741
|
|
|
$this->oodb->getAssociationManager()->associate( $addition, $bean ); |
|
|
|
|
6742
|
|
|
} else { |
6743
|
|
|
throw new RedException( 'Array may only contain OODBBeans' ); |
6744
|
|
|
} |
6745
|
|
|
} |
6746
|
|
|
} |
6747
|
|
|
|
6748
|
|
|
/** |
6749
|
|
|
* Processes a list of beans from a bean. A bean may contain lists. This |
6750
|
|
|
* method handles own lists; i.e. the $bean->ownObject properties. |
6751
|
|
|
* A residue is a bean in an own-list that stays where it is. This method |
6752
|
|
|
* checks if there have been any modification to this bean, in that case |
6753
|
|
|
* the bean is stored once again, otherwise the bean will be left untouched. |
6754
|
|
|
* |
6755
|
|
|
* @param OODBBean $bean the bean |
|
|
|
|
6756
|
|
|
* @param array $ownresidue list |
6757
|
|
|
* |
6758
|
|
|
* @return void |
6759
|
|
|
*/ |
6760
|
|
|
protected function processResidue( $ownresidue ) |
6761
|
|
|
{ |
6762
|
|
|
foreach ( $ownresidue as $residue ) { |
6763
|
|
|
if ( $residue->getMeta( 'tainted' ) ) { |
6764
|
|
|
$this->store( $residue ); |
6765
|
|
|
} |
6766
|
|
|
} |
6767
|
|
|
} |
6768
|
|
|
|
6769
|
|
|
/** |
6770
|
|
|
* Processes a list of beans from a bean. A bean may contain lists. This |
6771
|
|
|
* method handles own lists; i.e. the $bean->ownObject properties. |
6772
|
|
|
* A trash can bean is a bean in an own-list that has been removed |
6773
|
|
|
* (when checked with the shadow). This method |
6774
|
|
|
* checks if the bean is also in the dependency list. If it is the bean will be removed. |
6775
|
|
|
* If not, the connection between the bean and the owner bean will be broken by |
6776
|
|
|
* setting the ID to NULL. |
6777
|
|
|
* |
6778
|
|
|
* @param OODBBean $bean the bean |
6779
|
|
|
* @param array $ownTrashcan list |
6780
|
|
|
* |
6781
|
|
|
* @return void |
6782
|
|
|
*/ |
6783
|
|
|
protected function processTrashcan( $bean, $ownTrashcan ) |
6784
|
|
|
{ |
6785
|
|
|
|
6786
|
|
|
foreach ( $ownTrashcan as $trash ) { |
6787
|
|
|
|
6788
|
|
|
$myFieldLink = $bean->getMeta( 'type' ) . '_id'; |
6789
|
|
|
$alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) ); |
6790
|
|
|
if ( $alias ) $myFieldLink = $alias . '_id'; |
6791
|
|
|
|
6792
|
|
|
if ( $trash->getMeta( 'sys.garbage' ) === true ) { |
6793
|
|
|
$this->trash( $trash ); |
6794
|
|
|
} else { |
6795
|
|
|
$trash->$myFieldLink = NULL; |
6796
|
|
|
$this->store( $trash ); |
6797
|
|
|
} |
6798
|
|
|
} |
6799
|
|
|
} |
6800
|
|
|
|
6801
|
|
|
/** |
6802
|
|
|
* Unassociates the list items in the trashcan. |
6803
|
|
|
* |
6804
|
|
|
* @param OODBBean $bean bean |
6805
|
|
|
* @param array $sharedTrashcan list |
6806
|
|
|
* |
6807
|
|
|
* @return void |
6808
|
|
|
*/ |
6809
|
|
|
protected function processSharedTrashcan( $bean, $sharedTrashcan ) |
6810
|
|
|
{ |
6811
|
|
|
foreach ( $sharedTrashcan as $trash ) { |
6812
|
|
|
$this->oodb->getAssociationManager()->unassociate( $trash, $bean ); |
6813
|
|
|
} |
6814
|
|
|
} |
6815
|
|
|
|
6816
|
|
|
/** |
6817
|
|
|
* Stores all the beans in the residue group. |
6818
|
|
|
* |
6819
|
|
|
* @param OODBBean $bean bean |
6820
|
|
|
* @param array $sharedresidue list |
6821
|
|
|
* |
6822
|
|
|
* @return void |
6823
|
|
|
*/ |
6824
|
|
|
protected function processSharedResidue( $bean, $sharedresidue ) |
|
|
|
|
6825
|
|
|
{ |
6826
|
|
|
foreach ( $sharedresidue as $residue ) { |
6827
|
|
|
$this->store( $residue ); |
6828
|
|
|
} |
6829
|
|
|
} |
6830
|
|
|
|
6831
|
|
|
/** |
6832
|
|
|
* Determines whether the bean has 'loaded lists' or |
6833
|
|
|
* 'loaded embedded beans' that need to be processed |
6834
|
|
|
* by the store() method. |
6835
|
|
|
* |
6836
|
|
|
* @param OODBBean $bean bean to be examined |
6837
|
|
|
* |
6838
|
|
|
* @return boolean |
6839
|
|
|
*/ |
6840
|
|
|
protected function hasListsOrObjects( OODBBean $bean ) |
6841
|
|
|
{ |
6842
|
|
|
$processLists = FALSE; |
6843
|
|
|
foreach ( $bean as $value ) { |
6844
|
|
|
if ( is_array( $value ) || is_object( $value ) ) { |
6845
|
|
|
$processLists = TRUE; |
6846
|
|
|
break; |
6847
|
|
|
} |
6848
|
|
|
} |
6849
|
|
|
|
6850
|
|
|
return $processLists; |
6851
|
|
|
} |
6852
|
|
|
|
6853
|
|
|
|
6854
|
|
|
/** |
6855
|
|
|
* Converts an embedded bean to an ID, removed the bean property and |
6856
|
|
|
* stores the bean in the embedded beans array. |
6857
|
|
|
* |
6858
|
|
|
* @param array $embeddedBeans destination array for embedded bean |
6859
|
|
|
* @param OODBBean $bean target bean |
6860
|
|
|
* @param string $property property that contains the embedded bean |
6861
|
|
|
* @param OODBBean $value embedded bean itself |
6862
|
|
|
*/ |
6863
|
|
|
protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value ) |
6864
|
|
|
{ |
6865
|
|
|
$linkField = $property . '_id'; |
6866
|
|
|
$id = $this->prepareEmbeddedBean( $value ); |
6867
|
|
|
if ($bean->$linkField != $id) $bean->$linkField = $id; |
6868
|
|
|
$bean->setMeta( 'cast.' . $linkField, 'id' ); |
6869
|
|
|
$embeddedBeans[$linkField] = $value; |
6870
|
|
|
unset( $bean->$property ); |
6871
|
|
|
} |
6872
|
|
|
|
6873
|
|
|
|
6874
|
|
|
/** |
6875
|
|
|
* Constructor, requires a query writer. |
6876
|
|
|
* |
6877
|
|
|
* @param QueryWriter $writer writer |
6878
|
|
|
*/ |
6879
|
|
|
public function __construct( OODB $oodb, QueryWriter $writer ) |
6880
|
|
|
{ |
6881
|
|
|
$this->writer = $writer; |
|
|
|
|
6882
|
|
|
$this->oodb = $oodb; |
6883
|
|
|
} |
6884
|
|
|
|
6885
|
|
|
/** |
6886
|
|
|
* Checks whether a OODBBean bean is valid. |
6887
|
|
|
* If the type is not valid or the ID is not valid it will |
6888
|
|
|
* throw an exception: Security. |
6889
|
|
|
* |
6890
|
|
|
* @param OODBBean $bean the bean that needs to be checked |
6891
|
|
|
* |
6892
|
|
|
* @return void |
6893
|
|
|
* |
6894
|
|
|
* @throws Security $exception |
6895
|
|
|
*/ |
6896
|
|
|
public function check( OODBBean $bean ) |
6897
|
|
|
{ |
6898
|
|
|
//Is all meta information present? |
6899
|
|
|
if ( !isset( $bean->id ) ) { |
6900
|
|
|
throw new RedException( 'Bean has incomplete Meta Information id ' ); |
6901
|
|
|
} |
6902
|
|
|
if ( !( $bean->getMeta( 'type' ) ) ) { |
6903
|
|
|
throw new RedException( 'Bean has incomplete Meta Information II' ); |
6904
|
|
|
} |
6905
|
|
|
//Pattern of allowed characters |
6906
|
|
|
$pattern = '/[^a-z0-9_]/i'; |
6907
|
|
|
//Does the type contain invalid characters? |
6908
|
|
|
if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) { |
6909
|
|
|
throw new RedException( 'Bean Type is invalid' ); |
6910
|
|
|
} |
6911
|
|
|
//Are the properties and values valid? |
6912
|
|
|
foreach ( $bean as $prop => $value ) { |
6913
|
|
|
if ( |
6914
|
|
|
is_array( $value ) |
6915
|
|
|
|| ( is_object( $value ) ) |
6916
|
|
|
) { |
6917
|
|
|
throw new RedException( "Invalid Bean value: property $prop" ); |
6918
|
|
|
} else if ( |
6919
|
|
|
strlen( $prop ) < 1 |
6920
|
|
|
|| preg_match( $pattern, $prop ) |
6921
|
|
|
) { |
6922
|
|
|
throw new RedException( "Invalid Bean property: property $prop" ); |
6923
|
|
|
} |
6924
|
|
|
} |
6925
|
|
|
} |
6926
|
|
|
|
6927
|
|
|
/** |
6928
|
|
|
* Searches the database for a bean that matches conditions $conditions and sql $addSQL |
6929
|
|
|
* and returns an array containing all the beans that have been found. |
6930
|
|
|
* |
6931
|
|
|
* Conditions need to take form: |
6932
|
|
|
* |
6933
|
|
|
* array( |
6934
|
|
|
* 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) |
6935
|
|
|
* 'PROPERTY' => array( POSSIBLE VALUES... ) |
6936
|
|
|
* ); |
6937
|
|
|
* |
6938
|
|
|
* All conditions are glued together using the AND-operator, while all value lists |
6939
|
|
|
* are glued using IN-operators thus acting as OR-conditions. |
6940
|
|
|
* |
6941
|
|
|
* Note that you can use property names; the columns will be extracted using the |
6942
|
|
|
* appropriate bean formatter. |
6943
|
|
|
* |
6944
|
|
|
* @param string $type type of beans you are looking for |
6945
|
|
|
* @param array $conditions list of conditions |
6946
|
|
|
* @param string $addSQL SQL to be used in query |
|
|
|
|
6947
|
|
|
* @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) |
6948
|
|
|
* |
6949
|
|
|
* @return array |
6950
|
|
|
* |
6951
|
|
|
*/ |
6952
|
|
|
public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) |
6953
|
|
|
{ |
6954
|
|
|
//for backward compatibility, allow mismatch arguments: |
6955
|
|
|
if ( is_array( $sql ) ) { |
6956
|
|
|
if ( isset( $sql[1] ) ) { |
6957
|
|
|
$bindings = $sql[1]; |
6958
|
|
|
} |
6959
|
|
|
$sql = $sql[0]; |
6960
|
|
|
} |
6961
|
|
|
try { |
6962
|
|
|
$beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) ); |
|
|
|
|
6963
|
|
|
|
6964
|
|
|
return $beans; |
6965
|
|
|
} catch ( SQLException $exception ) { |
6966
|
|
|
$this->handleException( $exception ); |
6967
|
|
|
} |
6968
|
|
|
|
6969
|
|
|
return array(); |
6970
|
|
|
} |
6971
|
|
|
|
6972
|
|
|
/** |
6973
|
|
|
* Finds a BeanCollection. |
6974
|
|
|
* |
6975
|
|
|
* @param string $type type of beans you are looking for |
6976
|
|
|
* @param string $sql SQL to be used in query |
6977
|
|
|
* @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) |
6978
|
|
|
* |
6979
|
|
|
* @return BeanCollection |
6980
|
|
|
*/ |
6981
|
|
|
public function findCollection( $type, $sql, $bindings = array() ) |
6982
|
|
|
{ |
6983
|
|
|
try { |
6984
|
|
|
$cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings ); |
|
|
|
|
6985
|
|
|
return new BeanCollection( $type, $this, $cursor ); |
6986
|
|
|
} catch ( SQLException $exception ) { |
6987
|
|
|
$this->handleException( $exception ); |
6988
|
|
|
} |
6989
|
|
|
return new BeanCollection( $type, $this, new NullCursor ); |
6990
|
|
|
} |
6991
|
|
|
|
6992
|
|
|
/** |
6993
|
|
|
* Stores a bean in the database. This method takes a |
6994
|
|
|
* OODBBean Bean Object $bean and stores it |
6995
|
|
|
* in the database. If the database schema is not compatible |
6996
|
|
|
* with this bean and RedBean runs in fluid mode the schema |
6997
|
|
|
* will be altered to store the bean correctly. |
6998
|
|
|
* If the database schema is not compatible with this bean and |
6999
|
|
|
* RedBean runs in frozen mode it will throw an exception. |
7000
|
|
|
* This function returns the primary key ID of the inserted |
7001
|
|
|
* bean. |
7002
|
|
|
* |
7003
|
|
|
* The return value is an integer if possible. If it is not possible to |
7004
|
|
|
* represent the value as an integer a string will be returned. We use |
7005
|
|
|
* explicit casts instead of functions to preserve performance |
7006
|
|
|
* (0.13 vs 0.28 for 10000 iterations on Core i3). |
7007
|
|
|
* |
7008
|
|
|
* @param OODBBean|SimpleModel $bean bean to store |
7009
|
|
|
* |
7010
|
|
|
* @return integer|string |
7011
|
|
|
*/ |
7012
|
|
|
public function store( $bean ) |
7013
|
|
|
{ |
7014
|
|
|
$processLists = $this->hasListsOrObjects( $bean ); |
|
|
|
|
7015
|
|
|
if ( !$processLists && !$bean->getMeta( 'tainted' ) ) { |
|
|
|
|
7016
|
|
|
return $bean->getID(); //bail out! |
|
|
|
|
7017
|
|
|
} |
7018
|
|
|
$this->oodb->signal( 'update', $bean ); |
7019
|
|
|
$processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model! |
|
|
|
|
7020
|
|
|
if ( $processLists ) { |
7021
|
|
|
$this->storeBeanWithLists( $bean ); |
|
|
|
|
7022
|
|
|
} else { |
7023
|
|
|
$this->storeBean( $bean ); |
|
|
|
|
7024
|
|
|
} |
7025
|
|
|
$this->oodb->signal( 'after_update', $bean ); |
7026
|
|
|
|
7027
|
|
|
return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id; |
7028
|
|
|
} |
7029
|
|
|
|
7030
|
|
|
/** |
7031
|
|
|
* Returns an array of beans. Pass a type and a series of ids and |
7032
|
|
|
* this method will bring you the corresponding beans. |
7033
|
|
|
* |
7034
|
|
|
* important note: Because this method loads beans using the load() |
7035
|
|
|
* function (but faster) it will return empty beans with ID 0 for |
7036
|
|
|
* every bean that could not be located. The resulting beans will have the |
7037
|
|
|
* passed IDs as their keys. |
7038
|
|
|
* |
7039
|
|
|
* @param string $type type of beans |
7040
|
|
|
* @param array $ids ids to load |
7041
|
|
|
* |
7042
|
|
|
* @return array |
7043
|
|
|
*/ |
7044
|
|
|
public function batch( $type, $ids ) |
7045
|
|
|
{ |
7046
|
|
|
if ( !$ids ) { |
|
|
|
|
7047
|
|
|
return array(); |
7048
|
|
|
} |
7049
|
|
|
$collection = array(); |
7050
|
|
|
try { |
7051
|
|
|
$rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) ); |
|
|
|
|
7052
|
|
|
} catch ( SQLException $e ) { |
7053
|
|
|
$this->handleException( $e ); |
7054
|
|
|
$rows = FALSE; |
7055
|
|
|
} |
7056
|
|
|
$this->stash[$this->nesting] = array(); |
7057
|
|
|
if ( !$rows ) { |
7058
|
|
|
return array(); |
7059
|
|
|
} |
7060
|
|
|
foreach ( $rows as $row ) { |
7061
|
|
|
$this->stash[$this->nesting][$row['id']] = $row; |
7062
|
|
|
} |
7063
|
|
|
foreach ( $ids as $id ) { |
7064
|
|
|
$collection[$id] = $this->load( $type, $id ); |
7065
|
|
|
} |
7066
|
|
|
$this->stash[$this->nesting] = NULL; |
7067
|
|
|
|
7068
|
|
|
return $collection; |
7069
|
|
|
} |
7070
|
|
|
|
7071
|
|
|
/** |
7072
|
|
|
* This is a convenience method; it converts database rows |
7073
|
|
|
* (arrays) into beans. Given a type and a set of rows this method |
7074
|
|
|
* will return an array of beans of the specified type loaded with |
7075
|
|
|
* the data fields provided by the result set from the database. |
7076
|
|
|
* |
7077
|
|
|
* @param string $type type of beans you would like to have |
7078
|
|
|
* @param array $rows rows from the database result |
7079
|
|
|
* |
7080
|
|
|
* @return array |
7081
|
|
|
*/ |
7082
|
|
|
public function convertToBeans( $type, $rows ) |
7083
|
|
|
{ |
7084
|
|
|
$collection = array(); |
7085
|
|
|
$this->stash[$this->nesting] = array(); |
7086
|
|
|
foreach ( $rows as $row ) { |
7087
|
|
|
$id = $row['id']; |
7088
|
|
|
$this->stash[$this->nesting][$id] = $row; |
7089
|
|
|
$collection[$id] = $this->load( $type, $id ); |
7090
|
|
|
} |
7091
|
|
|
$this->stash[$this->nesting] = NULL; |
7092
|
|
|
|
7093
|
|
|
return $collection; |
7094
|
|
|
} |
7095
|
|
|
|
7096
|
|
|
/** |
7097
|
|
|
* Counts the number of beans of type $type. |
7098
|
|
|
* This method accepts a second argument to modify the count-query. |
7099
|
|
|
* A third argument can be used to provide bindings for the SQL snippet. |
7100
|
|
|
* |
7101
|
|
|
* @param string $type type of bean we are looking for |
7102
|
|
|
* @param string $addSQL additional SQL snippet |
7103
|
|
|
* @param array $bindings parameters to bind to SQL |
7104
|
|
|
* |
7105
|
|
|
* @return integer |
7106
|
|
|
* |
7107
|
|
|
* @throws SQLException |
7108
|
|
|
*/ |
7109
|
|
|
public function count( $type, $addSQL = '', $bindings = array() ) |
7110
|
|
|
{ |
7111
|
|
|
$type = AQueryWriter::camelsSnake( $type ); |
7112
|
|
|
if ( count( explode( '_', $type ) ) > 2 ) { |
7113
|
|
|
throw new RedException( 'Invalid type for count.' ); |
7114
|
|
|
} |
7115
|
|
|
|
7116
|
|
|
try { |
7117
|
|
|
return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings ); |
|
|
|
|
7118
|
|
|
} catch ( SQLException $exception ) { |
7119
|
|
|
if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( |
|
|
|
|
7120
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, |
7121
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) { |
7122
|
|
|
throw $exception; |
7123
|
|
|
} |
7124
|
|
|
} |
7125
|
|
|
|
7126
|
|
|
return 0; |
7127
|
|
|
} |
7128
|
|
|
|
7129
|
|
|
/** |
7130
|
|
|
* Removes a bean from the database. |
7131
|
|
|
* This function will remove the specified OODBBean |
7132
|
|
|
* Bean Object from the database. |
7133
|
|
|
* |
7134
|
|
|
* @param OODBBean|SimpleModel $bean bean you want to remove from database |
7135
|
|
|
* |
7136
|
|
|
* @return void |
7137
|
|
|
* |
7138
|
|
|
* @throws SQLException |
7139
|
|
|
*/ |
7140
|
|
|
public function trash( $bean ) |
7141
|
|
|
{ |
7142
|
|
|
$this->oodb->signal( 'delete', $bean ); |
7143
|
|
|
foreach ( $bean as $property => $value ) { |
|
|
|
|
7144
|
|
|
if ( $value instanceof OODBBean ) { |
7145
|
|
|
unset( $bean->$property ); |
7146
|
|
|
} |
7147
|
|
|
if ( is_array( $value ) ) { |
7148
|
|
|
if ( strpos( $property, 'own' ) === 0 ) { |
7149
|
|
|
unset( $bean->$property ); |
7150
|
|
|
} elseif ( strpos( $property, 'shared' ) === 0 ) { |
7151
|
|
|
unset( $bean->$property ); |
7152
|
|
|
} |
7153
|
|
|
} |
7154
|
|
|
} |
7155
|
|
|
try { |
7156
|
|
|
$this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL ); |
|
|
|
|
7157
|
|
|
} catch ( SQLException $exception ) { |
7158
|
|
|
$this->handleException( $exception ); |
7159
|
|
|
} |
7160
|
|
|
$bean->id = 0; |
7161
|
|
|
$this->oodb->signal( 'after_delete', $bean ); |
7162
|
|
|
} |
7163
|
|
|
|
7164
|
|
|
/** |
7165
|
|
|
* Checks whether the specified table already exists in the database. |
7166
|
|
|
* Not part of the Object Database interface! |
7167
|
|
|
* |
7168
|
|
|
* @deprecated Use AQueryWriter::typeExists() instead. |
7169
|
|
|
* |
7170
|
|
|
* @param string $table table name |
7171
|
|
|
* |
7172
|
|
|
* @return boolean |
7173
|
|
|
*/ |
7174
|
|
|
public function tableExists( $table ) |
7175
|
|
|
{ |
7176
|
|
|
return $this->writer->tableExists( $table ); |
|
|
|
|
7177
|
|
|
} |
7178
|
|
|
|
7179
|
|
|
/** |
7180
|
|
|
* Trash all beans of a given type. Wipes an entire type of bean. |
7181
|
|
|
* |
7182
|
|
|
* @param string $type type of bean you wish to delete all instances of |
7183
|
|
|
* |
7184
|
|
|
* @return boolean |
7185
|
|
|
* |
7186
|
|
|
* @throws SQLException |
7187
|
|
|
*/ |
7188
|
|
|
public function wipe( $type ) |
7189
|
|
|
{ |
7190
|
|
|
try { |
7191
|
|
|
$this->writer->wipe( $type ); |
|
|
|
|
7192
|
|
|
|
7193
|
|
|
return TRUE; |
7194
|
|
|
} catch ( SQLException $exception ) { |
7195
|
|
|
if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { |
|
|
|
|
7196
|
|
|
throw $exception; |
7197
|
|
|
} |
7198
|
|
|
|
7199
|
|
|
return FALSE; |
7200
|
|
|
} |
7201
|
|
|
} |
7202
|
|
|
} |
7203
|
|
|
} |
7204
|
|
|
|
7205
|
|
|
namespace RedBeanPHP\Repository { |
7206
|
|
|
|
7207
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
7208
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
7209
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
7210
|
|
|
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; |
|
|
|
|
7211
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
7212
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
7213
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
7214
|
|
|
use RedBeanPHP\SimpleModel as SimpleModel; |
|
|
|
|
7215
|
|
|
use RedBeanPHP\BeanHelper as BeanHelper; |
|
|
|
|
7216
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
7217
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
7218
|
|
|
use RedBeanPHP\Repository as Repository; |
|
|
|
|
7219
|
|
|
|
7220
|
|
|
/** |
7221
|
|
|
* Fluid Repository. |
7222
|
|
|
* OODB manages two repositories, a fluid one that |
7223
|
|
|
* adjust the database schema on-the-fly to accomodate for |
7224
|
|
|
* new bean types (tables) and new properties (columns) and |
7225
|
|
|
* a frozen one for use in a production environment. OODB |
7226
|
|
|
* allows you to swap the repository instances using the freeze() |
7227
|
|
|
* method. |
7228
|
|
|
* |
7229
|
|
|
* @file RedBeanPHP/Repository/Fluid.php |
7230
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
7231
|
|
|
* @license BSD/GPLv2 |
7232
|
|
|
* |
7233
|
|
|
* @copyright |
7234
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
7235
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
7236
|
|
|
* with this source code in the file license.txt. |
7237
|
|
|
*/ |
7238
|
|
|
class Fluid extends Repository |
|
|
|
|
7239
|
|
|
{ |
7240
|
|
|
/** |
7241
|
|
|
* Figures out the desired type given the cast string ID. |
7242
|
|
|
* |
7243
|
|
|
* @param string $cast cast identifier |
7244
|
|
|
* |
7245
|
|
|
* @return integer |
7246
|
|
|
* |
7247
|
|
|
* @throws Security |
7248
|
|
|
*/ |
7249
|
|
|
private function getTypeFromCast( $cast ) |
7250
|
|
|
{ |
7251
|
|
|
if ( $cast == 'string' ) { |
7252
|
|
|
$typeno = $this->writer->scanType( 'STRING' ); |
|
|
|
|
7253
|
|
|
} elseif ( $cast == 'id' ) { |
7254
|
|
|
$typeno = $this->writer->getTypeForID(); |
|
|
|
|
7255
|
|
|
} elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) { |
|
|
|
|
7256
|
|
|
$typeno = $this->writer->sqltype_typeno[$cast]; |
7257
|
|
|
} else { |
7258
|
|
|
throw new RedException( 'Invalid Cast' ); |
7259
|
|
|
} |
7260
|
|
|
|
7261
|
|
|
return $typeno; |
7262
|
|
|
} |
7263
|
|
|
|
7264
|
|
|
/** |
7265
|
|
|
* Orders the Query Writer to create a table if it does not exist already and |
7266
|
|
|
* adds a note in the build report about the creation. |
7267
|
|
|
* |
7268
|
|
|
* @param OODBBean $bean bean to update report of |
7269
|
|
|
* @param string $table table to check and create if not exists |
7270
|
|
|
* |
7271
|
|
|
* @return void |
7272
|
|
|
*/ |
7273
|
|
|
private function createTableIfNotExists( OODBBean $bean, $table ) |
7274
|
|
|
{ |
7275
|
|
|
//Does table exist? If not, create |
7276
|
|
|
if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) { |
|
|
|
|
7277
|
|
|
$this->writer->createTable( $table ); |
|
|
|
|
7278
|
|
|
$bean->setMeta( 'buildreport.flags.created', TRUE ); |
7279
|
|
|
} |
7280
|
|
|
} |
7281
|
|
|
|
7282
|
|
|
/** |
7283
|
|
|
* Modifies the table to fit the bean data. |
7284
|
|
|
* Given a property and a value and the bean, this method will |
7285
|
|
|
* adjust the table structure to fit the requirements of the property and value. |
7286
|
|
|
* This may include adding a new column or widening an existing column to hold a larger |
7287
|
|
|
* or different kind of value. This method employs the writer to adjust the table |
7288
|
|
|
* structure in the database. Schema updates are recorded in meta properties of the bean. |
7289
|
|
|
* |
7290
|
|
|
* This method will also apply indexes, unique constraints and foreign keys. |
7291
|
|
|
* |
7292
|
|
|
* @param OODBBean $bean bean to get cast data from and store meta in |
7293
|
|
|
* @param string $property property to store |
7294
|
|
|
* @param mixed $value value to store |
7295
|
|
|
* |
7296
|
|
|
* @return void |
7297
|
|
|
*/ |
7298
|
|
|
private function modifySchema( OODBBean $bean, $property, $value ) |
7299
|
|
|
{ |
7300
|
|
|
$doFKStuff = FALSE; |
7301
|
|
|
$table = $bean->getMeta( 'type' ); |
7302
|
|
|
$columns = $this->writer->getColumns( $table ); |
|
|
|
|
7303
|
|
|
$columnNoQ = $this->writer->esc( $property, TRUE ); |
|
|
|
|
7304
|
|
|
if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) { |
|
|
|
|
7305
|
|
|
if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types |
7306
|
|
|
$cast = $bean->getMeta( "cast.$property" ); |
7307
|
|
|
$typeno = $this->getTypeFromCast( $cast ); |
7308
|
|
|
} else { |
7309
|
|
|
$cast = FALSE; |
7310
|
|
|
$typeno = $this->writer->scanType( $value, TRUE ); |
|
|
|
|
7311
|
|
|
} |
7312
|
|
|
if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ? |
|
|
|
|
7313
|
|
|
if ( !$cast ) { //rescan without taking into account special types >80 |
7314
|
|
|
$typeno = $this->writer->scanType( $value, FALSE ); |
|
|
|
|
7315
|
|
|
} |
7316
|
|
|
$sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] ); |
|
|
|
|
7317
|
|
|
if ( $typeno > $sqlt ) { //no, we have to widen the database column type |
7318
|
|
|
$this->writer->widenColumn( $table, $property, $typeno ); |
|
|
|
|
7319
|
|
|
$bean->setMeta( 'buildreport.flags.widen', TRUE ); |
7320
|
|
|
$doFKStuff = TRUE; |
7321
|
|
|
} |
7322
|
|
|
} else { |
7323
|
|
|
$this->writer->addColumn( $table, $property, $typeno ); |
|
|
|
|
7324
|
|
|
$bean->setMeta( 'buildreport.flags.addcolumn', TRUE ); |
7325
|
|
|
$doFKStuff = TRUE; |
7326
|
|
|
} |
7327
|
|
|
if ($doFKStuff) { |
7328
|
|
|
if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) { |
7329
|
|
|
$destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3); |
7330
|
|
|
$indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}"; |
7331
|
|
|
$this->writer->addIndex($table, $indexName, $columnNoQ); |
|
|
|
|
7332
|
|
|
$typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ); |
7333
|
|
|
$isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE ); |
7334
|
|
|
//Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean |
7335
|
|
|
$isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) ); |
7336
|
|
|
$result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep ); |
|
|
|
|
7337
|
|
|
//If this is a link bean and all unique columns have been added already, then apply unique constraint |
7338
|
|
|
if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) { |
|
|
|
|
7339
|
|
|
$this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') ); |
|
|
|
|
7340
|
|
|
$bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL); |
7341
|
|
|
} |
7342
|
|
|
} |
7343
|
|
|
} |
7344
|
|
|
} |
7345
|
|
|
} |
7346
|
|
|
|
7347
|
|
|
/** |
7348
|
|
|
* Part of the store() functionality. |
7349
|
|
|
* Handles all new additions after the bean has been saved. |
7350
|
|
|
* Stores addition bean in own-list, extracts the id and |
7351
|
|
|
* adds a foreign key. Also adds a constraint in case the type is |
7352
|
|
|
* in the dependent list. |
7353
|
|
|
* |
7354
|
|
|
* @param OODBBean $bean bean |
7355
|
|
|
* @param array $ownAdditions list of addition beans in own-list |
7356
|
|
|
* |
7357
|
|
|
* @return void |
7358
|
|
|
* |
7359
|
|
|
* @throws Security |
7360
|
|
|
*/ |
7361
|
|
|
protected function processAdditions( $bean, $ownAdditions ) |
7362
|
|
|
{ |
7363
|
|
|
$beanType = $bean->getMeta( 'type' ); |
7364
|
|
|
|
7365
|
|
|
foreach ( $ownAdditions as $addition ) { |
7366
|
|
|
if ( $addition instanceof OODBBean ) { |
7367
|
|
|
|
7368
|
|
|
$myFieldLink = $beanType . '_id'; |
7369
|
|
|
$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); |
7370
|
|
|
if ( $alias ) $myFieldLink = $alias . '_id'; |
7371
|
|
|
|
7372
|
|
|
$addition->$myFieldLink = $bean->id; |
|
|
|
|
7373
|
|
|
$addition->setMeta( 'cast.' . $myFieldLink, 'id' ); |
7374
|
|
|
|
7375
|
|
|
if ($alias) { |
7376
|
|
|
$addition->setMeta( "sys.typeof.{$alias}", $beanType ); |
7377
|
|
|
} else { |
7378
|
|
|
$addition->setMeta( "sys.typeof.{$beanType}", $beanType ); |
7379
|
|
|
} |
7380
|
|
|
|
7381
|
|
|
$this->store( $addition ); |
7382
|
|
|
} else { |
7383
|
|
|
throw new RedException( 'Array may only contain OODBBeans' ); |
7384
|
|
|
} |
7385
|
|
|
} |
7386
|
|
|
} |
7387
|
|
|
|
7388
|
|
|
/** |
7389
|
|
|
* Stores a cleaned bean; i.e. only scalar values. This is the core of the store() |
7390
|
|
|
* method. When all lists and embedded beans (parent objects) have been processed and |
7391
|
|
|
* removed from the original bean the bean is passed to this method to be stored |
7392
|
|
|
* in the database. |
7393
|
|
|
* |
7394
|
|
|
* @param OODBBean $bean the clean bean |
7395
|
|
|
* |
7396
|
|
|
* @return void |
7397
|
|
|
*/ |
7398
|
|
|
protected function storeBean( OODBBean $bean ) |
7399
|
|
|
{ |
7400
|
|
|
if ( $bean->getMeta( 'changed' ) ) { |
7401
|
|
|
$this->check( $bean ); |
7402
|
|
|
$table = $bean->getMeta( 'type' ); |
7403
|
|
|
$this->createTableIfNotExists( $bean, $table ); |
7404
|
|
|
|
7405
|
|
|
$updateValues = array(); |
7406
|
|
|
foreach ( $bean as $property => $value ) { |
7407
|
|
|
if ( $property !== 'id' ) { |
7408
|
|
|
$this->modifySchema( $bean, $property, $value ); |
7409
|
|
|
} |
7410
|
|
|
if ( $property !== 'id' ) { |
7411
|
|
|
$updateValues[] = array( 'property' => $property, 'value' => $value ); |
7412
|
|
|
} |
7413
|
|
|
} |
7414
|
|
|
|
7415
|
|
|
$bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id ); |
|
|
|
|
7416
|
|
|
$bean->setMeta( 'changed', FALSE ); |
7417
|
|
|
} |
7418
|
|
|
$bean->setMeta( 'tainted', FALSE ); |
7419
|
|
|
} |
7420
|
|
|
|
7421
|
|
|
/** |
7422
|
|
|
* Handles\Exceptions. Suppresses exceptions caused by missing structures. |
7423
|
|
|
* |
7424
|
|
|
* @param\Exception $exception exception |
7425
|
|
|
* |
7426
|
|
|
* @return void |
7427
|
|
|
* |
7428
|
|
|
* @throws\Exception |
7429
|
|
|
*/ |
7430
|
|
View Code Duplication |
protected function handleException( \Exception $exception ) |
7431
|
|
|
{ |
7432
|
|
|
if ( !$this->writer->sqlStateIn( $exception->getSQLState(), |
|
|
|
|
7433
|
|
|
array( |
7434
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, |
7435
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) |
7436
|
|
|
) { |
7437
|
|
|
throw $exception; |
7438
|
|
|
} |
7439
|
|
|
} |
7440
|
|
|
|
7441
|
|
|
/** |
7442
|
|
|
* Dispenses a new bean (a OODBBean Bean Object) |
7443
|
|
|
* of the specified type. Always |
7444
|
|
|
* use this function to get an empty bean object. Never |
7445
|
|
|
* instantiate a OODBBean yourself because it needs |
7446
|
|
|
* to be configured before you can use it with RedBean. This |
7447
|
|
|
* function applies the appropriate initialization / |
7448
|
|
|
* configuration for you. |
7449
|
|
|
* |
7450
|
|
|
* @param string $type type of bean you want to dispense |
7451
|
|
|
* @param string $number number of beans you would like to get |
7452
|
|
|
* @param boolean $alwaysReturnArray if TRUE always returns the result as an array |
7453
|
|
|
* |
7454
|
|
|
* @return OODBBean |
7455
|
|
|
*/ |
7456
|
|
View Code Duplication |
public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) |
7457
|
|
|
{ |
7458
|
|
|
$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; |
7459
|
|
|
$beans = array(); |
7460
|
|
|
for ( $i = 0; $i < $number; $i++ ) { |
7461
|
|
|
$bean = new $OODBBEAN; |
7462
|
|
|
$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); |
7463
|
|
|
$this->check( $bean ); |
7464
|
|
|
$this->oodb->signal( 'dispense', $bean ); |
7465
|
|
|
$beans[] = $bean; |
7466
|
|
|
} |
7467
|
|
|
|
7468
|
|
|
return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; |
7469
|
|
|
} |
7470
|
|
|
|
7471
|
|
|
/** |
7472
|
|
|
* Loads a bean from the object database. |
7473
|
|
|
* It searches for a OODBBean Bean Object in the |
7474
|
|
|
* database. It does not matter how this bean has been stored. |
7475
|
|
|
* RedBean uses the primary key ID $id and the string $type |
7476
|
|
|
* to find the bean. The $type specifies what kind of bean you |
7477
|
|
|
* are looking for; this is the same type as used with the |
7478
|
|
|
* dispense() function. If RedBean finds the bean it will return |
7479
|
|
|
* the OODB Bean object; if it cannot find the bean |
7480
|
|
|
* RedBean will return a new bean of type $type and with |
7481
|
|
|
* primary key ID 0. In the latter case it acts basically the |
7482
|
|
|
* same as dispense(). |
7483
|
|
|
* |
7484
|
|
|
* Important note: |
7485
|
|
|
* If the bean cannot be found in the database a new bean of |
7486
|
|
|
* the specified type will be generated and returned. |
7487
|
|
|
* |
7488
|
|
|
* @param string $type type of bean you want to load |
7489
|
|
|
* @param integer $id ID of the bean you want to load |
7490
|
|
|
* |
7491
|
|
|
* @throws SQL |
7492
|
|
|
* |
7493
|
|
|
* @return OODBBean |
7494
|
|
|
* |
7495
|
|
|
*/ |
7496
|
|
View Code Duplication |
public function load( $type, $id ) |
7497
|
|
|
{ |
7498
|
|
|
$bean = $this->dispense( $type ); |
7499
|
|
|
if ( isset( $this->stash[$this->nesting][$id] ) ) { |
7500
|
|
|
$row = $this->stash[$this->nesting][$id]; |
7501
|
|
|
} else { |
7502
|
|
|
try { |
7503
|
|
|
$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); |
|
|
|
|
7504
|
|
|
} catch ( SQLException $exception ) { |
7505
|
|
|
if ( $this->writer->sqlStateIn( $exception->getSQLState(), |
|
|
|
|
7506
|
|
|
array( |
7507
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, |
7508
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) |
7509
|
|
|
) |
7510
|
|
|
) { |
7511
|
|
|
$rows = 0; |
7512
|
|
|
|
7513
|
|
|
} |
7514
|
|
|
} |
7515
|
|
|
if ( empty( $rows ) ) { |
7516
|
|
|
return $bean; |
7517
|
|
|
} |
7518
|
|
|
$row = array_pop( $rows ); |
7519
|
|
|
} |
7520
|
|
|
$bean->importRow( $row ); |
7521
|
|
|
$this->nesting++; |
7522
|
|
|
$this->oodb->signal( 'open', $bean ); |
7523
|
|
|
$this->nesting--; |
7524
|
|
|
|
7525
|
|
|
return $bean->setMeta( 'tainted', FALSE ); |
7526
|
|
|
} |
7527
|
|
|
} |
7528
|
|
|
} |
7529
|
|
|
|
7530
|
|
|
namespace RedBeanPHP\Repository { |
7531
|
|
|
|
7532
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
7533
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
7534
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
7535
|
|
|
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; |
|
|
|
|
7536
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
7537
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
7538
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
7539
|
|
|
use RedBeanPHP\SimpleModel as SimpleModel; |
|
|
|
|
7540
|
|
|
use RedBeanPHP\BeanHelper as BeanHelper; |
|
|
|
|
7541
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
7542
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
7543
|
|
|
use RedBeanPHP\Repository as Repository; |
|
|
|
|
7544
|
|
|
|
7545
|
|
|
/** |
7546
|
|
|
* Frozen Repository. |
7547
|
|
|
* OODB manages two repositories, a fluid one that |
7548
|
|
|
* adjust the database schema on-the-fly to accomodate for |
7549
|
|
|
* new bean types (tables) and new properties (columns) and |
7550
|
|
|
* a frozen one for use in a production environment. OODB |
7551
|
|
|
* allows you to swap the repository instances using the freeze() |
7552
|
|
|
* method. |
7553
|
|
|
* |
7554
|
|
|
* @file RedBeanPHP/Repository/Frozen.php |
7555
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
7556
|
|
|
* @license BSD/GPLv2 |
7557
|
|
|
* |
7558
|
|
|
* @copyright |
7559
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
7560
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
7561
|
|
|
* with this source code in the file license.txt. |
7562
|
|
|
*/ |
7563
|
|
|
class Frozen extends Repository |
|
|
|
|
7564
|
|
|
{ |
7565
|
|
|
/** |
7566
|
|
|
* Handles\Exceptions. Suppresses exceptions caused by missing structures. |
7567
|
|
|
* |
7568
|
|
|
* @param \Exception $exception exception |
7569
|
|
|
* |
7570
|
|
|
* @return void |
7571
|
|
|
* |
7572
|
|
|
* @throws \Exception |
7573
|
|
|
*/ |
7574
|
|
|
protected function handleException( \Exception $exception ) |
7575
|
|
|
{ |
7576
|
|
|
throw $exception; |
7577
|
|
|
} |
7578
|
|
|
|
7579
|
|
|
/** |
7580
|
|
|
* Stores a cleaned bean; i.e. only scalar values. This is the core of the store() |
7581
|
|
|
* method. When all lists and embedded beans (parent objects) have been processed and |
7582
|
|
|
* removed from the original bean the bean is passed to this method to be stored |
7583
|
|
|
* in the database. |
7584
|
|
|
* |
7585
|
|
|
* @param OODBBean $bean the clean bean |
7586
|
|
|
* |
7587
|
|
|
* @return void |
7588
|
|
|
*/ |
7589
|
|
|
protected function storeBean( OODBBean $bean ) |
7590
|
|
|
{ |
7591
|
|
|
if ( $bean->getMeta( 'changed' ) ) { |
7592
|
|
|
|
7593
|
|
|
list( $properties, $table ) = $bean->getPropertiesAndType(); |
7594
|
|
|
$id = $properties['id']; |
7595
|
|
|
unset($properties['id']); |
7596
|
|
|
$updateValues = array(); |
7597
|
|
|
$k1 = 'property'; |
7598
|
|
|
$k2 = 'value'; |
7599
|
|
|
foreach( $properties as $key => $value ) { |
7600
|
|
|
$updateValues[] = array( $k1 => $key, $k2 => $value ); |
7601
|
|
|
} |
7602
|
|
|
$bean->id = $this->writer->updateRecord( $table, $updateValues, $id ); |
|
|
|
|
7603
|
|
|
$bean->setMeta( 'changed', FALSE ); |
7604
|
|
|
} |
7605
|
|
|
$bean->setMeta( 'tainted', FALSE ); |
7606
|
|
|
} |
7607
|
|
|
|
7608
|
|
|
/** |
7609
|
|
|
* Part of the store() functionality. |
7610
|
|
|
* Handles all new additions after the bean has been saved. |
7611
|
|
|
* Stores addition bean in own-list, extracts the id and |
7612
|
|
|
* adds a foreign key. Also adds a constraint in case the type is |
7613
|
|
|
* in the dependent list. |
7614
|
|
|
* |
7615
|
|
|
* @param OODBBean $bean bean |
7616
|
|
|
* @param array $ownAdditions list of addition beans in own-list |
7617
|
|
|
* |
7618
|
|
|
* @return void |
7619
|
|
|
* |
7620
|
|
|
* @throws Security |
7621
|
|
|
*/ |
7622
|
|
|
protected function processAdditions( $bean, $ownAdditions ) |
7623
|
|
|
{ |
7624
|
|
|
$beanType = $bean->getMeta( 'type' ); |
7625
|
|
|
|
7626
|
|
|
$cachedIndex = array(); |
7627
|
|
|
foreach ( $ownAdditions as $addition ) { |
7628
|
|
|
if ( $addition instanceof OODBBean ) { |
7629
|
|
|
|
7630
|
|
|
$myFieldLink = $beanType . '_id'; |
7631
|
|
|
$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); |
7632
|
|
|
if ( $alias ) $myFieldLink = $alias . '_id'; |
7633
|
|
|
|
7634
|
|
|
$addition->$myFieldLink = $bean->id; |
|
|
|
|
7635
|
|
|
$addition->setMeta( 'cast.' . $myFieldLink, 'id' ); |
7636
|
|
|
$this->store( $addition ); |
7637
|
|
|
|
7638
|
|
|
} else { |
7639
|
|
|
throw new RedException( 'Array may only contain OODBBeans' ); |
7640
|
|
|
} |
7641
|
|
|
} |
7642
|
|
|
} |
7643
|
|
|
|
7644
|
|
|
/** |
7645
|
|
|
* Dispenses a new bean (a OODBBean Bean Object) |
7646
|
|
|
* of the specified type. Always |
7647
|
|
|
* use this function to get an empty bean object. Never |
7648
|
|
|
* instantiate a OODBBean yourself because it needs |
7649
|
|
|
* to be configured before you can use it with RedBean. This |
7650
|
|
|
* function applies the appropriate initialization / |
7651
|
|
|
* configuration for you. |
7652
|
|
|
* |
7653
|
|
|
* @param string $type type of bean you want to dispense |
7654
|
|
|
* @param string $number number of beans you would like to get |
7655
|
|
|
* @param boolean $alwaysReturnArray if TRUE always returns the result as an array |
7656
|
|
|
* |
7657
|
|
|
* @return OODBBean |
7658
|
|
|
*/ |
7659
|
|
View Code Duplication |
public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) |
7660
|
|
|
{ |
7661
|
|
|
$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; |
7662
|
|
|
$beans = array(); |
7663
|
|
|
for ( $i = 0; $i < $number; $i++ ) { |
7664
|
|
|
$bean = new $OODBBEAN; |
7665
|
|
|
$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); |
|
|
|
|
7666
|
|
|
$this->oodb->signal( 'dispense', $bean ); |
7667
|
|
|
$beans[] = $bean; |
7668
|
|
|
} |
7669
|
|
|
|
7670
|
|
|
return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; |
7671
|
|
|
} |
7672
|
|
|
|
7673
|
|
|
/** |
7674
|
|
|
* Loads a bean from the object database. |
7675
|
|
|
* It searches for a OODBBean Bean Object in the |
7676
|
|
|
* database. It does not matter how this bean has been stored. |
7677
|
|
|
* RedBean uses the primary key ID $id and the string $type |
7678
|
|
|
* to find the bean. The $type specifies what kind of bean you |
7679
|
|
|
* are looking for; this is the same type as used with the |
7680
|
|
|
* dispense() function. If RedBean finds the bean it will return |
7681
|
|
|
* the OODB Bean object; if it cannot find the bean |
7682
|
|
|
* RedBean will return a new bean of type $type and with |
7683
|
|
|
* primary key ID 0. In the latter case it acts basically the |
7684
|
|
|
* same as dispense(). |
7685
|
|
|
* |
7686
|
|
|
* Important note: |
7687
|
|
|
* If the bean cannot be found in the database a new bean of |
7688
|
|
|
* the specified type will be generated and returned. |
7689
|
|
|
* |
7690
|
|
|
* @param string $type type of bean you want to load |
7691
|
|
|
* @param integer $id ID of the bean you want to load |
7692
|
|
|
* |
7693
|
|
|
* @throws SQL |
7694
|
|
|
* |
7695
|
|
|
* @return OODBBean |
7696
|
|
|
* |
7697
|
|
|
*/ |
7698
|
|
View Code Duplication |
public function load( $type, $id ) |
7699
|
|
|
{ |
7700
|
|
|
$bean = $this->dispense( $type ); |
7701
|
|
|
if ( isset( $this->stash[$this->nesting][$id] ) ) { |
7702
|
|
|
$row = $this->stash[$this->nesting][$id]; |
7703
|
|
|
} else { |
7704
|
|
|
try { |
7705
|
|
|
$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); |
|
|
|
|
7706
|
|
|
} catch ( SQLException $exception ) { |
7707
|
|
|
if ( $this->writer->sqlStateIn( $exception->getSQLState(), |
|
|
|
|
7708
|
|
|
array( |
7709
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, |
7710
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) |
7711
|
|
|
) |
7712
|
|
|
) { |
7713
|
|
|
throw $exception; //only throw if frozen |
7714
|
|
|
} |
7715
|
|
|
} |
7716
|
|
|
if ( empty( $rows ) ) { |
7717
|
|
|
return $bean; |
7718
|
|
|
} |
7719
|
|
|
$row = array_pop( $rows ); |
7720
|
|
|
} |
7721
|
|
|
$bean->importRow( $row ); |
7722
|
|
|
$this->nesting++; |
7723
|
|
|
$this->oodb->signal( 'open', $bean ); |
7724
|
|
|
$this->nesting--; |
7725
|
|
|
|
7726
|
|
|
return $bean->setMeta( 'tainted', FALSE ); |
7727
|
|
|
} |
7728
|
|
|
} |
7729
|
|
|
} |
7730
|
|
|
|
7731
|
|
|
namespace RedBeanPHP { |
7732
|
|
|
|
7733
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
7734
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
7735
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
7736
|
|
|
use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; |
|
|
|
|
7737
|
|
|
use RedBeanPHP\AssociationManager as AssociationManager; |
|
|
|
|
7738
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
7739
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
7740
|
|
|
use RedBeanPHP\SimpleModel as SimpleModel; |
|
|
|
|
7741
|
|
|
use RedBeanPHP\BeanHelper as BeanHelper; |
|
|
|
|
7742
|
|
|
use RedBeanPHP\RedException\SQL as SQL; |
|
|
|
|
7743
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
7744
|
|
|
use RedBeanPHP\Repository as Repository; |
|
|
|
|
7745
|
|
|
use RedBeanPHP\Repository\Fluid as FluidRepo; |
|
|
|
|
7746
|
|
|
use RedBeanPHP\Repository\Frozen as FrozenRepo; |
|
|
|
|
7747
|
|
|
|
7748
|
|
|
/** |
7749
|
|
|
* RedBean Object Oriented DataBase. |
7750
|
|
|
* |
7751
|
|
|
* The RedBean OODB Class is the main class of RedBeanPHP. |
7752
|
|
|
* It takes OODBBean objects and stores them to and loads them from the |
7753
|
|
|
* database as well as providing other CRUD functions. This class acts as a |
7754
|
|
|
* object database. |
7755
|
|
|
* |
7756
|
|
|
* @file RedBeanPHP/OODB.php |
7757
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
7758
|
|
|
* @license BSD/GPLv2 |
7759
|
|
|
* |
7760
|
|
|
* @copyright |
7761
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
7762
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
7763
|
|
|
* with this source code in the file license.txt. |
7764
|
|
|
*/ |
7765
|
|
|
class OODB extends Observable |
|
|
|
|
7766
|
|
|
{ |
7767
|
|
|
/** |
7768
|
|
|
* @var array |
7769
|
|
|
*/ |
7770
|
|
|
private static $sqlFilters = array(); |
7771
|
|
|
|
7772
|
|
|
/** |
7773
|
|
|
* @var array |
7774
|
|
|
*/ |
7775
|
|
|
protected $chillList = array(); |
7776
|
|
|
|
7777
|
|
|
|
7778
|
|
|
/** |
7779
|
|
|
* @var array |
7780
|
|
|
*/ |
7781
|
|
|
protected $stash = NULL; |
7782
|
|
|
|
7783
|
|
|
/* |
7784
|
|
|
* @var integer |
7785
|
|
|
*/ |
7786
|
|
|
protected $nesting = 0; |
7787
|
|
|
|
7788
|
|
|
/** |
7789
|
|
|
* @var DBAdapter |
7790
|
|
|
*/ |
7791
|
|
|
protected $writer; |
7792
|
|
|
|
7793
|
|
|
/** |
7794
|
|
|
* @var boolean |
7795
|
|
|
*/ |
7796
|
|
|
protected $isFrozen = FALSE; |
7797
|
|
|
|
7798
|
|
|
/** |
7799
|
|
|
* @var FacadeBeanHelper |
7800
|
|
|
*/ |
7801
|
|
|
protected $beanhelper = NULL; |
7802
|
|
|
|
7803
|
|
|
/** |
7804
|
|
|
* @var AssociationManager |
7805
|
|
|
*/ |
7806
|
|
|
protected $assocManager = NULL; |
7807
|
|
|
|
7808
|
|
|
/** |
7809
|
|
|
* @var Repository |
7810
|
|
|
*/ |
7811
|
|
|
protected $repository = NULL; |
7812
|
|
|
|
7813
|
|
|
/** |
7814
|
|
|
* @var FrozenRepo |
7815
|
|
|
*/ |
7816
|
|
|
protected $frozenRepository = NULL; |
7817
|
|
|
|
7818
|
|
|
/** |
7819
|
|
|
* @var FluidRepo |
7820
|
|
|
*/ |
7821
|
|
|
protected $fluidRepository = NULL; |
7822
|
|
|
|
7823
|
|
|
/** |
7824
|
|
|
* Unboxes a bean from a FUSE model if needed and checks whether the bean is |
7825
|
|
|
* an instance of OODBBean. |
7826
|
|
|
* |
7827
|
|
|
* @param OODBBean $bean bean you wish to unbox |
7828
|
|
|
* |
7829
|
|
|
* @return OODBBean |
7830
|
|
|
* |
7831
|
|
|
* @throws Security |
7832
|
|
|
*/ |
7833
|
|
|
protected function unboxIfNeeded( $bean ) |
7834
|
|
|
{ |
7835
|
|
|
if ( $bean instanceof SimpleModel ) { |
7836
|
|
|
$bean = $bean->unbox(); |
7837
|
|
|
} |
7838
|
|
|
if ( !( $bean instanceof OODBBean ) ) { |
7839
|
|
|
throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) ); |
7840
|
|
|
} |
7841
|
|
|
|
7842
|
|
|
return $bean; |
7843
|
|
|
} |
7844
|
|
|
|
7845
|
|
|
/** |
7846
|
|
|
* Constructor, requires a query writer. |
7847
|
|
|
* |
7848
|
|
|
* @param QueryWriter $writer writer |
7849
|
|
|
* @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled) |
7850
|
|
|
*/ |
7851
|
|
|
public function __construct( QueryWriter $writer, $frozen = FALSE ) |
7852
|
|
|
{ |
7853
|
|
|
if ( $writer instanceof QueryWriter ) { |
7854
|
|
|
$this->writer = $writer; |
|
|
|
|
7855
|
|
|
} |
7856
|
|
|
|
7857
|
|
|
$this->freeze( $frozen ); |
7858
|
|
|
} |
7859
|
|
|
|
7860
|
|
|
/** |
7861
|
|
|
* Toggles fluid or frozen mode. In fluid mode the database |
7862
|
|
|
* structure is adjusted to accomodate your objects. In frozen mode |
7863
|
|
|
* this is not the case. |
7864
|
|
|
* |
7865
|
|
|
* You can also pass an array containing a selection of frozen types. |
7866
|
|
|
* Let's call this chilly mode, it's just like fluid mode except that |
7867
|
|
|
* certain types (i.e. tables) aren't touched. |
7868
|
|
|
* |
7869
|
|
|
* @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode |
7870
|
|
|
* |
7871
|
|
|
* @return void |
7872
|
|
|
*/ |
7873
|
|
|
public function freeze( $toggle ) |
7874
|
|
|
{ |
7875
|
|
|
if ( is_array( $toggle ) ) { |
7876
|
|
|
$this->chillList = $toggle; |
7877
|
|
|
$this->isFrozen = FALSE; |
7878
|
|
|
} else { |
7879
|
|
|
$this->isFrozen = (boolean) $toggle; |
7880
|
|
|
} |
7881
|
|
|
|
7882
|
|
|
if ( $this->isFrozen ) { |
7883
|
|
|
if ( !$this->frozenRepository ) { |
7884
|
|
|
$this->frozenRepository = new FrozenRepo( $this, $this->writer ); |
|
|
|
|
7885
|
|
|
} |
7886
|
|
|
|
7887
|
|
|
$this->repository = $this->frozenRepository; |
7888
|
|
|
|
7889
|
|
|
} else { |
7890
|
|
|
if ( !$this->fluidRepository ) { |
7891
|
|
|
$this->fluidRepository = new FluidRepo( $this, $this->writer ); |
|
|
|
|
7892
|
|
|
} |
7893
|
|
|
|
7894
|
|
|
$this->repository = $this->fluidRepository; |
7895
|
|
|
} |
7896
|
|
|
|
7897
|
|
|
if ( count( self::$sqlFilters ) ) { |
7898
|
|
|
AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); |
7899
|
|
|
} |
7900
|
|
|
|
7901
|
|
|
} |
7902
|
|
|
|
7903
|
|
|
/** |
7904
|
|
|
* Returns the current mode of operation of RedBean. |
7905
|
|
|
* In fluid mode the database |
7906
|
|
|
* structure is adjusted to accomodate your objects. |
7907
|
|
|
* In frozen mode |
7908
|
|
|
* this is not the case. |
7909
|
|
|
* |
7910
|
|
|
* @return boolean |
7911
|
|
|
*/ |
7912
|
|
|
public function isFrozen() |
7913
|
|
|
{ |
7914
|
|
|
return (bool) $this->isFrozen; |
7915
|
|
|
} |
7916
|
|
|
|
7917
|
|
|
/** |
7918
|
|
|
* Determines whether a type is in the chill list. |
7919
|
|
|
* If a type is 'chilled' it's frozen, so its schema cannot be |
7920
|
|
|
* changed anymore. However other bean types may still be modified. |
7921
|
|
|
* This method is a convenience method for other objects to check if |
7922
|
|
|
* the schema of a certain type is locked for modification. |
7923
|
|
|
* |
7924
|
|
|
* @param string $type the type you wish to check |
7925
|
|
|
* |
7926
|
|
|
* @return boolean |
7927
|
|
|
*/ |
7928
|
|
|
public function isChilled( $type ) |
7929
|
|
|
{ |
7930
|
|
|
return (boolean) ( in_array( $type, $this->chillList ) ); |
7931
|
|
|
} |
7932
|
|
|
|
7933
|
|
|
/** |
7934
|
|
|
* Dispenses a new bean (a OODBBean Bean Object) |
7935
|
|
|
* of the specified type. Always |
7936
|
|
|
* use this function to get an empty bean object. Never |
7937
|
|
|
* instantiate a OODBBean yourself because it needs |
7938
|
|
|
* to be configured before you can use it with RedBean. This |
7939
|
|
|
* function applies the appropriate initialization / |
7940
|
|
|
* configuration for you. |
7941
|
|
|
* |
7942
|
|
|
* @param string $type type of bean you want to dispense |
7943
|
|
|
* @param string $number number of beans you would like to get |
7944
|
|
|
* @param boolean $alwaysReturnArray if TRUE always returns the result as an array |
7945
|
|
|
* |
7946
|
|
|
* @return OODBBean |
7947
|
|
|
*/ |
7948
|
|
|
public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) |
7949
|
|
|
{ |
7950
|
|
|
if ( $number < 1 ) { |
7951
|
|
|
if ( $alwaysReturnArray ) return array(); |
|
|
|
|
7952
|
|
|
return NULL; |
7953
|
|
|
} |
7954
|
|
|
|
7955
|
|
|
return $this->repository->dispense( $type, $number, $alwaysReturnArray ); |
7956
|
|
|
} |
7957
|
|
|
|
7958
|
|
|
/** |
7959
|
|
|
* Sets bean helper to be given to beans. |
7960
|
|
|
* Bean helpers assist beans in getting a reference to a toolbox. |
7961
|
|
|
* |
7962
|
|
|
* @param BeanHelper $beanhelper helper |
7963
|
|
|
* |
7964
|
|
|
* @return void |
7965
|
|
|
*/ |
7966
|
|
|
public function setBeanHelper( BeanHelper $beanhelper ) |
7967
|
|
|
{ |
7968
|
|
|
$this->beanhelper = $beanhelper; |
|
|
|
|
7969
|
|
|
} |
7970
|
|
|
|
7971
|
|
|
/** |
7972
|
|
|
* Returns the current bean helper. |
7973
|
|
|
* Bean helpers assist beans in getting a reference to a toolbox. |
7974
|
|
|
* |
7975
|
|
|
* @return BeanHelper |
7976
|
|
|
*/ |
7977
|
|
|
public function getBeanHelper() |
7978
|
|
|
{ |
7979
|
|
|
return $this->beanhelper; |
7980
|
|
|
} |
7981
|
|
|
|
7982
|
|
|
/** |
7983
|
|
|
* Checks whether a OODBBean bean is valid. |
7984
|
|
|
* If the type is not valid or the ID is not valid it will |
7985
|
|
|
* throw an exception: Security. |
7986
|
|
|
* |
7987
|
|
|
* @param OODBBean $bean the bean that needs to be checked |
7988
|
|
|
* |
7989
|
|
|
* @return void |
7990
|
|
|
* |
7991
|
|
|
* @throws Security $exception |
7992
|
|
|
*/ |
7993
|
|
|
public function check( OODBBean $bean ) |
7994
|
|
|
{ |
7995
|
|
|
$this->repository->check( $bean ); |
7996
|
|
|
} |
7997
|
|
|
|
7998
|
|
|
/** |
7999
|
|
|
* Searches the database for a bean that matches conditions $conditions and sql $addSQL |
8000
|
|
|
* and returns an array containing all the beans that have been found. |
8001
|
|
|
* |
8002
|
|
|
* Conditions need to take form: |
8003
|
|
|
* |
8004
|
|
|
* array( |
8005
|
|
|
* 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) |
8006
|
|
|
* 'PROPERTY' => array( POSSIBLE VALUES... ) |
8007
|
|
|
* ); |
8008
|
|
|
* |
8009
|
|
|
* All conditions are glued together using the AND-operator, while all value lists |
8010
|
|
|
* are glued using IN-operators thus acting as OR-conditions. |
8011
|
|
|
* |
8012
|
|
|
* Note that you can use property names; the columns will be extracted using the |
8013
|
|
|
* appropriate bean formatter. |
8014
|
|
|
* |
8015
|
|
|
* @param string $type type of beans you are looking for |
8016
|
|
|
* @param array $conditions list of conditions |
8017
|
|
|
* @param string $addSQL SQL to be used in query |
|
|
|
|
8018
|
|
|
* @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) |
8019
|
|
|
* |
8020
|
|
|
* @return array |
8021
|
|
|
* |
8022
|
|
|
* @throws SQL |
8023
|
|
|
*/ |
8024
|
|
|
public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) |
8025
|
|
|
{ |
8026
|
|
|
return $this->repository->find( $type, $conditions, $sql, $bindings ); |
8027
|
|
|
} |
8028
|
|
|
|
8029
|
|
|
/** |
8030
|
|
|
* Same as find() but returns a BeanCollection. |
8031
|
|
|
* |
8032
|
|
|
* @param string $type type of beans you are looking for |
8033
|
|
|
* @param string $addSQL SQL to be used in query |
|
|
|
|
8034
|
|
|
* @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) |
8035
|
|
|
* |
8036
|
|
|
* @return array |
8037
|
|
|
* |
8038
|
|
|
* @throws SQL |
8039
|
|
|
*/ |
8040
|
|
|
public function findCollection( $type, $sql = NULL, $bindings = array() ) |
8041
|
|
|
{ |
8042
|
|
|
return $this->repository->findCollection( $type, $sql, $bindings ); |
8043
|
|
|
} |
8044
|
|
|
|
8045
|
|
|
/** |
8046
|
|
|
* Checks whether the specified table already exists in the database. |
8047
|
|
|
* Not part of the Object Database interface! |
8048
|
|
|
* |
8049
|
|
|
* @deprecated Use AQueryWriter::typeExists() instead. |
8050
|
|
|
* |
8051
|
|
|
* @param string $table table name |
8052
|
|
|
* |
8053
|
|
|
* @return boolean |
8054
|
|
|
*/ |
8055
|
|
|
public function tableExists( $table ) |
8056
|
|
|
{ |
8057
|
|
|
return $this->repository->tableExists( $table ); |
|
|
|
|
8058
|
|
|
} |
8059
|
|
|
|
8060
|
|
|
/** |
8061
|
|
|
* Stores a bean in the database. This method takes a |
8062
|
|
|
* OODBBean Bean Object $bean and stores it |
8063
|
|
|
* in the database. If the database schema is not compatible |
8064
|
|
|
* with this bean and RedBean runs in fluid mode the schema |
8065
|
|
|
* will be altered to store the bean correctly. |
8066
|
|
|
* If the database schema is not compatible with this bean and |
8067
|
|
|
* RedBean runs in frozen mode it will throw an exception. |
8068
|
|
|
* This function returns the primary key ID of the inserted |
8069
|
|
|
* bean. |
8070
|
|
|
* |
8071
|
|
|
* The return value is an integer if possible. If it is not possible to |
8072
|
|
|
* represent the value as an integer a string will be returned. We use |
8073
|
|
|
* explicit casts instead of functions to preserve performance |
8074
|
|
|
* (0.13 vs 0.28 for 10000 iterations on Core i3). |
8075
|
|
|
* |
8076
|
|
|
* @param OODBBean|SimpleModel $bean bean to store |
8077
|
|
|
* |
8078
|
|
|
* @return integer|string |
8079
|
|
|
* |
8080
|
|
|
* @throws Security |
8081
|
|
|
*/ |
8082
|
|
|
public function store( $bean ) |
8083
|
|
|
{ |
8084
|
|
|
$bean = $this->unboxIfNeeded( $bean ); |
|
|
|
|
8085
|
|
|
return $this->repository->store( $bean ); |
8086
|
|
|
} |
8087
|
|
|
|
8088
|
|
|
/** |
8089
|
|
|
* Loads a bean from the object database. |
8090
|
|
|
* It searches for a OODBBean Bean Object in the |
8091
|
|
|
* database. It does not matter how this bean has been stored. |
8092
|
|
|
* RedBean uses the primary key ID $id and the string $type |
8093
|
|
|
* to find the bean. The $type specifies what kind of bean you |
8094
|
|
|
* are looking for; this is the same type as used with the |
8095
|
|
|
* dispense() function. If RedBean finds the bean it will return |
8096
|
|
|
* the OODB Bean object; if it cannot find the bean |
8097
|
|
|
* RedBean will return a new bean of type $type and with |
8098
|
|
|
* primary key ID 0. In the latter case it acts basically the |
8099
|
|
|
* same as dispense(). |
8100
|
|
|
* |
8101
|
|
|
* Important note: |
8102
|
|
|
* If the bean cannot be found in the database a new bean of |
8103
|
|
|
* the specified type will be generated and returned. |
8104
|
|
|
* |
8105
|
|
|
* @param string $type type of bean you want to load |
8106
|
|
|
* @param integer $id ID of the bean you want to load |
8107
|
|
|
* |
8108
|
|
|
* @throws SQL |
8109
|
|
|
* |
8110
|
|
|
* @return OODBBean |
8111
|
|
|
* |
8112
|
|
|
*/ |
8113
|
|
|
public function load( $type, $id ) |
8114
|
|
|
{ |
8115
|
|
|
return $this->repository->load( $type, $id ); |
8116
|
|
|
} |
8117
|
|
|
|
8118
|
|
|
/** |
8119
|
|
|
* Removes a bean from the database. |
8120
|
|
|
* This function will remove the specified OODBBean |
8121
|
|
|
* Bean Object from the database. |
8122
|
|
|
* |
8123
|
|
|
* @param OODBBean|SimpleModel $bean bean you want to remove from database |
8124
|
|
|
* |
8125
|
|
|
* @return void |
8126
|
|
|
* |
8127
|
|
|
* @throws Security |
8128
|
|
|
*/ |
8129
|
|
|
public function trash( $bean ) |
8130
|
|
|
{ |
8131
|
|
|
$bean = $this->unboxIfNeeded( $bean ); |
|
|
|
|
8132
|
|
|
return $this->repository->trash( $bean ); |
8133
|
|
|
} |
8134
|
|
|
|
8135
|
|
|
/** |
8136
|
|
|
* Returns an array of beans. Pass a type and a series of ids and |
8137
|
|
|
* this method will bring you the corresponding beans. |
8138
|
|
|
* |
8139
|
|
|
* important note: Because this method loads beans using the load() |
8140
|
|
|
* function (but faster) it will return empty beans with ID 0 for |
8141
|
|
|
* every bean that could not be located. The resulting beans will have the |
8142
|
|
|
* passed IDs as their keys. |
8143
|
|
|
* |
8144
|
|
|
* @param string $type type of beans |
8145
|
|
|
* @param array $ids ids to load |
8146
|
|
|
* |
8147
|
|
|
* @return array |
8148
|
|
|
*/ |
8149
|
|
|
public function batch( $type, $ids ) |
8150
|
|
|
{ |
8151
|
|
|
return $this->repository->batch( $type, $ids ); |
8152
|
|
|
} |
8153
|
|
|
|
8154
|
|
|
/** |
8155
|
|
|
* This is a convenience method; it converts database rows |
8156
|
|
|
* (arrays) into beans. Given a type and a set of rows this method |
8157
|
|
|
* will return an array of beans of the specified type loaded with |
8158
|
|
|
* the data fields provided by the result set from the database. |
8159
|
|
|
* |
8160
|
|
|
* @param string $type type of beans you would like to have |
8161
|
|
|
* @param array $rows rows from the database result |
8162
|
|
|
* |
8163
|
|
|
* @return array |
8164
|
|
|
*/ |
8165
|
|
|
public function convertToBeans( $type, $rows ) |
8166
|
|
|
{ |
8167
|
|
|
return $this->repository->convertToBeans( $type, $rows ); |
8168
|
|
|
} |
8169
|
|
|
|
8170
|
|
|
/** |
8171
|
|
|
* Counts the number of beans of type $type. |
8172
|
|
|
* This method accepts a second argument to modify the count-query. |
8173
|
|
|
* A third argument can be used to provide bindings for the SQL snippet. |
8174
|
|
|
* |
8175
|
|
|
* @param string $type type of bean we are looking for |
8176
|
|
|
* @param string $addSQL additional SQL snippet |
8177
|
|
|
* @param array $bindings parameters to bind to SQL |
8178
|
|
|
* |
8179
|
|
|
* @return integer |
8180
|
|
|
* |
8181
|
|
|
* @throws SQL |
8182
|
|
|
*/ |
8183
|
|
|
public function count( $type, $addSQL = '', $bindings = array() ) |
8184
|
|
|
{ |
8185
|
|
|
return $this->repository->count( $type, $addSQL, $bindings ); |
8186
|
|
|
} |
8187
|
|
|
|
8188
|
|
|
/** |
8189
|
|
|
* Trash all beans of a given type. Wipes an entire type of bean. |
8190
|
|
|
* |
8191
|
|
|
* @param string $type type of bean you wish to delete all instances of |
8192
|
|
|
* |
8193
|
|
|
* @return boolean |
8194
|
|
|
* |
8195
|
|
|
* @throws SQL |
8196
|
|
|
*/ |
8197
|
|
|
public function wipe( $type ) |
8198
|
|
|
{ |
8199
|
|
|
return $this->repository->wipe( $type ); |
8200
|
|
|
} |
8201
|
|
|
|
8202
|
|
|
/** |
8203
|
|
|
* Returns an Association Manager for use with OODB. |
8204
|
|
|
* A simple getter function to obtain a reference to the association manager used for |
8205
|
|
|
* storage and more. |
8206
|
|
|
* |
8207
|
|
|
* @return AssociationManager |
8208
|
|
|
* |
8209
|
|
|
* @throws Security |
8210
|
|
|
*/ |
8211
|
|
|
public function getAssociationManager() |
8212
|
|
|
{ |
8213
|
|
|
if ( !isset( $this->assocManager ) ) { |
8214
|
|
|
throw new RedException( 'No association manager available.' ); |
8215
|
|
|
} |
8216
|
|
|
|
8217
|
|
|
return $this->assocManager; |
8218
|
|
|
} |
8219
|
|
|
|
8220
|
|
|
/** |
8221
|
|
|
* Sets the association manager instance to be used by this OODB. |
8222
|
|
|
* A simple setter function to set the association manager to be used for storage and |
8223
|
|
|
* more. |
8224
|
|
|
* |
8225
|
|
|
* @param AssociationManager $assoc sets the association manager to be used |
|
|
|
|
8226
|
|
|
* |
8227
|
|
|
* @return void |
8228
|
|
|
*/ |
8229
|
|
|
public function setAssociationManager( AssociationManager $assocManager ) |
8230
|
|
|
{ |
8231
|
|
|
$this->assocManager = $assocManager; |
8232
|
|
|
} |
8233
|
|
|
|
8234
|
|
|
/** |
8235
|
|
|
* Returns the currently used repository instance. |
8236
|
|
|
* For testing purposes only. |
8237
|
|
|
* |
8238
|
|
|
* @return Repository |
8239
|
|
|
*/ |
8240
|
|
|
public function getCurrentRepository() |
8241
|
|
|
{ |
8242
|
|
|
return $this->repository; |
8243
|
|
|
} |
8244
|
|
|
|
8245
|
|
|
/** |
8246
|
|
|
* Binds an SQL function to a column. |
8247
|
|
|
* This method can be used to setup a decode/encode scheme or |
8248
|
|
|
* perform UUID insertion. This method is especially useful for handling |
8249
|
|
|
* MySQL spatial columns, because they need to be processed first using |
8250
|
|
|
* the asText/GeomFromText functions. |
8251
|
|
|
* |
8252
|
|
|
* @param string $mode (read or write) |
8253
|
|
|
* @param string $field |
8254
|
|
|
* @param string $function |
8255
|
|
|
*/ |
8256
|
|
|
public function bindFunc( $mode, $field, $function ) |
8257
|
|
|
{ |
8258
|
|
|
list( $type, $property ) = explode( '.', $field ); |
8259
|
|
|
$mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ; |
8260
|
|
|
|
8261
|
|
|
if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array(); |
8262
|
|
|
if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array(); |
8263
|
|
|
|
8264
|
|
|
if ( is_null( $function ) ) { |
8265
|
|
|
unset( self::$sqlFilters[$mode][$type][$property] ); |
8266
|
|
|
} else { |
8267
|
|
|
if ($mode === QueryWriter::C_SQLFILTER_WRITE) { |
8268
|
|
|
self::$sqlFilters[$mode][$type][$property] = $function.'(?)'; |
8269
|
|
|
} else { |
8270
|
|
|
self::$sqlFilters[$mode][$type][$property] = $function."($field)"; |
8271
|
|
|
} |
8272
|
|
|
} |
8273
|
|
|
|
8274
|
|
|
AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); |
8275
|
|
|
} |
8276
|
|
|
} |
8277
|
|
|
} |
8278
|
|
|
|
8279
|
|
|
namespace RedBeanPHP { |
8280
|
|
|
|
8281
|
|
|
use RedBeanPHP\OODB as OODB; |
|
|
|
|
8282
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
8283
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
8284
|
|
|
use RedBeanPHP\Adapter as Adapter; |
|
|
|
|
8285
|
|
|
|
8286
|
|
|
/** |
8287
|
|
|
* ToolBox. |
8288
|
|
|
* |
8289
|
|
|
* The toolbox is an integral part of RedBeanPHP providing the basic |
8290
|
|
|
* architectural building blocks to manager objects, helpers and additional tools |
8291
|
|
|
* like plugins. A toolbox contains the three core components of RedBeanPHP: |
8292
|
|
|
* the adapter, the query writer and the core functionality of RedBeanPHP in |
8293
|
|
|
* OODB. |
8294
|
|
|
* |
8295
|
|
|
* @file RedBeanPHP/ToolBox.php |
8296
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
8297
|
|
|
* @license BSD/GPLv2 |
8298
|
|
|
* |
8299
|
|
|
* @copyright |
8300
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
8301
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
8302
|
|
|
* with this source code in the file license.txt. |
8303
|
|
|
*/ |
8304
|
|
|
class ToolBox |
|
|
|
|
8305
|
|
|
{ |
8306
|
|
|
|
8307
|
|
|
/** |
8308
|
|
|
* @var OODB |
8309
|
|
|
*/ |
8310
|
|
|
protected $oodb; |
8311
|
|
|
|
8312
|
|
|
/** |
8313
|
|
|
* @var QueryWriter |
8314
|
|
|
*/ |
8315
|
|
|
protected $writer; |
8316
|
|
|
|
8317
|
|
|
/** |
8318
|
|
|
* @var DBAdapter |
8319
|
|
|
*/ |
8320
|
|
|
protected $adapter; |
8321
|
|
|
|
8322
|
|
|
/** |
8323
|
|
|
* Constructor. |
8324
|
|
|
* The toolbox is an integral part of RedBeanPHP providing the basic |
8325
|
|
|
* architectural building blocks to manager objects, helpers and additional tools |
8326
|
|
|
* like plugins. A toolbox contains the three core components of RedBeanPHP: |
8327
|
|
|
* the adapter, the query writer and the core functionality of RedBeanPHP in |
8328
|
|
|
* OODB. |
8329
|
|
|
* |
8330
|
|
|
* @param OODB $oodb Object Database |
8331
|
|
|
* @param DBAdapter $adapter Adapter |
8332
|
|
|
* @param QueryWriter $writer Writer |
8333
|
|
|
* |
8334
|
|
|
* @return ToolBox |
|
|
|
|
8335
|
|
|
*/ |
8336
|
|
|
public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer ) |
8337
|
|
|
{ |
8338
|
|
|
$this->oodb = $oodb; |
8339
|
|
|
$this->adapter = $adapter; |
|
|
|
|
8340
|
|
|
$this->writer = $writer; |
8341
|
|
|
|
8342
|
|
|
return $this; |
|
|
|
|
8343
|
|
|
} |
8344
|
|
|
|
8345
|
|
|
/** |
8346
|
|
|
* Returns the query writer in this toolbox. |
8347
|
|
|
* The Query Writer is responsible for building the queries for a |
8348
|
|
|
* specific database and executing them through the adapter. |
8349
|
|
|
* |
8350
|
|
|
* @return QueryWriter |
8351
|
|
|
*/ |
8352
|
|
|
public function getWriter() |
8353
|
|
|
{ |
8354
|
|
|
return $this->writer; |
8355
|
|
|
} |
8356
|
|
|
|
8357
|
|
|
/** |
8358
|
|
|
* Returns the OODB instance in this toolbox. |
8359
|
|
|
* OODB is responsible for creating, storing, retrieving and deleting |
8360
|
|
|
* single beans. Other components rely |
8361
|
|
|
* on OODB for their basic functionality. |
8362
|
|
|
* |
8363
|
|
|
* @return OODB |
8364
|
|
|
*/ |
8365
|
|
|
public function getRedBean() |
8366
|
|
|
{ |
8367
|
|
|
return $this->oodb; |
8368
|
|
|
} |
8369
|
|
|
|
8370
|
|
|
/** |
8371
|
|
|
* Returns the database adapter in this toolbox. |
8372
|
|
|
* The adapter is responsible for executing the query and binding the values. |
8373
|
|
|
* The adapter also takes care of transaction handling. |
8374
|
|
|
* |
8375
|
|
|
* @return DBAdapter |
8376
|
|
|
*/ |
8377
|
|
|
public function getDatabaseAdapter() |
8378
|
|
|
{ |
8379
|
|
|
return $this->adapter; |
8380
|
|
|
} |
8381
|
|
|
} |
8382
|
|
|
} |
8383
|
|
|
|
8384
|
|
|
namespace RedBeanPHP { |
8385
|
|
|
|
8386
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
8387
|
|
|
use RedBeanPHP\OODB as OODB; |
|
|
|
|
8388
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
8389
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
8390
|
|
|
|
8391
|
|
|
/** |
8392
|
|
|
* RedBeanPHP Finder. |
8393
|
|
|
* Service class to find beans. For the most part this class |
8394
|
|
|
* offers user friendly utility methods for interacting with the |
8395
|
|
|
* OODB::find() method, which is rather complex. This class can be |
8396
|
|
|
* used to find beans using plain old SQL queries. |
8397
|
|
|
* |
8398
|
|
|
* @file RedBeanPHP/Finder.php |
8399
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
8400
|
|
|
* @license BSD/GPLv2 |
8401
|
|
|
* |
8402
|
|
|
* @copyright |
8403
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
8404
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
8405
|
|
|
* with this source code in the file license.txt. |
8406
|
|
|
*/ |
8407
|
|
|
class Finder |
|
|
|
|
8408
|
|
|
{ |
8409
|
|
|
/** |
8410
|
|
|
* @var ToolBox |
8411
|
|
|
*/ |
8412
|
|
|
protected $toolbox; |
8413
|
|
|
|
8414
|
|
|
/** |
8415
|
|
|
* @var OODB |
8416
|
|
|
*/ |
8417
|
|
|
protected $redbean; |
8418
|
|
|
|
8419
|
|
|
/** |
8420
|
|
|
* Constructor. |
8421
|
|
|
* The Finder requires a toolbox. |
8422
|
|
|
* |
8423
|
|
|
* @param ToolBox $toolbox |
8424
|
|
|
*/ |
8425
|
|
|
public function __construct( ToolBox $toolbox ) |
8426
|
|
|
{ |
8427
|
|
|
$this->toolbox = $toolbox; |
8428
|
|
|
$this->redbean = $toolbox->getRedBean(); |
8429
|
|
|
} |
8430
|
|
|
|
8431
|
|
|
/** |
8432
|
|
|
* Finds a bean using a type and a where clause (SQL). |
8433
|
|
|
* As with most Query tools in RedBean you can provide values to |
8434
|
|
|
* be inserted in the SQL statement by populating the value |
8435
|
|
|
* array parameter; you can either use the question mark notation |
8436
|
|
|
* or the slot-notation (:keyname). |
8437
|
|
|
* |
8438
|
|
|
* @param string $type type the type of bean you are looking for |
8439
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
8440
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
8441
|
|
|
* |
8442
|
|
|
* @return array |
8443
|
|
|
* |
8444
|
|
|
* @throws Security |
8445
|
|
|
*/ |
8446
|
|
|
public function find( $type, $sql = NULL, $bindings = array() ) |
8447
|
|
|
{ |
8448
|
|
|
if ( !is_array( $bindings ) ) { |
8449
|
|
|
throw new RedException( |
8450
|
|
|
'Expected array, ' . gettype( $bindings ) . ' given.' |
8451
|
|
|
); |
8452
|
|
|
} |
8453
|
|
|
|
8454
|
|
|
return $this->redbean->find( $type, array(), $sql, $bindings ); |
8455
|
|
|
} |
8456
|
|
|
|
8457
|
|
|
/** |
8458
|
|
|
* Like find() but also exports the beans as an array. |
8459
|
|
|
* |
8460
|
|
|
* @see Finder::findAndExport |
8461
|
|
|
* |
8462
|
|
|
* @param string $type type the type of bean you are looking for |
8463
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
8464
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
8465
|
|
|
* |
8466
|
|
|
* @return array |
8467
|
|
|
*/ |
8468
|
|
|
public function findAndExport( $type, $sql = NULL, $bindings = array() ) |
8469
|
|
|
{ |
8470
|
|
|
$arr = array(); |
8471
|
|
|
foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) { |
8472
|
|
|
$arr[] = $item->export(); |
8473
|
|
|
} |
8474
|
|
|
|
8475
|
|
|
return $arr; |
8476
|
|
|
} |
8477
|
|
|
|
8478
|
|
|
/** |
8479
|
|
|
* Like find() but returns just one bean instead of an array of beans. |
8480
|
|
|
* This method will return only the first bean of the array. |
8481
|
|
|
* |
8482
|
|
|
* @see Finder::find |
8483
|
|
|
* |
8484
|
|
|
* @param string $type type the type of bean you are looking for |
8485
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
8486
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
8487
|
|
|
* |
8488
|
|
|
* @return OODBBean |
8489
|
|
|
*/ |
8490
|
|
|
public function findOne( $type, $sql = NULL, $bindings = array() ) |
8491
|
|
|
{ |
8492
|
|
|
$sql = $this->toolbox->getWriter()->glueLimitOne( $sql ); |
8493
|
|
|
|
8494
|
|
|
$items = $this->find( $type, $sql, $bindings ); |
8495
|
|
|
|
8496
|
|
|
if ( empty($items) ) { |
8497
|
|
|
return NULL; |
8498
|
|
|
} |
8499
|
|
|
|
8500
|
|
|
return reset( $items ); |
8501
|
|
|
} |
8502
|
|
|
|
8503
|
|
|
/** |
8504
|
|
|
* Like find() but returns the last bean of the result array. |
8505
|
|
|
* Opposite of Finder::findLast(). |
8506
|
|
|
* |
8507
|
|
|
* @see Finder::find |
8508
|
|
|
* |
8509
|
|
|
* @param string $type the type of bean you are looking for |
8510
|
|
|
* @param string $sql SQL query to find the desired bean, starting right after WHERE clause |
8511
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
8512
|
|
|
* |
8513
|
|
|
* @return OODBBean |
8514
|
|
|
*/ |
8515
|
|
|
public function findLast( $type, $sql = NULL, $bindings = array() ) |
8516
|
|
|
{ |
8517
|
|
|
$items = $this->find( $type, $sql, $bindings ); |
8518
|
|
|
|
8519
|
|
|
if ( empty($items) ) { |
8520
|
|
|
return NULL; |
8521
|
|
|
} |
8522
|
|
|
|
8523
|
|
|
return end( $items ); |
8524
|
|
|
} |
8525
|
|
|
|
8526
|
|
|
/** |
8527
|
|
|
* Tries to find beans of a certain type, |
8528
|
|
|
* if no beans are found, it dispenses a bean of that type. |
8529
|
|
|
* |
8530
|
|
|
* @see Finder::find |
8531
|
|
|
* |
8532
|
|
|
* @param string $type the type of bean you are looking for |
8533
|
|
|
* @param string $sql SQL query to find the desired bean, starting right after WHERE clause |
8534
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
8535
|
|
|
* |
8536
|
|
|
* @return array |
8537
|
|
|
*/ |
8538
|
|
|
public function findOrDispense( $type, $sql = NULL, $bindings = array() ) |
8539
|
|
|
{ |
8540
|
|
|
$foundBeans = $this->find( $type, $sql, $bindings ); |
8541
|
|
|
|
8542
|
|
|
if ( empty( $foundBeans ) ) { |
8543
|
|
|
return array( $this->redbean->dispense( $type ) ); |
8544
|
|
|
} else { |
8545
|
|
|
return $foundBeans; |
8546
|
|
|
} |
8547
|
|
|
} |
8548
|
|
|
|
8549
|
|
|
/** |
8550
|
|
|
* Finds a BeanCollection using the repository. |
8551
|
|
|
* A bean collection can be used to retrieve one bean at a time using |
8552
|
|
|
* cursors - this is useful for processing large datasets. A bean collection |
8553
|
|
|
* will not load all beans into memory all at once, just one at a time. |
8554
|
|
|
* |
8555
|
|
|
* @param string $type the type of bean you are looking for |
8556
|
|
|
* @param string $sql SQL query to find the desired bean, starting right after WHERE clause |
8557
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
8558
|
|
|
* |
8559
|
|
|
* @return BeanCollection |
8560
|
|
|
*/ |
8561
|
|
|
public function findCollection( $type, $sql, $bindings = array() ) |
8562
|
|
|
{ |
8563
|
|
|
return $this->redbean->findCollection( $type, $sql, $bindings ); |
8564
|
|
|
} |
8565
|
|
|
|
8566
|
|
|
/** |
8567
|
|
|
* Finds or creates a bean. |
8568
|
|
|
* Tries to find a bean with certain properties specified in the second |
8569
|
|
|
* parameter ($like). If the bean is found, it will be returned. |
8570
|
|
|
* If multiple beans are found, only the first will be returned. |
8571
|
|
|
* If no beans match the criteria, a new bean will be dispensed, |
8572
|
|
|
* the criteria will be imported as properties and this new bean |
8573
|
|
|
* will be stored and returned. |
8574
|
|
|
* |
8575
|
|
|
* Format of criteria set: property => value |
8576
|
|
|
* The criteria set also supports OR-conditions: property => array( value1, orValue2 ) |
8577
|
|
|
* |
8578
|
|
|
* @param string $type type of bean to search for |
8579
|
|
|
* @param array $like criteria set describing bean to search for |
8580
|
|
|
* |
8581
|
|
|
* @return OODBBean |
8582
|
|
|
*/ |
8583
|
|
|
public function findOrCreate( $type, $like = array() ) |
8584
|
|
|
{ |
8585
|
|
|
$beans = $this->findLike( $type, $like ); |
8586
|
|
|
if ( count( $beans ) ) { |
8587
|
|
|
$bean = reset( $beans ); |
8588
|
|
|
return $bean; |
8589
|
|
|
} |
8590
|
|
|
|
8591
|
|
|
$bean = $this->redbean->dispense( $type ); |
8592
|
|
|
$bean->import( $like ); |
8593
|
|
|
$this->redbean->store( $bean ); |
8594
|
|
|
return $bean; |
8595
|
|
|
} |
8596
|
|
|
|
8597
|
|
|
/** |
8598
|
|
|
* Finds beans by its type and a certain criteria set. |
8599
|
|
|
* |
8600
|
|
|
* Format of criteria set: property => value |
8601
|
|
|
* The criteria set also supports OR-conditions: property => array( value1, orValue2 ) |
8602
|
|
|
* |
8603
|
|
|
* If the additional SQL is a condition, this condition will be glued to the rest |
8604
|
|
|
* of the query using an AND operator. Note that this is as far as this method |
8605
|
|
|
* can go, there is no way to glue additional SQL using an OR-condition. |
8606
|
|
|
* This method provides access to an underlying mechanism in the RedBeanPHP architecture |
8607
|
|
|
* to find beans using criteria sets. However, please do not use this method |
8608
|
|
|
* for complex queries, use plain SQL instead ( the regular find method ) as it is |
8609
|
|
|
* more suitable for the job. This method is |
8610
|
|
|
* meant for basic search-by-example operations. |
8611
|
|
|
* |
8612
|
|
|
* @param string $type type of bean to search for |
8613
|
|
|
* @param array $conditions criteria set describing the bean to search for |
8614
|
|
|
* @param string $sql additional SQL (for sorting) |
8615
|
|
|
* |
8616
|
|
|
* @return array |
8617
|
|
|
*/ |
8618
|
|
|
public function findLike( $type, $conditions = array(), $sql = '' ) |
8619
|
|
|
{ |
8620
|
|
|
if ( count( $conditions ) > 0 ) { |
8621
|
|
|
foreach( $conditions as $key => $condition ) { |
8622
|
|
|
if ( !count( $condition ) ) unset( $conditions[$key] ); |
8623
|
|
|
} |
8624
|
|
|
} |
8625
|
|
|
|
8626
|
|
|
return $this->redbean->find( $type, $conditions, $sql ); |
8627
|
|
|
} |
8628
|
|
|
|
8629
|
|
|
/** |
8630
|
|
|
* Returns a hashmap with bean arrays keyed by type using an SQL |
8631
|
|
|
* query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review' |
8632
|
|
|
* this method will return movie and review beans. |
8633
|
|
|
* |
8634
|
|
|
* Example: |
8635
|
|
|
* |
8636
|
|
|
* $stuff = $finder->findMulti('movie,review', ' |
8637
|
|
|
* SELECT movie.*, review.* FROM movie |
8638
|
|
|
* LEFT JOIN review ON review.movie_id = movie.id'); |
8639
|
|
|
* |
8640
|
|
|
* After this operation, $stuff will contain an entry 'movie' containing all |
8641
|
|
|
* movies and an entry named 'review' containing all reviews (all beans). |
8642
|
|
|
* You can also pass bindings. |
8643
|
|
|
* |
8644
|
|
|
* If you want to re-map your beans, so you can use $movie->ownReviewList without |
8645
|
|
|
* having RedBeanPHP executing an SQL query you can use the fourth parameter to |
8646
|
|
|
* define a selection of remapping closures. |
8647
|
|
|
* |
8648
|
|
|
* The remapping argument (optional) should contain an array of arrays. |
8649
|
|
|
* Each array in the remapping array should contain the following entries: |
8650
|
|
|
* |
8651
|
|
|
* array( |
8652
|
|
|
* 'a' => TYPE A |
8653
|
|
|
* 'b' => TYPE B |
8654
|
|
|
* 'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS |
8655
|
|
|
* 'do' => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS |
8656
|
|
|
* ) |
8657
|
|
|
* |
8658
|
|
|
* Using this mechanism you can build your own 'preloader' with tiny function |
8659
|
|
|
* snippets (and those can be re-used and shared online of course). |
8660
|
|
|
* |
8661
|
|
|
* Example: |
8662
|
|
|
* |
8663
|
|
|
* array( |
8664
|
|
|
* 'a' => 'movie' //define A as movie |
8665
|
|
|
* 'b' => 'review' //define B as review |
8666
|
|
|
* 'matcher' => function( $a, $b ) { |
8667
|
|
|
* return ( $b->movie_id == $a->id ); //Perform action if review.movie_id equals movie.id |
8668
|
|
|
* } |
8669
|
|
|
* 'do' => function( $a, $b ) { |
8670
|
|
|
* $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie |
8671
|
|
|
* $a->clearHistory(); //optional, act 'as if these beans have been loaded through ownReviewList'. |
8672
|
|
|
* } |
8673
|
|
|
* ) |
8674
|
|
|
* |
8675
|
|
|
* The Query Template parameter is optional as well but can be used to |
8676
|
|
|
* set a different SQL template (sprintf-style) for processing the original query. |
8677
|
|
|
* |
8678
|
|
|
* @note the SQL query provided IS NOT THE ONE used internally by this function, |
8679
|
|
|
* this function will pre-process the query to get all the data required to find the beans. |
8680
|
|
|
* |
8681
|
|
|
* @note if you use the 'book.*' notation make SURE you're |
8682
|
|
|
* selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because |
8683
|
|
|
* it's actually an SQL-like template SLOT, not real SQL. |
8684
|
|
|
* |
8685
|
|
|
* @note instead of an SQL query you can pass a result array as well. |
8686
|
|
|
* |
8687
|
|
|
* @param string|array $types a list of types (either array or comma separated string) |
8688
|
|
|
* @param string|array $sqlOrArr an SQL query or an array of prefetched records |
|
|
|
|
8689
|
|
|
* @param array $bindings optional, bindings for SQL query |
8690
|
|
|
* @param array $remappings optional, an array of remapping arrays |
8691
|
|
|
* @param string $queryTemplate optional, query template |
8692
|
|
|
* |
8693
|
|
|
* @return array |
8694
|
|
|
*/ |
8695
|
|
|
public function findMulti( $types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' ) |
8696
|
|
|
{ |
8697
|
|
|
if ( !is_array( $types ) ) $types = explode( ',', $types ); |
8698
|
|
|
if ( !is_array( $sql ) ) { |
8699
|
|
|
$writer = $this->toolbox->getWriter(); |
8700
|
|
|
$adapter = $this->toolbox->getDatabaseAdapter(); |
8701
|
|
|
|
8702
|
|
|
//Repair the query, replace book.* with book.id AS book_id etc.. |
8703
|
|
|
foreach( $types as $type ) { |
8704
|
|
|
$pattern = " {$type}.*"; |
8705
|
|
|
if ( strpos( $sql, $pattern ) !== FALSE ) { |
8706
|
|
|
$newSelectorArray = array(); |
8707
|
|
|
$columns = $writer->getColumns( $type ); |
8708
|
|
|
foreach( $columns as $column => $definition ) { |
8709
|
|
|
$newSelectorArray[] = sprintf( $queryTemplate, $type, $column, $type, $column ); |
8710
|
|
|
} |
8711
|
|
|
$newSelector = implode( ',', $newSelectorArray ); |
8712
|
|
|
$sql = str_replace( $pattern, $newSelector, $sql ); |
8713
|
|
|
} |
8714
|
|
|
} |
8715
|
|
|
|
8716
|
|
|
$rows = $adapter->get( $sql, $bindings ); |
8717
|
|
|
} else { |
8718
|
|
|
$rows = $sql; |
8719
|
|
|
} |
8720
|
|
|
|
8721
|
|
|
//Gather the bean data from the query results using the prefix |
8722
|
|
|
$wannaBeans = array(); |
8723
|
|
|
foreach( $types as $type ) { |
8724
|
|
|
$wannaBeans[$type] = array(); |
8725
|
|
|
$prefix = "{$type}__"; |
8726
|
|
|
foreach( $rows as $rowkey=>$row ) { |
8727
|
|
|
$wannaBean = array(); |
8728
|
|
|
foreach( $row as $cell => $value ) { |
8729
|
|
|
if ( strpos( $cell, $prefix ) === 0 ) { |
8730
|
|
|
$property = substr( $cell, strlen( $prefix ) ); |
8731
|
|
|
unset( $rows[$rowkey][$cell] ); |
8732
|
|
|
$wannaBean[$property] = $value; |
8733
|
|
|
} |
8734
|
|
|
} |
8735
|
|
|
if ( !isset( $wannaBean['id'] ) ) continue; |
8736
|
|
|
if ( is_null( $wannaBean['id'] ) ) continue; |
8737
|
|
|
$wannaBeans[$type][$wannaBean['id']] = $wannaBean; |
8738
|
|
|
} |
8739
|
|
|
} |
8740
|
|
|
|
8741
|
|
|
//Turn the rows into beans |
8742
|
|
|
$beans = array(); |
8743
|
|
|
foreach( $wannaBeans as $type => $wannabees ) { |
8744
|
|
|
$beans[$type] = $this->redbean->convertToBeans( $type, $wannabees ); |
8745
|
|
|
} |
8746
|
|
|
|
8747
|
|
|
//Apply additional re-mappings |
8748
|
|
|
foreach($remappings as $remapping) { |
8749
|
|
|
$a = $remapping['a']; |
8750
|
|
|
$b = $remapping['b']; |
8751
|
|
|
$matcher = $remapping['matcher']; |
8752
|
|
|
$do = $remapping['do']; |
8753
|
|
|
foreach( $beans[$a] as $bean ) { |
8754
|
|
|
foreach( $beans[$b] as $putBean ) { |
8755
|
|
|
if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping ); |
8756
|
|
|
} |
8757
|
|
|
} |
8758
|
|
|
} |
8759
|
|
|
return $beans; |
8760
|
|
|
} |
8761
|
|
|
} |
8762
|
|
|
} |
8763
|
|
|
|
8764
|
|
|
namespace RedBeanPHP { |
8765
|
|
|
|
8766
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
8767
|
|
|
use RedBeanPHP\OODB as OODB; |
|
|
|
|
8768
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
8769
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
8770
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
8771
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
8772
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
8773
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
8774
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
8775
|
|
|
|
8776
|
|
|
/** |
8777
|
|
|
* Association Manager. |
8778
|
|
|
* Manages simple bean associations. |
8779
|
|
|
* |
8780
|
|
|
* @file RedBeanPHP/AssociationManager.php |
8781
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
8782
|
|
|
* @license BSD/GPLv2 |
8783
|
|
|
* |
8784
|
|
|
* @copyright |
8785
|
|
|
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
8786
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
8787
|
|
|
* with this source code in the file license.txt. |
8788
|
|
|
*/ |
8789
|
|
|
class AssociationManager extends Observable |
|
|
|
|
8790
|
|
|
{ |
8791
|
|
|
/** |
8792
|
|
|
* @var OODB |
8793
|
|
|
*/ |
8794
|
|
|
protected $oodb; |
8795
|
|
|
|
8796
|
|
|
/** |
8797
|
|
|
* @var DBAdapter |
8798
|
|
|
*/ |
8799
|
|
|
protected $adapter; |
8800
|
|
|
|
8801
|
|
|
/** |
8802
|
|
|
* @var QueryWriter |
8803
|
|
|
*/ |
8804
|
|
|
protected $writer; |
8805
|
|
|
|
8806
|
|
|
/** |
8807
|
|
|
* Handles\Exceptions. Suppresses exceptions caused by missing structures. |
8808
|
|
|
* |
8809
|
|
|
* @param\Exception $exception |
8810
|
|
|
* |
8811
|
|
|
* @return void |
8812
|
|
|
* |
8813
|
|
|
* @throws\Exception |
8814
|
|
|
*/ |
8815
|
|
View Code Duplication |
private function handleException(\Exception $exception ) |
8816
|
|
|
{ |
8817
|
|
|
if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(), |
|
|
|
|
8818
|
|
|
array( |
8819
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, |
8820
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) |
8821
|
|
|
) |
8822
|
|
|
) { |
8823
|
|
|
throw $exception; |
8824
|
|
|
} |
8825
|
|
|
} |
8826
|
|
|
|
8827
|
|
|
/** |
8828
|
|
|
* Internal method. |
8829
|
|
|
* Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and |
8830
|
|
|
* $bindings bindings. If $getLinks is TRUE, link rows are returned instead. |
8831
|
|
|
* |
8832
|
|
|
* @param OODBBean $bean reference bean |
8833
|
|
|
* @param string $type target type |
8834
|
|
|
* @param string $sql additional SQL snippet |
8835
|
|
|
* @param array $bindings bindings |
8836
|
|
|
* |
8837
|
|
|
* @return array |
8838
|
|
|
* |
8839
|
|
|
* @throws Security |
8840
|
|
|
* @throws SQL |
8841
|
|
|
*/ |
8842
|
|
|
private function relatedRows( $bean, $type, $sql = '', $bindings = array() ) |
8843
|
|
|
{ |
8844
|
|
|
$ids = array( $bean->id ); |
|
|
|
|
8845
|
|
|
$sourceType = $bean->getMeta( 'type' ); |
8846
|
|
|
try { |
8847
|
|
|
return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings ); |
8848
|
|
|
} catch ( SQLException $exception ) { |
8849
|
|
|
$this->handleException( $exception ); |
8850
|
|
|
return array(); |
8851
|
|
|
} |
8852
|
|
|
} |
8853
|
|
|
|
8854
|
|
|
/** |
8855
|
|
|
* Associates a pair of beans. This method associates two beans, no matter |
8856
|
|
|
* what types. Accepts a base bean that contains data for the linking record. |
8857
|
|
|
* This method is used by associate. This method also accepts a base bean to be used |
8858
|
|
|
* as the template for the link record in the database. |
8859
|
|
|
* |
8860
|
|
|
* @param OODBBean $bean1 first bean |
8861
|
|
|
* @param OODBBean $bean2 second bean |
8862
|
|
|
* @param OODBBean $bean base bean (association record) |
8863
|
|
|
* |
8864
|
|
|
* @throws\Exception|SQL |
8865
|
|
|
* |
8866
|
|
|
* @return mixed |
8867
|
|
|
*/ |
8868
|
|
|
protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean ) |
8869
|
|
|
{ |
8870
|
|
|
$type = $bean->getMeta( 'type' ); |
8871
|
|
|
$property1 = $bean1->getMeta( 'type' ) . '_id'; |
8872
|
|
|
$property2 = $bean2->getMeta( 'type' ) . '_id'; |
8873
|
|
|
|
8874
|
|
|
if ( $property1 == $property2 ) { |
8875
|
|
|
$property2 = $bean2->getMeta( 'type' ) . '2_id'; |
8876
|
|
|
} |
8877
|
|
|
|
8878
|
|
|
$this->oodb->store( $bean1 ); |
8879
|
|
|
$this->oodb->store( $bean2 ); |
8880
|
|
|
|
8881
|
|
|
$bean->setMeta( "cast.$property1", "id" ); |
8882
|
|
|
$bean->setMeta( "cast.$property2", "id" ); |
8883
|
|
|
$bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) ); |
8884
|
|
|
|
8885
|
|
|
$bean->$property1 = $bean1->id; |
|
|
|
|
8886
|
|
|
$bean->$property2 = $bean2->id; |
|
|
|
|
8887
|
|
|
|
8888
|
|
|
$results = array(); |
8889
|
|
|
|
8890
|
|
|
try { |
8891
|
|
|
$id = $this->oodb->store( $bean ); |
8892
|
|
|
$results[] = $id; |
8893
|
|
|
} catch ( SQLException $exception ) { |
8894
|
|
|
if ( !$this->writer->sqlStateIn( $exception->getSQLState(), |
8895
|
|
|
array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) ) |
8896
|
|
|
) { |
8897
|
|
|
throw $exception; |
8898
|
|
|
} |
8899
|
|
|
} |
8900
|
|
|
|
8901
|
|
|
return $results; |
8902
|
|
|
} |
8903
|
|
|
|
8904
|
|
|
/** |
8905
|
|
|
* Constructor |
8906
|
|
|
* |
8907
|
|
|
* @param ToolBox $tools toolbox |
8908
|
|
|
*/ |
8909
|
|
|
public function __construct( ToolBox $tools ) |
8910
|
|
|
{ |
8911
|
|
|
$this->oodb = $tools->getRedBean(); |
8912
|
|
|
$this->adapter = $tools->getDatabaseAdapter(); |
8913
|
|
|
$this->writer = $tools->getWriter(); |
8914
|
|
|
$this->toolbox = $tools; |
|
|
|
|
8915
|
|
|
} |
8916
|
|
|
|
8917
|
|
|
/** |
8918
|
|
|
* Creates a table name based on a types array. |
8919
|
|
|
* Manages the get the correct name for the linking table for the |
8920
|
|
|
* types provided. |
8921
|
|
|
* |
8922
|
|
|
* @todo find a nice way to decouple this class from QueryWriter? |
8923
|
|
|
* |
8924
|
|
|
* @param array $types 2 types as strings |
8925
|
|
|
* |
8926
|
|
|
* @return string |
8927
|
|
|
*/ |
8928
|
|
|
public function getTable( $types ) |
8929
|
|
|
{ |
8930
|
|
|
return $this->writer->getAssocTable( $types ); |
8931
|
|
|
} |
8932
|
|
|
|
8933
|
|
|
/** |
8934
|
|
|
* Associates two beans in a many-to-many relation. |
8935
|
|
|
* This method will associate two beans and store the connection between the |
8936
|
|
|
* two in a link table. Instead of two single beans this method also accepts |
8937
|
|
|
* two sets of beans. Returns the ID or the IDs of the linking beans. |
8938
|
|
|
* |
8939
|
|
|
* @param OODBBean|array $beans1 one or more beans to form the association |
8940
|
|
|
* @param OODBBean|array $beans2 one or more beans to form the association |
8941
|
|
|
* |
8942
|
|
|
* @return array |
8943
|
|
|
*/ |
8944
|
|
|
public function associate( $beans1, $beans2 ) |
8945
|
|
|
{ |
8946
|
|
|
if ( !is_array( $beans1 ) ) { |
8947
|
|
|
$beans1 = array( $beans1 ); |
8948
|
|
|
} |
8949
|
|
|
|
8950
|
|
|
if ( !is_array( $beans2 ) ) { |
8951
|
|
|
$beans2 = array( $beans2 ); |
8952
|
|
|
} |
8953
|
|
|
|
8954
|
|
|
$results = array(); |
8955
|
|
|
foreach ( $beans1 as $bean1 ) { |
8956
|
|
|
foreach ( $beans2 as $bean2 ) { |
8957
|
|
|
$table = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) ); |
8958
|
|
|
$bean = $this->oodb->dispense( $table ); |
8959
|
|
|
$results[] = $this->associateBeans( $bean1, $bean2, $bean ); |
8960
|
|
|
} |
8961
|
|
|
} |
8962
|
|
|
|
8963
|
|
|
return ( count( $results ) > 1 ) ? $results : reset( $results ); |
8964
|
|
|
} |
8965
|
|
|
|
8966
|
|
|
/** |
8967
|
|
|
* Counts the number of related beans in an N-M relation. |
8968
|
|
|
* This method returns the number of beans of type $type associated |
8969
|
|
|
* with reference bean(s) $bean. The query can be tuned using an |
8970
|
|
|
* SQL snippet for additional filtering. |
8971
|
|
|
* |
8972
|
|
|
* @param OODBBean|array $bean a bean object or an array of beans |
8973
|
|
|
* @param string $type type of bean you're interested in |
8974
|
|
|
* @param string $sql SQL snippet (optional) |
8975
|
|
|
* @param array $bindings bindings for your SQL string |
8976
|
|
|
* |
8977
|
|
|
* @return integer |
8978
|
|
|
* |
8979
|
|
|
* @throws Security |
8980
|
|
|
*/ |
8981
|
|
|
public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() ) |
8982
|
|
|
{ |
8983
|
|
|
if ( !( $bean instanceof OODBBean ) ) { |
8984
|
|
|
throw new RedException( |
8985
|
|
|
'Expected array or OODBBean but got:' . gettype( $bean ) |
8986
|
|
|
); |
8987
|
|
|
} |
8988
|
|
|
|
8989
|
|
|
if ( !$bean->id ) { |
|
|
|
|
8990
|
|
|
return 0; |
8991
|
|
|
} |
8992
|
|
|
|
8993
|
|
|
$beanType = $bean->getMeta( 'type' ); |
8994
|
|
|
|
8995
|
|
|
try { |
8996
|
|
|
return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings ); |
|
|
|
|
8997
|
|
|
} catch ( SQLException $exception ) { |
8998
|
|
|
$this->handleException( $exception ); |
8999
|
|
|
|
9000
|
|
|
return 0; |
9001
|
|
|
} |
9002
|
|
|
} |
9003
|
|
|
|
9004
|
|
|
/** |
9005
|
|
|
* Breaks the association between two beans. This method unassociates two beans. If the |
9006
|
|
|
* method succeeds the beans will no longer form an association. In the database |
9007
|
|
|
* this means that the association record will be removed. This method uses the |
9008
|
|
|
* OODB trash() method to remove the association links, thus giving FUSE models the |
9009
|
|
|
* opportunity to hook-in additional business logic. If the $fast parameter is |
9010
|
|
|
* set to boolean TRUE this method will remove the beans without their consent, |
9011
|
|
|
* bypassing FUSE. This can be used to improve performance. |
9012
|
|
|
* |
9013
|
|
|
* @param OODBBean $bean1 first bean |
|
|
|
|
9014
|
|
|
* @param OODBBean $bean2 second bean |
|
|
|
|
9015
|
|
|
* @param boolean $fast If TRUE, removes the entries by query without FUSE |
9016
|
|
|
* |
9017
|
|
|
* @return void |
9018
|
|
|
*/ |
9019
|
|
|
public function unassociate( $beans1, $beans2, $fast = NULL ) |
9020
|
|
|
{ |
9021
|
|
|
$beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1; |
9022
|
|
|
$beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2; |
9023
|
|
|
|
9024
|
|
|
foreach ( $beans1 as $bean1 ) { |
9025
|
|
|
foreach ( $beans2 as $bean2 ) { |
9026
|
|
|
try { |
9027
|
|
|
$this->oodb->store( $bean1 ); |
9028
|
|
|
$this->oodb->store( $bean2 ); |
9029
|
|
|
|
9030
|
|
|
$type1 = $bean1->getMeta( 'type' ); |
9031
|
|
|
$type2 = $bean2->getMeta( 'type' ); |
9032
|
|
|
|
9033
|
|
|
$row = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id ); |
9034
|
|
|
$linkType = $this->getTable( array( $type1, $type2 ) ); |
9035
|
|
|
|
9036
|
|
|
if ( $fast ) { |
9037
|
|
|
$this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) ); |
9038
|
|
|
|
9039
|
|
|
return; |
9040
|
|
|
} |
9041
|
|
|
|
9042
|
|
|
$beans = $this->oodb->convertToBeans( $linkType, array( $row ) ); |
9043
|
|
|
|
9044
|
|
|
if ( count( $beans ) > 0 ) { |
9045
|
|
|
$bean = reset( $beans ); |
9046
|
|
|
$this->oodb->trash( $bean ); |
9047
|
|
|
} |
9048
|
|
|
} catch ( SQLException $exception ) { |
9049
|
|
|
$this->handleException( $exception ); |
9050
|
|
|
} |
9051
|
|
|
} |
9052
|
|
|
} |
9053
|
|
|
} |
9054
|
|
|
|
9055
|
|
|
/** |
9056
|
|
|
* Removes all relations for a bean. This method breaks every connection between |
9057
|
|
|
* a certain bean $bean and every other bean of type $type. Warning: this method |
9058
|
|
|
* is really fast because it uses a direct SQL query however it does not inform the |
9059
|
|
|
* models about this. If you want to notify FUSE models about deletion use a foreach-loop |
9060
|
|
|
* with unassociate() instead. (that might be slower though) |
9061
|
|
|
* |
9062
|
|
|
* @param OODBBean $bean reference bean |
9063
|
|
|
* @param string $type type of beans that need to be unassociated |
9064
|
|
|
* |
9065
|
|
|
* @return void |
9066
|
|
|
*/ |
9067
|
|
|
public function clearRelations( OODBBean $bean, $type ) |
9068
|
|
|
{ |
9069
|
|
|
$this->oodb->store( $bean ); |
9070
|
|
|
try { |
9071
|
|
|
$this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id ); |
|
|
|
|
9072
|
|
|
} catch ( SQLException $exception ) { |
9073
|
|
|
$this->handleException( $exception ); |
9074
|
|
|
} |
9075
|
|
|
} |
9076
|
|
|
|
9077
|
|
|
/** |
9078
|
|
|
* Returns all the beans associated with $bean. |
9079
|
|
|
* This method will return an array containing all the beans that have |
9080
|
|
|
* been associated once with the associate() function and are still |
9081
|
|
|
* associated with the bean specified. The type parameter indicates the |
9082
|
|
|
* type of beans you are looking for. You can also pass some extra SQL and |
9083
|
|
|
* values for that SQL to filter your results after fetching the |
9084
|
|
|
* related beans. |
9085
|
|
|
* |
9086
|
|
|
* Don't try to make use of subqueries, a subquery using IN() seems to |
9087
|
|
|
* be slower than two queries! |
9088
|
|
|
* |
9089
|
|
|
* Since 3.2, you can now also pass an array of beans instead just one |
9090
|
|
|
* bean as the first parameter. |
9091
|
|
|
* |
9092
|
|
|
* @param OODBBean|array $bean the bean you have |
9093
|
|
|
* @param string $type the type of beans you want |
9094
|
|
|
* @param string $sql SQL snippet for extra filtering |
9095
|
|
|
* @param array $bindings values to be inserted in SQL slots |
9096
|
|
|
* @param boolean $glue whether the SQL should be prefixed with WHERE |
|
|
|
|
9097
|
|
|
* |
9098
|
|
|
* @return array |
9099
|
|
|
*/ |
9100
|
|
|
public function related( $bean, $type, $sql = '', $bindings = array() ) |
9101
|
|
|
{ |
9102
|
|
|
$sql = $this->writer->glueSQLCondition( $sql ); |
9103
|
|
|
|
9104
|
|
|
$rows = $this->relatedRows( $bean, $type, $sql, $bindings ); |
|
|
|
|
9105
|
|
|
|
9106
|
|
|
$links = array(); |
9107
|
|
|
foreach ( $rows as $key => $row ) { |
9108
|
|
|
if ( !isset( $links[$row['id']] ) ) { |
9109
|
|
|
$links[$row['id']] = array(); |
9110
|
|
|
} |
9111
|
|
|
|
9112
|
|
|
$links[$row['id']][] = $row['linked_by']; |
9113
|
|
|
|
9114
|
|
|
unset( $rows[$key]['linked_by'] ); |
9115
|
|
|
} |
9116
|
|
|
|
9117
|
|
|
$beans = $this->oodb->convertToBeans( $type, $rows ); |
9118
|
|
|
|
9119
|
|
|
foreach ( $beans as $bean ) { |
9120
|
|
|
$bean->setMeta( 'sys.belongs-to', $links[$bean->id] ); |
9121
|
|
|
} |
9122
|
|
|
|
9123
|
|
|
return $beans; |
9124
|
|
|
} |
9125
|
|
|
} |
9126
|
|
|
} |
9127
|
|
|
|
9128
|
|
|
namespace RedBeanPHP { |
9129
|
|
|
|
9130
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
9131
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9132
|
|
|
|
9133
|
|
|
/** |
9134
|
|
|
* Bean Helper Interface. |
9135
|
|
|
* |
9136
|
|
|
* Interface for Bean Helper. |
9137
|
|
|
* A little bolt that glues the whole machinery together. |
9138
|
|
|
* |
9139
|
|
|
* @file RedBeanPHP/IBeanHelper.php |
9140
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
9141
|
|
|
* @license BSD/GPLv2 |
9142
|
|
|
* |
9143
|
|
|
* @copyright |
9144
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9145
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9146
|
|
|
* with this source code in the file license.txt. |
9147
|
|
|
*/ |
9148
|
|
|
interface BeanHelper |
|
|
|
|
9149
|
|
|
{ |
9150
|
|
|
/** |
9151
|
|
|
* Returns a toolbox to empower the bean. |
9152
|
|
|
* This allows beans to perform OODB operations by themselves, |
9153
|
|
|
* as such the bean is a proxy for OODB. This allows beans to implement |
9154
|
|
|
* their magic getters and setters and return lists. |
9155
|
|
|
* |
9156
|
|
|
* @return ToolBox $toolbox toolbox |
9157
|
|
|
*/ |
9158
|
|
|
public function getToolbox(); |
9159
|
|
|
|
9160
|
|
|
/** |
9161
|
|
|
* Does approximately the same as getToolbox but also extracts the |
9162
|
|
|
* toolbox for you. |
9163
|
|
|
* This method returns a list with all toolbox items in Toolbox Constructor order: |
9164
|
|
|
* OODB, adapter, writer and finally the toolbox itself!. |
9165
|
|
|
* |
9166
|
|
|
* @return array |
9167
|
|
|
*/ |
9168
|
|
|
public function getExtractedToolbox(); |
9169
|
|
|
|
9170
|
|
|
/** |
9171
|
|
|
* Given a certain bean this method will |
9172
|
|
|
* return the corresponding model. |
9173
|
|
|
* |
9174
|
|
|
* @param OODBBean $bean |
9175
|
|
|
* |
9176
|
|
|
* @return string |
9177
|
|
|
*/ |
9178
|
|
|
public function getModelForBean( OODBBean $bean ); |
9179
|
|
|
} |
9180
|
|
|
} |
9181
|
|
|
|
9182
|
|
|
namespace RedBeanPHP\BeanHelper { |
9183
|
|
|
|
9184
|
|
|
use RedBeanPHP\BeanHelper as BeanHelper; |
|
|
|
|
9185
|
|
|
use RedBeanPHP\Facade as Facade; |
|
|
|
|
9186
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9187
|
|
|
use RedBeanPHP\SimpleModelHelper as SimpleModelHelper; |
|
|
|
|
9188
|
|
|
|
9189
|
|
|
/** |
9190
|
|
|
* Bean Helper. |
9191
|
|
|
* |
9192
|
|
|
* The Bean helper helps beans to access access the toolbox and |
9193
|
|
|
* FUSE models. This Bean Helper makes use of the facade to obtain a |
9194
|
|
|
* reference to the toolbox. |
9195
|
|
|
* |
9196
|
|
|
* @file RedBeanPHP/BeanHelperFacade.php |
9197
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
9198
|
|
|
* @license BSD/GPLv2 |
9199
|
|
|
* |
9200
|
|
|
* @copyright |
9201
|
|
|
* (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9202
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9203
|
|
|
* with this source code in the file license.txt. |
9204
|
|
|
*/ |
9205
|
|
|
class SimpleFacadeBeanHelper implements BeanHelper |
|
|
|
|
9206
|
|
|
{ |
9207
|
|
|
/** |
9208
|
|
|
* Factory function to create instance of Simple Model, if any. |
9209
|
|
|
* |
9210
|
|
|
* @var closure |
9211
|
|
|
*/ |
9212
|
|
|
private static $factory = null; |
9213
|
|
|
|
9214
|
|
|
/** |
9215
|
|
|
* Factory method using a customizable factory function to create |
9216
|
|
|
* the instance of the Simple Model. |
9217
|
|
|
* |
9218
|
|
|
* @param string $modelClassName name of the class |
9219
|
|
|
* |
9220
|
|
|
* @return SimpleModel |
9221
|
|
|
*/ |
9222
|
|
|
public static function factory( $modelClassName ) |
9223
|
|
|
{ |
9224
|
|
|
$factory = self::$factory; |
9225
|
|
|
return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName(); |
9226
|
|
|
} |
9227
|
|
|
|
9228
|
|
|
/** |
9229
|
|
|
* Sets the factory function to create the model when using FUSE |
9230
|
|
|
* to connect a bean to a model. |
9231
|
|
|
* |
9232
|
|
|
* @param closure $factory |
9233
|
|
|
* |
9234
|
|
|
* @return void |
9235
|
|
|
*/ |
9236
|
|
|
public static function setFactoryFunction( $factory ) |
9237
|
|
|
{ |
9238
|
|
|
self::$factory = $factory; |
9239
|
|
|
} |
9240
|
|
|
|
9241
|
|
|
/** |
9242
|
|
|
* @see BeanHelper::getToolbox |
9243
|
|
|
*/ |
9244
|
|
|
public function getToolbox() |
9245
|
|
|
{ |
9246
|
|
|
return Facade::getToolBox(); |
9247
|
|
|
} |
9248
|
|
|
|
9249
|
|
|
/** |
9250
|
|
|
* @see BeanHelper::getModelForBean |
9251
|
|
|
*/ |
9252
|
|
|
public function getModelForBean( OODBBean $bean ) |
9253
|
|
|
{ |
9254
|
|
|
$model = $bean->getMeta( 'type' ); |
9255
|
|
|
$prefix = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_'; |
9256
|
|
|
|
9257
|
|
|
if ( strpos( $model, '_' ) !== FALSE ) { |
9258
|
|
|
$modelParts = explode( '_', $model ); |
9259
|
|
|
$modelName = ''; |
9260
|
|
|
foreach( $modelParts as $part ) { |
9261
|
|
|
$modelName .= ucfirst( $part ); |
9262
|
|
|
} |
9263
|
|
|
$modelName = $prefix . $modelName; |
9264
|
|
|
|
9265
|
|
View Code Duplication |
if ( !class_exists( $modelName ) ) { |
9266
|
|
|
//second try |
9267
|
|
|
$modelName = $prefix . ucfirst( $model ); |
9268
|
|
|
|
9269
|
|
|
if ( !class_exists( $modelName ) ) { |
9270
|
|
|
return NULL; |
9271
|
|
|
} |
9272
|
|
|
} |
9273
|
|
|
|
9274
|
|
View Code Duplication |
} else { |
9275
|
|
|
|
9276
|
|
|
$modelName = $prefix . ucfirst( $model ); |
9277
|
|
|
if ( !class_exists( $modelName ) ) { |
9278
|
|
|
return NULL; |
9279
|
|
|
} |
9280
|
|
|
} |
9281
|
|
|
$obj = self::factory( $modelName ); |
9282
|
|
|
$obj->loadBean( $bean ); |
9283
|
|
|
|
9284
|
|
|
return $obj; |
|
|
|
|
9285
|
|
|
} |
9286
|
|
|
|
9287
|
|
|
/** |
9288
|
|
|
* @see BeanHelper::getExtractedToolbox |
9289
|
|
|
*/ |
9290
|
|
|
public function getExtractedToolbox() |
9291
|
|
|
{ |
9292
|
|
|
return Facade::getExtractedToolbox(); |
9293
|
|
|
} |
9294
|
|
|
} |
9295
|
|
|
} |
9296
|
|
|
|
9297
|
|
|
namespace RedBeanPHP { |
9298
|
|
|
|
9299
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9300
|
|
|
|
9301
|
|
|
/** |
9302
|
|
|
* SimpleModel |
9303
|
|
|
* Base Model For All RedBeanPHP Models using FUSE. |
9304
|
|
|
* |
9305
|
|
|
* RedBeanPHP FUSE is a mechanism to connect beans to posthoc |
9306
|
|
|
* models. Models are connected to beans by naming conventions. |
9307
|
|
|
* Actions on beans will result in actions on models. |
9308
|
|
|
* |
9309
|
|
|
* @file RedBeanPHP/SimpleModel.php |
9310
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Team |
9311
|
|
|
* @license BSD/GPLv2 |
9312
|
|
|
* |
9313
|
|
|
* @copyright |
9314
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9315
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9316
|
|
|
* with this source code in the file license.txt. |
9317
|
|
|
*/ |
9318
|
|
|
class SimpleModel |
|
|
|
|
9319
|
|
|
{ |
9320
|
|
|
/** |
9321
|
|
|
* @var OODBBean |
9322
|
|
|
*/ |
9323
|
|
|
protected $bean; |
9324
|
|
|
|
9325
|
|
|
/** |
9326
|
|
|
* Used by FUSE: the ModelHelper class to connect a bean to a model. |
9327
|
|
|
* This method loads a bean in the model. |
9328
|
|
|
* |
9329
|
|
|
* @param OODBBean $bean bean |
9330
|
|
|
* |
9331
|
|
|
* @return void |
9332
|
|
|
*/ |
9333
|
|
|
public function loadBean( OODBBean $bean ) |
9334
|
|
|
{ |
9335
|
|
|
$this->bean = $bean; |
9336
|
|
|
} |
9337
|
|
|
|
9338
|
|
|
/** |
9339
|
|
|
* Magic Getter to make the bean properties available from |
9340
|
|
|
* the $this-scope. |
9341
|
|
|
* |
9342
|
|
|
* @note this method returns a value, not a reference! |
9343
|
|
|
* To obtain a reference unbox the bean first! |
9344
|
|
|
* |
9345
|
|
|
* @param string $prop property |
9346
|
|
|
* |
9347
|
|
|
* @return mixed |
9348
|
|
|
*/ |
9349
|
|
|
public function __get( $prop ) |
9350
|
|
|
{ |
9351
|
|
|
return $this->bean->$prop; |
9352
|
|
|
} |
9353
|
|
|
|
9354
|
|
|
/** |
9355
|
|
|
* Magic Setter. |
9356
|
|
|
* Sets the value directly as a bean property. |
9357
|
|
|
* |
9358
|
|
|
* @param string $prop property |
9359
|
|
|
* @param mixed $value value |
9360
|
|
|
* |
9361
|
|
|
* @return void |
9362
|
|
|
*/ |
9363
|
|
|
public function __set( $prop, $value ) |
9364
|
|
|
{ |
9365
|
|
|
$this->bean->$prop = $value; |
9366
|
|
|
} |
9367
|
|
|
|
9368
|
|
|
/** |
9369
|
|
|
* Isset implementation. |
9370
|
|
|
* Implements the isset function for array-like access. |
9371
|
|
|
* |
9372
|
|
|
* @param string $key key to check |
9373
|
|
|
* |
9374
|
|
|
* @return boolean |
9375
|
|
|
*/ |
9376
|
|
|
public function __isset( $key ) |
9377
|
|
|
{ |
9378
|
|
|
return isset( $this->bean->$key ); |
9379
|
|
|
} |
9380
|
|
|
|
9381
|
|
|
/** |
9382
|
|
|
* Box the bean using the current model. |
9383
|
|
|
* This method wraps the current bean in this model. |
9384
|
|
|
* This method can be reached using FUSE through a simple |
9385
|
|
|
* OODBBean. The method returns a RedBeanPHP Simple Model. |
9386
|
|
|
* This is useful if you would like to rely on PHP type hinting. |
9387
|
|
|
* You can box your beans before passing them to functions or methods |
9388
|
|
|
* with typed parameters. |
9389
|
|
|
* |
9390
|
|
|
* @return SimpleModel |
9391
|
|
|
*/ |
9392
|
|
|
public function box() |
9393
|
|
|
{ |
9394
|
|
|
return $this; |
9395
|
|
|
} |
9396
|
|
|
|
9397
|
|
|
/** |
9398
|
|
|
* Unbox the bean from the model. |
9399
|
|
|
* This method returns the bean inside the model. |
9400
|
|
|
* |
9401
|
|
|
* @return OODBBean |
9402
|
|
|
*/ |
9403
|
|
|
public function unbox() |
9404
|
|
|
{ |
9405
|
|
|
return $this->bean; |
9406
|
|
|
} |
9407
|
|
|
} |
9408
|
|
|
} |
9409
|
|
|
|
9410
|
|
|
namespace RedBeanPHP { |
9411
|
|
|
|
9412
|
|
|
use RedBeanPHP\Observer as Observer; |
|
|
|
|
9413
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9414
|
|
|
use RedBeanPHP\Observable as Observable; |
|
|
|
|
9415
|
|
|
|
9416
|
|
|
/** |
9417
|
|
|
* RedBean Model Helper. |
9418
|
|
|
* |
9419
|
|
|
* Connects beans to models. |
9420
|
|
|
* This is the core of so-called FUSE. |
9421
|
|
|
* |
9422
|
|
|
* @file RedBeanPHP/ModelHelper.php |
9423
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
9424
|
|
|
* @license BSD/GPLv2 |
9425
|
|
|
* |
9426
|
|
|
* @copyright |
9427
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9428
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9429
|
|
|
* with this source code in the file license.txt. |
9430
|
|
|
*/ |
9431
|
|
|
class SimpleModelHelper implements Observer |
|
|
|
|
9432
|
|
|
{ |
9433
|
|
|
|
9434
|
|
|
/** |
9435
|
|
|
* @see Observer::onEvent |
9436
|
|
|
*/ |
9437
|
|
|
public function onEvent( $eventName, $bean ) |
9438
|
|
|
{ |
9439
|
|
|
$bean->$eventName(); |
9440
|
|
|
} |
9441
|
|
|
|
9442
|
|
|
/** |
9443
|
|
|
* Attaches the FUSE event listeners. Now the Model Helper will listen for |
9444
|
|
|
* CRUD events. If a CRUD event occurs it will send a signal to the model |
9445
|
|
|
* that belongs to the CRUD bean and this model will take over control from |
9446
|
|
|
* there. |
9447
|
|
|
* |
9448
|
|
|
* @param Observable $observable |
9449
|
|
|
* |
9450
|
|
|
* @return void |
9451
|
|
|
*/ |
9452
|
|
|
public function attachEventListeners( Observable $observable ) |
9453
|
|
|
{ |
9454
|
|
|
foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $e ) { |
9455
|
|
|
$observable->addEventListener( $e, $this ); |
9456
|
|
|
} |
9457
|
|
|
} |
9458
|
|
|
} |
9459
|
|
|
} |
9460
|
|
|
|
9461
|
|
|
namespace RedBeanPHP { |
9462
|
|
|
|
9463
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
9464
|
|
|
use RedBeanPHP\AssociationManager as AssociationManager; |
|
|
|
|
9465
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9466
|
|
|
|
9467
|
|
|
/** |
9468
|
|
|
* RedBeanPHP Tag Manager. |
9469
|
|
|
* |
9470
|
|
|
* The tag manager offers an easy way to quickly implement basic tagging |
9471
|
|
|
* functionality. |
9472
|
|
|
* |
9473
|
|
|
* Provides methods to tag beans and perform tag-based searches in the |
9474
|
|
|
* bean database. |
9475
|
|
|
* |
9476
|
|
|
* @file RedBeanPHP/TagManager.php |
9477
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
9478
|
|
|
* @license BSD/GPLv2 |
9479
|
|
|
* |
9480
|
|
|
* @copyright |
9481
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9482
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9483
|
|
|
* with this source code in the file license.txt. |
9484
|
|
|
*/ |
9485
|
|
|
class TagManager |
|
|
|
|
9486
|
|
|
{ |
9487
|
|
|
/** |
9488
|
|
|
* @var ToolBox |
9489
|
|
|
*/ |
9490
|
|
|
protected $toolbox; |
9491
|
|
|
|
9492
|
|
|
/** |
9493
|
|
|
* @var AssociationManager |
9494
|
|
|
*/ |
9495
|
|
|
protected $associationManager; |
9496
|
|
|
|
9497
|
|
|
/** |
9498
|
|
|
* @var OODBBean |
9499
|
|
|
*/ |
9500
|
|
|
protected $redbean; |
9501
|
|
|
|
9502
|
|
|
/** |
9503
|
|
|
* Checks if the argument is a comma separated string, in this case |
9504
|
|
|
* it will split the string into words and return an array instead. |
9505
|
|
|
* In case of an array the argument will be returned 'as is'. |
9506
|
|
|
* |
9507
|
|
|
* @param array|string $tagList list of tags |
9508
|
|
|
* |
9509
|
|
|
* @return array |
9510
|
|
|
*/ |
9511
|
|
|
private function extractTagsIfNeeded( $tagList ) |
9512
|
|
|
{ |
9513
|
|
|
if ( $tagList !== FALSE && !is_array( $tagList ) ) { |
9514
|
|
|
$tags = explode( ',', (string) $tagList ); |
9515
|
|
|
} else { |
9516
|
|
|
$tags = $tagList; |
9517
|
|
|
} |
9518
|
|
|
|
9519
|
|
|
return $tags; |
9520
|
|
|
} |
9521
|
|
|
|
9522
|
|
|
/** |
9523
|
|
|
* Finds a tag bean by it's title. |
9524
|
|
|
* Internal method. |
9525
|
|
|
* |
9526
|
|
|
* @param string $title title |
9527
|
|
|
* |
9528
|
|
|
* @return OODBBean |
9529
|
|
|
*/ |
9530
|
|
|
protected function findTagByTitle( $title ) |
9531
|
|
|
{ |
9532
|
|
|
$beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) ); |
|
|
|
|
9533
|
|
|
|
9534
|
|
|
if ( $beans ) { |
9535
|
|
|
$bean = reset( $beans ); |
9536
|
|
|
|
9537
|
|
|
return $bean; |
9538
|
|
|
} |
9539
|
|
|
|
9540
|
|
|
return NULL; |
9541
|
|
|
} |
9542
|
|
|
|
9543
|
|
|
/** |
9544
|
|
|
* Constructor. |
9545
|
|
|
* The tag manager offers an easy way to quickly implement basic tagging |
9546
|
|
|
* functionality. |
9547
|
|
|
* |
9548
|
|
|
* @param ToolBox $toolbox |
9549
|
|
|
*/ |
9550
|
|
|
public function __construct( ToolBox $toolbox ) |
9551
|
|
|
{ |
9552
|
|
|
$this->toolbox = $toolbox; |
9553
|
|
|
$this->redbean = $toolbox->getRedBean(); |
|
|
|
|
9554
|
|
|
|
9555
|
|
|
$this->associationManager = $this->redbean->getAssociationManager(); |
9556
|
|
|
} |
9557
|
|
|
|
9558
|
|
|
/** |
9559
|
|
|
* Tests whether a bean has been associated with one ore more |
9560
|
|
|
* of the listed tags. If the third parameter is TRUE this method |
9561
|
|
|
* will return TRUE only if all tags that have been specified are indeed |
9562
|
|
|
* associated with the given bean, otherwise FALSE. |
9563
|
|
|
* If the third parameter is FALSE this |
9564
|
|
|
* method will return TRUE if one of the tags matches, FALSE if none |
9565
|
|
|
* match. |
9566
|
|
|
* |
9567
|
|
|
* Tag list can be either an array with tag names or a comma separated list |
9568
|
|
|
* of tag names. |
9569
|
|
|
* |
9570
|
|
|
* @param OODBBean $bean bean to check for tags |
9571
|
|
|
* @param array|string $tags list of tags |
9572
|
|
|
* @param boolean $all whether they must all match or just some |
9573
|
|
|
* |
9574
|
|
|
* @return boolean |
9575
|
|
|
*/ |
9576
|
|
|
public function hasTag( $bean, $tags, $all = FALSE ) |
9577
|
|
|
{ |
9578
|
|
|
$foundtags = $this->tag( $bean ); |
9579
|
|
|
|
9580
|
|
|
$tags = $this->extractTagsIfNeeded( $tags ); |
9581
|
|
|
$same = array_intersect( $tags, $foundtags ); |
9582
|
|
|
|
9583
|
|
|
if ( $all ) { |
9584
|
|
|
return ( implode( ',', $same ) === implode( ',', $tags ) ); |
9585
|
|
|
} |
9586
|
|
|
|
9587
|
|
|
return (bool) ( count( $same ) > 0 ); |
9588
|
|
|
} |
9589
|
|
|
|
9590
|
|
|
/** |
9591
|
|
|
* Removes all sepcified tags from the bean. The tags specified in |
9592
|
|
|
* the second parameter will no longer be associated with the bean. |
9593
|
|
|
* |
9594
|
|
|
* Tag list can be either an array with tag names or a comma separated list |
9595
|
|
|
* of tag names. |
9596
|
|
|
* |
9597
|
|
|
* @param OODBBean $bean tagged bean |
9598
|
|
|
* @param array|string $tagList list of tags (names) |
9599
|
|
|
* |
9600
|
|
|
* @return void |
9601
|
|
|
*/ |
9602
|
|
|
public function untag( $bean, $tagList ) |
9603
|
|
|
{ |
9604
|
|
|
$tags = $this->extractTagsIfNeeded( $tagList ); |
9605
|
|
|
|
9606
|
|
|
foreach ( $tags as $tag ) { |
9607
|
|
|
if ( $t = $this->findTagByTitle( $tag ) ) { |
9608
|
|
|
$this->associationManager->unassociate( $bean, $t ); |
9609
|
|
|
} |
9610
|
|
|
} |
9611
|
|
|
} |
9612
|
|
|
|
9613
|
|
|
/** |
9614
|
|
|
* Tags a bean or returns tags associated with a bean. |
9615
|
|
|
* If $tagList is NULL or omitted this method will return a |
9616
|
|
|
* comma separated list of tags associated with the bean provided. |
9617
|
|
|
* If $tagList is a comma separated list (string) of tags all tags will |
9618
|
|
|
* be associated with the bean. |
9619
|
|
|
* You may also pass an array instead of a string. |
9620
|
|
|
* |
9621
|
|
|
* Tag list can be either an array with tag names or a comma separated list |
9622
|
|
|
* of tag names. |
9623
|
|
|
* |
9624
|
|
|
* @param OODBBean $bean bean to be tagged |
9625
|
|
|
* @param array|string $tagList a list of tags |
9626
|
|
|
* |
9627
|
|
|
* @return array |
9628
|
|
|
*/ |
9629
|
|
|
public function tag( OODBBean $bean, $tagList = NULL ) |
9630
|
|
|
{ |
9631
|
|
|
if ( is_null( $tagList ) ) { |
9632
|
|
|
|
9633
|
|
|
$tags = $bean->sharedTag; |
|
|
|
|
9634
|
|
|
$foundTags = array(); |
9635
|
|
|
|
9636
|
|
|
foreach ( $tags as $tag ) { |
9637
|
|
|
$foundTags[] = $tag->title; |
9638
|
|
|
} |
9639
|
|
|
|
9640
|
|
|
return $foundTags; |
9641
|
|
|
} |
9642
|
|
|
|
9643
|
|
|
$this->associationManager->clearRelations( $bean, 'tag' ); |
9644
|
|
|
$this->addTags( $bean, $tagList ); |
9645
|
|
|
|
9646
|
|
|
return $tagList; |
9647
|
|
|
} |
9648
|
|
|
|
9649
|
|
|
/** |
9650
|
|
|
* Adds tags to a bean. |
9651
|
|
|
* If $tagList is a comma separated list of tags all tags will |
9652
|
|
|
* be associated with the bean. |
9653
|
|
|
* You may also pass an array instead of a string. |
9654
|
|
|
* |
9655
|
|
|
* Tag list can be either an array with tag names or a comma separated list |
9656
|
|
|
* of tag names. |
9657
|
|
|
* |
9658
|
|
|
* @param OODBBean $bean bean to add tags to |
9659
|
|
|
* @param array|string $tagList list of tags to add to bean |
9660
|
|
|
* |
9661
|
|
|
* @return void |
9662
|
|
|
*/ |
9663
|
|
|
public function addTags( OODBBean $bean, $tagList ) |
9664
|
|
|
{ |
9665
|
|
|
$tags = $this->extractTagsIfNeeded( $tagList ); |
9666
|
|
|
|
9667
|
|
|
if ( $tagList === FALSE ) { |
9668
|
|
|
return; |
9669
|
|
|
} |
9670
|
|
|
|
9671
|
|
|
foreach ( $tags as $tag ) { |
9672
|
|
|
if ( !$t = $this->findTagByTitle( $tag ) ) { |
9673
|
|
|
$t = $this->redbean->dispense( 'tag' ); |
|
|
|
|
9674
|
|
|
$t->title = $tag; |
9675
|
|
|
|
9676
|
|
|
$this->redbean->store( $t ); |
|
|
|
|
9677
|
|
|
} |
9678
|
|
|
|
9679
|
|
|
$this->associationManager->associate( $bean, $t ); |
9680
|
|
|
} |
9681
|
|
|
} |
9682
|
|
|
|
9683
|
|
|
/** |
9684
|
|
|
* Returns all beans that have been tagged with one or more |
9685
|
|
|
* of the specified tags. |
9686
|
|
|
* |
9687
|
|
|
* Tag list can be either an array with tag names or a comma separated list |
9688
|
|
|
* of tag names. |
9689
|
|
|
* |
9690
|
|
|
* @param string $beanType type of bean you are looking for |
9691
|
|
|
* @param array|string $tagList list of tags to match |
9692
|
|
|
* @param string $sql additional SQL (use only for pagination) |
9693
|
|
|
* @param array $bindings bindings |
9694
|
|
|
* |
9695
|
|
|
* @return array |
9696
|
|
|
*/ |
9697
|
|
View Code Duplication |
public function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) |
9698
|
|
|
{ |
9699
|
|
|
$tags = $this->extractTagsIfNeeded( $tagList ); |
9700
|
|
|
$records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings ); |
9701
|
|
|
|
9702
|
|
|
return $this->redbean->convertToBeans( $beanType, $records ); |
|
|
|
|
9703
|
|
|
} |
9704
|
|
|
|
9705
|
|
|
/** |
9706
|
|
|
* Returns all beans that have been tagged with ALL of the tags given. |
9707
|
|
|
* |
9708
|
|
|
* Tag list can be either an array with tag names or a comma separated list |
9709
|
|
|
* of tag names. |
9710
|
|
|
* |
9711
|
|
|
* @param string $beanType type of bean you are looking for |
9712
|
|
|
* @param array|string $tagList list of tags to match |
9713
|
|
|
* |
9714
|
|
|
* @return array |
9715
|
|
|
*/ |
9716
|
|
View Code Duplication |
public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) |
9717
|
|
|
{ |
9718
|
|
|
$tags = $this->extractTagsIfNeeded( $tagList ); |
9719
|
|
|
$records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings ); |
9720
|
|
|
|
9721
|
|
|
return $this->redbean->convertToBeans( $beanType, $records ); |
|
|
|
|
9722
|
|
|
} |
9723
|
|
|
} |
9724
|
|
|
} |
9725
|
|
|
|
9726
|
|
|
namespace RedBeanPHP { |
9727
|
|
|
|
9728
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
9729
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9730
|
|
|
|
9731
|
|
|
/** |
9732
|
|
|
* Label Maker. |
9733
|
|
|
* Makes so-called label beans. |
9734
|
|
|
* A label is a bean with only an id, type and name property. |
9735
|
|
|
* Labels can be used to create simple entities like categories, tags or enums. |
9736
|
|
|
* This service class provides convenience methods to deal with this kind of |
9737
|
|
|
* beans. |
9738
|
|
|
* |
9739
|
|
|
* @file RedBeanPHP/LabelMaker.php |
9740
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
9741
|
|
|
* @license BSD/GPLv2 |
9742
|
|
|
* |
9743
|
|
|
* @copyright |
9744
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9745
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9746
|
|
|
* with this source code in the file license.txt. |
9747
|
|
|
*/ |
9748
|
|
|
class LabelMaker |
|
|
|
|
9749
|
|
|
{ |
9750
|
|
|
/** |
9751
|
|
|
* @var ToolBox |
9752
|
|
|
*/ |
9753
|
|
|
protected $toolbox; |
9754
|
|
|
|
9755
|
|
|
/** |
9756
|
|
|
* Constructor. |
9757
|
|
|
* |
9758
|
|
|
* @param ToolBox $toolbox |
9759
|
|
|
*/ |
9760
|
|
|
public function __construct( ToolBox $toolbox ) |
9761
|
|
|
{ |
9762
|
|
|
$this->toolbox = $toolbox; |
9763
|
|
|
} |
9764
|
|
|
|
9765
|
|
|
/** |
9766
|
|
|
* A label is a bean with only an id, type and name property. |
9767
|
|
|
* This function will dispense beans for all entries in the array. The |
9768
|
|
|
* values of the array will be assigned to the name property of each |
9769
|
|
|
* individual bean. |
9770
|
|
|
* |
9771
|
|
|
* $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] ); |
9772
|
|
|
* |
9773
|
|
|
* @param string $type type of beans you would like to have |
9774
|
|
|
* @param array $labels list of labels, names for each bean |
9775
|
|
|
* |
9776
|
|
|
* @return array |
9777
|
|
|
*/ |
9778
|
|
|
public function dispenseLabels( $type, $labels ) |
9779
|
|
|
{ |
9780
|
|
|
$labelBeans = array(); |
9781
|
|
|
foreach ( $labels as $label ) { |
9782
|
|
|
$labelBean = $this->toolbox->getRedBean()->dispense( $type ); |
9783
|
|
|
$labelBean->name = $label; |
|
|
|
|
9784
|
|
|
$labelBeans[] = $labelBean; |
9785
|
|
|
} |
9786
|
|
|
|
9787
|
|
|
return $labelBeans; |
9788
|
|
|
} |
9789
|
|
|
|
9790
|
|
|
/** |
9791
|
|
|
* Gathers labels from beans. This function loops through the beans, |
9792
|
|
|
* collects the value of the name property for each individual bean |
9793
|
|
|
* and stores the names in a new array. The array then gets sorted using the |
9794
|
|
|
* default sort function of PHP (sort). |
9795
|
|
|
* |
9796
|
|
|
* Usage: |
9797
|
|
|
* |
9798
|
|
|
* $o1->name = 'hamburger'; |
9799
|
|
|
* $o2->name = 'pizza'; |
9800
|
|
|
* implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza |
9801
|
|
|
* |
9802
|
|
|
* Note that the return value is an array of strings, not beans. |
9803
|
|
|
* |
9804
|
|
|
* @param array $beans list of beans to loop through |
9805
|
|
|
* |
9806
|
|
|
* @return array |
9807
|
|
|
*/ |
9808
|
|
|
public function gatherLabels( $beans ) |
9809
|
|
|
{ |
9810
|
|
|
$labels = array(); |
9811
|
|
|
|
9812
|
|
|
foreach ( $beans as $bean ) { |
9813
|
|
|
$labels[] = $bean->name; |
9814
|
|
|
} |
9815
|
|
|
|
9816
|
|
|
sort( $labels ); |
9817
|
|
|
|
9818
|
|
|
return $labels; |
9819
|
|
|
} |
9820
|
|
|
|
9821
|
|
|
/** |
9822
|
|
|
* Fetches an ENUM from the database and creates it if necessary. |
9823
|
|
|
* An ENUM has the following format: |
9824
|
|
|
* |
9825
|
|
|
* ENUM:VALUE |
9826
|
|
|
* |
9827
|
|
|
* If you pass 'ENUM' only, this method will return an array of its |
9828
|
|
|
* values: |
9829
|
|
|
* |
9830
|
|
|
* implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA' |
9831
|
|
|
* |
9832
|
|
|
* If you pass 'ENUM:VALUE' this method will return the specified enum bean |
9833
|
|
|
* and create it in the database if it does not exist yet: |
9834
|
|
|
* |
9835
|
|
|
* $bananaFlavour = R::enum( 'flavour:banana' ); |
9836
|
|
|
* $bananaFlavour->name; |
9837
|
|
|
* |
9838
|
|
|
* So you can use this method to set an ENUM value in a bean: |
9839
|
|
|
* |
9840
|
|
|
* $shake->flavour = R::enum( 'flavour:banana' ); |
9841
|
|
|
* |
9842
|
|
|
* the property flavour now contains the enum bean, a parent bean. |
9843
|
|
|
* In the database, flavour_id will point to the flavour record with name 'banana'. |
9844
|
|
|
* |
9845
|
|
|
* @param string $enum ENUM specification for label |
9846
|
|
|
* |
9847
|
|
|
* @return array|OODBBean |
9848
|
|
|
*/ |
9849
|
|
|
public function enum( $enum ) |
9850
|
|
|
{ |
9851
|
|
|
$oodb = $this->toolbox->getRedBean(); |
9852
|
|
|
|
9853
|
|
|
if ( strpos( $enum, ':' ) === FALSE ) { |
9854
|
|
|
$type = $enum; |
9855
|
|
|
$value = FALSE; |
9856
|
|
|
} else { |
9857
|
|
|
list( $type, $value ) = explode( ':', $enum ); |
9858
|
|
|
$value = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) ); |
9859
|
|
|
} |
9860
|
|
|
|
9861
|
|
|
$values = $oodb->find( $type ); |
9862
|
|
|
|
9863
|
|
|
if ( $value === FALSE ) { |
9864
|
|
|
return $values; |
9865
|
|
|
} |
9866
|
|
|
|
9867
|
|
|
foreach( $values as $enumItem ) { |
9868
|
|
|
if ( $enumItem->name === $value ) return $enumItem; |
9869
|
|
|
} |
9870
|
|
|
|
9871
|
|
|
$newEnumItems = $this->dispenseLabels( $type, array( $value ) ); |
9872
|
|
|
$newEnumItem = reset( $newEnumItems ); |
9873
|
|
|
|
9874
|
|
|
$oodb->store( $newEnumItem ); |
9875
|
|
|
|
9876
|
|
|
return $newEnumItem; |
9877
|
|
|
} |
9878
|
|
|
} |
9879
|
|
|
} |
9880
|
|
|
|
9881
|
|
|
namespace RedBeanPHP { |
9882
|
|
|
|
9883
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
9884
|
|
|
use RedBeanPHP\OODB as OODB; |
|
|
|
|
9885
|
|
|
use RedBeanPHP\QueryWriter as QueryWriter; |
|
|
|
|
9886
|
|
|
use RedBeanPHP\Adapter\DBAdapter as DBAdapter; |
|
|
|
|
9887
|
|
|
use RedBeanPHP\AssociationManager as AssociationManager; |
|
|
|
|
9888
|
|
|
use RedBeanPHP\TagManager as TagManager; |
|
|
|
|
9889
|
|
|
use RedBeanPHP\DuplicationManager as DuplicationManager; |
|
|
|
|
9890
|
|
|
use RedBeanPHP\LabelMaker as LabelMaker; |
|
|
|
|
9891
|
|
|
use RedBeanPHP\Finder as Finder; |
|
|
|
|
9892
|
|
|
use RedBeanPHP\RedException\SQL as SQLException; |
|
|
|
|
9893
|
|
|
use RedBeanPHP\RedException\Security as Security; |
|
|
|
|
9894
|
|
|
use RedBeanPHP\Logger as Logger; |
|
|
|
|
9895
|
|
|
use RedBeanPHP\Logger\RDefault as RDefault; |
|
|
|
|
9896
|
|
|
use RedBeanPHP\Logger\RDefault\Debug as Debug; |
|
|
|
|
9897
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
9898
|
|
|
use RedBeanPHP\SimpleModel as SimpleModel; |
|
|
|
|
9899
|
|
|
use RedBeanPHP\SimpleModelHelper as SimpleModelHelper; |
|
|
|
|
9900
|
|
|
use RedBeanPHP\Adapter as Adapter; |
|
|
|
|
9901
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
9902
|
|
|
use RedBeanPHP\RedException as RedException; |
|
|
|
|
9903
|
|
|
use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper; |
|
|
|
|
9904
|
|
|
use RedBeanPHP\Driver\RPDO as RPDO; |
|
|
|
|
9905
|
|
|
|
9906
|
|
|
/** |
9907
|
|
|
* RedBean Facade |
9908
|
|
|
* |
9909
|
|
|
* Version Information |
9910
|
|
|
* RedBean Version @version 4.2 |
9911
|
|
|
* |
9912
|
|
|
* This class hides the object landscape of |
9913
|
|
|
* RedBeanPHP behind a single letter class providing |
9914
|
|
|
* almost all functionality with simple static calls. |
9915
|
|
|
* |
9916
|
|
|
* @file RedBeanPHP/Facade.php |
9917
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
9918
|
|
|
* @license BSD/GPLv2 |
9919
|
|
|
* |
9920
|
|
|
* @copyright |
9921
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
9922
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
9923
|
|
|
* with this source code in the file license.txt. |
9924
|
|
|
*/ |
9925
|
|
|
class Facade |
|
|
|
|
9926
|
|
|
{ |
9927
|
|
|
/** |
9928
|
|
|
* RedBeanPHP version constant. |
9929
|
|
|
*/ |
9930
|
|
|
const C_REDBEANPHP_VERSION = '4.2'; |
9931
|
|
|
|
9932
|
|
|
/** |
9933
|
|
|
* @var ToolBox |
9934
|
|
|
*/ |
9935
|
|
|
public static $toolbox; |
9936
|
|
|
|
9937
|
|
|
/** |
9938
|
|
|
* @var OODB |
9939
|
|
|
*/ |
9940
|
|
|
private static $redbean; |
9941
|
|
|
|
9942
|
|
|
/** |
9943
|
|
|
* @var QueryWriter |
9944
|
|
|
*/ |
9945
|
|
|
private static $writer; |
9946
|
|
|
|
9947
|
|
|
/** |
9948
|
|
|
* @var DBAdapter |
9949
|
|
|
*/ |
9950
|
|
|
private static $adapter; |
9951
|
|
|
|
9952
|
|
|
/** |
9953
|
|
|
* @var AssociationManager |
9954
|
|
|
*/ |
9955
|
|
|
private static $associationManager; |
9956
|
|
|
|
9957
|
|
|
/** |
9958
|
|
|
* @var TagManager |
9959
|
|
|
*/ |
9960
|
|
|
private static $tagManager; |
9961
|
|
|
|
9962
|
|
|
/** |
9963
|
|
|
* @var DuplicationManager |
9964
|
|
|
*/ |
9965
|
|
|
private static $duplicationManager; |
9966
|
|
|
|
9967
|
|
|
/** |
9968
|
|
|
* @var LabelMaker |
9969
|
|
|
*/ |
9970
|
|
|
private static $labelMaker; |
9971
|
|
|
|
9972
|
|
|
/** |
9973
|
|
|
* @var Finder |
9974
|
|
|
*/ |
9975
|
|
|
private static $finder; |
9976
|
|
|
|
9977
|
|
|
/** |
9978
|
|
|
* @var Logger |
9979
|
|
|
*/ |
9980
|
|
|
private static $logger; |
|
|
|
|
9981
|
|
|
|
9982
|
|
|
/** |
9983
|
|
|
* @var array |
9984
|
|
|
*/ |
9985
|
|
|
private static $plugins = array(); |
9986
|
|
|
|
9987
|
|
|
/** |
9988
|
|
|
* @var string |
9989
|
|
|
*/ |
9990
|
|
|
private static $exportCaseStyle = 'default'; |
9991
|
|
|
|
9992
|
|
|
/** |
9993
|
|
|
* Not in use (backward compatibility SQLHelper) |
9994
|
|
|
*/ |
9995
|
|
|
public static $f; |
9996
|
|
|
|
9997
|
|
|
/** |
9998
|
|
|
* @var string |
9999
|
|
|
*/ |
10000
|
|
|
public static $currentDB = ''; |
10001
|
|
|
|
10002
|
|
|
/** |
10003
|
|
|
* @var array |
10004
|
|
|
*/ |
10005
|
|
|
public static $toolboxes = array(); |
10006
|
|
|
|
10007
|
|
|
/** |
10008
|
|
|
* Internal Query function, executes the desired query. Used by |
10009
|
|
|
* all facade query functions. This keeps things DRY. |
10010
|
|
|
* |
10011
|
|
|
* @throws SQL |
10012
|
|
|
* |
10013
|
|
|
* @param string $method desired query method (i.e. 'cell', 'col', 'exec' etc..) |
10014
|
|
|
* @param string $sql the sql you want to execute |
10015
|
|
|
* @param array $bindings array of values to be bound to query statement |
10016
|
|
|
* |
10017
|
|
|
* @return array |
10018
|
|
|
*/ |
10019
|
|
|
private static function query( $method, $sql, $bindings ) |
10020
|
|
|
{ |
10021
|
|
|
if ( !self::$redbean->isFrozen() ) { |
10022
|
|
|
try { |
10023
|
|
|
$rs = Facade::$adapter->$method( $sql, $bindings ); |
10024
|
|
|
} catch ( SQLException $exception ) { |
10025
|
|
|
if ( self::$writer->sqlStateIn( $exception->getSQLState(), |
10026
|
|
|
array( |
10027
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, |
10028
|
|
|
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) |
10029
|
|
|
) |
10030
|
|
|
) { |
10031
|
|
|
return ( $method === 'getCell' ) ? NULL : array(); |
10032
|
|
|
} else { |
10033
|
|
|
throw $exception; |
10034
|
|
|
} |
10035
|
|
|
} |
10036
|
|
|
|
10037
|
|
|
return $rs; |
10038
|
|
|
} else { |
10039
|
|
|
return Facade::$adapter->$method( $sql, $bindings ); |
10040
|
|
|
} |
10041
|
|
|
} |
10042
|
|
|
|
10043
|
|
|
/** |
10044
|
|
|
* Returns the RedBeanPHP version string. |
10045
|
|
|
* The RedBeanPHP version string always has the same format "X.Y" |
10046
|
|
|
* where X is the major version number and Y is the minor version number. |
10047
|
|
|
* Point releases are not mentioned in the version string. |
10048
|
|
|
* |
10049
|
|
|
* @return string |
10050
|
|
|
*/ |
10051
|
|
|
public static function getVersion() |
10052
|
|
|
{ |
10053
|
|
|
return self::C_REDBEANPHP_VERSION; |
10054
|
|
|
} |
10055
|
|
|
|
10056
|
|
|
/** |
10057
|
|
|
* Tests the connection. |
10058
|
|
|
* Returns TRUE if connection has been established and |
10059
|
|
|
* FALSE otherwise. |
10060
|
|
|
* |
10061
|
|
|
* @return boolean |
10062
|
|
|
*/ |
10063
|
|
|
public static function testConnection() |
10064
|
|
|
{ |
10065
|
|
|
if ( !isset( self::$adapter ) ) return FALSE; |
10066
|
|
|
|
10067
|
|
|
$database = self::$adapter->getDatabase(); |
10068
|
|
|
try { |
10069
|
|
|
@$database->connect(); |
|
|
|
|
10070
|
|
|
} catch ( \Exception $e ) {} |
|
|
|
|
10071
|
|
|
return $database->isConnected(); |
10072
|
|
|
} |
10073
|
|
|
|
10074
|
|
|
/** |
10075
|
|
|
* Kickstarts redbean for you. This method should be called before you start using |
10076
|
|
|
* RedBean. The Setup() method can be called without any arguments, in this case it will |
10077
|
|
|
* try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems). |
10078
|
|
|
* |
10079
|
|
|
* @param string $dsn Database connection string |
10080
|
|
|
* @param string $username Username for database |
10081
|
|
|
* @param string $password Password for database |
10082
|
|
|
* @param boolean $frozen TRUE if you want to setup in frozen mode |
10083
|
|
|
* |
10084
|
|
|
* @return ToolBox |
10085
|
|
|
*/ |
10086
|
|
|
public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE ) |
10087
|
|
|
{ |
10088
|
|
|
if ( is_null( $dsn ) ) { |
10089
|
|
|
$dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db'; |
10090
|
|
|
} |
10091
|
|
|
|
10092
|
|
|
self::addDatabase( 'default', $dsn, $username, $password, $frozen ); |
10093
|
|
|
self::selectDatabase( 'default' ); |
10094
|
|
|
|
10095
|
|
|
return self::$toolbox; |
10096
|
|
|
} |
10097
|
|
|
|
10098
|
|
|
/** |
10099
|
|
|
* Toggles Narrow Field Mode. |
10100
|
|
|
* See documentation in QueryWriter. |
10101
|
|
|
* |
10102
|
|
|
* @param boolean $mode TRUE = Narrow Field Mode |
10103
|
|
|
* |
10104
|
|
|
* @return void |
10105
|
|
|
*/ |
10106
|
|
|
public static function setNarrowFieldMode( $mode ) |
10107
|
|
|
{ |
10108
|
|
|
AQueryWriter::setNarrowFieldMode( $mode ); |
10109
|
|
|
} |
10110
|
|
|
|
10111
|
|
|
/** |
10112
|
|
|
* Starts a transaction within a closure (or other valid callback). |
10113
|
|
|
* If an\Exception is thrown inside, the operation is automatically rolled back. |
10114
|
|
|
* If no\Exception happens, it commits automatically. |
10115
|
|
|
* It also supports (simulated) nested transactions (that is useful when |
10116
|
|
|
* you have many methods that needs transactions but are unaware of |
10117
|
|
|
* each other). |
10118
|
|
|
* ex: |
10119
|
|
|
* $from = 1; |
10120
|
|
|
* $to = 2; |
10121
|
|
|
* $amount = 300; |
10122
|
|
|
* |
10123
|
|
|
* R::transaction(function() use($from, $to, $amount) |
10124
|
|
|
* { |
10125
|
|
|
* $accountFrom = R::load('account', $from); |
10126
|
|
|
* $accountTo = R::load('account', $to); |
10127
|
|
|
* |
10128
|
|
|
* $accountFrom->money -= $amount; |
10129
|
|
|
* $accountTo->money += $amount; |
10130
|
|
|
* |
10131
|
|
|
* R::store($accountFrom); |
10132
|
|
|
* R::store($accountTo); |
10133
|
|
|
* }); |
10134
|
|
|
* |
10135
|
|
|
* @param callable $callback Closure (or other callable) with the transaction logic |
10136
|
|
|
* |
10137
|
|
|
* @throws Security |
10138
|
|
|
* |
10139
|
|
|
* @return mixed |
10140
|
|
|
* |
10141
|
|
|
*/ |
10142
|
|
|
public static function transaction( $callback ) |
10143
|
|
|
{ |
10144
|
|
|
if ( !is_callable( $callback ) ) { |
10145
|
|
|
throw new RedException( 'R::transaction needs a valid callback.' ); |
10146
|
|
|
} |
10147
|
|
|
|
10148
|
|
|
static $depth = 0; |
10149
|
|
|
$result = null; |
10150
|
|
|
try { |
10151
|
|
|
if ( $depth == 0 ) { |
10152
|
|
|
self::begin(); |
10153
|
|
|
} |
10154
|
|
|
$depth++; |
10155
|
|
|
$result = call_user_func( $callback ); //maintain 5.2 compatibility |
10156
|
|
|
$depth--; |
10157
|
|
|
if ( $depth == 0 ) { |
10158
|
|
|
self::commit(); |
10159
|
|
|
} |
10160
|
|
|
} catch (\Exception $exception ) { |
10161
|
|
|
$depth--; |
10162
|
|
|
if ( $depth == 0 ) { |
10163
|
|
|
self::rollback(); |
10164
|
|
|
} |
10165
|
|
|
throw $exception; |
10166
|
|
|
} |
10167
|
|
|
return $result; |
10168
|
|
|
} |
10169
|
|
|
|
10170
|
|
|
/** |
10171
|
|
|
* Adds a database to the facade, afterwards you can select the database using |
10172
|
|
|
* selectDatabase($key), where $key is the name you assigned to this database. |
10173
|
|
|
* |
10174
|
|
|
* Usage: |
10175
|
|
|
* |
10176
|
|
|
* R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' ); |
10177
|
|
|
* R::selectDatabase( 'database-1' ); //to select database again |
10178
|
|
|
* |
10179
|
|
|
* This method allows you to dynamically add (and select) new databases |
10180
|
|
|
* to the facade. Adding a database with the same key will cause an exception. |
10181
|
|
|
* |
10182
|
|
|
* @param string $key ID for the database |
10183
|
|
|
* @param string $dsn DSN for the database |
10184
|
|
|
* @param string $user User for connection |
10185
|
|
|
* @param NULL|string $pass Password for connection |
10186
|
|
|
* @param bool $frozen Whether this database is frozen or not |
10187
|
|
|
* |
10188
|
|
|
* @return void |
10189
|
|
|
*/ |
10190
|
|
|
public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE ) |
10191
|
|
|
{ |
10192
|
|
|
if ( isset( self::$toolboxes[$key] ) ) { |
10193
|
|
|
throw new RedException( 'A database has already be specified for this key.' ); |
10194
|
|
|
} |
10195
|
|
|
|
10196
|
|
|
if ( is_object($dsn) ) { |
10197
|
|
|
$db = new RPDO( $dsn ); |
10198
|
|
|
$dbType = $db->getDatabaseType(); |
10199
|
|
|
} else { |
10200
|
|
|
$db = new RPDO( $dsn, $user, $pass, TRUE ); |
|
|
|
|
10201
|
|
|
$dbType = substr( $dsn, 0, strpos( $dsn, ':' ) ); |
10202
|
|
|
} |
10203
|
|
|
|
10204
|
|
|
$adapter = new DBAdapter( $db ); |
10205
|
|
|
|
10206
|
|
|
$writers = array( |
10207
|
|
|
'pgsql' => 'PostgreSQL', |
10208
|
|
|
'sqlite' => 'SQLiteT', |
10209
|
|
|
'cubrid' => 'CUBRID', |
10210
|
|
|
'mysql' => 'MySQL', |
10211
|
|
|
'sqlsrv' => 'SQLServer', |
10212
|
|
|
); |
10213
|
|
|
|
10214
|
|
|
$wkey = trim( strtolower( $dbType ) ); |
10215
|
|
|
if ( !isset( $writers[$wkey] ) ) trigger_error( 'Unsupported DSN: '.$wkey ); |
10216
|
|
|
$writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey]; |
10217
|
|
|
$writer = new $writerClass( $adapter ); |
10218
|
|
|
$redbean = new OODB( $writer, $frozen ); |
10219
|
|
|
|
10220
|
|
|
self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer ); |
10221
|
|
|
} |
10222
|
|
|
|
10223
|
|
|
/** |
10224
|
|
|
* Selects a different database for the Facade to work with. |
10225
|
|
|
* If you use the R::setup() you don't need this method. This method is meant |
10226
|
|
|
* for multiple database setups. This method selects the database identified by the |
10227
|
|
|
* database ID ($key). Use addDatabase() to add a new database, which in turn |
10228
|
|
|
* can be selected using selectDatabase(). If you use R::setup(), the resulting |
10229
|
|
|
* database will be stored under key 'default', to switch (back) to this database |
10230
|
|
|
* use R::selectDatabase( 'default' ). This method returns TRUE if the database has been |
10231
|
|
|
* switched and FALSE otherwise (for instance if you already using the specified database). |
10232
|
|
|
* |
10233
|
|
|
* @param string $key Key of the database to select |
10234
|
|
|
* |
10235
|
|
|
* @return boolean |
10236
|
|
|
*/ |
10237
|
|
|
public static function selectDatabase( $key ) |
10238
|
|
|
{ |
10239
|
|
|
if ( self::$currentDB === $key ) { |
10240
|
|
|
return FALSE; |
10241
|
|
|
} |
10242
|
|
|
|
10243
|
|
|
self::configureFacadeWithToolbox( self::$toolboxes[$key] ); |
10244
|
|
|
self::$currentDB = $key; |
10245
|
|
|
|
10246
|
|
|
return TRUE; |
10247
|
|
|
} |
10248
|
|
|
|
10249
|
|
|
/** |
10250
|
|
|
* Toggles DEBUG mode. |
10251
|
|
|
* In Debug mode all SQL that happens under the hood will |
10252
|
|
|
* be printed to the screen or logged by provided logger. |
10253
|
|
|
* If no database connection has been configured using R::setup() or |
10254
|
|
|
* R::selectDatabase() this method will throw an exception. |
10255
|
|
|
* Returns the attached logger instance. |
10256
|
|
|
* |
10257
|
|
|
* @param boolean $tf |
10258
|
|
|
* @param integer $mode (0 = to STDOUT, 1 = to ARRAY) |
10259
|
|
|
* |
10260
|
|
|
* @throws Security |
10261
|
|
|
* |
10262
|
|
|
* @return Logger\RDefault |
10263
|
|
|
*/ |
10264
|
|
|
public static function debug( $tf = TRUE, $mode = 0 ) |
10265
|
|
|
{ |
10266
|
|
|
if ($mode > 1) { |
10267
|
|
|
$mode -= 2; |
10268
|
|
|
$logger = new Debug; |
10269
|
|
|
} else { |
10270
|
|
|
$logger = new RDefault; |
10271
|
|
|
} |
10272
|
|
|
|
10273
|
|
|
if ( !isset( self::$adapter ) ) { |
10274
|
|
|
throw new RedException( 'Use R::setup() first.' ); |
10275
|
|
|
} |
10276
|
|
|
$logger->setMode($mode); |
10277
|
|
|
self::$adapter->getDatabase()->setDebugMode( $tf, $logger ); |
|
|
|
|
10278
|
|
|
|
10279
|
|
|
return $logger; |
10280
|
|
|
} |
10281
|
|
|
|
10282
|
|
|
/** |
10283
|
|
|
* Turns on the fancy debugger. |
10284
|
|
|
*/ |
10285
|
|
|
public static function fancyDebug( $toggle ) |
10286
|
|
|
{ |
10287
|
|
|
self::debug( $toggle, 2 ); |
10288
|
|
|
} |
10289
|
|
|
|
10290
|
|
|
/** |
10291
|
|
|
* Inspects the database schema. If you pass the type of a bean this |
10292
|
|
|
* method will return the fields of its table in the database. |
10293
|
|
|
* The keys of this array will be the field names and the values will be |
10294
|
|
|
* the column types used to store their values. |
10295
|
|
|
* If no type is passed, this method returns a list of all tables in the database. |
10296
|
|
|
* |
10297
|
|
|
* @param string $type Type of bean (i.e. table) you want to inspect |
10298
|
|
|
* |
10299
|
|
|
* @return array |
10300
|
|
|
*/ |
10301
|
|
|
public static function inspect( $type = NULL ) |
10302
|
|
|
{ |
10303
|
|
|
return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type ); |
10304
|
|
|
} |
10305
|
|
|
|
10306
|
|
|
/** |
10307
|
|
|
* Stores a bean in the database. This method takes a |
10308
|
|
|
* OODBBean Bean Object $bean and stores it |
10309
|
|
|
* in the database. If the database schema is not compatible |
10310
|
|
|
* with this bean and RedBean runs in fluid mode the schema |
10311
|
|
|
* will be altered to store the bean correctly. |
10312
|
|
|
* If the database schema is not compatible with this bean and |
10313
|
|
|
* RedBean runs in frozen mode it will throw an exception. |
10314
|
|
|
* This function returns the primary key ID of the inserted |
10315
|
|
|
* bean. |
10316
|
|
|
* |
10317
|
|
|
* The return value is an integer if possible. If it is not possible to |
10318
|
|
|
* represent the value as an integer a string will be returned. |
10319
|
|
|
* |
10320
|
|
|
* @param OODBBean|SimpleModel $bean bean to store |
10321
|
|
|
* |
10322
|
|
|
* @return integer|string |
10323
|
|
|
* |
10324
|
|
|
* @throws Security |
10325
|
|
|
*/ |
10326
|
|
|
public static function store( $bean ) |
10327
|
|
|
{ |
10328
|
|
|
return self::$redbean->store( $bean ); |
10329
|
|
|
} |
10330
|
|
|
|
10331
|
|
|
/** |
10332
|
|
|
* Toggles fluid or frozen mode. In fluid mode the database |
10333
|
|
|
* structure is adjusted to accomodate your objects. In frozen mode |
10334
|
|
|
* this is not the case. |
10335
|
|
|
* |
10336
|
|
|
* You can also pass an array containing a selection of frozen types. |
10337
|
|
|
* Let's call this chilly mode, it's just like fluid mode except that |
10338
|
|
|
* certain types (i.e. tables) aren't touched. |
10339
|
|
|
* |
10340
|
|
|
* @param boolean|array $trueFalse |
|
|
|
|
10341
|
|
|
*/ |
10342
|
|
|
public static function freeze( $tf = TRUE ) |
10343
|
|
|
{ |
10344
|
|
|
self::$redbean->freeze( $tf ); |
10345
|
|
|
} |
10346
|
|
|
|
10347
|
|
|
/** |
10348
|
|
|
* Loads multiple types of beans with the same ID. |
10349
|
|
|
* This might look like a strange method, however it can be useful |
10350
|
|
|
* for loading a one-to-one relation. |
10351
|
|
|
* |
10352
|
|
|
* Usage: |
10353
|
|
|
* list($author, $bio) = R::load('author, bio', $id); |
10354
|
|
|
* |
10355
|
|
|
* @param string|array $types |
10356
|
|
|
* @param mixed $id |
10357
|
|
|
* |
10358
|
|
|
* @return OODBBean |
10359
|
|
|
*/ |
10360
|
|
|
public static function loadMulti( $types, $id ) |
10361
|
|
|
{ |
10362
|
|
|
if ( is_string( $types ) ) { |
10363
|
|
|
$types = explode( ',', $types ); |
10364
|
|
|
} |
10365
|
|
|
|
10366
|
|
|
if ( !is_array( $types ) ) { |
10367
|
|
|
return array(); |
10368
|
|
|
} |
10369
|
|
|
|
10370
|
|
|
foreach ( $types as $k => $typeItem ) { |
10371
|
|
|
$types[$k] = self::$redbean->load( $typeItem, $id ); |
10372
|
|
|
} |
10373
|
|
|
|
10374
|
|
|
return $types; |
10375
|
|
|
} |
10376
|
|
|
|
10377
|
|
|
/** |
10378
|
|
|
* Loads a bean from the object database. |
10379
|
|
|
* It searches for a OODBBean Bean Object in the |
10380
|
|
|
* database. It does not matter how this bean has been stored. |
10381
|
|
|
* RedBean uses the primary key ID $id and the string $type |
10382
|
|
|
* to find the bean. The $type specifies what kind of bean you |
10383
|
|
|
* are looking for; this is the same type as used with the |
10384
|
|
|
* dispense() function. If RedBean finds the bean it will return |
10385
|
|
|
* the OODB Bean object; if it cannot find the bean |
10386
|
|
|
* RedBean will return a new bean of type $type and with |
10387
|
|
|
* primary key ID 0. In the latter case it acts basically the |
10388
|
|
|
* same as dispense(). |
10389
|
|
|
* |
10390
|
|
|
* Important note: |
10391
|
|
|
* If the bean cannot be found in the database a new bean of |
10392
|
|
|
* the specified type will be generated and returned. |
10393
|
|
|
* |
10394
|
|
|
* @param string $type type of bean you want to load |
10395
|
|
|
* @param integer $id ID of the bean you want to load |
10396
|
|
|
* |
10397
|
|
|
* @throws SQL |
10398
|
|
|
* |
10399
|
|
|
* @return OODBBean |
10400
|
|
|
*/ |
10401
|
|
|
public static function load( $type, $id ) |
10402
|
|
|
{ |
10403
|
|
|
return self::$redbean->load( $type, $id ); |
10404
|
|
|
} |
10405
|
|
|
|
10406
|
|
|
/** |
10407
|
|
|
* Removes a bean from the database. |
10408
|
|
|
* This function will remove the specified OODBBean |
10409
|
|
|
* Bean Object from the database. |
10410
|
|
|
* |
10411
|
|
|
* This facade method also accepts a type-id combination, |
10412
|
|
|
* in the latter case this method will attempt to load the specified bean |
10413
|
|
|
* and THEN trash it. |
10414
|
|
|
* |
10415
|
|
|
* @param string|OODBBean|SimpleModel $bean bean you want to remove from database |
|
|
|
|
10416
|
|
|
* @param integer $id (optional) |
10417
|
|
|
* |
10418
|
|
|
* @return void |
10419
|
|
|
*/ |
10420
|
|
|
public static function trash( $beanOrType, $id = NULL ) |
10421
|
|
|
{ |
10422
|
|
|
if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) ); |
10423
|
|
|
return self::$redbean->trash( $beanOrType ); |
10424
|
|
|
} |
10425
|
|
|
|
10426
|
|
|
/** |
10427
|
|
|
* Dispenses a new RedBean OODB Bean for use with |
10428
|
|
|
* the rest of the methods. |
10429
|
|
|
* |
10430
|
|
|
* @param string|array $typeOrBeanArray type or bean array to import |
10431
|
|
|
* @param integer $number number of beans to dispense |
|
|
|
|
10432
|
|
|
* @param boolean $alwaysReturnArray if TRUE always returns the result as an array |
10433
|
|
|
* |
10434
|
|
|
* @return array|OODBBean |
10435
|
|
|
* |
10436
|
|
|
* @throws Security |
10437
|
|
|
*/ |
10438
|
|
|
public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) |
10439
|
|
|
{ |
10440
|
|
|
if ( is_array($typeOrBeanArray) ) { |
10441
|
|
|
|
10442
|
|
|
if ( !isset( $typeOrBeanArray['_type'] ) ) { |
10443
|
|
|
$list = array(); |
10444
|
|
|
foreach( $typeOrBeanArray as $beanArray ) if ( !( is_array( $beanArray ) && isset( $beanArray['_type'] ) ) ) throw new RedException( 'Invalid Array Bean' ); |
10445
|
|
|
foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $beanArray ); |
10446
|
|
|
return $list; |
10447
|
|
|
} |
10448
|
|
|
|
10449
|
|
|
$import = $typeOrBeanArray; |
10450
|
|
|
$type = $import['_type']; |
10451
|
|
|
unset( $import['_type'] ); |
10452
|
|
|
} else { |
10453
|
|
|
$type = $typeOrBeanArray; |
10454
|
|
|
} |
10455
|
|
|
|
10456
|
|
|
if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) { |
10457
|
|
|
throw new RedException( 'Invalid type: ' . $type ); |
10458
|
|
|
} |
10459
|
|
|
|
10460
|
|
|
$beanOrBeans = self::$redbean->dispense( $type, $num, $alwaysReturnArray ); |
10461
|
|
|
|
10462
|
|
|
if ( isset( $import ) ) { |
10463
|
|
|
$beanOrBeans->import( $import ); |
10464
|
|
|
} |
10465
|
|
|
|
10466
|
|
|
return $beanOrBeans; |
10467
|
|
|
} |
10468
|
|
|
|
10469
|
|
|
/** |
10470
|
|
|
* Takes a comma separated list of bean types |
10471
|
|
|
* and dispenses these beans. For each type in the list |
10472
|
|
|
* you can specify the number of beans to be dispensed. |
10473
|
|
|
* |
10474
|
|
|
* Usage: |
10475
|
|
|
* |
10476
|
|
|
* list($book, $page, $text) = R::dispenseAll('book,page,text'); |
10477
|
|
|
* |
10478
|
|
|
* This will dispense a book, a page and a text. This way you can |
10479
|
|
|
* quickly dispense beans of various types in just one line of code. |
10480
|
|
|
* |
10481
|
|
|
* Usage: |
10482
|
|
|
* |
10483
|
|
|
* list($book, $pages) = R::dispenseAll('book,page*100'); |
10484
|
|
|
* |
10485
|
|
|
* This returns an array with a book bean and then another array |
10486
|
|
|
* containing 100 page beans. |
10487
|
|
|
* |
10488
|
|
|
* @param string $order a description of the desired dispense order using the syntax above |
10489
|
|
|
* @param boolean $onlyArrays return only arrays even if amount < 2 |
10490
|
|
|
* |
10491
|
|
|
* @return array |
10492
|
|
|
*/ |
10493
|
|
|
public static function dispenseAll( $order, $onlyArrays = FALSE ) |
10494
|
|
|
{ |
10495
|
|
|
|
10496
|
|
|
$list = array(); |
10497
|
|
|
|
10498
|
|
|
foreach( explode( ',', $order ) as $order ) { |
10499
|
|
|
if ( strpos( $order, '*' ) !== false ) { |
10500
|
|
|
list( $type, $amount ) = explode( '*', $order ); |
10501
|
|
|
} else { |
10502
|
|
|
$type = $order; |
10503
|
|
|
$amount = 1; |
10504
|
|
|
} |
10505
|
|
|
|
10506
|
|
|
$list[] = self::dispense( $type, $amount, $onlyArrays ); |
10507
|
|
|
} |
10508
|
|
|
|
10509
|
|
|
return $list; |
10510
|
|
|
} |
10511
|
|
|
|
10512
|
|
|
/** |
10513
|
|
|
* Convience method. Tries to find beans of a certain type, |
10514
|
|
|
* if no beans are found, it dispenses a bean of that type. |
10515
|
|
|
* |
10516
|
|
|
* @param string $type type of bean you are looking for |
10517
|
|
|
* @param string $sql SQL code for finding the bean |
10518
|
|
|
* @param array $bindings parameters to bind to SQL |
10519
|
|
|
* |
10520
|
|
|
* @return array |
10521
|
|
|
*/ |
10522
|
|
|
public static function findOrDispense( $type, $sql = NULL, $bindings = array() ) |
10523
|
|
|
{ |
10524
|
|
|
return self::$finder->findOrDispense( $type, $sql, $bindings ); |
10525
|
|
|
} |
10526
|
|
|
|
10527
|
|
|
/** |
10528
|
|
|
* Finds a bean using a type and a where clause (SQL). |
10529
|
|
|
* As with most Query tools in RedBean you can provide values to |
10530
|
|
|
* be inserted in the SQL statement by populating the value |
10531
|
|
|
* array parameter; you can either use the question mark notation |
10532
|
|
|
* or the slot-notation (:keyname). |
10533
|
|
|
* |
10534
|
|
|
* @param string $type type the type of bean you are looking for |
10535
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
10536
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
10537
|
|
|
* |
10538
|
|
|
* @return array |
10539
|
|
|
*/ |
10540
|
|
|
public static function find( $type, $sql = NULL, $bindings = array() ) |
10541
|
|
|
{ |
10542
|
|
|
return self::$finder->find( $type, $sql, $bindings ); |
10543
|
|
|
} |
10544
|
|
|
|
10545
|
|
|
/** |
10546
|
|
|
* @see Facade::find |
10547
|
|
|
* The findAll() method differs from the find() method in that it does |
10548
|
|
|
* not assume a WHERE-clause, so this is valid: |
10549
|
|
|
* |
10550
|
|
|
* R::findAll('person',' ORDER BY name DESC '); |
10551
|
|
|
* |
10552
|
|
|
* Your SQL does not have to start with a valid WHERE-clause condition. |
10553
|
|
|
* |
10554
|
|
|
* @param string $type type the type of bean you are looking for |
10555
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
10556
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
10557
|
|
|
* |
10558
|
|
|
* @return array |
10559
|
|
|
*/ |
10560
|
|
|
public static function findAll( $type, $sql = NULL, $bindings = array() ) |
10561
|
|
|
{ |
10562
|
|
|
return self::$finder->find( $type, $sql, $bindings ); |
10563
|
|
|
} |
10564
|
|
|
|
10565
|
|
|
/** |
10566
|
|
|
* @see Facade::find |
10567
|
|
|
* The variation also exports the beans (i.e. it returns arrays). |
10568
|
|
|
* |
10569
|
|
|
* @param string $type type the type of bean you are looking for |
10570
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
10571
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
10572
|
|
|
* |
10573
|
|
|
* @return array |
10574
|
|
|
*/ |
10575
|
|
|
public static function findAndExport( $type, $sql = NULL, $bindings = array() ) |
10576
|
|
|
{ |
10577
|
|
|
return self::$finder->findAndExport( $type, $sql, $bindings ); |
10578
|
|
|
} |
10579
|
|
|
|
10580
|
|
|
/** |
10581
|
|
|
* @see Facade::find |
10582
|
|
|
* This variation returns the first bean only. |
10583
|
|
|
* |
10584
|
|
|
* @param string $type type the type of bean you are looking for |
10585
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
10586
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
10587
|
|
|
* |
10588
|
|
|
* @return OODBBean |
10589
|
|
|
*/ |
10590
|
|
|
public static function findOne( $type, $sql = NULL, $bindings = array() ) |
10591
|
|
|
{ |
10592
|
|
|
return self::$finder->findOne( $type, $sql, $bindings ); |
10593
|
|
|
} |
10594
|
|
|
|
10595
|
|
|
/** |
10596
|
|
|
* @see Facade::find |
10597
|
|
|
* This variation returns the last bean only. |
10598
|
|
|
* |
10599
|
|
|
* @param string $type type the type of bean you are looking for |
10600
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
10601
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
10602
|
|
|
* |
10603
|
|
|
* @return OODBBean |
10604
|
|
|
*/ |
10605
|
|
|
public static function findLast( $type, $sql = NULL, $bindings = array() ) |
10606
|
|
|
{ |
10607
|
|
|
return self::$finder->findLast( $type, $sql, $bindings ); |
10608
|
|
|
} |
10609
|
|
|
|
10610
|
|
|
/** |
10611
|
|
|
* Finds a bean collection. |
10612
|
|
|
* Use this for large datasets. |
10613
|
|
|
* |
10614
|
|
|
* @param string $type type the type of bean you are looking for |
10615
|
|
|
* @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause |
10616
|
|
|
* @param array $bindings values array of values to be bound to parameters in query |
10617
|
|
|
* |
10618
|
|
|
* @return BeanCollection |
10619
|
|
|
*/ |
10620
|
|
|
public static function findCollection( $type, $sql = NULL, $bindings = array() ) |
10621
|
|
|
{ |
10622
|
|
|
return self::$finder->findCollection( $type, $sql, $bindings ); |
10623
|
|
|
} |
10624
|
|
|
|
10625
|
|
|
/** |
10626
|
|
|
* Finds multiple types of beans at once and offers additional |
10627
|
|
|
* remapping functionality. This is a very powerful yet complex function. |
10628
|
|
|
* For details see Finder::findMulti(). |
10629
|
|
|
* |
10630
|
|
|
* @see Finder::findMulti() |
10631
|
|
|
* |
10632
|
|
|
* @param array|string $types a list of bean types to find |
10633
|
|
|
* @param string|array $sqlOrArr SQL query string or result set array |
|
|
|
|
10634
|
|
|
* @param array $bindings SQL bindings |
10635
|
|
|
* @param array $remappings An array of remapping arrays containing closures |
10636
|
|
|
* |
10637
|
|
|
* @return array |
10638
|
|
|
*/ |
10639
|
|
|
public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() ) |
10640
|
|
|
{ |
10641
|
|
|
return self::$finder->findMulti( $types, $sql, $bindings, $remappings ); |
10642
|
|
|
} |
10643
|
|
|
|
10644
|
|
|
/** |
10645
|
|
|
* Returns an array of beans. Pass a type and a series of ids and |
10646
|
|
|
* this method will bring you the corresponding beans. |
10647
|
|
|
* |
10648
|
|
|
* important note: Because this method loads beans using the load() |
10649
|
|
|
* function (but faster) it will return empty beans with ID 0 for |
10650
|
|
|
* every bean that could not be located. The resulting beans will have the |
10651
|
|
|
* passed IDs as their keys. |
10652
|
|
|
* |
10653
|
|
|
* @param string $type type of beans |
10654
|
|
|
* @param array $ids ids to load |
10655
|
|
|
* |
10656
|
|
|
* @return array |
10657
|
|
|
*/ |
10658
|
|
|
public static function batch( $type, $ids ) |
10659
|
|
|
{ |
10660
|
|
|
return self::$redbean->batch( $type, $ids ); |
10661
|
|
|
} |
10662
|
|
|
|
10663
|
|
|
/** |
10664
|
|
|
* @see Facade::batch |
10665
|
|
|
* |
10666
|
|
|
* Alias for batch(). Batch method is older but since we added so-called *All |
10667
|
|
|
* methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to |
10668
|
|
|
* improve the consistency of the Facade API and also add an alias for batch() called |
10669
|
|
|
* loadAll. |
10670
|
|
|
* |
10671
|
|
|
* @param string $type type of beans |
10672
|
|
|
* @param array $ids ids to load |
10673
|
|
|
* |
10674
|
|
|
* @return array |
10675
|
|
|
*/ |
10676
|
|
|
public static function loadAll( $type, $ids ) |
10677
|
|
|
{ |
10678
|
|
|
return self::$redbean->batch( $type, $ids ); |
10679
|
|
|
} |
10680
|
|
|
|
10681
|
|
|
/** |
10682
|
|
|
* Convenience function to execute Queries directly. |
10683
|
|
|
* Executes SQL. |
10684
|
|
|
* |
10685
|
|
|
* @param string $sql sql SQL query to execute |
10686
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10687
|
|
|
* |
10688
|
|
|
* @return integer |
10689
|
|
|
*/ |
10690
|
|
|
public static function exec( $sql, $bindings = array() ) |
10691
|
|
|
{ |
10692
|
|
|
return self::query( 'exec', $sql, $bindings ); |
10693
|
|
|
} |
10694
|
|
|
|
10695
|
|
|
/** |
10696
|
|
|
* Convenience function to execute Queries directly. |
10697
|
|
|
* Executes SQL. |
10698
|
|
|
* |
10699
|
|
|
* @param string $sql sql SQL query to execute |
10700
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10701
|
|
|
* |
10702
|
|
|
* @return array |
10703
|
|
|
*/ |
10704
|
|
|
public static function getAll( $sql, $bindings = array() ) |
10705
|
|
|
{ |
10706
|
|
|
return self::query( 'get', $sql, $bindings ); |
10707
|
|
|
} |
10708
|
|
|
|
10709
|
|
|
/** |
10710
|
|
|
* Convenience function to execute Queries directly. |
10711
|
|
|
* Executes SQL. |
10712
|
|
|
* |
10713
|
|
|
* @param string $sql sql SQL query to execute |
10714
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10715
|
|
|
* |
10716
|
|
|
* @return string |
10717
|
|
|
*/ |
10718
|
|
|
public static function getCell( $sql, $bindings = array() ) |
10719
|
|
|
{ |
10720
|
|
|
return self::query( 'getCell', $sql, $bindings ); |
10721
|
|
|
} |
10722
|
|
|
|
10723
|
|
|
/** |
10724
|
|
|
* Convenience function to execute Queries directly. |
10725
|
|
|
* Executes SQL. |
10726
|
|
|
* |
10727
|
|
|
* @param string $sql sql SQL query to execute |
10728
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10729
|
|
|
* |
10730
|
|
|
* @return array |
10731
|
|
|
*/ |
10732
|
|
|
public static function getRow( $sql, $bindings = array() ) |
10733
|
|
|
{ |
10734
|
|
|
return self::query( 'getRow', $sql, $bindings ); |
10735
|
|
|
} |
10736
|
|
|
|
10737
|
|
|
/** |
10738
|
|
|
* Convenience function to execute Queries directly. |
10739
|
|
|
* Executes SQL. |
10740
|
|
|
* |
10741
|
|
|
* @param string $sql sql SQL query to execute |
10742
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10743
|
|
|
* |
10744
|
|
|
* @return array |
10745
|
|
|
*/ |
10746
|
|
|
public static function getCol( $sql, $bindings = array() ) |
10747
|
|
|
{ |
10748
|
|
|
return self::query( 'getCol', $sql, $bindings ); |
10749
|
|
|
} |
10750
|
|
|
|
10751
|
|
|
/** |
10752
|
|
|
* Convenience function to execute Queries directly. |
10753
|
|
|
* Executes SQL. |
10754
|
|
|
* Results will be returned as an associative array. The first |
10755
|
|
|
* column in the select clause will be used for the keys in this array and |
10756
|
|
|
* the second column will be used for the values. If only one column is |
10757
|
|
|
* selected in the query, both key and value of the array will have the |
10758
|
|
|
* value of this field for each row. |
10759
|
|
|
* |
10760
|
|
|
* @param string $sql sql SQL query to execute |
10761
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10762
|
|
|
* |
10763
|
|
|
* @return array |
10764
|
|
|
*/ |
10765
|
|
|
public static function getAssoc( $sql, $bindings = array() ) |
10766
|
|
|
{ |
10767
|
|
|
return self::query( 'getAssoc', $sql, $bindings ); |
10768
|
|
|
} |
10769
|
|
|
|
10770
|
|
|
/** |
10771
|
|
|
* Convenience function to execute Queries directly. |
10772
|
|
|
* Executes SQL. |
10773
|
|
|
* Results will be returned as an associative array indexed by the first |
10774
|
|
|
* column in the select. |
10775
|
|
|
* |
10776
|
|
|
* @param string $sql sql SQL query to execute |
10777
|
|
|
* @param array $bindings values a list of values to be bound to query parameters |
10778
|
|
|
* |
10779
|
|
|
* @return array |
10780
|
|
|
*/ |
10781
|
|
|
public static function getAssocRow( $sql, $bindings = array() ) |
10782
|
|
|
{ |
10783
|
|
|
return self::query( 'getAssocRow', $sql, $bindings ); |
10784
|
|
|
} |
10785
|
|
|
|
10786
|
|
|
/** |
10787
|
|
|
* Returns the insert ID for databases that support/require this |
10788
|
|
|
* functionality. Alias for R::getAdapter()->getInsertID(). |
10789
|
|
|
* |
10790
|
|
|
* @return mixed |
10791
|
|
|
*/ |
10792
|
|
|
public static function getInsertID() |
10793
|
|
|
{ |
10794
|
|
|
return self::$adapter->getInsertID(); |
10795
|
|
|
} |
10796
|
|
|
|
10797
|
|
|
/** |
10798
|
|
|
* Makes a copy of a bean. This method makes a deep copy |
10799
|
|
|
* of the bean.The copy will have the following features. |
10800
|
|
|
* - All beans in own-lists will be duplicated as well |
10801
|
|
|
* - All references to shared beans will be copied but not the shared beans themselves |
10802
|
|
|
* - All references to parent objects (_id fields) will be copied but not the parents themselves |
10803
|
|
|
* In most cases this is the desired scenario for copying beans. |
10804
|
|
|
* This function uses a trail-array to prevent infinite recursion, if a recursive bean is found |
10805
|
|
|
* (i.e. one that already has been processed) the ID of the bean will be returned. |
10806
|
|
|
* This should not happen though. |
10807
|
|
|
* |
10808
|
|
|
* Note: |
10809
|
|
|
* This function does a reflectional database query so it may be slow. |
10810
|
|
|
* |
10811
|
|
|
* @deprecated |
10812
|
|
|
* This function is deprecated in favour of R::duplicate(). |
10813
|
|
|
* This function has a confusing method signature, the R::duplicate() function |
10814
|
|
|
* only accepts two arguments: bean and filters. |
10815
|
|
|
* |
10816
|
|
|
* @param OODBBean $bean bean to be copied |
10817
|
|
|
* @param array $trail for internal usage, pass array() |
10818
|
|
|
* @param boolean $pid for internal usage |
10819
|
|
|
* @param array $white white list filter with bean types to duplicate |
|
|
|
|
10820
|
|
|
* |
10821
|
|
|
* @return array |
10822
|
|
|
*/ |
10823
|
|
|
public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() ) |
10824
|
|
|
{ |
10825
|
|
|
self::$duplicationManager->setFilters( $filters ); |
10826
|
|
|
return self::$duplicationManager->dup( $bean, $trail, $pid ); |
10827
|
|
|
} |
10828
|
|
|
|
10829
|
|
|
/** |
10830
|
|
|
* Makes a deep copy of a bean. This method makes a deep copy |
10831
|
|
|
* of the bean.The copy will have the following: |
10832
|
|
|
* |
10833
|
|
|
* - All beans in own-lists will be duplicated as well |
10834
|
|
|
* - All references to shared beans will be copied but not the shared beans themselves |
10835
|
|
|
* - All references to parent objects (_id fields) will be copied but not the parents themselves |
10836
|
|
|
* |
10837
|
|
|
* In most cases this is the desired scenario for copying beans. |
10838
|
|
|
* This function uses a trail-array to prevent infinite recursion, if a recursive bean is found |
10839
|
|
|
* (i.e. one that already has been processed) the ID of the bean will be returned. |
10840
|
|
|
* This should not happen though. |
10841
|
|
|
* |
10842
|
|
|
* Note: |
10843
|
|
|
* This function does a reflectional database query so it may be slow. |
10844
|
|
|
* |
10845
|
|
|
* Note: |
10846
|
|
|
* This is a simplified version of the deprecated R::dup() function. |
10847
|
|
|
* |
10848
|
|
|
* @param OODBBean $bean bean to be copied |
10849
|
|
|
* @param array $white white list filter with bean types to duplicate |
|
|
|
|
10850
|
|
|
* |
10851
|
|
|
* @return array |
10852
|
|
|
*/ |
10853
|
|
|
public static function duplicate( $bean, $filters = array() ) |
10854
|
|
|
{ |
10855
|
|
|
return self::dup( $bean, array(), FALSE, $filters ); |
|
|
|
|
10856
|
|
|
} |
10857
|
|
|
|
10858
|
|
|
/** |
10859
|
|
|
* Exports a collection of beans. Handy for XML/JSON exports with a |
10860
|
|
|
* Javascript framework like Dojo or ExtJS. |
10861
|
|
|
* What will be exported: |
10862
|
|
|
* - contents of the bean |
10863
|
|
|
* - all own bean lists (recursively) |
10864
|
|
|
* - all shared beans (not THEIR own lists) |
10865
|
|
|
* |
10866
|
|
|
* @param array|OODBBean $beans beans to be exported |
10867
|
|
|
* @param boolean $parents whether you want parent beans to be exported |
10868
|
|
|
* @param array $filters whitelist of types |
10869
|
|
|
* |
10870
|
|
|
* @return array |
10871
|
|
|
*/ |
10872
|
|
|
public static function exportAll( $beans, $parents = FALSE, $filters = array()) |
10873
|
|
|
{ |
10874
|
|
|
return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle ); |
10875
|
|
|
} |
10876
|
|
|
|
10877
|
|
|
/** |
10878
|
|
|
* Selects case style for export. |
10879
|
|
|
* This will determine the case style for the keys of exported beans (see exportAll). |
10880
|
|
|
* The following options are accepted: |
10881
|
|
|
* |
10882
|
|
|
* 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid ) |
10883
|
|
|
* 'camel' Camel Case (i.e. bookId isValid ) |
10884
|
|
|
* 'dolphin' Dolphin Case (i.e. bookID isValid ) Like CamelCase but ID is written all uppercase |
10885
|
|
|
* |
10886
|
|
|
* @warning RedBeanPHP transforms camelCase to snake_case using a slightly different |
10887
|
|
|
* algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id. |
10888
|
|
|
* Due to information loss this cannot be corrected. However if you might try |
10889
|
|
|
* DolphinCase for IDs it takes into account the exception concerning IDs. |
10890
|
|
|
* |
10891
|
|
|
* @param string $caseStyle case style identifier |
10892
|
|
|
* |
10893
|
|
|
* @return void |
10894
|
|
|
*/ |
10895
|
|
|
public static function useExportCase( $caseStyle = 'default' ) |
10896
|
|
|
{ |
10897
|
|
|
if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' ); |
10898
|
|
|
self::$exportCaseStyle = $caseStyle; |
10899
|
|
|
} |
10900
|
|
|
|
10901
|
|
|
/** |
10902
|
|
|
* Converts a series of rows to beans. |
10903
|
|
|
* This method converts a series of rows to beans. |
10904
|
|
|
* The type of the desired output beans can be specified in the |
10905
|
|
|
* first parameter. The second parameter is meant for the database |
10906
|
|
|
* result rows. |
10907
|
|
|
* |
10908
|
|
|
* @param string $type type of beans to produce |
10909
|
|
|
* @param array $rows must contain an array of array |
10910
|
|
|
* |
10911
|
|
|
* @return array |
10912
|
|
|
*/ |
10913
|
|
|
public static function convertToBeans( $type, $rows ) |
10914
|
|
|
{ |
10915
|
|
|
return self::$redbean->convertToBeans( $type, $rows ); |
10916
|
|
|
} |
10917
|
|
|
|
10918
|
|
|
/** |
10919
|
|
|
* Part of RedBeanPHP Tagging API. |
10920
|
|
|
* Tests whether a bean has been associated with one ore more |
10921
|
|
|
* of the listed tags. If the third parameter is TRUE this method |
10922
|
|
|
* will return TRUE only if all tags that have been specified are indeed |
10923
|
|
|
* associated with the given bean, otherwise FALSE. |
10924
|
|
|
* If the third parameter is FALSE this |
10925
|
|
|
* method will return TRUE if one of the tags matches, FALSE if none |
10926
|
|
|
* match. |
10927
|
|
|
* |
10928
|
|
|
* @param OODBBean $bean bean to check for tags |
10929
|
|
|
* @param array $tags list of tags |
10930
|
|
|
* @param boolean $all whether they must all match or just some |
10931
|
|
|
* |
10932
|
|
|
* @return boolean |
10933
|
|
|
*/ |
10934
|
|
|
public static function hasTag( $bean, $tags, $all = FALSE ) |
10935
|
|
|
{ |
10936
|
|
|
return self::$tagManager->hasTag( $bean, $tags, $all ); |
10937
|
|
|
} |
10938
|
|
|
|
10939
|
|
|
/** |
10940
|
|
|
* Part of RedBeanPHP Tagging API. |
10941
|
|
|
* Removes all specified tags from the bean. The tags specified in |
10942
|
|
|
* the second parameter will no longer be associated with the bean. |
10943
|
|
|
* |
10944
|
|
|
* @param OODBBean $bean tagged bean |
10945
|
|
|
* @param array $tagList list of tags (names) |
10946
|
|
|
* |
10947
|
|
|
* @return void |
10948
|
|
|
*/ |
10949
|
|
|
public static function untag( $bean, $tagList ) |
10950
|
|
|
{ |
10951
|
|
|
self::$tagManager->untag( $bean, $tagList ); |
10952
|
|
|
} |
10953
|
|
|
|
10954
|
|
|
/** |
10955
|
|
|
* Part of RedBeanPHP Tagging API. |
10956
|
|
|
* Tags a bean or returns tags associated with a bean. |
10957
|
|
|
* If $tagList is NULL or omitted this method will return a |
10958
|
|
|
* comma separated list of tags associated with the bean provided. |
10959
|
|
|
* If $tagList is a comma separated list (string) of tags all tags will |
10960
|
|
|
* be associated with the bean. |
10961
|
|
|
* You may also pass an array instead of a string. |
10962
|
|
|
* |
10963
|
|
|
* @param OODBBean $bean bean |
10964
|
|
|
* @param mixed $tagList tags |
10965
|
|
|
* |
10966
|
|
|
* @return string |
10967
|
|
|
*/ |
10968
|
|
|
public static function tag( OODBBean $bean, $tagList = NULL ) |
10969
|
|
|
{ |
10970
|
|
|
return self::$tagManager->tag( $bean, $tagList ); |
10971
|
|
|
} |
10972
|
|
|
|
10973
|
|
|
/** |
10974
|
|
|
* Part of RedBeanPHP Tagging API. |
10975
|
|
|
* Adds tags to a bean. |
10976
|
|
|
* If $tagList is a comma separated list of tags all tags will |
10977
|
|
|
* be associated with the bean. |
10978
|
|
|
* You may also pass an array instead of a string. |
10979
|
|
|
* |
10980
|
|
|
* @param OODBBean $bean bean |
10981
|
|
|
* @param array $tagList list of tags to add to bean |
10982
|
|
|
* |
10983
|
|
|
* @return void |
10984
|
|
|
*/ |
10985
|
|
|
public static function addTags( OODBBean $bean, $tagList ) |
10986
|
|
|
{ |
10987
|
|
|
self::$tagManager->addTags( $bean, $tagList ); |
10988
|
|
|
} |
10989
|
|
|
|
10990
|
|
|
/** |
10991
|
|
|
* Part of RedBeanPHP Tagging API. |
10992
|
|
|
* Returns all beans that have been tagged with one of the tags given. |
10993
|
|
|
* |
10994
|
|
|
* @param string $beanType type of bean you are looking for |
10995
|
|
|
* @param array $tagList list of tags to match |
10996
|
|
|
* @param string $sql additional SQL |
10997
|
|
|
* @param array $bindings bindings |
10998
|
|
|
* |
10999
|
|
|
* @return array |
11000
|
|
|
*/ |
11001
|
|
|
public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) |
11002
|
|
|
{ |
11003
|
|
|
return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings ); |
11004
|
|
|
} |
11005
|
|
|
|
11006
|
|
|
/** |
11007
|
|
|
* Part of RedBeanPHP Tagging API. |
11008
|
|
|
* Returns all beans that have been tagged with ALL of the tags given. |
11009
|
|
|
* |
11010
|
|
|
* @param string $beanType type of bean you are looking for |
11011
|
|
|
* @param array $tagList list of tags to match |
11012
|
|
|
* @param string $sql additional SQL |
11013
|
|
|
* @param array $bindings bindings |
11014
|
|
|
* |
11015
|
|
|
* @return array |
11016
|
|
|
*/ |
11017
|
|
|
public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) |
11018
|
|
|
{ |
11019
|
|
|
return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings ); |
11020
|
|
|
} |
11021
|
|
|
|
11022
|
|
|
/** |
11023
|
|
|
* Wipes all beans of type $beanType. |
11024
|
|
|
* |
11025
|
|
|
* @param string $beanType type of bean you want to destroy entirely |
11026
|
|
|
* |
11027
|
|
|
* @return boolean |
11028
|
|
|
*/ |
11029
|
|
|
public static function wipe( $beanType ) |
11030
|
|
|
{ |
11031
|
|
|
return Facade::$redbean->wipe( $beanType ); |
11032
|
|
|
} |
11033
|
|
|
|
11034
|
|
|
/** |
11035
|
|
|
* Counts the number of beans of type $type. |
11036
|
|
|
* This method accepts a second argument to modify the count-query. |
11037
|
|
|
* A third argument can be used to provide bindings for the SQL snippet. |
11038
|
|
|
* |
11039
|
|
|
* @param string $type type of bean we are looking for |
11040
|
|
|
* @param string $addSQL additional SQL snippet |
11041
|
|
|
* @param array $bindings parameters to bind to SQL |
11042
|
|
|
* |
11043
|
|
|
* @return integer |
11044
|
|
|
* |
11045
|
|
|
* @throws SQL |
11046
|
|
|
*/ |
11047
|
|
|
public static function count( $type, $addSQL = '', $bindings = array() ) |
11048
|
|
|
{ |
11049
|
|
|
return Facade::$redbean->count( $type, $addSQL, $bindings ); |
11050
|
|
|
} |
11051
|
|
|
|
11052
|
|
|
/** |
11053
|
|
|
* Configures the facade, want to have a new Writer? A new Object Database or a new |
11054
|
|
|
* Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new |
11055
|
|
|
* toolbox. |
11056
|
|
|
* |
11057
|
|
|
* @param ToolBox $tb toolbox |
11058
|
|
|
* |
11059
|
|
|
* @return ToolBox |
11060
|
|
|
*/ |
11061
|
|
|
public static function configureFacadeWithToolbox( ToolBox $tb ) |
11062
|
|
|
{ |
11063
|
|
|
$oldTools = self::$toolbox; |
11064
|
|
|
|
11065
|
|
|
self::$toolbox = $tb; |
11066
|
|
|
|
11067
|
|
|
self::$writer = self::$toolbox->getWriter(); |
11068
|
|
|
self::$adapter = self::$toolbox->getDatabaseAdapter(); |
11069
|
|
|
self::$redbean = self::$toolbox->getRedBean(); |
11070
|
|
|
self::$finder = new Finder( self::$toolbox ); |
11071
|
|
|
|
11072
|
|
|
self::$associationManager = new AssociationManager( self::$toolbox ); |
11073
|
|
|
|
11074
|
|
|
self::$redbean->setAssociationManager( self::$associationManager ); |
11075
|
|
|
|
11076
|
|
|
self::$labelMaker = new LabelMaker( self::$toolbox ); |
11077
|
|
|
|
11078
|
|
|
$helper = new SimpleModelHelper(); |
11079
|
|
|
|
11080
|
|
|
$helper->attachEventListeners( self::$redbean ); |
11081
|
|
|
|
11082
|
|
|
self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper ); |
11083
|
|
|
|
11084
|
|
|
self::$duplicationManager = new DuplicationManager( self::$toolbox ); |
11085
|
|
|
self::$tagManager = new TagManager( self::$toolbox ); |
11086
|
|
|
|
11087
|
|
|
return $oldTools; |
11088
|
|
|
} |
11089
|
|
|
|
11090
|
|
|
/** |
11091
|
|
|
* Facade Convience method for adapter transaction system. |
11092
|
|
|
* Begins a transaction. |
11093
|
|
|
* |
11094
|
|
|
* @return bool |
11095
|
|
|
*/ |
11096
|
|
|
public static function begin() |
11097
|
|
|
{ |
11098
|
|
|
if ( !self::$redbean->isFrozen() ) return FALSE; |
11099
|
|
|
|
11100
|
|
|
self::$adapter->startTransaction(); |
11101
|
|
|
|
11102
|
|
|
return TRUE; |
11103
|
|
|
} |
11104
|
|
|
|
11105
|
|
|
/** |
11106
|
|
|
* Facade Convience method for adapter transaction system. |
11107
|
|
|
* Commits a transaction. |
11108
|
|
|
* |
11109
|
|
|
* @return bool |
11110
|
|
|
*/ |
11111
|
|
|
public static function commit() |
11112
|
|
|
{ |
11113
|
|
|
if ( !self::$redbean->isFrozen() ) return FALSE; |
11114
|
|
|
|
11115
|
|
|
self::$adapter->commit(); |
11116
|
|
|
|
11117
|
|
|
return TRUE; |
11118
|
|
|
} |
11119
|
|
|
|
11120
|
|
|
/** |
11121
|
|
|
* Facade Convience method for adapter transaction system. |
11122
|
|
|
* Rolls back a transaction. |
11123
|
|
|
* |
11124
|
|
|
* @return bool |
11125
|
|
|
*/ |
11126
|
|
|
public static function rollback() |
11127
|
|
|
{ |
11128
|
|
|
if ( !self::$redbean->isFrozen() ) return FALSE; |
11129
|
|
|
|
11130
|
|
|
self::$adapter->rollback(); |
11131
|
|
|
|
11132
|
|
|
return TRUE; |
11133
|
|
|
} |
11134
|
|
|
|
11135
|
|
|
/** |
11136
|
|
|
* Returns a list of columns. Format of this array: |
11137
|
|
|
* array( fieldname => type ) |
11138
|
|
|
* Note that this method only works in fluid mode because it might be |
11139
|
|
|
* quite heavy on production servers! |
11140
|
|
|
* |
11141
|
|
|
* @param string $table name of the table (not type) you want to get columns of |
11142
|
|
|
* |
11143
|
|
|
* @return array |
11144
|
|
|
*/ |
11145
|
|
|
public static function getColumns( $table ) |
11146
|
|
|
{ |
11147
|
|
|
return self::$writer->getColumns( $table ); |
11148
|
|
|
} |
11149
|
|
|
|
11150
|
|
|
/** |
11151
|
|
|
* Generates question mark slots for an array of values. |
11152
|
|
|
* |
11153
|
|
|
* @param array $array array to generate question mark slots for |
11154
|
|
|
* |
11155
|
|
|
* @return string |
11156
|
|
|
*/ |
11157
|
|
|
public static function genSlots( $array, $template = NULL ) |
11158
|
|
|
{ |
11159
|
|
|
$str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : ''; |
11160
|
|
|
return ( is_null( $template ) || $str === '' ) ? $str : sprintf( $template, $str ); |
11161
|
|
|
} |
11162
|
|
|
|
11163
|
|
|
/** |
11164
|
|
|
* Flattens a multi dimensional bindings array for use with genSlots(). |
11165
|
|
|
* |
11166
|
|
|
* @param array $array array to flatten |
11167
|
|
|
* |
11168
|
|
|
* @return array |
11169
|
|
|
*/ |
11170
|
|
|
public static function flat( $array, $result = array() ) |
11171
|
|
|
{ |
11172
|
|
|
foreach( $array as $value ) { |
11173
|
|
|
if ( is_array( $value ) ) $result = self::flat( $value, $result ); |
11174
|
|
|
else $result[] = $value; |
11175
|
|
|
} |
11176
|
|
|
return $result; |
11177
|
|
|
} |
11178
|
|
|
|
11179
|
|
|
/** |
11180
|
|
|
* Nukes the entire database. |
11181
|
|
|
* This will remove all schema structures from the database. |
11182
|
|
|
* Only works in fluid mode. Be careful with this method. |
11183
|
|
|
* |
11184
|
|
|
* @warning dangerous method, will remove all tables, columns etc. |
11185
|
|
|
* |
11186
|
|
|
* @return void |
11187
|
|
|
*/ |
11188
|
|
|
public static function nuke() |
11189
|
|
|
{ |
11190
|
|
|
if ( !self::$redbean->isFrozen() ) { |
11191
|
|
|
self::$writer->wipeAll(); |
11192
|
|
|
} |
11193
|
|
|
} |
11194
|
|
|
|
11195
|
|
|
/** |
11196
|
|
|
* Short hand function to store a set of beans at once, IDs will be |
11197
|
|
|
* returned as an array. For information please consult the R::store() |
11198
|
|
|
* function. |
11199
|
|
|
* A loop saver. |
11200
|
|
|
* |
11201
|
|
|
* @param array $beans list of beans to be stored |
11202
|
|
|
* |
11203
|
|
|
* @return array |
11204
|
|
|
*/ |
11205
|
|
|
public static function storeAll( $beans ) |
11206
|
|
|
{ |
11207
|
|
|
$ids = array(); |
11208
|
|
|
foreach ( $beans as $bean ) { |
11209
|
|
|
$ids[] = self::store( $bean ); |
11210
|
|
|
} |
11211
|
|
|
|
11212
|
|
|
return $ids; |
11213
|
|
|
} |
11214
|
|
|
|
11215
|
|
|
/** |
11216
|
|
|
* Short hand function to trash a set of beans at once. |
11217
|
|
|
* For information please consult the R::trash() function. |
11218
|
|
|
* A loop saver. |
11219
|
|
|
* |
11220
|
|
|
* @param array $beans list of beans to be trashed |
11221
|
|
|
* |
11222
|
|
|
* @return void |
11223
|
|
|
*/ |
11224
|
|
|
public static function trashAll( $beans ) |
11225
|
|
|
{ |
11226
|
|
|
foreach ( $beans as $bean ) { |
11227
|
|
|
self::trash( $bean ); |
11228
|
|
|
} |
11229
|
|
|
} |
11230
|
|
|
|
11231
|
|
|
/** |
11232
|
|
|
* Toggles Writer Cache. |
11233
|
|
|
* Turns the Writer Cache on or off. The Writer Cache is a simple |
11234
|
|
|
* query based caching system that may improve performance without the need |
11235
|
|
|
* for cache management. This caching system will cache non-modifying queries |
11236
|
|
|
* that are marked with special SQL comments. As soon as a non-marked query |
11237
|
|
|
* gets executed the cache will be flushed. Only non-modifying select queries |
11238
|
|
|
* have been marked therefore this mechanism is a rather safe way of caching, requiring |
11239
|
|
|
* no explicit flushes or reloads. Of course this does not apply if you intend to test |
11240
|
|
|
* or simulate concurrent querying. |
11241
|
|
|
* |
11242
|
|
|
* @param boolean $yesNo TRUE to enable cache, FALSE to disable cache |
11243
|
|
|
* |
11244
|
|
|
* @return void |
11245
|
|
|
*/ |
11246
|
|
|
public static function useWriterCache( $yesNo ) |
11247
|
|
|
{ |
11248
|
|
|
self::getWriter()->setUseCache( $yesNo ); |
11249
|
|
|
} |
11250
|
|
|
|
11251
|
|
|
|
11252
|
|
|
/** |
11253
|
|
|
* A label is a bean with only an id, type and name property. |
11254
|
|
|
* This function will dispense beans for all entries in the array. The |
11255
|
|
|
* values of the array will be assigned to the name property of each |
11256
|
|
|
* individual bean. |
11257
|
|
|
* |
11258
|
|
|
* @param string $type type of beans you would like to have |
11259
|
|
|
* @param array $labels list of labels, names for each bean |
11260
|
|
|
* |
11261
|
|
|
* @return array |
11262
|
|
|
*/ |
11263
|
|
|
public static function dispenseLabels( $type, $labels ) |
11264
|
|
|
{ |
11265
|
|
|
return self::$labelMaker->dispenseLabels( $type, $labels ); |
11266
|
|
|
} |
11267
|
|
|
|
11268
|
|
|
/** |
11269
|
|
|
* Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs. |
11270
|
|
|
* Either returns a (newly created) bean respresenting the desired ENUM |
11271
|
|
|
* value or returns a list of all enums for the type. |
11272
|
|
|
* |
11273
|
|
|
* To obtain (and add if necessary) an ENUM value: |
11274
|
|
|
* |
11275
|
|
|
* $tea->flavour = R::enum( 'flavour:apple' ); |
11276
|
|
|
* |
11277
|
|
|
* Returns a bean of type 'flavour' with name = apple. |
11278
|
|
|
* This will add a bean with property name (set to APPLE) to the database |
11279
|
|
|
* if it does not exist yet. |
11280
|
|
|
* |
11281
|
|
|
* To obtain all flavours: |
11282
|
|
|
* |
11283
|
|
|
* R::enum('flavour'); |
11284
|
|
|
* |
11285
|
|
|
* To get a list of all flavour names: |
11286
|
|
|
* |
11287
|
|
|
* R::gatherLabels( R::enum( 'flavour' ) ); |
11288
|
|
|
* |
11289
|
|
|
* @param string $enum either type or type-value |
11290
|
|
|
* |
11291
|
|
|
* @return array|OODBBean |
11292
|
|
|
*/ |
11293
|
|
|
public static function enum( $enum ) |
11294
|
|
|
{ |
11295
|
|
|
return self::$labelMaker->enum( $enum ); |
11296
|
|
|
} |
11297
|
|
|
|
11298
|
|
|
/** |
11299
|
|
|
* Gathers labels from beans. This function loops through the beans, |
11300
|
|
|
* collects the values of the name properties of each individual bean |
11301
|
|
|
* and stores the names in a new array. The array then gets sorted using the |
11302
|
|
|
* default sort function of PHP (sort). |
11303
|
|
|
* |
11304
|
|
|
* @param array $beans list of beans to loop |
11305
|
|
|
* |
11306
|
|
|
* @return array |
11307
|
|
|
*/ |
11308
|
|
|
public static function gatherLabels( $beans ) |
11309
|
|
|
{ |
11310
|
|
|
return self::$labelMaker->gatherLabels( $beans ); |
11311
|
|
|
} |
11312
|
|
|
|
11313
|
|
|
/** |
11314
|
|
|
* Closes the database connection. |
11315
|
|
|
* |
11316
|
|
|
* @return void |
11317
|
|
|
*/ |
11318
|
|
|
public static function close() |
11319
|
|
|
{ |
11320
|
|
|
if ( isset( self::$adapter ) ) { |
11321
|
|
|
self::$adapter->close(); |
11322
|
|
|
} |
11323
|
|
|
} |
11324
|
|
|
|
11325
|
|
|
/** |
11326
|
|
|
* Simple convenience function, returns ISO date formatted representation |
11327
|
|
|
* of $time. |
11328
|
|
|
* |
11329
|
|
|
* @param mixed $time UNIX timestamp |
11330
|
|
|
* |
11331
|
|
|
* @return string |
11332
|
|
|
*/ |
11333
|
|
|
public static function isoDate( $time = NULL ) |
11334
|
|
|
{ |
11335
|
|
|
if ( !$time ) { |
11336
|
|
|
$time = time(); |
11337
|
|
|
} |
11338
|
|
|
|
11339
|
|
|
return @date( 'Y-m-d', $time ); |
11340
|
|
|
} |
11341
|
|
|
|
11342
|
|
|
/** |
11343
|
|
|
* Simple convenience function, returns ISO date time |
11344
|
|
|
* formatted representation |
11345
|
|
|
* of $time. |
11346
|
|
|
* |
11347
|
|
|
* @param mixed $time UNIX timestamp |
11348
|
|
|
* |
11349
|
|
|
* @return string |
11350
|
|
|
*/ |
11351
|
|
|
public static function isoDateTime( $time = NULL ) |
11352
|
|
|
{ |
11353
|
|
|
if ( !$time ) $time = time(); |
11354
|
|
|
|
11355
|
|
|
return @date( 'Y-m-d H:i:s', $time ); |
11356
|
|
|
} |
11357
|
|
|
|
11358
|
|
|
/** |
11359
|
|
|
* Optional accessor for neat code. |
11360
|
|
|
* Sets the database adapter you want to use. |
11361
|
|
|
* |
11362
|
|
|
* @param Adapter $adapter |
11363
|
|
|
* |
11364
|
|
|
* @return void |
11365
|
|
|
*/ |
11366
|
|
|
public static function setDatabaseAdapter( Adapter $adapter ) |
11367
|
|
|
{ |
11368
|
|
|
self::$adapter = $adapter; |
|
|
|
|
11369
|
|
|
} |
11370
|
|
|
|
11371
|
|
|
/** |
11372
|
|
|
* Optional accessor for neat code. |
11373
|
|
|
* Sets the database adapter you want to use. |
11374
|
|
|
* |
11375
|
|
|
* @param QueryWriter $writer |
11376
|
|
|
* |
11377
|
|
|
* @return void |
11378
|
|
|
*/ |
11379
|
|
|
public static function setWriter( QueryWriter $writer ) |
11380
|
|
|
{ |
11381
|
|
|
self::$writer = $writer; |
11382
|
|
|
} |
11383
|
|
|
|
11384
|
|
|
/** |
11385
|
|
|
* Optional accessor for neat code. |
11386
|
|
|
* Sets the database adapter you want to use. |
11387
|
|
|
* |
11388
|
|
|
* @param OODB $redbean |
11389
|
|
|
*/ |
11390
|
|
|
public static function setRedBean( OODB $redbean ) |
11391
|
|
|
{ |
11392
|
|
|
self::$redbean = $redbean; |
11393
|
|
|
} |
11394
|
|
|
|
11395
|
|
|
/** |
11396
|
|
|
* Optional accessor for neat code. |
11397
|
|
|
* Sets the database adapter you want to use. |
11398
|
|
|
* |
11399
|
|
|
* @return DBAdapter |
11400
|
|
|
*/ |
11401
|
|
|
public static function getDatabaseAdapter() |
11402
|
|
|
{ |
11403
|
|
|
return self::$adapter; |
11404
|
|
|
} |
11405
|
|
|
|
11406
|
|
|
/** |
11407
|
|
|
* Returns the current duplication manager instance. |
11408
|
|
|
* |
11409
|
|
|
* @return DuplicationManager |
11410
|
|
|
*/ |
11411
|
|
|
public static function getDuplicationManager() |
11412
|
|
|
{ |
11413
|
|
|
return self::$duplicationManager; |
11414
|
|
|
} |
11415
|
|
|
|
11416
|
|
|
/** |
11417
|
|
|
* Optional accessor for neat code. |
11418
|
|
|
* Sets the database adapter you want to use. |
11419
|
|
|
* |
11420
|
|
|
* @return QueryWriter |
11421
|
|
|
*/ |
11422
|
|
|
public static function getWriter() |
11423
|
|
|
{ |
11424
|
|
|
return self::$writer; |
11425
|
|
|
} |
11426
|
|
|
|
11427
|
|
|
/** |
11428
|
|
|
* Optional accessor for neat code. |
11429
|
|
|
* Sets the database adapter you want to use. |
11430
|
|
|
* |
11431
|
|
|
* @return OODB |
11432
|
|
|
*/ |
11433
|
|
|
public static function getRedBean() |
11434
|
|
|
{ |
11435
|
|
|
return self::$redbean; |
11436
|
|
|
} |
11437
|
|
|
|
11438
|
|
|
/** |
11439
|
|
|
* Returns the toolbox currently used by the facade. |
11440
|
|
|
* To set the toolbox use R::setup() or R::configureFacadeWithToolbox(). |
11441
|
|
|
* To create a toolbox use Setup::kickstart(). Or create a manual |
11442
|
|
|
* toolbox using the ToolBox class. |
11443
|
|
|
* |
11444
|
|
|
* @return ToolBox |
11445
|
|
|
*/ |
11446
|
|
|
public static function getToolBox() |
11447
|
|
|
{ |
11448
|
|
|
return self::$toolbox; |
11449
|
|
|
} |
11450
|
|
|
|
11451
|
|
|
/** |
11452
|
|
|
* Mostly for internal use, but might be handy |
11453
|
|
|
* for some users. |
11454
|
|
|
* This returns all the components of the currently |
11455
|
|
|
* selected toolbox. |
11456
|
|
|
* |
11457
|
|
|
* Returns the components in the following order: |
11458
|
|
|
* |
11459
|
|
|
* 0 - OODB instance (getRedBean()) |
11460
|
|
|
* 1 - Database Adapter |
11461
|
|
|
* 2 - Query Writer |
11462
|
|
|
* 3 - Toolbox itself |
11463
|
|
|
* |
11464
|
|
|
* @return array |
11465
|
|
|
*/ |
11466
|
|
|
public static function getExtractedToolbox() |
11467
|
|
|
{ |
11468
|
|
|
return array( |
11469
|
|
|
self::$redbean, |
11470
|
|
|
self::$adapter, |
11471
|
|
|
self::$writer, |
11472
|
|
|
self::$toolbox |
11473
|
|
|
); |
11474
|
|
|
} |
11475
|
|
|
|
11476
|
|
|
/** |
11477
|
|
|
* Facade method for AQueryWriter::renameAssociation() |
11478
|
|
|
* |
11479
|
|
|
* @param string|array $from |
11480
|
|
|
* @param string $to |
11481
|
|
|
* |
11482
|
|
|
* @return void |
11483
|
|
|
*/ |
11484
|
|
|
public static function renameAssociation( $from, $to = NULL ) |
11485
|
|
|
{ |
11486
|
|
|
AQueryWriter::renameAssociation( $from, $to ); |
11487
|
|
|
} |
11488
|
|
|
|
11489
|
|
|
/** |
11490
|
|
|
* Little helper method for Resty Bean Can server and others. |
11491
|
|
|
* Takes an array of beans and exports each bean. |
11492
|
|
|
* Unlike exportAll this method does not recurse into own lists |
11493
|
|
|
* and shared lists, the beans are exported as-is, only loaded lists |
11494
|
|
|
* are exported. |
11495
|
|
|
* |
11496
|
|
|
* @param array $beans beans |
11497
|
|
|
* |
11498
|
|
|
* @return array |
11499
|
|
|
*/ |
11500
|
|
|
public static function beansToArray( $beans ) |
11501
|
|
|
{ |
11502
|
|
|
$list = array(); |
11503
|
|
|
foreach( $beans as $bean ) { |
11504
|
|
|
$list[] = $bean->export(); |
11505
|
|
|
} |
11506
|
|
|
return $list; |
11507
|
|
|
} |
11508
|
|
|
|
11509
|
|
|
/** |
11510
|
|
|
* Sets the error mode for FUSE. |
11511
|
|
|
* What to do if a FUSE model method does not exist? |
11512
|
|
|
* You can set the following options: |
11513
|
|
|
* |
11514
|
|
|
* OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL |
11515
|
|
|
* OODBBean::C_ERR_LOG, logs the incident using error_log |
11516
|
|
|
* OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE |
11517
|
|
|
* OODBBean::C_ERR_WARN, triggers a E_USER_WARNING |
11518
|
|
|
* OODBBean::C_ERR_EXCEPTION, throws an exception |
11519
|
|
|
* OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function) |
11520
|
|
|
* OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR |
11521
|
|
|
* |
11522
|
|
|
* Custom handler method signature: handler( array ( |
11523
|
|
|
* 'message' => string |
11524
|
|
|
* 'bean' => OODBBean |
11525
|
|
|
* 'method' => string |
11526
|
|
|
* ) ) |
11527
|
|
|
* |
11528
|
|
|
* This method returns the old mode and handler as an array. |
11529
|
|
|
* |
11530
|
|
|
* @param integer $mode mode |
11531
|
|
|
* @param callable|NULL $func custom handler |
11532
|
|
|
* |
11533
|
|
|
* @return array |
11534
|
|
|
*/ |
11535
|
|
|
public static function setErrorHandlingFUSE( $mode, $func = NULL ) |
11536
|
|
|
{ |
11537
|
|
|
return OODBBean::setErrorHandlingFUSE( $mode, $func ); |
11538
|
|
|
} |
11539
|
|
|
|
11540
|
|
|
/** |
11541
|
|
|
* Simple but effective debug function. |
11542
|
|
|
* Given a one or more beans this method will |
11543
|
|
|
* return an array containing first part of the string |
11544
|
|
|
* representation of each item in the array. |
11545
|
|
|
* |
11546
|
|
|
* @param OODBBean|array $data either a bean or an array of beans |
11547
|
|
|
* |
11548
|
|
|
* @return array |
11549
|
|
|
* |
11550
|
|
|
*/ |
11551
|
|
|
public static function dump( $data ) |
11552
|
|
|
{ |
11553
|
|
|
$array = array(); |
11554
|
|
|
|
11555
|
|
|
if ( $data instanceof OODBBean ) { |
11556
|
|
|
$str = strval( $data ); |
11557
|
|
|
if (strlen($str) > 35) { |
11558
|
|
|
$beanStr = substr( $str, 0, 35 ).'... '; |
11559
|
|
|
} else { |
11560
|
|
|
$beanStr = $str; |
11561
|
|
|
} |
11562
|
|
|
return $beanStr; |
11563
|
|
|
} |
11564
|
|
|
|
11565
|
|
|
if ( is_array( $data ) ) { |
11566
|
|
|
foreach( $data as $key => $item ) { |
11567
|
|
|
$array[$key] = self::dump( $item ); |
11568
|
|
|
} |
11569
|
|
|
} |
11570
|
|
|
|
11571
|
|
|
return $array; |
11572
|
|
|
} |
11573
|
|
|
|
11574
|
|
|
/** |
11575
|
|
|
* Binds an SQL function to a column. |
11576
|
|
|
* This method can be used to setup a decode/encode scheme or |
11577
|
|
|
* perform UUID insertion. This method is especially useful for handling |
11578
|
|
|
* MySQL spatial columns, because they need to be processed first using |
11579
|
|
|
* the asText/GeomFromText functions. |
11580
|
|
|
* |
11581
|
|
|
* Example: |
11582
|
|
|
* |
11583
|
|
|
* R::bindFunc( 'read', 'location.point', 'asText' ); |
11584
|
|
|
* R::bindFunc( 'write', 'location.point', 'GeomFromText' ); |
11585
|
|
|
* |
11586
|
|
|
* Passing NULL as the function will reset (clear) the function |
11587
|
|
|
* for this column/mode. |
11588
|
|
|
* |
11589
|
|
|
* @param string $mode (read or write) |
11590
|
|
|
* @param string $field |
11591
|
|
|
* @param string $function |
11592
|
|
|
* |
11593
|
|
|
*/ |
11594
|
|
|
public static function bindFunc( $mode, $field, $function ) |
11595
|
|
|
{ |
11596
|
|
|
self::$redbean->bindFunc( $mode, $field, $function ); |
11597
|
|
|
} |
11598
|
|
|
|
11599
|
|
|
/** |
11600
|
|
|
* Sets global aliases. |
11601
|
|
|
* |
11602
|
|
|
* @param array $list |
11603
|
|
|
* |
11604
|
|
|
* @return void |
11605
|
|
|
*/ |
11606
|
|
|
public static function aliases( $list ) |
11607
|
|
|
{ |
11608
|
|
|
OODBBean::aliases( $list ); |
11609
|
|
|
} |
11610
|
|
|
|
11611
|
|
|
/** |
11612
|
|
|
* Tries to find a bean matching a certain type and |
11613
|
|
|
* criteria set. If no beans are found a new bean |
11614
|
|
|
* will be created, the criteria will be imported into this |
11615
|
|
|
* bean and the bean will be stored and returned. |
11616
|
|
|
* If multiple beans match the criteria only the first one |
11617
|
|
|
* will be returned. |
11618
|
|
|
* |
11619
|
|
|
* @param string $type type of bean to search for |
11620
|
|
|
* @param array $like criteria set describing the bean to search for |
11621
|
|
|
* |
11622
|
|
|
* @return OODBBean |
11623
|
|
|
*/ |
11624
|
|
|
public static function findOrCreate( $type, $like = array() ) |
11625
|
|
|
{ |
11626
|
|
|
return self::$finder->findOrCreate( $type, $like ); |
11627
|
|
|
} |
11628
|
|
|
|
11629
|
|
|
/** |
11630
|
|
|
* Tries to find beans matching the specified type and |
11631
|
|
|
* criteria set. |
11632
|
|
|
* |
11633
|
|
|
* If the optional additional SQL snippet is a condition, it will |
11634
|
|
|
* be glued to the rest of the query using the AND operator. |
11635
|
|
|
* |
11636
|
|
|
* @param string $type type of bean to search for |
11637
|
|
|
* @param array $like optional criteria set describing the bean to search for |
11638
|
|
|
* @param string $sql optional additional SQL for sorting |
11639
|
|
|
* |
11640
|
|
|
* @return array |
11641
|
|
|
*/ |
11642
|
|
|
public static function findLike( $type, $like = array(), $sql = '' ) |
11643
|
|
|
{ |
11644
|
|
|
return self::$finder->findLike( $type, $like, $sql ); |
11645
|
|
|
} |
11646
|
|
|
|
11647
|
|
|
/** |
11648
|
|
|
* Starts logging queries. |
11649
|
|
|
* Use this method to start logging SQL queries being |
11650
|
|
|
* executed by the adapter. |
11651
|
|
|
* |
11652
|
|
|
* @note you cannot use R::debug and R::startLogging |
11653
|
|
|
* at the same time because R::debug is essentially a |
11654
|
|
|
* special kind of logging. |
11655
|
|
|
* |
11656
|
|
|
* @return void |
11657
|
|
|
*/ |
11658
|
|
|
public static function startLogging() |
11659
|
|
|
{ |
11660
|
|
|
self::debug( TRUE, RDefault::C_LOGGER_ARRAY ); |
11661
|
|
|
} |
11662
|
|
|
|
11663
|
|
|
/** |
11664
|
|
|
* Stops logging, comfortable method to stop logging of queries. |
11665
|
|
|
* |
11666
|
|
|
* @return void |
11667
|
|
|
*/ |
11668
|
|
|
public static function stopLogging() |
11669
|
|
|
{ |
11670
|
|
|
self::debug( FALSE ); |
11671
|
|
|
} |
11672
|
|
|
|
11673
|
|
|
/** |
11674
|
|
|
* Returns the log entries written after the startLogging. |
11675
|
|
|
* |
11676
|
|
|
* @return array |
11677
|
|
|
*/ |
11678
|
|
|
public static function getLogs() |
11679
|
|
|
{ |
11680
|
|
|
return self::getLogger()->getLogs(); |
11681
|
|
|
} |
11682
|
|
|
|
11683
|
|
|
/** |
11684
|
|
|
* Resets the Query counter. |
11685
|
|
|
* |
11686
|
|
|
* @return integer |
11687
|
|
|
*/ |
11688
|
|
|
public static function resetQueryCount() |
11689
|
|
|
{ |
11690
|
|
|
self::$adapter->getDatabase()->resetCounter(); |
11691
|
|
|
} |
11692
|
|
|
|
11693
|
|
|
/** |
11694
|
|
|
* Returns the number of SQL queries processed. |
11695
|
|
|
* |
11696
|
|
|
* @return integer |
11697
|
|
|
*/ |
11698
|
|
|
public static function getQueryCount() |
11699
|
|
|
{ |
11700
|
|
|
return self::$adapter->getDatabase()->getQueryCount(); |
11701
|
|
|
} |
11702
|
|
|
|
11703
|
|
|
/** |
11704
|
|
|
* Returns the current logger instance being used by the |
11705
|
|
|
* database object. |
11706
|
|
|
* |
11707
|
|
|
* @return Logger |
11708
|
|
|
*/ |
11709
|
|
|
public static function getLogger() |
11710
|
|
|
{ |
11711
|
|
|
return self::$adapter->getDatabase()->getLogger(); |
11712
|
|
|
} |
11713
|
|
|
|
11714
|
|
|
/** |
11715
|
|
|
* Alias for setAutoResolve() method on OODBBean. |
11716
|
|
|
* Enables or disables auto-resolving fetch types. |
11717
|
|
|
* Auto-resolving aliased parent beans is convenient but can |
11718
|
|
|
* be slower and can create infinite recursion if you |
11719
|
|
|
* used aliases to break cyclic relations in your domain. |
11720
|
|
|
* |
11721
|
|
|
* @param boolean $automatic TRUE to enable automatic resolving aliased parents |
11722
|
|
|
* |
11723
|
|
|
* @return void |
11724
|
|
|
*/ |
11725
|
|
|
public static function setAutoResolve( $automatic = TRUE ) |
11726
|
|
|
{ |
11727
|
|
|
OODBBean::setAutoResolve( (boolean) $automatic ); |
11728
|
|
|
} |
11729
|
|
|
|
11730
|
|
|
/** |
11731
|
|
|
* Dynamically extends the facade with a plugin. |
11732
|
|
|
* Using this method you can register your plugin with the facade and then |
11733
|
|
|
* use the plugin by invoking the name specified plugin name as a method on |
11734
|
|
|
* the facade. |
11735
|
|
|
* |
11736
|
|
|
* Usage: |
11737
|
|
|
* |
11738
|
|
|
* R::ext( 'makeTea', function() { ... } ); |
11739
|
|
|
* |
11740
|
|
|
* Now you can use your makeTea plugin like this: |
11741
|
|
|
* |
11742
|
|
|
* R::makeTea(); |
11743
|
|
|
* |
11744
|
|
|
* @param string $pluginName name of the method to call the plugin |
11745
|
|
|
* @param callable $callable a PHP callable |
11746
|
|
|
*/ |
11747
|
|
|
public static function ext( $pluginName, $callable ) |
11748
|
|
|
{ |
11749
|
|
|
if ( !ctype_alnum( $pluginName ) ) { |
11750
|
|
|
throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); |
11751
|
|
|
} |
11752
|
|
|
self::$plugins[$pluginName] = $callable; |
11753
|
|
|
} |
11754
|
|
|
|
11755
|
|
|
/** |
11756
|
|
|
* Call static for use with dynamic plugins. This magic method will |
11757
|
|
|
* intercept static calls and route them to the specified plugin. |
11758
|
|
|
* |
11759
|
|
|
* @param string $pluginName name of the plugin |
11760
|
|
|
* @param array $params list of arguments to pass to plugin method |
11761
|
|
|
* |
11762
|
|
|
* @return mixed |
11763
|
|
|
*/ |
11764
|
|
|
public static function __callStatic( $pluginName, $params ) |
11765
|
|
|
{ |
11766
|
|
|
if ( !ctype_alnum( $pluginName) ) { |
11767
|
|
|
throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); |
11768
|
|
|
} |
11769
|
|
|
if ( !isset( self::$plugins[$pluginName] ) ) { |
11770
|
|
|
throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' ); |
11771
|
|
|
} |
11772
|
|
|
return call_user_func_array( self::$plugins[$pluginName], $params ); |
11773
|
|
|
} |
11774
|
|
|
} |
11775
|
|
|
|
11776
|
|
|
} |
11777
|
|
|
|
11778
|
|
|
namespace RedBeanPHP { |
11779
|
|
|
|
11780
|
|
|
use RedBeanPHP\ToolBox as ToolBox; |
|
|
|
|
11781
|
|
|
use RedBeanPHP\AssociationManager as AssociationManager; |
|
|
|
|
11782
|
|
|
use RedBeanPHP\OODB as OODB; |
|
|
|
|
11783
|
|
|
use RedBeanPHP\OODBBean as OODBBean; |
|
|
|
|
11784
|
|
|
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; |
|
|
|
|
11785
|
|
|
|
11786
|
|
|
/** |
11787
|
|
|
* Duplication Manager. |
11788
|
|
|
* Creates deep copies of beans. |
11789
|
|
|
* |
11790
|
|
|
* @file RedBeanPHP/DuplicationManager.php |
11791
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
11792
|
|
|
* @license BSD/GPLv2 |
11793
|
|
|
* |
11794
|
|
|
* @copyright |
11795
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
11796
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
11797
|
|
|
* with this source code in the file license.txt. |
11798
|
|
|
*/ |
11799
|
|
|
class DuplicationManager |
|
|
|
|
11800
|
|
|
{ |
11801
|
|
|
/** |
11802
|
|
|
* @var ToolBox |
11803
|
|
|
*/ |
11804
|
|
|
protected $toolbox; |
11805
|
|
|
|
11806
|
|
|
/** |
11807
|
|
|
* @var AssociationManager |
11808
|
|
|
*/ |
11809
|
|
|
protected $associationManager; |
11810
|
|
|
|
11811
|
|
|
/** |
11812
|
|
|
* @var OODB |
11813
|
|
|
*/ |
11814
|
|
|
protected $redbean; |
11815
|
|
|
|
11816
|
|
|
/** |
11817
|
|
|
* @var array |
11818
|
|
|
*/ |
11819
|
|
|
protected $tables = array(); |
11820
|
|
|
|
11821
|
|
|
/** |
11822
|
|
|
* @var array |
11823
|
|
|
*/ |
11824
|
|
|
protected $columns = array(); |
11825
|
|
|
|
11826
|
|
|
/** |
11827
|
|
|
* @var array |
11828
|
|
|
*/ |
11829
|
|
|
protected $filters = array(); |
11830
|
|
|
|
11831
|
|
|
/** |
11832
|
|
|
* @var array |
11833
|
|
|
*/ |
11834
|
|
|
protected $cacheTables = FALSE; |
11835
|
|
|
|
11836
|
|
|
/** |
11837
|
|
|
* Copies the shared beans in a bean, i.e. all the sharedBean-lists. |
11838
|
|
|
* |
11839
|
|
|
* @param OODBBean $copy target bean to copy lists to |
11840
|
|
|
* @param string $shared name of the shared list |
11841
|
|
|
* @param array $beans array with shared beans to copy |
11842
|
|
|
* |
11843
|
|
|
* @return void |
11844
|
|
|
*/ |
11845
|
|
|
private function copySharedBeans( OODBBean $copy, $shared, $beans ) |
11846
|
|
|
{ |
11847
|
|
|
$copy->$shared = array(); |
11848
|
|
|
|
11849
|
|
|
foreach ( $beans as $subBean ) { |
11850
|
|
|
array_push( $copy->$shared, $subBean ); |
11851
|
|
|
} |
11852
|
|
|
} |
11853
|
|
|
|
11854
|
|
|
/** |
11855
|
|
|
* Copies the own beans in a bean, i.e. all the ownBean-lists. |
11856
|
|
|
* Each bean in the own-list belongs exclusively to its owner so |
11857
|
|
|
* we need to invoke the duplicate method again to duplicate each bean here. |
11858
|
|
|
* |
11859
|
|
|
* @param OODBBean $copy target bean to copy lists to |
11860
|
|
|
* @param string $owned name of the own list |
11861
|
|
|
* @param array $beans array with shared beans to copy |
11862
|
|
|
* @param array $trail array with former beans to detect recursion |
11863
|
|
|
* @param boolean $preserveIDs TRUE means preserve IDs, for export only |
11864
|
|
|
* |
11865
|
|
|
* @return void |
11866
|
|
|
*/ |
11867
|
|
|
private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs ) |
11868
|
|
|
{ |
11869
|
|
|
$copy->$owned = array(); |
11870
|
|
|
foreach ( $beans as $subBean ) { |
11871
|
|
|
array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) ); |
11872
|
|
|
} |
11873
|
|
|
} |
11874
|
|
|
|
11875
|
|
|
/** |
11876
|
|
|
* Creates a copy of bean $bean and copies all primitive properties (not lists) |
11877
|
|
|
* and the parents beans to the newly created bean. Also sets the ID of the bean |
11878
|
|
|
* to 0. |
11879
|
|
|
* |
11880
|
|
|
* @param OODBBean $bean bean to copy |
11881
|
|
|
* |
11882
|
|
|
* @return OODBBean |
11883
|
|
|
*/ |
11884
|
|
|
private function createCopy( OODBBean $bean ) |
11885
|
|
|
{ |
11886
|
|
|
$type = $bean->getMeta( 'type' ); |
11887
|
|
|
|
11888
|
|
|
$copy = $this->redbean->dispense( $type ); |
11889
|
|
|
$copy->setMeta( 'sys.dup-from-id', $bean->id ); |
|
|
|
|
11890
|
|
|
$copy->setMeta( 'sys.old-id', $bean->id ); |
|
|
|
|
11891
|
|
|
$copy->importFrom( $bean ); |
11892
|
|
|
$copy->id = 0; |
|
|
|
|
11893
|
|
|
|
11894
|
|
|
return $copy; |
11895
|
|
|
} |
11896
|
|
|
|
11897
|
|
|
/** |
11898
|
|
|
* Generates a key from the bean type and its ID and determines if the bean |
11899
|
|
|
* occurs in the trail, if not the bean will be added to the trail. |
11900
|
|
|
* Returns TRUE if the bean occurs in the trail and FALSE otherwise. |
11901
|
|
|
* |
11902
|
|
|
* @param array $trail list of former beans |
11903
|
|
|
* @param OODBBean $bean currently selected bean |
11904
|
|
|
* |
11905
|
|
|
* @return boolean |
11906
|
|
|
*/ |
11907
|
|
|
private function inTrailOrAdd( &$trail, OODBBean $bean ) |
11908
|
|
|
{ |
11909
|
|
|
$type = $bean->getMeta( 'type' ); |
11910
|
|
|
$key = $type . $bean->getID(); |
11911
|
|
|
|
11912
|
|
|
if ( isset( $trail[$key] ) ) { |
11913
|
|
|
return TRUE; |
11914
|
|
|
} |
11915
|
|
|
|
11916
|
|
|
$trail[$key] = $bean; |
11917
|
|
|
|
11918
|
|
|
return FALSE; |
11919
|
|
|
} |
11920
|
|
|
|
11921
|
|
|
/** |
11922
|
|
|
* Given the type name of a bean this method returns the canonical names |
11923
|
|
|
* of the own-list and the shared-list properties respectively. |
11924
|
|
|
* Returns a list with two elements: name of the own-list, and name |
11925
|
|
|
* of the shared list. |
11926
|
|
|
* |
11927
|
|
|
* @param string $typeName bean type name |
11928
|
|
|
* |
11929
|
|
|
* @return array |
11930
|
|
|
*/ |
11931
|
|
|
private function getListNames( $typeName ) |
11932
|
|
|
{ |
11933
|
|
|
$owned = 'own' . ucfirst( $typeName ); |
11934
|
|
|
$shared = 'shared' . ucfirst( $typeName ); |
11935
|
|
|
|
11936
|
|
|
return array( $owned, $shared ); |
11937
|
|
|
} |
11938
|
|
|
|
11939
|
|
|
/** |
11940
|
|
|
* Determines whether the bean has an own list based on |
11941
|
|
|
* schema inspection from realtime schema or cache. |
11942
|
|
|
* |
11943
|
|
|
* @param string $type bean type to get list for |
11944
|
|
|
* @param string $target type of list you want to detect |
11945
|
|
|
* |
11946
|
|
|
* @return boolean |
11947
|
|
|
*/ |
11948
|
|
|
protected function hasOwnList( $type, $target ) |
11949
|
|
|
{ |
11950
|
|
|
return isset( $this->columns[$target][$type . '_id'] ); |
11951
|
|
|
} |
11952
|
|
|
|
11953
|
|
|
/** |
11954
|
|
|
* Determines whether the bea has a shared list based on |
11955
|
|
|
* schema inspection from realtime schema or cache. |
11956
|
|
|
* |
11957
|
|
|
* @param string $type bean type to get list for |
11958
|
|
|
* @param string $target type of list you are looking for |
11959
|
|
|
* |
11960
|
|
|
* @return boolean |
11961
|
|
|
*/ |
11962
|
|
|
protected function hasSharedList( $type, $target ) |
11963
|
|
|
{ |
11964
|
|
|
return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables ); |
11965
|
|
|
} |
11966
|
|
|
|
11967
|
|
|
/** |
11968
|
|
|
* @see DuplicationManager::dup |
11969
|
|
|
* |
11970
|
|
|
* @param OODBBean $bean bean to be copied |
11971
|
|
|
* @param array $trail trail to prevent infinite loops |
11972
|
|
|
* @param boolean $preserveIDs preserve IDs |
11973
|
|
|
* |
11974
|
|
|
* @return OODBBean |
11975
|
|
|
*/ |
11976
|
|
|
protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) |
11977
|
|
|
{ |
11978
|
|
|
if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean; |
11979
|
|
|
|
11980
|
|
|
$type = $bean->getMeta( 'type' ); |
11981
|
|
|
|
11982
|
|
|
$copy = $this->createCopy( $bean ); |
11983
|
|
|
foreach ( $this->tables as $table ) { |
11984
|
|
|
|
11985
|
|
|
if ( !empty( $this->filters ) ) { |
11986
|
|
|
if ( !in_array( $table, $this->filters ) ) continue; |
11987
|
|
|
} |
11988
|
|
|
|
11989
|
|
|
list( $owned, $shared ) = $this->getListNames( $table ); |
11990
|
|
|
|
11991
|
|
|
if ( $this->hasSharedList( $type, $table ) ) { |
11992
|
|
|
if ( $beans = $bean->$shared ) { |
11993
|
|
|
$this->copySharedBeans( $copy, $shared, $beans ); |
11994
|
|
|
} |
11995
|
|
|
} elseif ( $this->hasOwnList( $type, $table ) ) { |
11996
|
|
|
if ( $beans = $bean->$owned ) { |
11997
|
|
|
$this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs ); |
11998
|
|
|
} |
11999
|
|
|
|
12000
|
|
|
$copy->setMeta( 'sys.shadow.' . $owned, NULL ); |
12001
|
|
|
} |
12002
|
|
|
|
12003
|
|
|
$copy->setMeta( 'sys.shadow.' . $shared, NULL ); |
12004
|
|
|
} |
12005
|
|
|
|
12006
|
|
|
$copy->id = ( $preserveIDs ) ? $bean->id : $copy->id; |
|
|
|
|
12007
|
|
|
|
12008
|
|
|
return $copy; |
12009
|
|
|
} |
12010
|
|
|
|
12011
|
|
|
/** |
12012
|
|
|
* Constructor, |
12013
|
|
|
* creates a new instance of DupManager. |
12014
|
|
|
* |
12015
|
|
|
* @param ToolBox $toolbox |
12016
|
|
|
*/ |
12017
|
|
|
public function __construct( ToolBox $toolbox ) |
12018
|
|
|
{ |
12019
|
|
|
$this->toolbox = $toolbox; |
12020
|
|
|
$this->redbean = $toolbox->getRedBean(); |
12021
|
|
|
$this->associationManager = $this->redbean->getAssociationManager(); |
12022
|
|
|
} |
12023
|
|
|
|
12024
|
|
|
/** |
12025
|
|
|
* Recursively turns the keys of an array into |
12026
|
|
|
* camelCase. |
12027
|
|
|
* |
12028
|
|
|
* @param array $array array to camelize |
12029
|
|
|
* @param boolean $dolphinMode whether you want the exception for IDs. |
12030
|
|
|
* |
12031
|
|
|
* @return array |
12032
|
|
|
*/ |
12033
|
|
|
public function camelfy( $array, $dolphinMode = false ) { |
12034
|
|
|
$newArray = array(); |
12035
|
|
|
foreach( $array as $key => $element ) { |
12036
|
|
|
$newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){ |
12037
|
|
|
return strtoupper( $matches[1] ); |
12038
|
|
|
}, $key); |
12039
|
|
|
|
12040
|
|
|
if ( $dolphinMode ) { |
12041
|
|
|
$newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey ); |
12042
|
|
|
} |
12043
|
|
|
|
12044
|
|
|
$newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element; |
12045
|
|
|
} |
12046
|
|
|
return $newArray; |
12047
|
|
|
} |
12048
|
|
|
|
12049
|
|
|
/** |
12050
|
|
|
* For better performance you can pass the tables in an array to this method. |
12051
|
|
|
* If the tables are available the duplication manager will not query them so |
12052
|
|
|
* this might be beneficial for performance. |
12053
|
|
|
* |
12054
|
|
|
* This method allows two array formats: |
12055
|
|
|
* |
12056
|
|
|
* array( TABLE1, TABLE2 ... ) |
12057
|
|
|
* |
12058
|
|
|
* or |
12059
|
|
|
* |
12060
|
|
|
* array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... ) |
12061
|
|
|
* |
12062
|
|
|
* @param array $tables a table cache array |
12063
|
|
|
* |
12064
|
|
|
* @return void |
12065
|
|
|
*/ |
12066
|
|
|
public function setTables( $tables ) |
12067
|
|
|
{ |
12068
|
|
|
foreach ( $tables as $key => $value ) { |
12069
|
|
|
if ( is_numeric( $key ) ) { |
12070
|
|
|
$this->tables[] = $value; |
12071
|
|
|
} else { |
12072
|
|
|
$this->tables[] = $key; |
12073
|
|
|
$this->columns[$key] = $value; |
12074
|
|
|
} |
12075
|
|
|
} |
12076
|
|
|
|
12077
|
|
|
$this->cacheTables = TRUE; |
|
|
|
|
12078
|
|
|
} |
12079
|
|
|
|
12080
|
|
|
/** |
12081
|
|
|
* Returns a schema array for cache. |
12082
|
|
|
* You can use the return value of this method as a cache, |
12083
|
|
|
* store it in RAM or on disk and pass it to setTables later. |
12084
|
|
|
* |
12085
|
|
|
* @return array |
12086
|
|
|
*/ |
12087
|
|
|
public function getSchema() |
12088
|
|
|
{ |
12089
|
|
|
return $this->columns; |
12090
|
|
|
} |
12091
|
|
|
|
12092
|
|
|
/** |
12093
|
|
|
* Indicates whether you want the duplication manager to cache the database schema. |
12094
|
|
|
* If this flag is set to TRUE the duplication manager will query the database schema |
12095
|
|
|
* only once. Otherwise the duplicationmanager will, by default, query the schema |
12096
|
|
|
* every time a duplication action is performed (dup()). |
12097
|
|
|
* |
12098
|
|
|
* @param boolean $yesNo TRUE to use caching, FALSE otherwise |
12099
|
|
|
*/ |
12100
|
|
|
public function setCacheTables( $yesNo ) |
12101
|
|
|
{ |
12102
|
|
|
$this->cacheTables = $yesNo; |
|
|
|
|
12103
|
|
|
} |
12104
|
|
|
|
12105
|
|
|
/** |
12106
|
|
|
* A filter array is an array with table names. |
12107
|
|
|
* By setting a table filter you can make the duplication manager only take into account |
12108
|
|
|
* certain bean types. Other bean types will be ignored when exporting or making a |
12109
|
|
|
* deep copy. If no filters are set all types will be taking into account, this is |
12110
|
|
|
* the default behavior. |
12111
|
|
|
* |
12112
|
|
|
* @param array $filters list of tables to be filtered |
12113
|
|
|
*/ |
12114
|
|
|
public function setFilters( $filters ) |
12115
|
|
|
{ |
12116
|
|
|
if ( !is_array( $filters ) ) { |
12117
|
|
|
$filters = array( $filters ); |
12118
|
|
|
} |
12119
|
|
|
|
12120
|
|
|
$this->filters = $filters; |
12121
|
|
|
} |
12122
|
|
|
|
12123
|
|
|
/** |
12124
|
|
|
* Makes a copy of a bean. This method makes a deep copy |
12125
|
|
|
* of the bean.The copy will have the following features. |
12126
|
|
|
* - All beans in own-lists will be duplicated as well |
12127
|
|
|
* - All references to shared beans will be copied but not the shared beans themselves |
12128
|
|
|
* - All references to parent objects (_id fields) will be copied but not the parents themselves |
12129
|
|
|
* In most cases this is the desired scenario for copying beans. |
12130
|
|
|
* This function uses a trail-array to prevent infinite recursion, if a recursive bean is found |
12131
|
|
|
* (i.e. one that already has been processed) the ID of the bean will be returned. |
12132
|
|
|
* This should not happen though. |
12133
|
|
|
* |
12134
|
|
|
* Note: |
12135
|
|
|
* This function does a reflectional database query so it may be slow. |
12136
|
|
|
* |
12137
|
|
|
* Note: |
12138
|
|
|
* this function actually passes the arguments to a protected function called |
12139
|
|
|
* duplicate() that does all the work. This method takes care of creating a clone |
12140
|
|
|
* of the bean to avoid the bean getting tainted (triggering saving when storing it). |
12141
|
|
|
* |
12142
|
|
|
* @param OODBBean $bean bean to be copied |
12143
|
|
|
* @param array $trail for internal usage, pass array() |
12144
|
|
|
* @param boolean $preserveIDs for internal usage |
12145
|
|
|
* |
12146
|
|
|
* @return OODBBean |
12147
|
|
|
*/ |
12148
|
|
|
public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) |
12149
|
|
|
{ |
12150
|
|
|
if ( !count( $this->tables ) ) { |
12151
|
|
|
$this->tables = $this->toolbox->getWriter()->getTables(); |
12152
|
|
|
} |
12153
|
|
|
|
12154
|
|
|
if ( !count( $this->columns ) ) { |
12155
|
|
|
foreach ( $this->tables as $table ) { |
12156
|
|
|
$this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table ); |
12157
|
|
|
} |
12158
|
|
|
} |
12159
|
|
|
|
12160
|
|
|
$rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs ); |
12161
|
|
|
|
12162
|
|
|
if ( !$this->cacheTables ) { |
|
|
|
|
12163
|
|
|
$this->tables = array(); |
12164
|
|
|
$this->columns = array(); |
12165
|
|
|
} |
12166
|
|
|
|
12167
|
|
|
return $rs; |
12168
|
|
|
} |
12169
|
|
|
|
12170
|
|
|
/** |
12171
|
|
|
* Exports a collection of beans recursively. |
12172
|
|
|
* This method will export an array of beans in the first argument to a |
12173
|
|
|
* set of arrays. This can be used to send JSON or XML representations |
12174
|
|
|
* of bean hierarchies to the client. |
12175
|
|
|
* |
12176
|
|
|
* For every bean in the array this method will export: |
12177
|
|
|
* |
12178
|
|
|
* - contents of the bean |
12179
|
|
|
* - all own bean lists (recursively) |
12180
|
|
|
* - all shared beans (but not THEIR own lists) |
12181
|
|
|
* |
12182
|
|
|
* If the second parameter is set to TRUE the parents of the beans in the |
12183
|
|
|
* array will be exported as well (but not THEIR parents). |
12184
|
|
|
* |
12185
|
|
|
* The third parameter can be used to provide a white-list array |
12186
|
|
|
* for filtering. This is an array of strings representing type names, |
12187
|
|
|
* only the type names in the filter list will be exported. |
12188
|
|
|
* |
12189
|
|
|
* The fourth parameter can be used to change the keys of the resulting |
12190
|
|
|
* export arrays. The default mode is 'snake case' but this leaves the |
12191
|
|
|
* keys as-is, because 'snake' is the default case style used by |
12192
|
|
|
* RedBeanPHP in the database. You can set this to 'camel' for |
12193
|
|
|
* camel cased keys or 'dolphin' (same as camelcase but id will be |
12194
|
|
|
* converted to ID instead of Id). |
12195
|
|
|
* |
12196
|
|
|
* @param array|OODBBean $beans beans to be exported |
12197
|
|
|
* @param boolean $parents also export parents |
12198
|
|
|
* @param array $filters only these types (whitelist) |
12199
|
|
|
* @param string $caseStyle case style identifier |
12200
|
|
|
* |
12201
|
|
|
* @return array |
12202
|
|
|
*/ |
12203
|
|
|
public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake') |
12204
|
|
|
{ |
12205
|
|
|
$array = array(); |
12206
|
|
|
|
12207
|
|
|
if ( !is_array( $beans ) ) { |
12208
|
|
|
$beans = array( $beans ); |
12209
|
|
|
} |
12210
|
|
|
|
12211
|
|
|
foreach ( $beans as $bean ) { |
12212
|
|
|
$this->setFilters( $filters ); |
12213
|
|
|
|
12214
|
|
|
$duplicate = $this->dup( $bean, array(), TRUE ); |
12215
|
|
|
|
12216
|
|
|
$array[] = $duplicate->export( FALSE, $parents, FALSE, $filters ); |
12217
|
|
|
} |
12218
|
|
|
|
12219
|
|
|
if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array ); |
12220
|
|
|
if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true ); |
12221
|
|
|
|
12222
|
|
|
return $array; |
12223
|
|
|
} |
12224
|
|
|
} |
12225
|
|
|
} |
12226
|
|
|
|
12227
|
|
|
namespace RedBeanPHP { |
12228
|
|
|
|
12229
|
|
|
/** |
12230
|
|
|
* RedBean Plugin. |
12231
|
|
|
* Marker interface for plugins. |
12232
|
|
|
* |
12233
|
|
|
* @file RedBean/Plugin.php |
12234
|
|
|
* @author Gabor de Mooij and the RedBeanPHP Community |
12235
|
|
|
* @license BSD/GPLv2 |
12236
|
|
|
* |
12237
|
|
|
* @copyright |
12238
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community |
12239
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
12240
|
|
|
* with this source code in the file license.txt. |
12241
|
|
|
*/ |
12242
|
|
|
interface Plugin |
|
|
|
|
12243
|
|
|
{ |
12244
|
|
|
} |
12245
|
|
|
|
12246
|
|
|
; |
12247
|
|
|
} |
12248
|
|
|
namespace { |
12249
|
|
|
|
12250
|
|
|
//make some classes available for backward compatibility |
12251
|
|
|
class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {}; |
|
|
|
|
12252
|
|
|
|
12253
|
|
|
if (!class_exists('R')) { |
12254
|
|
|
class R extends \RedBeanPHP\Facade{}; |
|
|
|
|
12255
|
|
|
} |
12256
|
|
|
|
12257
|
|
|
|
12258
|
|
|
|
12259
|
|
|
/** |
12260
|
|
|
* Support functions for RedBeanPHP. |
12261
|
|
|
* Additional convenience shortcut functions for RedBeanPHP. |
12262
|
|
|
* |
12263
|
|
|
* @file RedBeanPHP/Functions.php |
12264
|
|
|
* @author Gabor de Mooij and the RedBeanPHP community |
12265
|
|
|
* @license BSD/GPLv2 |
12266
|
|
|
* |
12267
|
|
|
* @copyright |
12268
|
|
|
* copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. |
12269
|
|
|
* This source file is subject to the BSD/GPLv2 License that is bundled |
12270
|
|
|
* with this source code in the file license.txt. |
12271
|
|
|
*/ |
12272
|
|
|
|
12273
|
|
|
/** |
12274
|
|
|
* Convenience function for ENUM short syntax in queries. |
12275
|
|
|
* |
12276
|
|
|
* Usage: |
12277
|
|
|
* |
12278
|
|
|
* R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] ); |
12279
|
|
|
* |
12280
|
|
|
* If a function called EID() already exists you'll have to write this |
12281
|
|
|
* wrapper yourself ;) |
12282
|
|
|
* |
12283
|
|
|
* @param string $enumName enum code as you would pass to R::enum() |
12284
|
|
|
* |
12285
|
|
|
* @return mixed |
12286
|
|
|
*/ |
12287
|
|
|
if (!function_exists('EID')) { |
12288
|
|
|
|
12289
|
|
|
function EID($enumName) |
12290
|
|
|
{ |
12291
|
|
|
return \RedBeanPHP\Facade::enum( $enumName )->id; |
12292
|
|
|
} |
12293
|
|
|
|
12294
|
|
|
} |
12295
|
|
|
|
12296
|
|
|
/** |
12297
|
|
|
* Prints the result of R::dump() to the screen using |
12298
|
|
|
* print_r. |
12299
|
|
|
* |
12300
|
|
|
* @param mixed $data data to dump |
12301
|
|
|
* |
12302
|
|
|
* @return void |
12303
|
|
|
*/ |
12304
|
|
|
if ( !function_exists( 'dump' ) ) { |
12305
|
|
|
|
12306
|
|
|
function dmp( $list ) |
12307
|
|
|
{ |
12308
|
|
|
print_r( \RedBeanPHP\Facade::dump( $list ) ); |
12309
|
|
|
} |
12310
|
|
|
} |
12311
|
|
|
|
12312
|
|
|
/** |
12313
|
|
|
* Function alias for R::genSlots(). |
12314
|
|
|
*/ |
12315
|
|
|
if ( !function_exists( 'genslots' ) ) { |
12316
|
|
|
|
12317
|
|
|
function genslots( $slots, $tpl = NULL ) |
12318
|
|
|
{ |
12319
|
|
|
return \RedBeanPHP\Facade::genSlots( $slots, $tpl ); |
12320
|
|
|
} |
12321
|
|
|
} |
12322
|
|
|
|
12323
|
|
|
/** |
12324
|
|
|
* Function alias for R::flat(). |
12325
|
|
|
*/ |
12326
|
|
|
if ( !function_exists( 'array_flatten' ) ) { |
12327
|
|
|
|
12328
|
|
|
function array_flatten( $array ) |
12329
|
|
|
{ |
12330
|
|
|
return \RedBeanPHP\Facade::flat( $array ); |
12331
|
|
|
} |
12332
|
|
|
} |
12333
|
|
|
|
12334
|
|
|
|
12335
|
|
|
} |
12336
|
|
|
|