/** * MySQL specific tests. * * @return void */ public function testMySQL() { if ($this->currentlyActiveDriverID !== 'mysql') { return; } testpack('Throw exception in case of issue with assoc constraint'); $bunny = R::dispense('bunny'); $carrot = R::dispense('carrot'); $faultyWriter = new \FaultyWriter(R::getToolBox()->getDatabaseAdapter()); $faultyOODB = new OODB($faultyWriter); $faultyOODB->setBeanHelper(R::getRedBean()->getBeanHelper()); $faultyToolbox = new ToolBox($faultyOODB, R::getToolBox()->getDatabaseAdapter(), $faultyWriter); $faultyAssociationManager = new AssociationManager($faultyToolbox); $faultyWriter->setSQLState('23000'); $faultyAssociationManager->associate($bunny, $carrot); pass(); $faultyWriter->setSQLState('42S22'); R::nuke(); try { $faultyAssociationManager->associate($bunny, $carrot); fail(); } catch (SQL $exception) { pass(); } }
/** * Loads multiple types of beans with the same ID. * This might look like a strange method, however it can be useful * for loading a one-to-one relation. * * @param OODB $oodb OODB object * @param string|array $types the set of types to load at once * @param mixed $id the common ID * * @return OODBBean */ public static function load(OODB $oodb, $types, $id) { if (is_string($types)) { $types = explode(',', $types); } if (!is_array($types)) { return array(); } foreach ($types as $k => $typeItem) { $types[$k] = $oodb->load($typeItem, $id); } return $types; }
/** * @see Finder::find * Convience method. Tries to find beans of a certain type, * if no beans are found, it dispenses a bean of that type. * * @param string $type the type of bean you are looking for * @param string $sql SQL query to find the desired bean, starting right after WHERE clause * @param array $bindings values array of values to be bound to parameters in query * * @return array */ public function findOrDispense($type, $sql = NULL, $bindings = array()) { $foundBeans = $this->find($type, $sql, $bindings); if (empty($foundBeans)) { return array($this->redbean->dispense($type)); } else { return $foundBeans; } }
/** * Returns all the beans associated with $bean. * This method will return an array containing all the beans that have * been associated once with the associate() function and are still * associated with the bean specified. The type parameter indicates the * type of beans you are looking for. You can also pass some extra SQL and * values for that SQL to filter your results after fetching the * related beans. * * Don't try to make use of subqueries, a subquery using IN() seems to * be slower than two queries! * * Since 3.2, you can now also pass an array of beans instead just one * bean as the first parameter. * * @param OODBBean|array $bean the bean you have * @param string $type the type of beans you want * @param string $sql SQL snippet for extra filtering * @param array $bindings values to be inserted in SQL slots * * @return array */ public function related($bean, $type, $sql = '', $bindings = array()) { $sql = $this->writer->glueSQLCondition($sql); $rows = $this->relatedRows($bean, $type, $sql, $bindings); $links = array(); foreach ($rows as $key => $row) { if (!isset($links[$row['id']])) { $links[$row['id']] = array(); } $links[$row['id']][] = $row['linked_by']; unset($rows[$key]['linked_by']); } $beans = $this->oodb->convertToBeans($type, $rows); foreach ($beans as $bean) { $bean->setMeta('sys.belongs-to', $links[$bean->id]); } return $beans; }
/** * 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. * * Example: * * R::bindFunc( 'read', 'location.point', 'asText' ); * R::bindFunc( 'write', 'location.point', 'GeomFromText' ); * * Passing NULL as the function will reset (clear) the function * for this column/mode. * * @param string $mode mode for function: i.e. read or write * @param string $field field (table.column) to bind function to * @param string $function SQL function to bind to specified column * * @return void */ public static function bindFunc($mode, $field, $function) { self::$redbean->bindFunc($mode, $field, $function); }
/** * Test trashAll(). */ public function testMultiDeleteUpdate() { testpack('test multi delete and multi update'); $beans = R::dispenseLabels('bean', array('a', 'b')); $ids = R::storeAll($beans); asrt((int) R::count('bean'), 2); R::trashAll(R::batch('bean', $ids)); asrt((int) R::count('bean'), 0); testpack('test assocManager check'); $rb = new OODB(R::getWriter()); try { $rb->getAssociationManager(); fail(); } catch (RedException $e) { pass(); } }
/** * Test the database driver and low level functions. * * @return void */ public function testDriver() { $currentDriver = $this->currentlyActiveDriverID; R::store(R::dispense('justabean')); $adapter = new TroubleDapter(R::getToolBox()->getDatabaseAdapter()->getDatabase()); $adapter->setSQLState('HY000'); $writer = new SQLiteT($adapter); $redbean = new OODB($writer); $toolbox = new ToolBox($redbean, $adapter, $writer); // We can only test this for a known driver... if ($currentDriver === 'sqlite') { try { $redbean->find('bean'); pass(); } catch (\Exception $e) { var_dump($e->getSQLState()); fail(); } } $adapter->setSQLState(-999); try { $redbean->find('bean'); fail(); } catch (\Exception $e) { pass(); } try { $redbean->wipe('justabean'); fail(); } catch (\Exception $e) { pass(); } $toolbox = R::getToolBox(); $adapter = $toolbox->getDatabaseAdapter(); $writer = $toolbox->getWriter(); $redbean = $toolbox->getRedBean(); $pdo = $adapter->getDatabase(); $page = $redbean->dispense("page"); try { $adapter->exec("an invalid query"); fail(); } catch (SQL $e) { pass(); } // Special data type description should result in magic number 99 (specified) if ($currentDriver == 'mysql') { asrt($writer->code(MySQL::C_DATATYPE_SPECIAL_DATE), 99); } if ($currentDriver == 'pgsql') { asrt($writer->code(PostgreSQL::C_DATATYPE_SPECIAL_DATE), 99); } if ($currentDriver == 'CUBRID') { asrt($writer->code(CUBRID::C_DATATYPE_SPECIAL_DATE), 99); } asrt((int) $adapter->getCell("SELECT 123"), 123); $page->aname = "my page"; $id = (int) $redbean->store($page); asrt((int) $page->id, 1); asrt((int) $pdo->GetCell("SELECT count(*) FROM page"), 1); asrt($pdo->GetCell("SELECT aname FROM page LIMIT 1"), "my page"); asrt((int) $id, 1); $page = $redbean->load("page", 1); asrt($page->aname, "my page"); asrt((bool) $page->getMeta("type"), TRUE); asrt(isset($page->id), TRUE); asrt($page->getMeta("type"), "page"); asrt((int) $page->id, $id); }
/** * Constructor, * creates a new instance of DupManager. * * @param ToolBox $toolbox */ public function __construct(ToolBox $toolbox) { $this->toolbox = $toolbox; $this->redbean = $toolbox->getRedBean(); $this->associationManager = $this->redbean->getAssociationManager(); }
/** * Returns a hashmap with bean arrays keyed by type using an SQL * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review' * this method will return movie and review beans. * * Example: * * $stuff = $finder->findMulti('movie,review', ' * SELECT movie.*, review.* FROM movie * LEFT JOIN review ON review.movie_id = movie.id'); * * After this operation, $stuff will contain an entry 'movie' containing all * movies and an entry named 'review' containing all reviews (all beans). * You can also pass bindings. * * If you want to re-map your beans, so you can use $movie->ownReviewList without * having RedBeanPHP executing an SQL query you can use the fourth parameter to * define a selection of remapping closures. * * The remapping argument (optional) should contain an array of arrays. * Each array in the remapping array should contain the following entries: * * array( * 'a' => TYPE A * 'b' => TYPE B * 'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS * 'do' => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS * ) * * Using this mechanism you can build your own 'preloader' with tiny function * snippets (and those can be re-used and shared online of course). * * Example: * * array( * 'a' => 'movie' //define A as movie * 'b' => 'review' //define B as review * 'matcher' => function( $a, $b ) { * return ( $b->movie_id == $a->id ); //Perform action if review.movie_id equals movie.id * } * 'do' => function( $a, $b ) { * $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie * $a->clearHistory(); //optional, act 'as if these beans have been loaded through ownReviewList'. * } * ) * * The Query Template parameter is optional as well but can be used to * set a different SQL template (sprintf-style) for processing the original query. * * @note the SQL query provided IS NOT THE ONE used internally by this function, * this function will pre-process the query to get all the data required to find the beans. * * @note if you use the 'book.*' notation make SURE you're * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because * it's actually an SQL-like template SLOT, not real SQL. * * @note instead of an SQL query you can pass a result array as well. * * @param string|array $types a list of types (either array or comma separated string) * @param string|array $sqlOrArr an SQL query or an array of prefetched records * @param array $bindings optional, bindings for SQL query * @param array $remappings optional, an array of remapping arrays * @param string $queryTemplate optional, query template * * @return array */ public function findMulti($types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s') { if (!is_array($types)) { $types = explode(',', $types); } if (!is_array($sql)) { $writer = $this->toolbox->getWriter(); $adapter = $this->toolbox->getDatabaseAdapter(); //Repair the query, replace book.* with book.id AS book_id etc.. foreach ($types as $type) { $pattern = " {$type}.*"; if (strpos($sql, $pattern) !== FALSE) { $newSelectorArray = array(); $columns = $writer->getColumns($type); foreach ($columns as $column => $definition) { $newSelectorArray[] = sprintf($queryTemplate, $type, $column, $type, $column); } $newSelector = implode(',', $newSelectorArray); $sql = str_replace($pattern, $newSelector, $sql); } } $rows = $adapter->get($sql, $bindings); } else { $rows = $sql; } //Gather the bean data from the query results using the prefix $wannaBeans = array(); foreach ($types as $type) { $wannaBeans[$type] = array(); $prefix = "{$type}__"; foreach ($rows as $rowkey => $row) { $wannaBean = array(); foreach ($row as $cell => $value) { if (strpos($cell, $prefix) === 0) { $property = substr($cell, strlen($prefix)); unset($rows[$rowkey][$cell]); $wannaBean[$property] = $value; } } if (!isset($wannaBean['id'])) { continue; } if (is_null($wannaBean['id'])) { continue; } $wannaBeans[$type][$wannaBean['id']] = $wannaBean; } } //Turn the rows into beans $beans = array(); foreach ($wannaBeans as $type => $wannabees) { $beans[$type] = $this->redbean->convertToBeans($type, $wannabees); } //Apply additional re-mappings foreach ($remappings as $remapping) { $a = $remapping['a']; $b = $remapping['b']; $matcher = $remapping['matcher']; $do = $remapping['do']; foreach ($beans[$a] as $bean) { foreach ($beans[$b] as $putBean) { if ($matcher($bean, $putBean, $beans)) { $do($bean, $putBean, $beans, $remapping); } } } } return $beans; }
/** * Nukes the entire database. * This will remove all schema structures from the database. * Only works in fluid mode. Be careful with this method. * * @warning dangerous method, will remove all tables, columns etc. * * @return void */ public static function nuke() { if (!self::$redbean->isFrozen()) { self::$writer->wipeAll(); } }