Begin Refactor

This commit is contained in:
color.diff=auto
2021-03-21 13:47:29 -06:00
parent 7622dc0d02
commit 018931d7ae
197 changed files with 47799 additions and 6 deletions

View File

@@ -0,0 +1,89 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\RedException as RedException;
/**
* Array Tool Helper
*
* This code was originally part of the facade, however it has
* been decided to remove unique features to service classes like
* this to make them available to developers not using the facade class.
*
* This is a helper or service class containing frequently used
* array functions for dealing with SQL queries.
*
* @file RedBeanPHP/Util/ArrayTool.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 ArrayTool
{
/**
* Generates question mark slots for an array of values.
* Given an array and an optional template string this method
* will produce string containing parameter slots for use in
* an SQL query string.
*
* Usage:
*
* <code>
* R::genSlots( array( 'a', 'b' ) );
* </code>
*
* The statement in the example will produce the string:
* '?,?'.
*
* Another example, using a template string:
*
* <code>
* R::genSlots( array('a', 'b'), ' IN( %s ) ' );
* </code>
*
* The statement in the example will produce the string:
* ' IN( ?,? ) '.
*
* @param array $array array to generate question mark slots for
* @param string $template template to use
*
* @return string
*/
public static function genSlots( $array, $template = NULL )
{
$str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : '';
return ( is_null( $template ) || $str === '' ) ? $str : sprintf( $template, $str );
}
/**
* Flattens a multi dimensional bindings array for use with genSlots().
*
* Usage:
*
* <code>
* R::flat( array( 'a', array( 'b' ), 'c' ) );
* </code>
*
* produces an array like: [ 'a', 'b', 'c' ]
*
* @param array $array array to flatten
* @param array $result result array parameter (for recursion)
*
* @return array
*/
public static function flat( $array, $result = array() )
{
foreach( $array as $value ) {
if ( is_array( $value ) ) $result = self::flat( $value, $result );
else $result[] = $value;
}
return $result;
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\Finder;
/**
* Diff Utility
*
* The Look Utility class provides an easy way to generate
* tables and selects (pulldowns) from the database.
*
* @file RedBeanPHP/Util/Diff.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 Diff
{
/**
* @var Toolbox
*/
protected $toolbox;
/**
* Constructor.
* The MatchUp class requires a toolbox
*
* @param ToolBox $toolbox
*/
public function __construct( ToolBox $toolbox )
{
$this->toolbox = $toolbox;
}
/**
* Calculates a diff between two beans (or arrays of beans).
* The result of this method is an array describing the differences of the second bean compared to
* the first, where the first bean is taken as reference. The array is keyed by type/property, id and property name, where
* type/property is either the type (in case of the root bean) or the property of the parent bean where the type resides.
* The diffs are mainly intended for logging, you cannot apply these diffs as patches to other beans.
* However this functionality might be added in the future.
*
* The keys of the array can be formatted using the $format parameter.
* A key will be composed of a path (1st), id (2nd) and property (3rd).
* Using printf-style notation you can determine the exact format of the key.
* The default format will look like:
*
* 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
*
* If you only want a simple diff of one bean and you don't care about ids,
* you might pass a format like: '%1$s.%3$s' which gives:
*
* 'book.1.title' => array( <OLDVALUE>, <NEWVALUE> )
*
* The filter parameter can be used to set filters, it should be an array
* of property names that have to be skipped. By default this array is filled with
* two strings: 'created' and 'modified'.
*
* @param OODBBean|array $beans reference beans
* @param OODBBean|array $others beans to compare
* @param array $filters names of properties of all beans to skip
* @param string $format the format of the key, defaults to '%s.%s.%s'
* @param string $type type/property of bean to use for key generation
*
* @return array
*/
public function diff( $beans, $others, $filters = array( 'created', 'modified' ), $format = '%s.%s.%s', $type = NULL )
{
$diff = array();
if ( !is_array( $beans ) ) $beans = array( $beans );
$beansI = array();
foreach ( $beans as $bean ) {
if ( !( $bean instanceof OODBBean ) ) continue;
$beansI[$bean->id] = $bean;
}
if ( !is_array( $others ) ) $others = array( $others );
$othersI = array();
foreach ( $others as $other ) {
if ( !( $other instanceof OODBBean ) ) continue;
$othersI[$other->id] = $other;
}
if ( count( $beansI ) == 0 || count( $othersI ) == 0 ) {
return array();
}
$type = $type != NULL ? $type : reset($beansI)->getMeta( 'type' );
foreach( $beansI as $id => $bean ) {
if ( !isset( $othersI[$id] ) ) continue;
$other = $othersI[$id];
foreach( $bean as $property => $value ) {
if ( in_array( $property, $filters ) ) continue;
$key = vsprintf( $format, array( $type, $bean->id, $property ) );
$compare = $other->{$property};
if ( !is_object( $value ) && !is_array( $value ) && $value != $compare ) {
$diff[$key] = array( $value, $compare );
} else {
$diff = array_merge( $diff, $this->diff( $value, $compare, $filters, $format, $key ) );
}
}
}
return $diff;
}
}

View File

@@ -0,0 +1,209 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\RedException as RedException;
/**
* Dispense Helper
*
* A helper class containing a dispense utility.
*
* @file RedBeanPHP/Util/DispenseHelper.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 DispenseHelper
{
/**
* @var boolean
*/
private static $enforceNamingPolicy = TRUE;
/**
* Sets the enforce naming policy flag. If set to
* TRUE the RedBeanPHP naming policy will be enforced.
* Otherwise it will not. Use at your own risk.
* Setting this to FALSE is not recommended.
*
* @param boolean $yesNo whether to enforce RB name policy
*
* @return void
*/
public static function setEnforceNamingPolicy( $yesNo )
{
self::$enforceNamingPolicy = (boolean) $yesNo;
}
/**
* Checks whether the bean type conforms to the RedbeanPHP
* naming policy. This method will throw an exception if the
* type does not conform to the RedBeanPHP database column naming
* policy.
*
* The RedBeanPHP naming policy for beans states that valid
* bean type names contain only:
*
* - lowercase alphanumeric characters a-z
* - numbers 0-9
* - at least one character
*
* Although there are no restrictions on length, database
* specific implementations may apply further restrictions
* regarding the length of a table which means these restrictions
* also apply to bean types.
*
* The RedBeanPHP naming policy ensures that, without any
* configuration, the core functionalities work across many
* databases and operating systems, including those that are
* case insensitive or restricted to the ASCII character set.
*
* Although these restrictions can be bypassed, this is not
* recommended.
*
* @param string $type type of bean
*
* @return void
*/
public static function checkType( $type )
{
if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) {
throw new RedException( 'Invalid type: ' . $type );
}
}
/**
* Dispenses a new RedBean OODB Bean for use with
* the rest of the methods. RedBeanPHP thinks in beans, the bean is the
* primary way to interact with RedBeanPHP and the database managed by
* RedBeanPHP. To load, store and delete data from the database using RedBeanPHP
* you exchange these RedBeanPHP OODB Beans. The only exception to this rule
* are the raw query methods like R::getCell() or R::exec() and so on.
* The dispense method is the 'preferred way' to create a new bean.
*
* Usage:
*
* <code>
* $book = R::dispense( 'book' );
* $book->title = 'My Book';
* R::store( $book );
* </code>
*
* This method can also be used to create an entire bean graph at once.
* Given an array with keys specifying the property names of the beans
* and a special _type key to indicate the type of bean, one can
* make the Dispense Helper generate an entire hierarchy of beans, including
* lists. To make dispense() generate a list, simply add a key like:
* ownXList or sharedXList where X is the type of beans it contains and
* a set its value to an array filled with arrays representing the beans.
* Note that, although the type may have been hinted at in the list name,
* you still have to specify a _type key for every bean array in the list.
* Note that, if you specify an array to generate a bean graph, the number
* parameter will be ignored.
*
* Usage:
*
* <code>
* $book = R::dispense( [
* '_type' => 'book',
* 'title' => 'Gifted Programmers',
* 'author' => [ '_type' => 'author', 'name' => 'Xavier' ],
* 'ownPageList' => [ ['_type'=>'page', 'text' => '...'] ]
* ] );
* </code>
*
* @param string|array $typeOrBeanArray type or bean array to import
* @param integer $num number of beans to dispense
* @param boolean $alwaysReturnArray if TRUE always returns the result as an array
*
* @return array|OODBBean
*/
public static function dispense( OODB $oodb, $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) {
if ( is_array($typeOrBeanArray) ) {
if ( !isset( $typeOrBeanArray['_type'] ) ) {
$list = array();
foreach( $typeOrBeanArray as $beanArray ) {
if (
!( is_array( $beanArray )
&& isset( $beanArray['_type'] ) ) ) {
throw new RedException( 'Invalid Array Bean' );
}
}
foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $oodb, $beanArray );
return $list;
}
$import = $typeOrBeanArray;
$type = $import['_type'];
unset( $import['_type'] );
} else {
$type = $typeOrBeanArray;
}
if (self::$enforceNamingPolicy) self::checkType( $type );
$beanOrBeans = $oodb->dispense( $type, $num, $alwaysReturnArray );
if ( isset( $import ) ) {
$beanOrBeans->import( $import );
}
return $beanOrBeans;
}
/**
* Takes a comma separated list of bean types
* and dispenses these beans. For each type in the list
* you can specify the number of beans to be dispensed.
*
* Usage:
*
* <code>
* list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' );
* </code>
*
* This will dispense a book, a page and a text. This way you can
* quickly dispense beans of various types in just one line of code.
*
* Usage:
*
* <code>
* list($book, $pages) = R::dispenseAll('book,page*100');
* </code>
*
* This returns an array with a book bean and then another array
* containing 100 page beans.
*
* @param OODB $oodb OODB
* @param string $order a description of the desired dispense order using the syntax above
* @param boolean $onlyArrays return only arrays even if amount < 2
*
* @return array
*/
public static function dispenseAll( OODB $oodb, $order, $onlyArrays = FALSE )
{
$list = array();
foreach( explode( ',', $order ) as $order ) {
if ( strpos( $order, '*' ) !== FALSE ) {
list( $type, $amount ) = explode( '*', $order );
} else {
$type = $order;
$amount = 1;
}
$list[] = self::dispense( $oodb, $type, $amount, $onlyArrays );
}
return $list;
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Dump helper
*
* This code was originally part of the facade, however it has
* been decided to remove unique features to service classes like
* this to make them available to developers not using the facade class.
*
* Dumps the contents of a bean in an array for
* debugging purposes.
*
* @file RedBeanPHP/Util/Dump.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 Dump
{
/**
* Dumps bean data to array.
* Given a one or more beans this method will
* return an array containing first part of the string
* representation of each item in the array.
*
* Usage:
*
* <code>
* echo R::dump( $bean );
* </code>
*
* The example shows how to echo the result of a simple
* dump. This will print the string representation of the
* specified bean to the screen, limiting the output per bean
* to 35 characters to improve readability. Nested beans will
* also be dumped.
*
* @param OODBBean|array $data either a bean or an array of beans
*
* @return array
*/
public static function dump( $data )
{
$array = array();
if ( $data instanceof OODBBean ) {
$str = strval( $data );
if (strlen($str) > 35) {
$beanStr = substr( $str, 0, 35 ).'... ';
} else {
$beanStr = $str;
}
return $beanStr;
}
if ( is_array( $data ) ) {
foreach( $data as $key => $item ) {
$array[$key] = self::dump( $item );
}
}
return $array;
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean;
/**
* Feature Utility
*
* The Feature Utility class provides an easy way to turn
* on or off features. This allows us to introduce new features
* without accidentally breaking backward compatibility.
*
* @file RedBeanPHP/Util/Feature.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 Feature
{
/* Feature set constants */
const C_FEATURE_NOVICE_LATEST = 'novice/latest';
const C_FEATURE_LATEST = 'latest';
const C_FEATURE_NOVICE_5_5 = 'novice/5.5';
const C_FEATURE_5_5 = '5.5';
const C_FEATURE_NOVICE_5_4 = 'novice/5.4';
const C_FEATURE_5_4 = '5.4';
const C_FEATURE_NOVICE_5_3 = 'novice/5.3';
const C_FEATURE_5_3 = '5.3';
const C_FEATURE_ORIGINAL = 'original';
/**
* Selects the feature set you want as specified by
* the label.
*
* Available labels:
*
* novice/latest:
* - forbid R::nuke()
* - enable automatic relation resolver based on foreign keys
* - forbid R::store(All)( $bean, TRUE ) (Hybrid mode)
* - use IS-NULL conditions in findLike() etc
*
* latest:
* - allow R::nuke()
* - enable auto resolve
* - allow hybrid mode
* - use IS-NULL conditions in findLike() etc
*
* novice/X or X:
* - keep everything as it was in version X
*
* Usage:
*
* <code>
* R::useFeatureSet( 'novice/latest' );
* </code>
*
* @param string $label label
*
* @return void
*/
public static function feature( $label ) {
switch( $label ) {
case self::C_FEATURE_NOVICE_LATEST:
case self::C_FEATURE_NOVICE_5_4:
case self::C_FEATURE_NOVICE_5_5:
OODBBean::useFluidCount( FALSE );
R::noNuke( TRUE );
R::setAllowHybridMode( FALSE );
R::useISNULLConditions( TRUE );
break;
case self::C_FEATURE_LATEST:
case self::C_FEATURE_5_4:
case self::C_FEATURE_5_5:
OODBBean::useFluidCount( FALSE );
R::noNuke( FALSE );
R::setAllowHybridMode( TRUE );
R::useISNULLConditions( TRUE );
break;
case self::C_FEATURE_NOVICE_5_3:
OODBBean::useFluidCount( TRUE );
R::noNuke( TRUE );
R::setAllowHybridMode( FALSE );
R::useISNULLConditions( FALSE );
break;
case self::C_FEATURE_5_3:
OODBBean::useFluidCount( TRUE );
R::noNuke( FALSE );
R::setAllowHybridMode( FALSE );
R::useISNULLConditions( FALSE );
break;
case self::C_FEATURE_ORIGINAL:
OODBBean::useFluidCount( TRUE );
R::noNuke( FALSE );
R::setAllowHybridMode( FALSE );
R::useISNULLConditions( FALSE );
break;
default:
throw new \Exception("Unknown feature set label.");
break;
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\Finder;
/**
* Look Utility
*
* The Look Utility class provides an easy way to generate
* tables and selects (pulldowns) from the database.
*
* @file RedBeanPHP/Util/Look.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 Look
{
/**
* @var Toolbox
*/
protected $toolbox;
/**
* Constructor.
* The MatchUp class requires a toolbox
*
* @param ToolBox $toolbox
*/
public function __construct( ToolBox $toolbox )
{
$this->toolbox = $toolbox;
}
/**
* Takes an full SQL query with optional bindings, a series of keys, a template
* and optionally a filter function and glue and assembles a view from all this.
* This is the fastest way from SQL to view. Typically this function is used to
* generate pulldown (select tag) menus with options queried from the database.
*
* Usage:
*
* <code>
* $htmlPulldown = R::look(
* 'SELECT * FROM color WHERE value != ? ORDER BY value ASC',
* [ 'g' ],
* [ 'value', 'name' ],
* '<option value="%s">%s</option>',
* 'strtoupper',
* "\n"
* );
*</code>
*
* The example above creates an HTML fragment like this:
*
* <option value="B">BLUE</option>
* <option value="R">RED</option>
*
* to pick a color from a palette. The HTML fragment gets constructed by
* an SQL query that selects all colors that do not have value 'g' - this
* excludes green. Next, the bean properties 'value' and 'name' are mapped to the
* HTML template string, note that the order here is important. The mapping and
* the HTML template string follow vsprintf-rules. All property values are then
* passed through the specified filter function 'strtoupper' which in this case
* is a native PHP function to convert strings to uppercase characters only.
* Finally the resulting HTML fragment strings are glued together using a
* newline character specified in the last parameter for readability.
*
* In previous versions of RedBeanPHP you had to use:
* R::getLook()->look() instead of R::look(). However to improve useability of the
* library the look() function can now directly be invoked from the facade.
*
* @param string $sql query to execute
* @param array $bindings parameters to bind to slots mentioned in query or an empty array
* @param array $keys names in result collection to map to template
* @param string $template HTML template to fill with values associated with keys, use printf notation (i.e. %s)
* @param callable $filter function to pass values through (for translation for instance)
* @param string $glue optional glue to use when joining resulting strings
*
* @return string
*/
public function look( $sql, $bindings = array(), $keys = array( 'selected', 'id', 'name' ), $template = '<option %s value="%s">%s</option>', $filter = 'trim', $glue = '' )
{
$adapter = $this->toolbox->getDatabaseAdapter();
$lines = array();
$rows = $adapter->get( $sql, $bindings );
foreach( $rows as $row ) {
$values = array();
foreach( $keys as $key ) {
if (!empty($filter)) {
$values[] = call_user_func_array( $filter, array( $row[$key] ) );
} else {
$values[] = $row[$key];
}
}
$lines[] = vsprintf( $template, $values );
}
$string = implode( $glue, $lines );
return $string;
}
}

View File

@@ -0,0 +1,118 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\Finder;
/**
* MatchUp Utility
*
* Tired of creating login systems and password-forget systems?
* MatchUp is an ORM-translation of these kind of problems.
* A matchUp is a match-and-update combination in terms of beans.
* Typically login related problems are all about a match and
* a conditional update.
*
* @file RedBeanPHP/Util/MatchUp.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 MatchUp
{
/**
* @var Toolbox
*/
protected $toolbox;
/**
* Constructor.
* The MatchUp class requires a toolbox
*
* @param ToolBox $toolbox
*/
public function __construct( ToolBox $toolbox )
{
$this->toolbox = $toolbox;
}
/**
* MatchUp is a powerful productivity boosting method that can replace simple control
* scripts with a single RedBeanPHP command. Typically, matchUp() is used to
* replace login scripts, token generation scripts and password reset scripts.
* The MatchUp method takes a bean type, an SQL query snippet (starting at the WHERE clause),
* SQL bindings, a pair of task arrays and a bean reference.
*
* If the first 3 parameters match a bean, the first task list will be considered,
* otherwise the second one will be considered. On consideration, each task list,
* an array of keys and values will be executed. Every key in the task list should
* correspond to a bean property while every value can either be an expression to
* be evaluated or a closure (PHP 5.3+). After applying the task list to the bean
* it will be stored. If no bean has been found, a new bean will be dispensed.
*
* This method will return TRUE if the bean was found and FALSE if not AND
* there was a NOT-FOUND task list. If no bean was found AND there was also
* no second task list, NULL will be returned.
*
* To obtain the bean, pass a variable as the sixth parameter.
* The function will put the matching bean in the specified variable.
*
* Usage (this example resets a password in one go):
*
* <code>
* $newpass = '1234';
* $didResetPass = R::matchUp(
* 'account', ' token = ? AND tokentime > ? ',
* [ $token, time()-100 ],
* [ 'pass' => $newpass, 'token' => '' ],
* NULL,
* $account );
* </code>
*
* @param string $type type of bean you're looking for
* @param string $sql SQL snippet (starting at the WHERE clause, omit WHERE-keyword)
* @param array $bindings array of parameter bindings for SQL snippet
* @param array $onFoundDo task list to be considered on finding the bean
* @param array $onNotFoundDo task list to be considered on NOT finding the bean
* @param OODBBean &$bean reference to obtain the found bean
*
* @return mixed
*/
public function matchUp( $type, $sql, $bindings = array(), $onFoundDo = NULL, $onNotFoundDo = NULL, &$bean = NULL )
{
$finder = new Finder( $this->toolbox );
$oodb = $this->toolbox->getRedBean();
$bean = $finder->findOne( $type, $sql, $bindings );
if ( $bean && $onFoundDo ) {
foreach( $onFoundDo as $property => $value ) {
if ( function_exists('is_callable') && is_callable( $value ) ) {
$bean[$property] = call_user_func_array( $value, array( $bean ) );
} else {
$bean[$property] = $value;
}
}
$oodb->store( $bean );
return TRUE;
}
if ( $onNotFoundDo ) {
$bean = $oodb->dispense( $type );
foreach( $onNotFoundDo as $property => $value ) {
if ( function_exists('is_callable') && is_callable( $value ) ) {
$bean[$property] = call_user_func_array( $value, array( $bean ) );
} else {
$bean[$property] = $value;
}
}
$oodb->store( $bean );
return FALSE;
}
return NULL;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
/**
* Multi Bean Loader Helper
*
* This code was originally part of the facade, however it has
* been decided to remove unique features to service classes like
* this to make them available to developers not using the facade class.
*
* This helper class offers limited support for one-to-one
* relations by providing a service to load a set of beans
* with differnt types and a common ID.
*
* @file RedBeanPHP/Util/MultiLoader.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 MultiLoader
{
/**
* 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. In a typical 1-1 relation,
* you have two records sharing the same primary key.
* RedBeanPHP has only limited support for 1-1 relations.
* In general it is recommended to use 1-N for this.
*
* Usage:
*
* <code>
* list( $author, $bio ) = R::loadMulti( 'author, bio', $id );
* </code>
*
* @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;
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\ToolBox as ToolBox;
/**
* Quick Export Utility
*
* The Quick Export Utility Class provides functionality to easily
* expose the result of SQL queries as well-known formats like CSV.
*
* @file RedBeanPHP/Util/QuickExporft.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 QuickExport
{
/**
* @var Finder
*/
protected $toolbox;
/**
* @boolean
*/
private static $test = FALSE;
/**
* Constructor.
* The Quick Export requires a toolbox.
*
* @param ToolBox $toolbox
*/
public function __construct( ToolBox $toolbox )
{
$this->toolbox = $toolbox;
}
/**
* Makes csv() testable.
*/
public static function operation( $name, $arg1, $arg2 = TRUE ) {
$out = '';
switch( $name ) {
case 'test':
self::$test = (boolean) $arg1;
break;
case 'header':
$out = ( self::$test ) ? $arg1 : header( $arg1, $arg2 );
break;
case 'readfile':
$out = ( self::$test ) ? file_get_contents( $arg1 ) : readfile( $arg1 );
break;
case 'exit':
$out = ( self::$test ) ? 'exit' : exit();
break;
}
return $out;
}
/**
* Exposes the result of the specified SQL query as a CSV file.
*
* Usage:
*
* <code>
* R::csv( 'SELECT
* `name`,
* population
* FROM city
* WHERE region = :region ',
* array( ':region' => 'Denmark' ),
* array( 'city', 'population' ),
* '/tmp/cities.csv'
* );
* </code>
*
* The command above will select all cities in Denmark
* and create a CSV with columns 'city' and 'population' and
* populate the cells under these column headers with the
* names of the cities and the population numbers respectively.
*
* @param string $sql SQL query to expose result of
* @param array $bindings parameter bindings
* @param array $columns column headers for CSV file
* @param string $path path to save CSV file to
* @param boolean $output TRUE to output CSV directly using readfile
* @param array $options delimiter, quote and escape character respectively
*
* @return void
*/
public function csv( $sql = '', $bindings = array(), $columns = NULL, $path = '/tmp/redexport_%s.csv', $output = TRUE, $options = array(',','"','\\') )
{
list( $delimiter, $enclosure, $escapeChar ) = $options;
$path = sprintf( $path, date('Ymd_his') );
$handle = fopen( $path, 'w' );
if ($columns) if (PHP_VERSION_ID>=505040) fputcsv($handle, $columns, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $columns, $delimiter, $enclosure );
$cursor = $this->toolbox->getDatabaseAdapter()->getCursor( $sql, $bindings );
while( $row = $cursor->getNextItem() ) {
if (PHP_VERSION_ID>=505040) fputcsv($handle, $row, $delimiter, $enclosure, $escapeChar ); else fputcsv($handle, $row, $delimiter, $enclosure );
}
fclose($handle);
if ( $output ) {
$file = basename($path);
$out = self::operation('header',"Pragma: public");
$out .= self::operation('header',"Expires: 0");
$out .= self::operation('header',"Cache-Control: must-revalidate, post-check=0, pre-check=0");
$out .= self::operation('header',"Cache-Control: private", FALSE );
$out .= self::operation('header',"Content-Type: text/csv");
$out .= self::operation('header',"Content-Disposition: attachment; filename={$file}" );
$out .= self::operation('header',"Content-Transfer-Encoding: binary");
$out .= self::operation('readfile',$path );
@unlink( $path );
self::operation('exit', FALSE);
return $out;
}
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\Adapter as Adapter;
/**
* Transaction Helper
*
* This code was originally part of the facade, however it has
* been decided to remove unique features to service classes like
* this to make them available to developers not using the facade class.
*
* Database transaction helper. This is a convenience class
* to perform a callback in a database transaction. This class
* contains a method to wrap your callback in a transaction.
*
* @file RedBeanPHP/Util/Transaction.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 Transaction
{
/**
* Wraps a transaction around a closure or string callback.
* If an Exception is thrown inside, the operation is automatically rolled back.
* If no Exception happens, it commits automatically.
* It also supports (simulated) nested transactions (that is useful when
* you have many methods that needs transactions but are unaware of
* each other).
*
* Example:
*
* <code>
* $from = 1;
* $to = 2;
* $amount = 300;
*
* R::transaction(function() use($from, $to, $amount)
* {
* $accountFrom = R::load('account', $from);
* $accountTo = R::load('account', $to);
* $accountFrom->money -= $amount;
* $accountTo->money += $amount;
* R::store($accountFrom);
* R::store($accountTo);
* });
* </code>
*
* @param Adapter $adapter Database Adapter providing transaction mechanisms.
* @param callable $callback Closure (or other callable) with the transaction logic
*
* @return mixed
*/
public static function transaction( Adapter $adapter, $callback )
{
if ( !is_callable( $callback ) ) {
throw new RedException( 'R::transaction needs a valid callback.' );
}
static $depth = 0;
$result = null;
try {
if ( $depth == 0 ) {
$adapter->startTransaction();
}
$depth++;
$result = call_user_func( $callback ); //maintain 5.2 compatibility
$depth--;
if ( $depth == 0 ) {
$adapter->commit();
}
} catch ( \Exception $exception ) {
$depth--;
if ( $depth == 0 ) {
$adapter->rollback();
}
throw $exception;
}
return $result;
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace RedBeanPHP\Util;
use RedBeanPHP\ToolBox;
use RedBeanPHP\OODBBean;
/**
* Tree
*
* Given a bean, finds it children or parents
* in a hierchical structure.
*
* @experimental feature
*
* @file RedBeanPHP/Util/Tree.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 Tree {
/**
* @var ToolBox
*/
protected $toolbox;
/**
* @var QueryWriter
*/
protected $writer;
/**
* @var OODB
*/
protected $oodb;
/**
* Constructor, creates a new instance of
* the Tree.
*
* @param ToolBox $toolbox toolbox
*/
public function __construct( ToolBox $toolbox )
{
$this->toolbox = $toolbox;
$this->writer = $toolbox->getWriter();
$this->oodb = $toolbox->getRedBean();
}
/**
* Returns all child beans associates with the specified
* bean in a tree structure.
*
* @note this only works for databases that support
* recusrive common table expressions.
*
* Usage:
*
* <code>
* $newsArticles = R::children( $newsPage, ' ORDER BY title ASC ' )
* $newsArticles = R::children( $newsPage, ' WHERE title = ? ', [ $t ] );
* $newsArticles = R::children( $newsPage, ' WHERE title = :t ', [ ':t' => $t ] );
* </code>
*
* Note:
* You are allowed to use named parameter bindings as well as
* numeric parameter bindings (using the question mark notation).
* However, you can not mix. Also, if using named parameter bindings,
* parameter binding key ':slot0' is reserved for the ID of the bean
* and used in the query.
*
* @param OODBBean $bean reference bean to find children of
* @param string $sql optional SQL snippet
* @param array $bindings optional parameter bindings for SQL snippet
*
* @return array
*/
public function children( OODBBean $bean, $sql = NULL, $bindings = array() )
{
$type = $bean->getMeta('type');
$id = $bean->id;
$rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, FALSE, $sql, $bindings );
return $this->oodb->convertToBeans( $type, $rows );
}
/**
* Returns all parent beans associates with the specified
* bean in a tree structure.
*
* @note this only works for databases that support
* recusrive common table expressions.
*
* <code>
* $newsPages = R::parents( $newsArticle, ' ORDER BY title ASC ' );
* $newsPages = R::parents( $newsArticle, ' WHERE title = ? ', [ $t ] );
* $newsPages = R::parents( $newsArticle, ' WHERE title = :t ', [ ':t' => $t ] );
* </code>
*
* Note:
* You are allowed to use named parameter bindings as well as
* numeric parameter bindings (using the question mark notation).
* However, you can not mix. Also, if using named parameter bindings,
* parameter binding key ':slot0' is reserved for the ID of the bean
* and used in the query.
*
* @param OODBBean $bean reference bean to find parents of
* @param string $sql optional SQL snippet
* @param array $bindings optional parameter bindings for SQL snippet
*
* @return array
*/
public function parents( OODBBean $bean, $sql = NULL, $bindings = array() )
{
$type = $bean->getMeta('type');
$id = $bean->id;
$rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, TRUE, $sql, $bindings );
return $this->oodb->convertToBeans( $type, $rows );
}
/**
* Counts all children beans associates with the specified
* bean in a tree structure.
*
* @note this only works for databases that support
* recusrive common table expressions.
*
* <code>
* $count = R::countChildren( $newsArticle );
* $count = R::countChildren( $newsArticle, ' WHERE title = ? ', [ $t ] );
* $count = R::countChildren( $newsArticle, ' WHERE title = :t ', [ ':t' => $t ] );
* </code>
*
* @note:
* You are allowed to use named parameter bindings as well as
* numeric parameter bindings (using the question mark notation).
* However, you can not mix. Also, if using named parameter bindings,
* parameter binding key ':slot0' is reserved for the ID of the bean
* and used in the query.
*
* @note:
* By default, if no SQL or select is given or select=TRUE this method will subtract 1 of
* the total count to omit the starting bean. If you provide your own select,
* this method assumes you take control of the resulting total yourself since
* it cannot 'predict' what or how you are trying to 'count'.
*
* @param OODBBean $bean reference bean to find children of
* @param string $sql optional SQL snippet
* @param array $bindings optional parameter bindings for SQL snippet
* @param string|boolean $select select snippet to use (advanced, optional, see QueryWriter::queryRecursiveCommonTableExpression)
*
* @return integer
*/
public function countChildren( OODBBean $bean, $sql = NULL, $bindings = array(), $select = TRUE ) {
$type = $bean->getMeta('type');
$id = $bean->id;
$rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, FALSE, $sql, $bindings, $select );
$first = reset($rows);
$cell = reset($first);
return (intval($cell) - (($select === TRUE && is_null($sql)) ? 1 : 0));
}
/**
* Counts all parent beans associates with the specified
* bean in a tree structure.
*
* @note this only works for databases that support
* recusrive common table expressions.
*
* <code>
* $count = R::countParents( $newsArticle );
* $count = R::countParents( $newsArticle, ' WHERE title = ? ', [ $t ] );
* $count = R::countParents( $newsArticle, ' WHERE title = :t ', [ ':t' => $t ] );
* </code>
*
* Note:
* You are allowed to use named parameter bindings as well as
* numeric parameter bindings (using the question mark notation).
* However, you can not mix. Also, if using named parameter bindings,
* parameter binding key ':slot0' is reserved for the ID of the bean
* and used in the query.
*
* Note:
* By default, if no SQL or select is given or select=TRUE this method will subtract 1 of
* the total count to omit the starting bean. If you provide your own select,
* this method assumes you take control of the resulting total yourself since
* it cannot 'predict' what or how you are trying to 'count'.
*
* @param OODBBean $bean reference bean to find parents of
* @param string $sql optional SQL snippet
* @param array $bindings optional parameter bindings for SQL snippet
* @param string|boolean $select select snippet to use (advanced, optional, see QueryWriter::queryRecursiveCommonTableExpression)
*
* @return integer
*/
public function countParents( OODBBean $bean, $sql = NULL, $bindings = array(), $select = TRUE ) {
$type = $bean->getMeta('type');
$id = $bean->id;
$rows = $this->writer->queryRecursiveCommonTableExpression( $type, $id, TRUE, $sql, $bindings, $select );
$first = reset($rows);
$cell = reset($first);
return (intval($cell) - (($select === TRUE && is_null($sql)) ? 1 : 0));
}
}