--- /dev/null
+<?php
+
+namespace RedBeanPHP;
+
+use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
+use RedBeanPHP\QueryWriter as QueryWriter;
+use RedBeanPHP\BeanHelper as BeanHelper;
+use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
+use RedBeanPHP\Repository as Repository;
+use RedBeanPHP\Repository\Fluid as FluidRepo;
+use RedBeanPHP\Repository\Frozen as FrozenRepo;
+
+/**
+ * RedBean Object Oriented DataBase.
+ *
+ * The RedBean OODB Class is the main class of RedBeanPHP.
+ * It takes OODBBean objects and stores them to and loads them from the
+ * database as well as providing other CRUD functions. This class acts as a
+ * object database.
+ *
+ * @file RedBeanPHP/OODB.php
+ * @author Gabor de Mooij and the RedBeanPHP community
+ * @license BSD/GPLv2
+ *
+ * @copyright
+ * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
+ * This source file is subject to the BSD/GPLv2 License that is bundled
+ * with this source code in the file license.txt.
+ */
+class OODB extends Observable
+{
+ /**
+ * @var array
+ */
+ private static $sqlFilters = array();
+
+ /**
+ * @var array
+ */
+ protected $chillList = array();
+
+ /**
+ * @var array
+ */
+ protected $stash = NULL;
+
+ /*
+ * @var integer
+ */
+ protected $nesting = 0;
+
+ /**
+ * @var DBAdapter
+ */
+ protected $writer;
+
+ /**
+ * @var boolean
+ */
+ protected $isFrozen = FALSE;
+
+ /**
+ * @var FacadeBeanHelper
+ */
+ protected $beanhelper = NULL;
+
+ /**
+ * @var AssociationManager
+ */
+ protected $assocManager = NULL;
+
+ /**
+ * @var Repository
+ */
+ protected $repository = NULL;
+
+ /**
+ * @var FrozenRepo
+ */
+ protected $frozenRepository = NULL;
+
+ /**
+ * @var FluidRepo
+ */
+ protected $fluidRepository = NULL;
+
+ /**
+ * @var boolean
+ */
+ protected static $autoClearHistoryAfterStore = FALSE;
+
+ /**
+ * If set to TRUE, this method will call clearHistory every time
+ * the bean gets stored.
+ *
+ * @param boolean $autoClear auto clear option
+ *
+ * @return void
+ */
+ public static function autoClearHistoryAfterStore( $autoClear = TRUE )
+ {
+ self::$autoClearHistoryAfterStore = (boolean) $autoClear;
+ }
+
+ /**
+ * Unboxes a bean from a FUSE model if needed and checks whether the bean is
+ * an instance of OODBBean.
+ *
+ * @param OODBBean $bean bean you wish to unbox
+ *
+ * @return OODBBean
+ */
+ protected function unboxIfNeeded( $bean )
+ {
+ if ( $bean instanceof SimpleModel ) {
+ $bean = $bean->unbox();
+ }
+ if ( !( $bean instanceof OODBBean ) ) {
+ throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) );
+ }
+
+ return $bean;
+ }
+
+ /**
+ * Constructor, requires a query writer.
+ *
+ * @param QueryWriter $writer writer
+ * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled)
+ */
+ public function __construct( QueryWriter $writer, $frozen = FALSE )
+ {
+ if ( $writer instanceof QueryWriter ) {
+ $this->writer = $writer;
+ }
+
+ $this->freeze( $frozen );
+ }
+
+ /**
+ * Toggles fluid or frozen mode. In fluid mode the database
+ * structure is adjusted to accomodate your objects. In frozen mode
+ * this is not the case.
+ *
+ * You can also pass an array containing a selection of frozen types.
+ * Let's call this chilly mode, it's just like fluid mode except that
+ * certain types (i.e. tables) aren't touched.
+ *
+ * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode
+ *
+ * @return void
+ */
+ public function freeze( $toggle )
+ {
+ if ( is_array( $toggle ) ) {
+ $this->chillList = $toggle;
+ $this->isFrozen = FALSE;
+ } else {
+ $this->isFrozen = (boolean) $toggle;
+ }
+
+ if ( $this->isFrozen ) {
+ if ( !$this->frozenRepository ) {
+ $this->frozenRepository = new FrozenRepo( $this, $this->writer );
+ }
+
+ $this->repository = $this->frozenRepository;
+
+ } else {
+ if ( !$this->fluidRepository ) {
+ $this->fluidRepository = new FluidRepo( $this, $this->writer );
+ }
+
+ $this->repository = $this->fluidRepository;
+ }
+
+ if ( count( self::$sqlFilters ) ) {
+ AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
+ }
+
+ }
+
+ /**
+ * Returns the current mode of operation of RedBean.
+ * In fluid mode the database
+ * structure is adjusted to accomodate your objects.
+ * In frozen mode
+ * this is not the case.
+ *
+ * @return boolean
+ */
+ public function isFrozen()
+ {
+ return (bool) $this->isFrozen;
+ }
+
+ /**
+ * Determines whether a type is in the chill list.
+ * If a type is 'chilled' it's frozen, so its schema cannot be
+ * changed anymore. However other bean types may still be modified.
+ * This method is a convenience method for other objects to check if
+ * the schema of a certain type is locked for modification.
+ *
+ * @param string $type the type you wish to check
+ *
+ * @return boolean
+ */
+ public function isChilled( $type )
+ {
+ return (boolean) ( in_array( $type, $this->chillList ) );
+ }
+
+ /**
+ * Dispenses a new bean (a OODBBean Bean Object)
+ * of the specified type. Always
+ * use this function to get an empty bean object. Never
+ * instantiate a OODBBean yourself because it needs
+ * to be configured before you can use it with RedBean. This
+ * function applies the appropriate initialization /
+ * configuration for you.
+ *
+ * @param string $type type of bean you want to dispense
+ * @param string $number number of beans you would like to get
+ * @param boolean $alwaysReturnArray if TRUE always returns the result as an array
+ *
+ * @return OODBBean
+ */
+ public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
+ {
+ if ( $number < 1 ) {
+ if ( $alwaysReturnArray ) return array();
+ return NULL;
+ }
+
+ return $this->repository->dispense( $type, $number, $alwaysReturnArray );
+ }
+
+ /**
+ * Sets bean helper to be given to beans.
+ * Bean helpers assist beans in getting a reference to a toolbox.
+ *
+ * @param BeanHelper $beanhelper helper
+ *
+ * @return void
+ */
+ public function setBeanHelper( BeanHelper $beanhelper )
+ {
+ $this->beanhelper = $beanhelper;
+ }
+
+ /**
+ * Returns the current bean helper.
+ * Bean helpers assist beans in getting a reference to a toolbox.
+ *
+ * @return BeanHelper
+ */
+ public function getBeanHelper()
+ {
+ return $this->beanhelper;
+ }
+
+ /**
+ * Checks whether a OODBBean bean is valid.
+ * If the type is not valid or the ID is not valid it will
+ * throw an exception: Security.
+ *
+ * @param OODBBean $bean the bean that needs to be checked
+ *
+ * @return void
+ */
+ public function check( OODBBean $bean )
+ {
+ $this->repository->check( $bean );
+ }
+
+ /**
+ * Searches the database for a bean that matches conditions $conditions and sql $addSQL
+ * and returns an array containing all the beans that have been found.
+ *
+ * Conditions need to take form:
+ *
+ * <code>
+ * array(
+ * 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' )
+ * 'PROPERTY' => array( POSSIBLE VALUES... )
+ * );
+ * </code>
+ *
+ * All conditions are glued together using the AND-operator, while all value lists
+ * are glued using IN-operators thus acting as OR-conditions.
+ *
+ * Note that you can use property names; the columns will be extracted using the
+ * appropriate bean formatter.
+ *
+ * @param string $type type of beans you are looking for
+ * @param array $conditions list of conditions
+ * @param string $addSQL SQL to be used in query
+ * @param array $bindings a list of values to bind to query parameters
+ *
+ * @return array
+ */
+ public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
+ {
+ return $this->repository->find( $type, $conditions, $sql, $bindings );
+ }
+
+ /**
+ * Same as find() but returns a BeanCollection.
+ *
+ * @param string $type type of beans you are looking for
+ * @param string $addSQL SQL to be used in query
+ * @param array $bindings a list of values to bind to query parameters
+ *
+ * @return array
+ */
+ public function findCollection( $type, $sql = NULL, $bindings = array() )
+ {
+ return $this->repository->findCollection( $type, $sql, $bindings );
+ }
+
+ /**
+ * Checks whether the specified table already exists in the database.
+ * Not part of the Object Database interface!
+ *
+ * @deprecated Use AQueryWriter::typeExists() instead.
+ *
+ * @param string $table table name
+ *
+ * @return boolean
+ */
+ public function tableExists( $table )
+ {
+ return $this->repository->tableExists( $table );
+ }
+
+ /**
+ * Stores a bean in the database. This method takes a
+ * OODBBean Bean Object $bean and stores it
+ * in the database. If the database schema is not compatible
+ * with this bean and RedBean runs in fluid mode the schema
+ * will be altered to store the bean correctly.
+ * If the database schema is not compatible with this bean and
+ * RedBean runs in frozen mode it will throw an exception.
+ * This function returns the primary key ID of the inserted
+ * bean.
+ *
+ * The return value is an integer if possible. If it is not possible to
+ * represent the value as an integer a string will be returned. We use
+ * explicit casts instead of functions to preserve performance
+ * (0.13 vs 0.28 for 10000 iterations on Core i3).
+ *
+ * @param OODBBean|SimpleModel $bean bean to store
+ *
+ * @return integer|string
+ */
+ public function store( $bean )
+ {
+ $bean = $this->unboxIfNeeded( $bean );
+ $id = $this->repository->store( $bean );
+ if ( self::$autoClearHistoryAfterStore ) {
+ $bean->clearHistory();
+ }
+ return $id;
+ }
+
+ /**
+ * Loads a bean from the object database.
+ * It searches for a OODBBean Bean Object in the
+ * database. It does not matter how this bean has been stored.
+ * RedBean uses the primary key ID $id and the string $type
+ * to find the bean. The $type specifies what kind of bean you
+ * are looking for; this is the same type as used with the
+ * dispense() function. If RedBean finds the bean it will return
+ * the OODB Bean object; if it cannot find the bean
+ * RedBean will return a new bean of type $type and with
+ * primary key ID 0. In the latter case it acts basically the
+ * same as dispense().
+ *
+ * Important note:
+ * If the bean cannot be found in the database a new bean of
+ * the specified type will be generated and returned.
+ *
+ * @param string $type type of bean you want to load
+ * @param integer $id ID of the bean you want to load
+ *
+ * @return OODBBean
+ */
+ public function load( $type, $id )
+ {
+ return $this->repository->load( $type, $id );
+ }
+
+ /**
+ * Removes a bean from the database.
+ * This function will remove the specified OODBBean
+ * Bean Object from the database.
+ *
+ * @param OODBBean|SimpleModel $bean bean you want to remove from database
+ *
+ * @return void
+ */
+ public function trash( $bean )
+ {
+ $bean = $this->unboxIfNeeded( $bean );
+ return $this->repository->trash( $bean );
+ }
+
+ /**
+ * Returns an array of beans. Pass a type and a series of ids and
+ * this method will bring you the corresponding beans.
+ *
+ * important note: Because this method loads beans using the load()
+ * function (but faster) it will return empty beans with ID 0 for
+ * every bean that could not be located. The resulting beans will have the
+ * passed IDs as their keys.
+ *
+ * @param string $type type of beans
+ * @param array $ids ids to load
+ *
+ * @return array
+ */
+ public function batch( $type, $ids )
+ {
+ return $this->repository->batch( $type, $ids );
+ }
+
+ /**
+ * This is a convenience method; it converts database rows
+ * (arrays) into beans. Given a type and a set of rows this method
+ * will return an array of beans of the specified type loaded with
+ * the data fields provided by the result set from the database.
+ *
+ * @param string $type type of beans you would like to have
+ * @param array $rows rows from the database result
+ * @param string $mask mask to apply for meta data
+ *
+ * @return array
+ */
+ public function convertToBeans( $type, $rows, $mask = NULL )
+ {
+ return $this->repository->convertToBeans( $type, $rows, $mask );
+ }
+
+ /**
+ * Counts the number of beans of type $type.
+ * This method accepts a second argument to modify the count-query.
+ * A third argument can be used to provide bindings for the SQL snippet.
+ *
+ * @param string $type type of bean we are looking for
+ * @param string $addSQL additional SQL snippet
+ * @param array $bindings parameters to bind to SQL
+ *
+ * @return integer
+ */
+ public function count( $type, $addSQL = '', $bindings = array() )
+ {
+ return $this->repository->count( $type, $addSQL, $bindings );
+ }
+
+ /**
+ * Trash all beans of a given type. Wipes an entire type of bean.
+ *
+ * @param string $type type of bean you wish to delete all instances of
+ *
+ * @return boolean
+ */
+ public function wipe( $type )
+ {
+ return $this->repository->wipe( $type );
+ }
+
+ /**
+ * Returns an Association Manager for use with OODB.
+ * A simple getter function to obtain a reference to the association manager used for
+ * storage and more.
+ *
+ * @return AssociationManager
+ */
+ public function getAssociationManager()
+ {
+ if ( !isset( $this->assocManager ) ) {
+ throw new RedException( 'No association manager available.' );
+ }
+
+ return $this->assocManager;
+ }
+
+ /**
+ * Sets the association manager instance to be used by this OODB.
+ * A simple setter function to set the association manager to be used for storage and
+ * more.
+ *
+ * @param AssociationManager $assoc sets the association manager to be used
+ *
+ * @return void
+ */
+ public function setAssociationManager( AssociationManager $assocManager )
+ {
+ $this->assocManager = $assocManager;
+ }
+
+ /**
+ * Returns the currently used repository instance.
+ * For testing purposes only.
+ *
+ * @return Repository
+ */
+ public function getCurrentRepository()
+ {
+ return $this->repository;
+ }
+
+ /**
+ * Binds an SQL function to a column.
+ * This method can be used to setup a decode/encode scheme or
+ * perform UUID insertion. This method is especially useful for handling
+ * MySQL spatial columns, because they need to be processed first using
+ * the asText/GeomFromText functions.
+ *
+ * @param string $mode mode to set function for, i.e. read or write
+ * @param string $field field (table.column) to bind SQL function to
+ * @param string $function SQL function to bind to field
+ *
+ * @return void
+ */
+ public function bindFunc( $mode, $field, $function )
+ {
+ list( $type, $property ) = explode( '.', $field );
+ $mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
+
+ if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array();
+ if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array();
+
+ if ( is_null( $function ) ) {
+ unset( self::$sqlFilters[$mode][$type][$property] );
+ } else {
+ if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
+ self::$sqlFilters[$mode][$type][$property] = $function.'(?)';
+ } else {
+ self::$sqlFilters[$mode][$type][$property] = $function."($field)";
+ }
+ }
+
+ AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
+ }
+}