1: <?php
2:
3: namespace RedBeanPHP;
4:
5: use RedBeanPHP\QueryWriter as QueryWriter;
6: use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
7: use RedBeanPHP\RedException\SQL as SQLException;
8: use RedBeanPHP\Logger as Logger;
9: use RedBeanPHP\Logger\RDefault as RDefault;
10: use RedBeanPHP\Logger\RDefault\Debug as Debug;
11: use RedBeanPHP\Adapter as Adapter;
12: use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
13: use RedBeanPHP\RedException as RedException;
14: use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper;
15: use RedBeanPHP\Driver\RPDO as RPDO;
16: use RedBeanPHP\Util\MultiLoader as MultiLoader;
17: use RedBeanPHP\Util\Transaction as Transaction;
18: use RedBeanPHP\Util\Dump as Dump;
19: use RedBeanPHP\Util\DispenseHelper as DispenseHelper;
20: use RedBeanPHP\Util\ArrayTool as ArrayTool;
21:
22: /**
23: * RedBean Facade
24: *
25: * Version Information
26: * RedBean Version @version 4.3
27: *
28: * This class hides the object landscape of
29: * RedBeanPHP behind a single letter class providing
30: * almost all functionality with simple static calls.
31: *
32: * @file RedBeanPHP/Facade.php
33: * @author Gabor de Mooij and the RedBeanPHP Community
34: * @license BSD/GPLv2
35: *
36: * @copyright
37: * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
38: * This source file is subject to the BSD/GPLv2 License that is bundled
39: * with this source code in the file license.txt.
40: */
41: class Facade
42: {
43: /**
44: * RedBeanPHP version constant.
45: */
46: const C_REDBEANPHP_VERSION = '4.3';
47:
48: /**
49: * @var ToolBox
50: */
51: public static $toolbox;
52:
53: /**
54: * @var OODB
55: */
56: private static $redbean;
57:
58: /**
59: * @var QueryWriter
60: */
61: private static $writer;
62:
63: /**
64: * @var DBAdapter
65: */
66: private static $adapter;
67:
68: /**
69: * @var AssociationManager
70: */
71: private static $associationManager;
72:
73: /**
74: * @var TagManager
75: */
76: private static $tagManager;
77:
78: /**
79: * @var DuplicationManager
80: */
81: private static $duplicationManager;
82:
83: /**
84: * @var LabelMaker
85: */
86: private static $labelMaker;
87:
88: /**
89: * @var Finder
90: */
91: private static $finder;
92:
93: /**
94: * @var Logger
95: */
96: private static $logger;
97:
98: /**
99: * @var array
100: */
101: private static $plugins = array();
102:
103: /**
104: * @var string
105: */
106: private static $exportCaseStyle = 'default';
107:
108: /**
109: * Not in use (backward compatibility SQLHelper)
110: */
111: public static $f;
112:
113: /**
114: * @var string
115: */
116: public static $currentDB = '';
117:
118: /**
119: * @var array
120: */
121: public static $toolboxes = array();
122:
123: /**
124: * Internal Query function, executes the desired query. Used by
125: * all facade query functions. This keeps things DRY.
126: *
127: * @param string $method desired query method (i.e. 'cell', 'col', 'exec' etc..)
128: * @param string $sql the sql you want to execute
129: * @param array $bindings array of values to be bound to query statement
130: *
131: * @return array
132: */
133: private static function query( $method, $sql, $bindings )
134: {
135: if ( !self::$redbean->isFrozen() ) {
136: try {
137: $rs = Facade::$adapter->$method( $sql, $bindings );
138: } catch ( SQLException $exception ) {
139: if ( self::$writer->sqlStateIn( $exception->getSQLState(),
140: array(
141: QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
142: QueryWriter::C_SQLSTATE_NO_SUCH_TABLE )
143: )
144: ) {
145: return ( $method === 'getCell' ) ? NULL : array();
146: } else {
147: throw $exception;
148: }
149: }
150:
151: return $rs;
152: } else {
153: return Facade::$adapter->$method( $sql, $bindings );
154: }
155: }
156:
157: /**
158: * Returns the RedBeanPHP version string.
159: * The RedBeanPHP version string always has the same format "X.Y"
160: * where X is the major version number and Y is the minor version number.
161: * Point releases are not mentioned in the version string.
162: *
163: * @return string
164: */
165: public static function getVersion()
166: {
167: return self::C_REDBEANPHP_VERSION;
168: }
169:
170: /**
171: * Tests the connection.
172: * Returns TRUE if connection has been established and
173: * FALSE otherwise.
174: *
175: * @return boolean
176: */
177: public static function testConnection()
178: {
179: if ( !isset( self::$adapter ) ) return FALSE;
180:
181: $database = self::$adapter->getDatabase();
182: try {
183: @$database->connect();
184: } catch ( \Exception $e ) {}
185: return $database->isConnected();
186: }
187:
188: /**
189: * Kickstarts redbean for you. This method should be called before you start using
190: * RedBean. The Setup() method can be called without any arguments, in this case it will
191: * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
192: *
193: * @param string $dsn Database connection string
194: * @param string $username Username for database
195: * @param string $password Password for database
196: * @param boolean $frozen TRUE if you want to setup in frozen mode
197: *
198: * @return ToolBox
199: */
200: public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE )
201: {
202: if ( is_null( $dsn ) ) {
203: $dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db';
204: }
205:
206: self::addDatabase( 'default', $dsn, $username, $password, $frozen );
207: self::selectDatabase( 'default' );
208:
209: return self::$toolbox;
210: }
211:
212: /**
213: * Toggles Narrow Field Mode.
214: * See documentation in QueryWriter.
215: *
216: * @param boolean $mode TRUE = Narrow Field Mode
217: *
218: * @return void
219: */
220: public static function setNarrowFieldMode( $mode )
221: {
222: AQueryWriter::setNarrowFieldMode( $mode );
223: }
224:
225: /**
226: * Wraps a transaction around a closure or string callback.
227: * If an Exception is thrown inside, the operation is automatically rolled back.
228: * If no Exception happens, it commits automatically.
229: * It also supports (simulated) nested transactions (that is useful when
230: * you have many methods that needs transactions but are unaware of
231: * each other).
232: *
233: * Example:
234: *
235: * <code>
236: * $from = 1;
237: * $to = 2;
238: * $amount = 300;
239: *
240: * R::transaction(function() use($from, $to, $amount)
241: * {
242: * $accountFrom = R::load('account', $from);
243: * $accountTo = R::load('account', $to);
244: * $accountFrom->money -= $amount;
245: * $accountTo->money += $amount;
246: * R::store($accountFrom);
247: * R::store($accountTo);
248: * });
249: * </code>
250: *
251: * @param callable $callback Closure (or other callable) with the transaction logic
252: *
253: * @return mixed
254: */
255: public static function transaction( $callback )
256: {
257: return Transaction::transaction( self::$adapter, $callback );
258: }
259:
260: /**
261: * Adds a database to the facade, afterwards you can select the database using
262: * selectDatabase($key), where $key is the name you assigned to this database.
263: *
264: * Usage:
265: *
266: * <code>
267: * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' );
268: * R::selectDatabase( 'database-1' ); //to select database again
269: * </code>
270: *
271: * This method allows you to dynamically add (and select) new databases
272: * to the facade. Adding a database with the same key will cause an exception.
273: *
274: * @param string $key ID for the database
275: * @param string $dsn DSN for the database
276: * @param string $user user for connection
277: * @param NULL|string $pass password for connection
278: * @param bool $frozen whether this database is frozen or not
279: *
280: * @return void
281: */
282: public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE )
283: {
284: if ( isset( self::$toolboxes[$key] ) ) {
285: throw new RedException( 'A database has already been specified for this key.' );
286: }
287:
288: if ( is_object($dsn) ) {
289: $db = new RPDO( $dsn );
290: $dbType = $db->getDatabaseType();
291: } else {
292: $db = new RPDO( $dsn, $user, $pass, TRUE );
293: $dbType = substr( $dsn, 0, strpos( $dsn, ':' ) );
294: }
295:
296: $adapter = new DBAdapter( $db );
297:
298: $writers = array(
299: 'pgsql' => 'PostgreSQL',
300: 'sqlite' => 'SQLiteT',
301: 'cubrid' => 'CUBRID',
302: 'mysql' => 'MySQL',
303: 'sqlsrv' => 'SQLServer',
304: );
305:
306: $wkey = trim( strtolower( $dbType ) );
307: if ( !isset( $writers[$wkey] ) ) {
308: $wkey = preg_replace( '/\W/', '' , $wkey );
309: throw new RedException( 'Unsupported database ('.$wkey.').' );
310: }
311: $writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey];
312: $writer = new $writerClass( $adapter );
313: $redbean = new OODB( $writer, $frozen );
314:
315: self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer );
316: }
317:
318: /**
319: * Determines whether a database identified with the specified key has
320: * already been added to the facade. This function will return TRUE
321: * if the database indicated by the key is available and FALSE otherwise.
322: *
323: * @param string $key the key/name of the database to check for
324: *
325: * @return boolean
326: */
327: public static function hasDatabase( $key )
328: {
329: return ( isset( self::$toolboxes[$key] ) );
330: }
331:
332: /**
333: * Selects a different database for the Facade to work with.
334: * If you use the R::setup() you don't need this method. This method is meant
335: * for multiple database setups. This method selects the database identified by the
336: * database ID ($key). Use addDatabase() to add a new database, which in turn
337: * can be selected using selectDatabase(). If you use R::setup(), the resulting
338: * database will be stored under key 'default', to switch (back) to this database
339: * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been
340: * switched and FALSE otherwise (for instance if you already using the specified database).
341: *
342: * @param string $key Key of the database to select
343: *
344: * @return boolean
345: */
346: public static function selectDatabase( $key )
347: {
348: if ( self::$currentDB === $key ) {
349: return FALSE;
350: }
351:
352: if ( !isset( self::$toolboxes[$key] ) ) {
353: throw new RedException( 'Database not found in registry. Add database using R::addDatabase().' );
354: }
355:
356: self::configureFacadeWithToolbox( self::$toolboxes[$key] );
357: self::$currentDB = $key;
358:
359: return TRUE;
360: }
361:
362: /**
363: * Toggles DEBUG mode.
364: * In Debug mode all SQL that happens under the hood will
365: * be printed to the screen and/or logged.
366: * If no database connection has been configured using R::setup() or
367: * R::selectDatabase() this method will throw an exception.
368: *
369: * There are 2 debug styles:
370: *
371: * Classic: separate parameter bindings, explicit and complete but less readable
372: * Fancy: interpersed bindings, truncates large strings, highlighted schema changes
373: *
374: * Fancy style is more readable but sometimes incomplete.
375: *
376: * The first parameter turns debugging ON or OFF.
377: * The second parameter indicates the mode of operation:
378: *
379: * 0 Log and write to STDOUT classic style (default)
380: * 1 Log only, class style
381: * 2 Log and write to STDOUT fancy style
382: * 3 Log only, fancy style
383: *
384: * This function always returns the logger instance created to generate the
385: * debug messages.
386: *
387: * @param boolean $tf debug mode (TRUE or FALSE)
388: * @param integer $mode mode of operation
389: *
390: * @return RDefault
391: * @throws RedException
392: */
393: public static function debug( $tf = TRUE, $mode = 0 )
394: {
395: if ($mode > 1) {
396: $mode -= 2;
397: $logger = new Debug;
398: } else {
399: $logger = new RDefault;
400: }
401:
402: if ( !isset( self::$adapter ) ) {
403: throw new RedException( 'Use R::setup() first.' );
404: }
405: $logger->setMode($mode);
406: self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
407:
408: return $logger;
409: }
410:
411: /**
412: * Turns on the fancy debugger.
413: * In 'fancy' mode the debugger will output queries with bound
414: * parameters inside the SQL itself. This method has been added to
415: * offer a convenient way to activate the fancy debugger system
416: * in one call.
417: *
418: * @param boolean $toggle TRUE to activate debugger and select 'fancy' mode
419: *
420: * @return void
421: */
422: public static function fancyDebug( $toggle = TRUE )
423: {
424: self::debug( $toggle, 2 );
425: }
426:
427: /**
428: * Inspects the database schema. If you pass the type of a bean this
429: * method will return the fields of its table in the database.
430: * The keys of this array will be the field names and the values will be
431: * the column types used to store their values.
432: * If no type is passed, this method returns a list of all tables in the database.
433: *
434: * @param string $type Type of bean (i.e. table) you want to inspect
435: *
436: * @return array
437: */
438: public static function inspect( $type = NULL )
439: {
440: return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type );
441: }
442:
443: /**
444: * Stores a bean in the database. This method takes a
445: * OODBBean Bean Object $bean and stores it
446: * in the database. If the database schema is not compatible
447: * with this bean and RedBean runs in fluid mode the schema
448: * will be altered to store the bean correctly.
449: * If the database schema is not compatible with this bean and
450: * RedBean runs in frozen mode it will throw an exception.
451: * This function returns the primary key ID of the inserted
452: * bean.
453: *
454: * The return value is an integer if possible. If it is not possible to
455: * represent the value as an integer a string will be returned.
456: *
457: * @param OODBBean|SimpleModel $bean bean to store
458: *
459: * @return integer|string
460: */
461: public static function store( $bean )
462: {
463: return self::$redbean->store( $bean );
464: }
465:
466: /**
467: * Toggles fluid or frozen mode. In fluid mode the database
468: * structure is adjusted to accomodate your objects. In frozen mode
469: * this is not the case.
470: *
471: * You can also pass an array containing a selection of frozen types.
472: * Let's call this chilly mode, it's just like fluid mode except that
473: * certain types (i.e. tables) aren't touched.
474: *
475: * @param boolean|array $trueFalse
476: */
477: public static function freeze( $tf = TRUE )
478: {
479: self::$redbean->freeze( $tf );
480: }
481:
482: /**
483: * Loads multiple types of beans with the same ID.
484: * This might look like a strange method, however it can be useful
485: * for loading a one-to-one relation.
486: *
487: * Usage:
488: * list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
489: *
490: * @param string|array $types the set of types to load at once
491: * @param mixed $id the common ID
492: *
493: * @return OODBBean
494: */
495: public static function loadMulti( $types, $id )
496: {
497: return MultiLoader::load( self::$redbean, $types, $id );
498: }
499:
500: /**
501: * Loads a bean from the object database.
502: * It searches for a OODBBean Bean Object in the
503: * database. It does not matter how this bean has been stored.
504: * RedBean uses the primary key ID $id and the string $type
505: * to find the bean. The $type specifies what kind of bean you
506: * are looking for; this is the same type as used with the
507: * dispense() function. If RedBean finds the bean it will return
508: * the OODB Bean object; if it cannot find the bean
509: * RedBean will return a new bean of type $type and with
510: * primary key ID 0. In the latter case it acts basically the
511: * same as dispense().
512: *
513: * Important note:
514: * If the bean cannot be found in the database a new bean of
515: * the specified type will be generated and returned.
516: *
517: * @param string $type type of bean you want to load
518: * @param integer $id ID of the bean you want to load
519: *
520: * @return OODBBean
521: */
522: public static function load( $type, $id )
523: {
524: return self::$redbean->load( $type, $id );
525: }
526:
527: /**
528: * Removes a bean from the database.
529: * This function will remove the specified OODBBean
530: * Bean Object from the database.
531: *
532: * This facade method also accepts a type-id combination,
533: * in the latter case this method will attempt to load the specified bean
534: * and THEN trash it.
535: *
536: * @param string|OODBBean|SimpleModel $bean bean you want to remove from database
537: * @param integer $id ID if the bean to trash (optional, type-id variant only)
538: *
539: * @return void
540: */
541: public static function trash( $beanOrType, $id = NULL )
542: {
543: if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) );
544: return self::$redbean->trash( $beanOrType );
545: }
546:
547: /**
548: * Dispenses a new RedBean OODB Bean for use with
549: * the rest of the methods.
550: *
551: * @param string|array $typeOrBeanArray type or bean array to import
552: * @param integer $number number of beans to dispense
553: * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
554: *
555: * @return array|OODBBean
556: */
557: public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE )
558: {
559: return DispenseHelper::dispense( self::$redbean, $typeOrBeanArray, $num, $alwaysReturnArray );
560: }
561:
562: /**
563: * Takes a comma separated list of bean types
564: * and dispenses these beans. For each type in the list
565: * you can specify the number of beans to be dispensed.
566: *
567: * Usage:
568: *
569: * <code>
570: * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
571: * </code>
572: *
573: * This will dispense a book, a page and a text. This way you can
574: * quickly dispense beans of various types in just one line of code.
575: *
576: * Usage:
577: *
578: * <code>
579: * list($book, $pages) = R::dispenseAll('book,page*100');
580: * </code>
581: *
582: * This returns an array with a book bean and then another array
583: * containing 100 page beans.
584: *
585: * @param string $order a description of the desired dispense order using the syntax above
586: * @param boolean $onlyArrays return only arrays even if amount < 2
587: *
588: * @return array
589: */
590: public static function dispenseAll( $order, $onlyArrays = FALSE )
591: {
592: return DispenseHelper::dispenseAll( self::$redbean, $order, $onlyArrays );
593: }
594:
595: /**
596: * Convience method. Tries to find beans of a certain type,
597: * if no beans are found, it dispenses a bean of that type.
598: *
599: * @param string $type type of bean you are looking for
600: * @param string $sql SQL code for finding the bean
601: * @param array $bindings parameters to bind to SQL
602: *
603: * @return array
604: */
605: public static function findOrDispense( $type, $sql = NULL, $bindings = array() )
606: {
607: return self::$finder->findOrDispense( $type, $sql, $bindings );
608: }
609:
610: /**
611: * Finds a bean using a type and a where clause (SQL).
612: * As with most Query tools in RedBean you can provide values to
613: * be inserted in the SQL statement by populating the value
614: * array parameter; you can either use the question mark notation
615: * or the slot-notation (:keyname).
616: *
617: * @param string $type the type of bean you are looking for
618: * @param string $sql SQL query to find the desired bean, starting right after WHERE clause
619: * @param array $bindings array of values to be bound to parameters in query
620: *
621: * @return array
622: */
623: public static function find( $type, $sql = NULL, $bindings = array() )
624: {
625: return self::$finder->find( $type, $sql, $bindings );
626: }
627:
628: /**
629: * @see Facade::find
630: * The findAll() method differs from the find() method in that it does
631: * not assume a WHERE-clause, so this is valid:
632: *
633: * R::findAll('person',' ORDER BY name DESC ');
634: *
635: * Your SQL does not have to start with a valid WHERE-clause condition.
636: *
637: * @param string $type the type of bean you are looking for
638: * @param string $sql SQL query to find the desired bean, starting right after WHERE clause
639: * @param array $bindings array of values to be bound to parameters in query
640: *
641: * @return array
642: */
643: public static function findAll( $type, $sql = NULL, $bindings = array() )
644: {
645: return self::$finder->find( $type, $sql, $bindings );
646: }
647:
648: /**
649: * @see Facade::find
650: * The variation also exports the beans (i.e. it returns arrays).
651: *
652: * @param string $type the type of bean you are looking for
653: * @param string $sql SQL query to find the desired bean, starting right after WHERE clause
654: * @param array $bindings array of values to be bound to parameters in query
655: *
656: * @return array
657: */
658: public static function findAndExport( $type, $sql = NULL, $bindings = array() )
659: {
660: return self::$finder->findAndExport( $type, $sql, $bindings );
661: }
662:
663: /**
664: * @see Facade::find
665: * This variation returns the first bean only.
666: *
667: * @param string $type the type of bean you are looking for
668: * @param string $sql SQL query to find the desired bean, starting right after WHERE clause
669: * @param array $bindings array of values to be bound to parameters in query
670: *
671: * @return OODBBean
672: */
673: public static function findOne( $type, $sql = NULL, $bindings = array() )
674: {
675: return self::$finder->findOne( $type, $sql, $bindings );
676: }
677:
678: /**
679: * @see Facade::find
680: * This variation returns the last bean only.
681: *
682: * @param string $type the type of bean you are looking for
683: * @param string $sql SQL query to find the desired bean, starting right after WHERE clause
684: * @param array $bindings array of values to be bound to parameters in query
685: *
686: * @return OODBBean
687: */
688: public static function findLast( $type, $sql = NULL, $bindings = array() )
689: {
690: return self::$finder->findLast( $type, $sql, $bindings );
691: }
692:
693: /**
694: * Finds a bean collection.
695: * Use this for large datasets.
696: *
697: * @param string $type the type of bean you are looking for
698: * @param string $sql SQL query to find the desired bean, starting right after WHERE clause
699: * @param array $bindings array of values to be bound to parameters in query
700: *
701: * @return BeanCollection
702: */
703: public static function findCollection( $type, $sql = NULL, $bindings = array() )
704: {
705: return self::$finder->findCollection( $type, $sql, $bindings );
706: }
707:
708: /**
709: * Finds multiple types of beans at once and offers additional
710: * remapping functionality. This is a very powerful yet complex function.
711: * For details see Finder::findMulti().
712: *
713: * @see Finder::findMulti()
714: *
715: * @param array|string $types a list of bean types to find
716: * @param string|array $sqlOrArr SQL query string or result set array
717: * @param array $bindings SQL bindings
718: * @param array $remappings an array of remapping arrays containing closures
719: *
720: * @return array
721: */
722: public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() )
723: {
724: return self::$finder->findMulti( $types, $sql, $bindings, $remappings );
725: }
726:
727: /**
728: * Returns an array of beans. Pass a type and a series of ids and
729: * this method will bring you the corresponding beans.
730: *
731: * important note: Because this method loads beans using the load()
732: * function (but faster) it will return empty beans with ID 0 for
733: * every bean that could not be located. The resulting beans will have the
734: * passed IDs as their keys.
735: *
736: * @param string $type type of beans
737: * @param array $ids ids to load
738: *
739: * @return array
740: */
741: public static function batch( $type, $ids )
742: {
743: return self::$redbean->batch( $type, $ids );
744: }
745:
746: /**
747: * @see Facade::batch
748: *
749: * Alias for batch(). Batch method is older but since we added so-called *All
750: * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to
751: * improve the consistency of the Facade API and also add an alias for batch() called
752: * loadAll.
753: *
754: * @param string $type type of beans
755: * @param array $ids ids to load
756: *
757: * @return array
758: */
759: public static function loadAll( $type, $ids )
760: {
761: return self::$redbean->batch( $type, $ids );
762: }
763:
764: /**
765: * Convenience function to execute Queries directly.
766: * Executes SQL.
767: *
768: * @param string $sql SQL query to execute
769: * @param array $bindings a list of values to be bound to query parameters
770: *
771: * @return integer
772: */
773: public static function exec( $sql, $bindings = array() )
774: {
775: return self::query( 'exec', $sql, $bindings );
776: }
777:
778: /**
779: * Convenience function to execute Queries directly.
780: * Executes SQL.
781: *
782: * @param string $sql SQL query to execute
783: * @param array $bindings a list of values to be bound to query parameters
784: *
785: * @return array
786: */
787: public static function getAll( $sql, $bindings = array() )
788: {
789: return self::query( 'get', $sql, $bindings );
790: }
791:
792: /**
793: * Convenience function to execute Queries directly.
794: * Executes SQL.
795: *
796: * @param string $sql SQL query to execute
797: * @param array $bindings a list of values to be bound to query parameters
798: *
799: * @return string
800: */
801: public static function getCell( $sql, $bindings = array() )
802: {
803: return self::query( 'getCell', $sql, $bindings );
804: }
805:
806: /**
807: * Convenience function to execute Queries directly.
808: * Executes SQL.
809: *
810: * @param string $sql SQL query to execute
811: * @param array $bindings a list of values to be bound to query parameters
812: *
813: * @return array
814: */
815: public static function getRow( $sql, $bindings = array() )
816: {
817: return self::query( 'getRow', $sql, $bindings );
818: }
819:
820: /**
821: * Convenience function to execute Queries directly.
822: * Executes SQL.
823: *
824: * @param string $sql SQL query to execute
825: * @param array $bindings a list of values to be bound to query parameters
826: *
827: * @return array
828: */
829: public static function getCol( $sql, $bindings = array() )
830: {
831: return self::query( 'getCol', $sql, $bindings );
832: }
833:
834: /**
835: * Convenience function to execute Queries directly.
836: * Executes SQL.
837: * Results will be returned as an associative array. The first
838: * column in the select clause will be used for the keys in this array and
839: * the second column will be used for the values. If only one column is
840: * selected in the query, both key and value of the array will have the
841: * value of this field for each row.
842: *
843: * @param string $sql SQL query to execute
844: * @param array $bindings a list of values to be bound to query parameters
845: *
846: * @return array
847: */
848: public static function getAssoc( $sql, $bindings = array() )
849: {
850: return self::query( 'getAssoc', $sql, $bindings );
851: }
852:
853: /**
854: * Convenience function to execute Queries directly.
855: * Executes SQL.
856: * Results will be returned as an associative array indexed by the first
857: * column in the select.
858: *
859: * @param string $sql SQL query to execute
860: * @param array $bindings a list of values to be bound to query parameters
861: *
862: * @return array
863: */
864: public static function getAssocRow( $sql, $bindings = array() )
865: {
866: return self::query( 'getAssocRow', $sql, $bindings );
867: }
868:
869: /**
870: * Returns the insert ID for databases that support/require this
871: * functionality. Alias for R::getAdapter()->getInsertID().
872: *
873: * @return mixed
874: */
875: public static function getInsertID()
876: {
877: return self::$adapter->getInsertID();
878: }
879:
880: /**
881: * Makes a copy of a bean. This method makes a deep copy
882: * of the bean.The copy will have the following features.
883: * - All beans in own-lists will be duplicated as well
884: * - All references to shared beans will be copied but not the shared beans themselves
885: * - All references to parent objects (_id fields) will be copied but not the parents themselves
886: * In most cases this is the desired scenario for copying beans.
887: * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
888: * (i.e. one that already has been processed) the ID of the bean will be returned.
889: * This should not happen though.
890: *
891: * Note:
892: * This function does a reflectional database query so it may be slow.
893: *
894: * @deprecated
895: * This function is deprecated in favour of R::duplicate().
896: * This function has a confusing method signature, the R::duplicate() function
897: * only accepts two arguments: bean and filters.
898: *
899: * @param OODBBean $bean bean to be copied
900: * @param array $trail for internal usage, pass array()
901: * @param boolean $pid for internal usage
902: * @param array $white white list filter with bean types to duplicate
903: *
904: * @return array
905: */
906: public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() )
907: {
908: self::$duplicationManager->setFilters( $filters );
909: return self::$duplicationManager->dup( $bean, $trail, $pid );
910: }
911:
912: /**
913: * Makes a deep copy of a bean. This method makes a deep copy
914: * of the bean.The copy will have the following:
915: *
916: * * All beans in own-lists will be duplicated as well
917: * * All references to shared beans will be copied but not the shared beans themselves
918: * * All references to parent objects (_id fields) will be copied but not the parents themselves
919: *
920: * In most cases this is the desired scenario for copying beans.
921: * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
922: * (i.e. one that already has been processed) the ID of the bean will be returned.
923: * This should not happen though.
924: *
925: * Note:
926: * This function does a reflectional database query so it may be slow.
927: *
928: * Note:
929: * This is a simplified version of the deprecated R::dup() function.
930: *
931: * @param OODBBean $bean bean to be copied
932: * @param array $white white list filter with bean types to duplicate
933: *
934: * @return array
935: */
936: public static function duplicate( $bean, $filters = array() )
937: {
938: return self::dup( $bean, array(), FALSE, $filters );
939: }
940:
941: /**
942: * Exports a collection of beans. Handy for XML/JSON exports with a
943: * Javascript framework like Dojo or ExtJS.
944: * What will be exported:
945: *
946: * * contents of the bean
947: * * all own bean lists (recursively)
948: * * all shared beans (not THEIR own lists)
949: *
950: * @param array|OODBBean $beans beans to be exported
951: * @param boolean $parents whether you want parent beans to be exported
952: * @param array $filters whitelist of types
953: *
954: * @return array
955: */
956: public static function exportAll( $beans, $parents = FALSE, $filters = array())
957: {
958: return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle );
959: }
960:
961: /**
962: * Selects case style for export.
963: * This will determine the case style for the keys of exported beans (see exportAll).
964: * The following options are accepted:
965: *
966: * * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid )
967: * * 'camel' Camel Case (i.e. bookId isValid )
968: * * 'dolphin' Dolphin Case (i.e. bookID isValid ) Like CamelCase but ID is written all uppercase
969: *
970: * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different
971: * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id.
972: * Due to information loss this cannot be corrected. However if you might try
973: * DolphinCase for IDs it takes into account the exception concerning IDs.
974: *
975: * @param string $caseStyle case style identifier
976: *
977: * @return void
978: */
979: public static function useExportCase( $caseStyle = 'default' )
980: {
981: if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' );
982: self::$exportCaseStyle = $caseStyle;
983: }
984:
985: /**
986: * Converts a series of rows to beans.
987: * This method converts a series of rows to beans.
988: * The type of the desired output beans can be specified in the
989: * first parameter. The second parameter is meant for the database
990: * result rows.
991: *
992: * Usage:
993: *
994: * <code>
995: * $rows = R::getAll( 'SELECT * FROM ...' )
996: * $beans = R::convertToBeans( $rows );
997: * </code>
998: *
999: * As of version 4.3.2 you can specify a meta-mask.
1000: * Data from columns with names starting with the value specified in the mask
1001: * will be transferred to the meta section of a bean (under data.bundle).
1002: *
1003: * <code>
1004: * $rows = R::getAll( 'SELECT FROM... COUNT(*) AS extra_count ...' );
1005: * $beans = R::convertToBeans( $rows );
1006: * $bean = reset( $beans );
1007: * $data = $bean->getMeta( 'data.bundle' );
1008: * $extra_count = $data['extra_count'];
1009: * </code>
1010: *
1011: * @param string $type type of beans to produce
1012: * @param array $rows must contain an array of array
1013: *
1014: * @return array
1015: */
1016: public static function convertToBeans( $type, $rows, $metamask = NULL )
1017: {
1018: return self::$redbean->convertToBeans( $type, $rows, $metamask );
1019: }
1020:
1021: /**
1022: * Just like converToBeans, but for one bean.
1023: * @see convertToBeans for more details.
1024: *
1025: * @param string $type type of beans to produce
1026: * @param array $row one row from the database
1027: *
1028: * @return array
1029: */
1030: public static function convertToBean( $type, $row, $metamask = NULL )
1031: {
1032: $beans = self::$redbean->convertToBeans( $type, array( $row ), $metamask );
1033: $bean = reset( $beans );
1034: return $bean;
1035: }
1036:
1037: /**
1038: * Part of RedBeanPHP Tagging API.
1039: * Tests whether a bean has been associated with one ore more
1040: * of the listed tags. If the third parameter is TRUE this method
1041: * will return TRUE only if all tags that have been specified are indeed
1042: * associated with the given bean, otherwise FALSE.
1043: * If the third parameter is FALSE this
1044: * method will return TRUE if one of the tags matches, FALSE if none
1045: * match.
1046: *
1047: * @param OODBBean $bean bean to check for tags
1048: * @param array $tags list of tags
1049: * @param boolean $all whether they must all match or just some
1050: *
1051: * @return boolean
1052: */
1053: public static function hasTag( $bean, $tags, $all = FALSE )
1054: {
1055: return self::$tagManager->hasTag( $bean, $tags, $all );
1056: }
1057:
1058: /**
1059: * Part of RedBeanPHP Tagging API.
1060: * Removes all specified tags from the bean. The tags specified in
1061: * the second parameter will no longer be associated with the bean.
1062: *
1063: * @param OODBBean $bean tagged bean
1064: * @param array $tagList list of tags (names)
1065: *
1066: * @return void
1067: */
1068: public static function untag( $bean, $tagList )
1069: {
1070: self::$tagManager->untag( $bean, $tagList );
1071: }
1072:
1073: /**
1074: * Part of RedBeanPHP Tagging API.
1075: * Tags a bean or returns tags associated with a bean.
1076: * If $tagList is NULL or omitted this method will return a
1077: * comma separated list of tags associated with the bean provided.
1078: * If $tagList is a comma separated list (string) of tags all tags will
1079: * be associated with the bean.
1080: * You may also pass an array instead of a string.
1081: *
1082: * @param OODBBean $bean bean to tag
1083: * @param mixed $tagList tags to attach to the specified bean
1084: *
1085: * @return string
1086: */
1087: public static function tag( OODBBean $bean, $tagList = NULL )
1088: {
1089: return self::$tagManager->tag( $bean, $tagList );
1090: }
1091:
1092: /**
1093: * Part of RedBeanPHP Tagging API.
1094: * Adds tags to a bean.
1095: * If $tagList is a comma separated list of tags all tags will
1096: * be associated with the bean.
1097: * You may also pass an array instead of a string.
1098: *
1099: * @param OODBBean $bean bean to tag
1100: * @param array $tagList list of tags to add to bean
1101: *
1102: * @return void
1103: */
1104: public static function addTags( OODBBean $bean, $tagList )
1105: {
1106: self::$tagManager->addTags( $bean, $tagList );
1107: }
1108:
1109: /**
1110: * Part of RedBeanPHP Tagging API.
1111: * Returns all beans that have been tagged with one of the tags given.
1112: *
1113: * @param string $beanType type of bean you are looking for
1114: * @param array $tagList list of tags to match
1115: * @param string $sql additional SQL query snippet
1116: * @param array $bindings a list of values to bind to the query parameters
1117: *
1118: * @return array
1119: */
1120: public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
1121: {
1122: return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings );
1123: }
1124:
1125: /**
1126: * Part of RedBeanPHP Tagging API.
1127: * Returns all beans that have been tagged with ALL of the tags given.
1128: *
1129: * @param string $beanType type of bean you are looking for
1130: * @param array $tagList list of tags to match
1131: * @param string $sql additional SQL query snippet
1132: * @param array $bindings a list of values to bind to the query parameters
1133: *
1134: * @return array
1135: */
1136: public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
1137: {
1138: return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings );
1139: }
1140:
1141: /**
1142: * Wipes all beans of type $beanType.
1143: *
1144: * @param string $beanType type of bean you want to destroy entirely
1145: *
1146: * @return boolean
1147: */
1148: public static function wipe( $beanType )
1149: {
1150: return Facade::$redbean->wipe( $beanType );
1151: }
1152:
1153: /**
1154: * Counts the number of beans of type $type.
1155: * This method accepts a second argument to modify the count-query.
1156: * A third argument can be used to provide bindings for the SQL snippet.
1157: *
1158: * @param string $type type of bean we are looking for
1159: * @param string $addSQL additional SQL snippet
1160: * @param array $bindings parameters to bind to SQL
1161: *
1162: * @return integer
1163: */
1164: public static function count( $type, $addSQL = '', $bindings = array() )
1165: {
1166: return Facade::$redbean->count( $type, $addSQL, $bindings );
1167: }
1168:
1169: /**
1170: * Configures the facade, want to have a new Writer? A new Object Database or a new
1171: * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
1172: * toolbox.
1173: *
1174: * @param ToolBox $tb toolbox to configure facade with
1175: *
1176: * @return ToolBox
1177: */
1178: public static function configureFacadeWithToolbox( ToolBox $tb )
1179: {
1180: $oldTools = self::$toolbox;
1181: self::$toolbox = $tb;
1182: self::$writer = self::$toolbox->getWriter();
1183: self::$adapter = self::$toolbox->getDatabaseAdapter();
1184: self::$redbean = self::$toolbox->getRedBean();
1185: self::$finder = new Finder( self::$toolbox );
1186: self::$associationManager = new AssociationManager( self::$toolbox );
1187: self::$redbean->setAssociationManager( self::$associationManager );
1188: self::$labelMaker = new LabelMaker( self::$toolbox );
1189: $helper = new SimpleModelHelper();
1190: $helper->attachEventListeners( self::$redbean );
1191: self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper );
1192: self::$duplicationManager = new DuplicationManager( self::$toolbox );
1193: self::$tagManager = new TagManager( self::$toolbox );
1194: return $oldTools;
1195: }
1196:
1197: /**
1198: * Facade Convience method for adapter transaction system.
1199: * Begins a transaction.
1200: *
1201: * @return bool
1202: */
1203: public static function begin()
1204: {
1205: if ( !self::$redbean->isFrozen() ) return FALSE;
1206: self::$adapter->startTransaction();
1207: return TRUE;
1208: }
1209:
1210: /**
1211: * Facade Convience method for adapter transaction system.
1212: * Commits a transaction.
1213: *
1214: * @return bool
1215: */
1216: public static function commit()
1217: {
1218: if ( !self::$redbean->isFrozen() ) return FALSE;
1219: self::$adapter->commit();
1220: return TRUE;
1221: }
1222:
1223: /**
1224: * Facade Convience method for adapter transaction system.
1225: * Rolls back a transaction.
1226: *
1227: * @return bool
1228: */
1229: public static function rollback()
1230: {
1231: if ( !self::$redbean->isFrozen() ) return FALSE;
1232: self::$adapter->rollback();
1233: return TRUE;
1234: }
1235:
1236: /**
1237: * Returns a list of columns. Format of this array:
1238: * array( fieldname => type )
1239: * Note that this method only works in fluid mode because it might be
1240: * quite heavy on production servers!
1241: *
1242: * @param string $table name of the table (not type) you want to get columns of
1243: *
1244: * @return array
1245: */
1246: public static function getColumns( $table )
1247: {
1248: return self::$writer->getColumns( $table );
1249: }
1250:
1251: /**
1252: * Generates question mark slots for an array of values.
1253: *
1254: * @param array $array array to generate question mark slots for
1255: *
1256: * @return string
1257: */
1258: public static function genSlots( $array, $template = NULL )
1259: {
1260: return ArrayTool::genSlots( $array, $template );
1261: }
1262:
1263: /**
1264: * Flattens a multi dimensional bindings array for use with genSlots().
1265: *
1266: * @param array $array array to flatten
1267: *
1268: * @return array
1269: */
1270: public static function flat( $array, $result = array() )
1271: {
1272: return ArrayTool::flat( $array, $result );
1273: }
1274:
1275: /**
1276: * Nukes the entire database.
1277: * This will remove all schema structures from the database.
1278: * Only works in fluid mode. Be careful with this method.
1279: *
1280: * @warning dangerous method, will remove all tables, columns etc.
1281: *
1282: * @return void
1283: */
1284: public static function nuke()
1285: {
1286: if ( !self::$redbean->isFrozen() ) {
1287: self::$writer->wipeAll();
1288: }
1289: }
1290:
1291: /**
1292: * Short hand function to store a set of beans at once, IDs will be
1293: * returned as an array. For information please consult the R::store()
1294: * function.
1295: * A loop saver.
1296: *
1297: * @param array $beans list of beans to be stored
1298: *
1299: * @return array
1300: */
1301: public static function storeAll( $beans )
1302: {
1303: $ids = array();
1304: foreach ( $beans as $bean ) {
1305: $ids[] = self::store( $bean );
1306: }
1307: return $ids;
1308: }
1309:
1310: /**
1311: * Short hand function to trash a set of beans at once.
1312: * For information please consult the R::trash() function.
1313: * A loop saver.
1314: *
1315: * @param array $beans list of beans to be trashed
1316: *
1317: * @return void
1318: */
1319: public static function trashAll( $beans )
1320: {
1321: foreach ( $beans as $bean ) {
1322: self::trash( $bean );
1323: }
1324: }
1325:
1326: /**
1327: * Toggles Writer Cache.
1328: * Turns the Writer Cache on or off. The Writer Cache is a simple
1329: * query based caching system that may improve performance without the need
1330: * for cache management. This caching system will cache non-modifying queries
1331: * that are marked with special SQL comments. As soon as a non-marked query
1332: * gets executed the cache will be flushed. Only non-modifying select queries
1333: * have been marked therefore this mechanism is a rather safe way of caching, requiring
1334: * no explicit flushes or reloads. Of course this does not apply if you intend to test
1335: * or simulate concurrent querying.
1336: *
1337: * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache
1338: *
1339: * @return void
1340: */
1341: public static function useWriterCache( $yesNo )
1342: {
1343: self::getWriter()->setUseCache( $yesNo );
1344: }
1345:
1346: /**
1347: * A label is a bean with only an id, type and name property.
1348: * This function will dispense beans for all entries in the array. The
1349: * values of the array will be assigned to the name property of each
1350: * individual bean.
1351: *
1352: * @param string $type type of beans you would like to have
1353: * @param array $labels list of labels, names for each bean
1354: *
1355: * @return array
1356: */
1357: public static function dispenseLabels( $type, $labels )
1358: {
1359: return self::$labelMaker->dispenseLabels( $type, $labels );
1360: }
1361:
1362: /**
1363: * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs.
1364: * Either returns a (newly created) bean respresenting the desired ENUM
1365: * value or returns a list of all enums for the type.
1366: *
1367: * To obtain (and add if necessary) an ENUM value:
1368: *
1369: * <code>
1370: * $tea->flavour = R::enum( 'flavour:apple' );
1371: * </code>
1372: *
1373: * Returns a bean of type 'flavour' with name = apple.
1374: * This will add a bean with property name (set to APPLE) to the database
1375: * if it does not exist yet.
1376: *
1377: * To obtain all flavours:
1378: *
1379: * <code>
1380: * R::enum('flavour');
1381: * </code>
1382: *
1383: * To get a list of all flavour names:
1384: *
1385: * <code>
1386: * R::gatherLabels( R::enum( 'flavour' ) );
1387: * </code>
1388: *
1389: * @param string $enum either type or type-value
1390: *
1391: * @return array|OODBBean
1392: */
1393: public static function enum( $enum )
1394: {
1395: return self::$labelMaker->enum( $enum );
1396: }
1397:
1398: /**
1399: * Gathers labels from beans. This function loops through the beans,
1400: * collects the values of the name properties of each individual bean
1401: * and stores the names in a new array. The array then gets sorted using the
1402: * default sort function of PHP (sort).
1403: *
1404: * @param array $beans list of beans to loop
1405: *
1406: * @return array
1407: */
1408: public static function gatherLabels( $beans )
1409: {
1410: return self::$labelMaker->gatherLabels( $beans );
1411: }
1412:
1413: /**
1414: * Closes the database connection.
1415: *
1416: * @return void
1417: */
1418: public static function close()
1419: {
1420: if ( isset( self::$adapter ) ) {
1421: self::$adapter->close();
1422: }
1423: }
1424:
1425: /**
1426: * Simple convenience function, returns ISO date formatted representation
1427: * of $time.
1428: *
1429: * @param mixed $time UNIX timestamp
1430: *
1431: * @return string
1432: */
1433: public static function isoDate( $time = NULL )
1434: {
1435: if ( !$time ) {
1436: $time = time();
1437: }
1438:
1439: return @date( 'Y-m-d', $time );
1440: }
1441:
1442: /**
1443: * Simple convenience function, returns ISO date time
1444: * formatted representation
1445: * of $time.
1446: *
1447: * @param mixed $time UNIX timestamp
1448: *
1449: * @return string
1450: */
1451: public static function isoDateTime( $time = NULL )
1452: {
1453: if ( !$time ) $time = time();
1454: return @date( 'Y-m-d H:i:s', $time );
1455: }
1456:
1457: /**
1458: * Optional accessor for neat code.
1459: * Sets the database adapter you want to use.
1460: *
1461: * @param Adapter $adapter Database Adapter for facade to use
1462: *
1463: * @return void
1464: */
1465: public static function setDatabaseAdapter( Adapter $adapter )
1466: {
1467: self::$adapter = $adapter;
1468: }
1469:
1470: /**
1471: * Optional accessor for neat code.
1472: * Sets the database adapter you want to use.
1473: *
1474: * @param QueryWriter $writer Query Writer instance for facade to use
1475: *
1476: * @return void
1477: */
1478: public static function setWriter( QueryWriter $writer )
1479: {
1480: self::$writer = $writer;
1481: }
1482:
1483: /**
1484: * Optional accessor for neat code.
1485: * Sets the database adapter you want to use.
1486: *
1487: * @param OODB $redbean Object Database for facade to use
1488: */
1489: public static function setRedBean( OODB $redbean )
1490: {
1491: self::$redbean = $redbean;
1492: }
1493:
1494: /**
1495: * Optional accessor for neat code.
1496: * Sets the database adapter you want to use.
1497: *
1498: * @return DBAdapter
1499: */
1500: public static function getDatabaseAdapter()
1501: {
1502: return self::$adapter;
1503: }
1504:
1505: /**
1506: * In case you use PDO (which is recommended and the default but not mandatory, hence
1507: * the database adapter), you can use this method to obtain the PDO object directly.
1508: * This is a convenience method, it will do the same as:
1509: *
1510: * <code>
1511: * R::getDatabaseAdapter()->getDatabase()->getPDO();
1512: * </code>
1513: *
1514: * If the PDO object could not be found, for whatever reason, this method
1515: * will return NULL instead.
1516: *
1517: * @return NULL|PDO
1518: */
1519: public static function getPDO()
1520: {
1521: $databaseAdapter = self::getDatabaseAdapter();
1522: if ( is_null( $databaseAdapter ) ) return NULL;
1523: $database = $databaseAdapter->getDatabase();
1524: if ( is_null( $database ) ) return NULL;
1525: if ( !method_exists( $database, 'getPDO' ) ) return NULL;
1526: return $database->getPDO();
1527: }
1528:
1529: /**
1530: * Returns the current duplication manager instance.
1531: *
1532: * @return DuplicationManager
1533: */
1534: public static function getDuplicationManager()
1535: {
1536: return self::$duplicationManager;
1537: }
1538:
1539: /**
1540: * Optional accessor for neat code.
1541: * Sets the database adapter you want to use.
1542: *
1543: * @return QueryWriter
1544: */
1545: public static function getWriter()
1546: {
1547: return self::$writer;
1548: }
1549:
1550: /**
1551: * Optional accessor for neat code.
1552: * Sets the database adapter you want to use.
1553: *
1554: * @return OODB
1555: */
1556: public static function getRedBean()
1557: {
1558: return self::$redbean;
1559: }
1560:
1561: /**
1562: * Returns the toolbox currently used by the facade.
1563: * To set the toolbox use R::setup() or R::configureFacadeWithToolbox().
1564: * To create a toolbox use Setup::kickstart(). Or create a manual
1565: * toolbox using the ToolBox class.
1566: *
1567: * @return ToolBox
1568: */
1569: public static function getToolBox()
1570: {
1571: return self::$toolbox;
1572: }
1573:
1574: /**
1575: * Mostly for internal use, but might be handy
1576: * for some users.
1577: * This returns all the components of the currently
1578: * selected toolbox.
1579: *
1580: * Returns the components in the following order:
1581: *
1582: * # OODB instance (getRedBean())
1583: * # Database Adapter
1584: * # Query Writer
1585: * # Toolbox itself
1586: *
1587: * @return array
1588: */
1589: public static function getExtractedToolbox()
1590: {
1591: return array( self::$redbean, self::$adapter, self::$writer, self::$toolbox );
1592: }
1593:
1594: /**
1595: * Facade method for AQueryWriter::renameAssociation()
1596: *
1597: * @param string|array $from
1598: * @param string $to
1599: *
1600: * @return void
1601: */
1602: public static function renameAssociation( $from, $to = NULL )
1603: {
1604: AQueryWriter::renameAssociation( $from, $to );
1605: }
1606:
1607: /**
1608: * Little helper method for Resty Bean Can server and others.
1609: * Takes an array of beans and exports each bean.
1610: * Unlike exportAll this method does not recurse into own lists
1611: * and shared lists, the beans are exported as-is, only loaded lists
1612: * are exported.
1613: *
1614: * @param array $beans beans
1615: *
1616: * @return array
1617: */
1618: public static function beansToArray( $beans )
1619: {
1620: $list = array();
1621: foreach( $beans as $bean ) $list[] = $bean->export();
1622: return $list;
1623: }
1624:
1625: /**
1626: * Sets the error mode for FUSE.
1627: * What to do if a FUSE model method does not exist?
1628: * You can set the following options:
1629: *
1630: * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL
1631: * * OODBBean::C_ERR_LOG, logs the incident using error_log
1632: * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE
1633: * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING
1634: * * OODBBean::C_ERR_EXCEPTION, throws an exception
1635: * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function)
1636: * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR
1637: *
1638: * <code>
1639: * Custom handler method signature: handler( array (
1640: * 'message' => string
1641: * 'bean' => OODBBean
1642: * 'method' => string
1643: * ) )
1644: * </code>
1645: *
1646: * This method returns the old mode and handler as an array.
1647: *
1648: * @param integer $mode mode, determines how to handle errors
1649: * @param callable|NULL $func custom handler (if applicable)
1650: *
1651: * @return array
1652: */
1653: public static function setErrorHandlingFUSE( $mode, $func = NULL )
1654: {
1655: return OODBBean::setErrorHandlingFUSE( $mode, $func );
1656: }
1657:
1658: /**
1659: * Simple but effective debug function.
1660: * Given a one or more beans this method will
1661: * return an array containing first part of the string
1662: * representation of each item in the array.
1663: *
1664: * @param OODBBean|array $data either a bean or an array of beans
1665: *
1666: * @return array
1667: */
1668: public static function dump( $data )
1669: {
1670: return Dump::dump( $data );
1671: }
1672:
1673: /**
1674: * Binds an SQL function to a column.
1675: * This method can be used to setup a decode/encode scheme or
1676: * perform UUID insertion. This method is especially useful for handling
1677: * MySQL spatial columns, because they need to be processed first using
1678: * the asText/GeomFromText functions.
1679: *
1680: * Example:
1681: *
1682: * <code>
1683: * R::bindFunc( 'read', 'location.point', 'asText' );
1684: * R::bindFunc( 'write', 'location.point', 'GeomFromText' );
1685: * </code>
1686: *
1687: * Passing NULL as the function will reset (clear) the function
1688: * for this column/mode.
1689: *
1690: * @param string $mode mode for function: i.e. read or write
1691: * @param string $field field (table.column) to bind function to
1692: * @param string $function SQL function to bind to specified column
1693: *
1694: * @return void
1695: */
1696: public static function bindFunc( $mode, $field, $function )
1697: {
1698: self::$redbean->bindFunc( $mode, $field, $function );
1699: }
1700:
1701: /**
1702: * Sets global aliases.
1703: * Registers a batch of aliases in one go. This works the same as
1704: * fetchAs and setAutoResolve but explicitly. For instance if you register
1705: * the alias 'cover' for 'page' a property containing a reference to a
1706: * page bean called 'cover' will correctly return the page bean and not
1707: * a (non-existant) cover bean.
1708: *
1709: * <code>
1710: * R::aliases( array( 'cover' => 'page' ) );
1711: * $book = R::dispense( 'book' );
1712: * $page = R::dispense( 'page' );
1713: * $book->cover = $page;
1714: * R::store( $book );
1715: * $book = $book->fresh();
1716: * $cover = $book->cover;
1717: * echo $cover->getMeta( 'type' ); //page
1718: * </code>
1719: *
1720: * The format of the aliases registration array is:
1721: *
1722: * {alias} => {actual type}
1723: *
1724: * In the example above we use:
1725: *
1726: * cover => page
1727: *
1728: * From that point on, every bean reference to a cover
1729: * will return a 'page' bean. Note that with autoResolve this
1730: * feature along with fetchAs() is no longer very important, although
1731: * relying on explicit aliases can be a bit faster.
1732: *
1733: * @param array $list list of global aliases to use
1734: *
1735: * @return void
1736: */
1737: public static function aliases( $list )
1738: {
1739: OODBBean::aliases( $list );
1740: }
1741:
1742: /**
1743: * Tries to find a bean matching a certain type and
1744: * criteria set. If no beans are found a new bean
1745: * will be created, the criteria will be imported into this
1746: * bean and the bean will be stored and returned.
1747: * If multiple beans match the criteria only the first one
1748: * will be returned.
1749: *
1750: * @param string $type type of bean to search for
1751: * @param array $like criteria set describing the bean to search for
1752: *
1753: * @return OODBBean
1754: */
1755: public static function findOrCreate( $type, $like = array() )
1756: {
1757: return self::$finder->findOrCreate( $type, $like );
1758: }
1759:
1760: /**
1761: * Tries to find beans matching the specified type and
1762: * criteria set.
1763: *
1764: * If the optional additional SQL snippet is a condition, it will
1765: * be glued to the rest of the query using the AND operator.
1766: *
1767: * @param string $type type of bean to search for
1768: * @param array $like optional criteria set describing the bean to search for
1769: * @param string $sql optional additional SQL for sorting
1770: *
1771: * @return array
1772: */
1773: public static function findLike( $type, $like = array(), $sql = '' )
1774: {
1775: return self::$finder->findLike( $type, $like, $sql );
1776: }
1777:
1778: /**
1779: * Starts logging queries.
1780: * Use this method to start logging SQL queries being
1781: * executed by the adapter.
1782: *
1783: * @note you cannot use R::debug and R::startLogging
1784: * at the same time because R::debug is essentially a
1785: * special kind of logging.
1786: *
1787: * @return void
1788: */
1789: public static function startLogging()
1790: {
1791: self::debug( TRUE, RDefault::C_LOGGER_ARRAY );
1792: }
1793:
1794: /**
1795: * Stops logging, comfortable method to stop logging of queries.
1796: *
1797: * @return void
1798: */
1799: public static function stopLogging()
1800: {
1801: self::debug( FALSE );
1802: }
1803:
1804: /**
1805: * Returns the log entries written after the startLogging.
1806: *
1807: * @return array
1808: */
1809: public static function getLogs()
1810: {
1811: return self::getLogger()->getLogs();
1812: }
1813:
1814: /**
1815: * Resets the Query counter.
1816: *
1817: * @return integer
1818: */
1819: public static function resetQueryCount()
1820: {
1821: self::$adapter->getDatabase()->resetCounter();
1822: }
1823:
1824: /**
1825: * Returns the number of SQL queries processed.
1826: *
1827: * @return integer
1828: */
1829: public static function getQueryCount()
1830: {
1831: return self::$adapter->getDatabase()->getQueryCount();
1832: }
1833:
1834: /**
1835: * Returns the current logger instance being used by the
1836: * database object.
1837: *
1838: * @return Logger
1839: */
1840: public static function getLogger()
1841: {
1842: return self::$adapter->getDatabase()->getLogger();
1843: }
1844:
1845: /**
1846: * Alias for setAutoResolve() method on OODBBean.
1847: * Enables or disables auto-resolving fetch types.
1848: * Auto-resolving aliased parent beans is convenient but can
1849: * be slower and can create infinite recursion if you
1850: * used aliases to break cyclic relations in your domain.
1851: *
1852: * @param boolean $automatic TRUE to enable automatic resolving aliased parents
1853: *
1854: * @return void
1855: */
1856: public static function setAutoResolve( $automatic = TRUE )
1857: {
1858: OODBBean::setAutoResolve( (boolean) $automatic );
1859: }
1860:
1861: /**
1862: * Dynamically extends the facade with a plugin.
1863: * Using this method you can register your plugin with the facade and then
1864: * use the plugin by invoking the name specified plugin name as a method on
1865: * the facade.
1866: *
1867: * Usage:
1868: *
1869: * <code>
1870: * R::ext( 'makeTea', function() { ... } );
1871: * </code>
1872: *
1873: * Now you can use your makeTea plugin like this:
1874: *
1875: * <code>
1876: * R::makeTea();
1877: * </code>
1878: *
1879: * @param string $pluginName name of the method to call the plugin
1880: * @param callable $callable a PHP callable
1881: *
1882: * @return void
1883: */
1884: public static function ext( $pluginName, $callable )
1885: {
1886: if ( !ctype_alnum( $pluginName ) ) {
1887: throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
1888: }
1889: self::$plugins[$pluginName] = $callable;
1890: }
1891:
1892: /**
1893: * Call static for use with dynamic plugins. This magic method will
1894: * intercept static calls and route them to the specified plugin.
1895: *
1896: * @param string $pluginName name of the plugin
1897: * @param array $params list of arguments to pass to plugin method
1898: *
1899: * @return mixed
1900: */
1901: public static function __callStatic( $pluginName, $params )
1902: {
1903: if ( !ctype_alnum( $pluginName) ) {
1904: throw new RedException( 'Plugin name may only contain alphanumeric characters.' );
1905: }
1906: if ( !isset( self::$plugins[$pluginName] ) ) {
1907: throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' );
1908: }
1909: return call_user_func_array( self::$plugins[$pluginName], $params );
1910: }
1911: }
1912:
1913: