mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2025-11-29 13:11:34 -06:00
Begin Refactor
This commit is contained in:
29
vendor/gabordemooij/redbean/.gitignore
vendored
Normal file
29
vendor/gabordemooij/redbean/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
*.pyc
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store*
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
/.project
|
||||
/.settings/org.eclipse.php.core.prefs
|
||||
/.settings/org.eclipse.php.debug.core.Debug_Process_Preferences.prefs
|
||||
/rb.phar
|
||||
/rb.php
|
||||
/rb-mysql.php
|
||||
/rb-postgres.php
|
||||
/rb-sqlite.php
|
||||
build/
|
||||
38
vendor/gabordemooij/redbean/.travis.yml
vendored
Normal file
38
vendor/gabordemooij/redbean/.travis.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
language: php
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
- php: 5.4
|
||||
dist: trusty
|
||||
- php: 5.5
|
||||
dist: trusty
|
||||
- php: 5.6
|
||||
dist: trusty
|
||||
- php: 7.0
|
||||
dist: trusty
|
||||
- php: 7.1
|
||||
dist: trusty
|
||||
- php: 7.2
|
||||
dist: trusty
|
||||
- php: 7.3
|
||||
dist: trusty
|
||||
- php: 7.4
|
||||
dist: trusty
|
||||
- php: 8.0
|
||||
dist: trusty
|
||||
- php: nightly
|
||||
dist: trusty
|
||||
|
||||
before_script:
|
||||
- touch /tmp/oodb.db
|
||||
- mysql -e 'create database oodb;'
|
||||
- psql template1 -c 'CREATE EXTENSION "uuid-ossp";' -U postgres
|
||||
- psql -c 'create database oodb;' -U postgres
|
||||
- php replica2.php onlyphp
|
||||
- cp rb.php testing/cli/testcontainer/rb.php
|
||||
- cd testing/cli
|
||||
|
||||
|
||||
script: php runtests.php
|
||||
77
vendor/gabordemooij/redbean/README.markdown
vendored
Normal file
77
vendor/gabordemooij/redbean/README.markdown
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
RedBeanPHP 5
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/gabordemooij/redbean)
|
||||
|
||||
RedBeanPHP is an easy to use ORM tool for PHP.
|
||||
|
||||
* Automatically creates tables and columns as you go
|
||||
* No configuration, just fire and forget
|
||||
* No complicated package tools, no autoloaders, just ONE file
|
||||
|
||||
Installation (recommended)
|
||||
---------------------------
|
||||
|
||||
Download RedBeanPHP from the website:
|
||||
|
||||
https://redbeanphp.com/download
|
||||
|
||||
Extract the archive and put it in your PHP project, voila!
|
||||
|
||||
Optional: sha256sum and check signature.
|
||||
|
||||
|
||||
Installation via Composer (not recommended)
|
||||
-----------------------------------------
|
||||
|
||||
Just open your composer.json file and add the package name ```(e.g. "gabordemooij/redbean": "dev-master")``` in your require list.
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"gabordemooij/redbean": "dev-master"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE**:
|
||||
You will find many examples on the RedBean website make use of RedBean's `R` class. Because of namespaced autoloading in Composer, this class will be available as `\RedbeanPHP\R` instead of `R`. If you desire to use the much shorter `R` alias, you can add a `use` statement at the beginning of your code:
|
||||
|
||||
```php
|
||||
use \RedBeanPHP\R as R;
|
||||
```
|
||||
**NOTE:**
|
||||
It is important to note that when using RedBeanPHP with Composer, there are some extra precautions needed when working with [Models](https://redbeanphp.com/index.php?p=/models). Due to the namespace requirements of Composer, when creating Models we need to use the `SimpleModel` to extend, not `RedBean_SimpleModel`. Furthermore, we need to specify the namespace of the `SimpleModel`, so a full example of using a Model with RedBean with Composer is as follows:
|
||||
|
||||
```php
|
||||
use \RedBeanPHP\R;
|
||||
|
||||
class Model_User extends \RedBeanPHP\SimpleModel
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
Notice that we also need to add the `use \RedBeanPHP\R` statement so that we can use the `R::` shortcut within the Model.
|
||||
|
||||
|
||||
Quick Example
|
||||
-------------
|
||||
|
||||
How we store a book object with RedBeanPHP:
|
||||
```php
|
||||
$book = R::dispense("book");
|
||||
$book->author = "Santa Claus";
|
||||
$book->title = "Secrets of Christmas";
|
||||
$id = R::store( $book );
|
||||
```
|
||||
|
||||
Yep, it's that simple.
|
||||
|
||||
|
||||
More information
|
||||
----------------
|
||||
|
||||
For more information about RedBeanPHP please consult
|
||||
the RedBeanPHP website:
|
||||
|
||||
https://www.redbeanphp.com/
|
||||
207
vendor/gabordemooij/redbean/RedBeanPHP/Adapter.php
vendored
Normal file
207
vendor/gabordemooij/redbean/RedBeanPHP/Adapter.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* Adapter Interface.
|
||||
* Describes the API for a RedBeanPHP Database Adapter.
|
||||
* This interface defines the API contract for
|
||||
* a RedBeanPHP Database Adapter.
|
||||
*
|
||||
* @file RedBeanPHP/Adapter.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
interface Adapter
|
||||
{
|
||||
/**
|
||||
* Should returns a string containing the most recent SQL query
|
||||
* that has been processed by the adapter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSQL();
|
||||
|
||||
/**
|
||||
* Executes an SQL Statement using an array of values to bind
|
||||
* If $noevent is TRUE then this function will not signal its
|
||||
* observers to notify about the SQL execution; this to prevent
|
||||
* infinite recursion when using observers.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
* @param boolean $noevent no event firing
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function exec( $sql, $bindings = array(), $noevent = FALSE );
|
||||
|
||||
/**
|
||||
* Executes an SQL Query and returns a resultset.
|
||||
* This method returns a multi dimensional resultset similar to getAll
|
||||
* The values array can be used to bind values to the place holders in the
|
||||
* SQL query.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Executes an SQL Query and returns a resultset.
|
||||
* This method returns a single row (one array) resultset.
|
||||
* The values array can be used to bind values to the place holders in the
|
||||
* SQL query.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRow( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Executes an SQL Query and returns a resultset.
|
||||
* This method returns a single column (one array) resultset.
|
||||
* The values array can be used to bind values to the place holders in the
|
||||
* SQL query.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCol( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Executes an SQL Query and returns a resultset.
|
||||
* This method returns a single cell, a scalar value as the resultset.
|
||||
* The values array can be used to bind values to the place holders in the
|
||||
* SQL query.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCell( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Executes the SQL query specified in $sql and indexes
|
||||
* the row by the first column.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAssoc( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Executes the SQL query specified in $sql and returns
|
||||
* an associative array where the column names are the keys.
|
||||
*
|
||||
* @param string $sql Sstring containing SQL code for databaseQL
|
||||
* @param array $bindings values to bind
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAssocRow( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns the latest insert ID.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getInsertID();
|
||||
|
||||
/**
|
||||
* Returns the number of rows that have been
|
||||
* affected by the last update statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getAffectedRows();
|
||||
|
||||
/**
|
||||
* Returns a database agnostic Cursor object.
|
||||
*
|
||||
* @param string $sql string containing SQL code for database
|
||||
* @param array $bindings array of values to bind to parameters in query string
|
||||
*
|
||||
* @return Cursor
|
||||
*/
|
||||
public function getCursor( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns the original database resource. This is useful if you want to
|
||||
* perform operations on the driver directly instead of working with the
|
||||
* adapter. RedBean will only access the adapter and never to talk
|
||||
* directly to the driver though.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDatabase();
|
||||
|
||||
/**
|
||||
* This method is part of the RedBean Transaction Management
|
||||
* mechanisms.
|
||||
* Starts a transaction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startTransaction();
|
||||
|
||||
/**
|
||||
* This method is part of the RedBean Transaction Management
|
||||
* mechanisms.
|
||||
* Commits the transaction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function commit();
|
||||
|
||||
/**
|
||||
* This method is part of the RedBean Transaction Management
|
||||
* mechanisms.
|
||||
* Rolls back the transaction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rollback();
|
||||
|
||||
/**
|
||||
* Closes database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close();
|
||||
|
||||
/**
|
||||
* Sets a driver specific option.
|
||||
* Using this method you can access driver-specific functions.
|
||||
* If the selected option exists the value will be passed and
|
||||
* this method will return boolean TRUE, otherwise it will return
|
||||
* boolean FALSE.
|
||||
*
|
||||
* @param string $optionKey option key
|
||||
* @param string $optionValue option value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setOption( $optionKey, $optionValue );
|
||||
|
||||
/**
|
||||
* Returns the version string from the database server.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseServerVersion();
|
||||
}
|
||||
293
vendor/gabordemooij/redbean/RedBeanPHP/Adapter/DBAdapter.php
vendored
Normal file
293
vendor/gabordemooij/redbean/RedBeanPHP/Adapter/DBAdapter.php
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Adapter;
|
||||
|
||||
use RedBeanPHP\Observable as Observable;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\Driver as Driver;
|
||||
|
||||
/**
|
||||
* DBAdapter (Database Adapter)
|
||||
*
|
||||
* An adapter class to connect various database systems to RedBean
|
||||
* Database Adapter Class. The task of the database adapter class is to
|
||||
* communicate with the database driver. You can use all sorts of database
|
||||
* drivers with RedBeanPHP. The default database drivers that ships with
|
||||
* the RedBeanPHP library is the RPDO driver ( which uses the PHP Data Objects
|
||||
* Architecture aka PDO ).
|
||||
*
|
||||
* @file RedBeanPHP/Adapter/DBAdapter.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community.
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* (c) copyright 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 DBAdapter extends Observable implements Adapter
|
||||
{
|
||||
/**
|
||||
* @var Driver
|
||||
*/
|
||||
private $db = NULL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sql = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Creates an instance of the RedBean Adapter Class.
|
||||
* This class provides an interface for RedBean to work
|
||||
* with ADO compatible DB instances.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $database = new RPDO( $dsn, $user, $pass );
|
||||
* $adapter = new DBAdapter( $database );
|
||||
* $writer = new PostgresWriter( $adapter );
|
||||
* $oodb = new OODB( $writer, FALSE );
|
||||
* $bean = $oodb->dispense( 'bean' );
|
||||
* $bean->name = 'coffeeBean';
|
||||
* $id = $oodb->store( $bean );
|
||||
* $bean = $oodb->load( 'bean', $id );
|
||||
* </code>
|
||||
*
|
||||
* The example above creates the 3 RedBeanPHP core objects:
|
||||
* the Adapter, the Query Writer and the OODB instance and
|
||||
* wires them together. The example also demonstrates some of
|
||||
* the methods that can be used with OODB, as you see, they
|
||||
* closely resemble their facade counterparts.
|
||||
*
|
||||
* The wiring process: create an RPDO instance using your database
|
||||
* connection parameters. Create a database adapter from the RPDO
|
||||
* object and pass that to the constructor of the writer. Next,
|
||||
* create an OODB instance from the writer. Now you have an OODB
|
||||
* object.
|
||||
*
|
||||
* @param Driver $database ADO Compatible DB Instance
|
||||
*/
|
||||
public function __construct( $database )
|
||||
{
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string containing the most recent SQL query
|
||||
* processed by the database adapter, thus conforming to the
|
||||
* interface:
|
||||
*
|
||||
* @see Adapter::getSQL
|
||||
*
|
||||
* Methods like get(), getRow() and exec() cause this SQL cache
|
||||
* to get filled. If no SQL query has been processed yet this function
|
||||
* will return an empty string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSQL()
|
||||
{
|
||||
return $this->sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::exec
|
||||
*/
|
||||
public function exec( $sql, $bindings = array(), $noevent = FALSE )
|
||||
{
|
||||
if ( !$noevent ) {
|
||||
$this->sql = $sql;
|
||||
$this->signal( 'sql_exec', $this );
|
||||
}
|
||||
|
||||
return $this->db->Execute( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::get
|
||||
*/
|
||||
public function get( $sql, $bindings = array() )
|
||||
{
|
||||
$this->sql = $sql;
|
||||
$this->signal( 'sql_exec', $this );
|
||||
|
||||
return $this->db->GetAll( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getRow
|
||||
*/
|
||||
public function getRow( $sql, $bindings = array() )
|
||||
{
|
||||
$this->sql = $sql;
|
||||
$this->signal( 'sql_exec', $this );
|
||||
|
||||
return $this->db->GetRow( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getCol
|
||||
*/
|
||||
public function getCol( $sql, $bindings = array() )
|
||||
{
|
||||
$this->sql = $sql;
|
||||
$this->signal( 'sql_exec', $this );
|
||||
|
||||
return $this->db->GetCol( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getAssoc
|
||||
*/
|
||||
public function getAssoc( $sql, $bindings = array() )
|
||||
{
|
||||
$this->sql = $sql;
|
||||
|
||||
$this->signal( 'sql_exec', $this );
|
||||
|
||||
$rows = $this->db->GetAll( $sql, $bindings );
|
||||
|
||||
if ( !$rows ) return array();
|
||||
|
||||
$assoc = array();
|
||||
|
||||
foreach ( $rows as $row ) {
|
||||
if ( empty( $row ) ) continue;
|
||||
|
||||
$key = array_shift( $row );
|
||||
switch ( count( $row ) ) {
|
||||
case 0:
|
||||
$value = $key;
|
||||
break;
|
||||
case 1:
|
||||
$value = reset( $row );
|
||||
break;
|
||||
default:
|
||||
$value = $row;
|
||||
}
|
||||
|
||||
$assoc[$key] = $value;
|
||||
}
|
||||
|
||||
return $assoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getAssocRow
|
||||
*/
|
||||
public function getAssocRow($sql, $bindings = array())
|
||||
{
|
||||
$this->sql = $sql;
|
||||
$this->signal( 'sql_exec', $this );
|
||||
|
||||
return $this->db->GetAssocRow( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getCell
|
||||
*/
|
||||
public function getCell( $sql, $bindings = array(), $noSignal = NULL )
|
||||
{
|
||||
$this->sql = $sql;
|
||||
|
||||
if ( !$noSignal ) $this->signal( 'sql_exec', $this );
|
||||
|
||||
return $this->db->GetOne( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getCursor
|
||||
*/
|
||||
public function getCursor( $sql, $bindings = array() )
|
||||
{
|
||||
return $this->db->GetCursor( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getInsertID
|
||||
*/
|
||||
public function getInsertID()
|
||||
{
|
||||
return $this->db->getInsertID();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getAffectedRows
|
||||
*/
|
||||
public function getAffectedRows()
|
||||
{
|
||||
return $this->db->Affected_Rows();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getDatabase
|
||||
*/
|
||||
public function getDatabase()
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::startTransaction
|
||||
*/
|
||||
public function startTransaction()
|
||||
{
|
||||
$this->db->StartTrans();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::commit
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->db->CommitTrans();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::rollback
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->db->FailTrans();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::close.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->db->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets initialization code for connection.
|
||||
*
|
||||
* @param callable $code
|
||||
*/
|
||||
public function setInitCode($code) {
|
||||
$this->db->setInitCode($code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::setOption
|
||||
*/
|
||||
public function setOption( $optionKey, $optionValue ) {
|
||||
if ( method_exists( $this->db, $optionKey ) ) {
|
||||
call_user_func( array( $this->db, $optionKey ), $optionValue );
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Adapter::getDatabaseServerVersion
|
||||
*/
|
||||
public function getDatabaseServerVersion()
|
||||
{
|
||||
return $this->db->DatabaseServerVersion();
|
||||
}
|
||||
}
|
||||
364
vendor/gabordemooij/redbean/RedBeanPHP/AssociationManager.php
vendored
Normal file
364
vendor/gabordemooij/redbean/RedBeanPHP/AssociationManager.php
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
|
||||
/**
|
||||
* Association Manager.
|
||||
* The association manager can be used to create and manage
|
||||
* many-to-many relations (for example sharedLists). In a many-to-many relation,
|
||||
* one bean can be associated with many other beans, while each of those beans
|
||||
* can also be related to multiple beans.
|
||||
*
|
||||
* @file RedBeanPHP/AssociationManager.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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 AssociationManager extends Observable
|
||||
{
|
||||
/**
|
||||
* @var OODB
|
||||
*/
|
||||
protected $oodb;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var QueryWriter
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
/**
|
||||
* Exception handler.
|
||||
* Fluid and Frozen mode have different ways of handling
|
||||
* exceptions. Fluid mode (using the fluid repository) ignores
|
||||
* exceptions caused by the following:
|
||||
*
|
||||
* - missing tables
|
||||
* - missing column
|
||||
*
|
||||
* In these situations, the repository will behave as if
|
||||
* no beans could be found. This is because in fluid mode
|
||||
* it might happen to query a table or column that has not been
|
||||
* created yet. In frozen mode, this is not supposed to happen
|
||||
* and the corresponding exceptions will be thrown.
|
||||
*
|
||||
* @param \Exception $exception exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleException( \Exception $exception )
|
||||
{
|
||||
if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(),
|
||||
array(
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
|
||||
$exception->getDriverDetails()
|
||||
)
|
||||
) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method.
|
||||
* Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and
|
||||
* $bindings bindings. If $getLinks is TRUE, link rows are returned instead.
|
||||
*
|
||||
* @param OODBBean $bean reference bean instance
|
||||
* @param string $type target bean type
|
||||
* @param string $sql additional SQL snippet
|
||||
* @param array $bindings bindings for query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function relatedRows( $bean, $type, $sql = '', $bindings = array() )
|
||||
{
|
||||
$ids = array( $bean->id );
|
||||
$sourceType = $bean->getMeta( 'type' );
|
||||
try {
|
||||
return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings );
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a pair of beans. This method associates two beans, no matter
|
||||
* what types. Accepts a base bean that contains data for the linking record.
|
||||
* This method is used by associate. This method also accepts a base bean to be used
|
||||
* as the template for the link record in the database.
|
||||
*
|
||||
* @param OODBBean $bean1 first bean
|
||||
* @param OODBBean $bean2 second bean
|
||||
* @param OODBBean $bean base bean (association record)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean )
|
||||
{
|
||||
$type = $bean->getMeta( 'type' );
|
||||
$property1 = $bean1->getMeta( 'type' ) . '_id';
|
||||
$property2 = $bean2->getMeta( 'type' ) . '_id';
|
||||
|
||||
if ( $property1 == $property2 ) {
|
||||
$property2 = $bean2->getMeta( 'type' ) . '2_id';
|
||||
}
|
||||
|
||||
$this->oodb->store( $bean1 );
|
||||
$this->oodb->store( $bean2 );
|
||||
|
||||
$bean->setMeta( "cast.$property1", "id" );
|
||||
$bean->setMeta( "cast.$property2", "id" );
|
||||
$bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) );
|
||||
|
||||
$bean->$property1 = $bean1->id;
|
||||
$bean->$property2 = $bean2->id;
|
||||
|
||||
$results = array();
|
||||
|
||||
try {
|
||||
$id = $this->oodb->store( $bean );
|
||||
$results[] = $id;
|
||||
} catch ( SQLException $exception ) {
|
||||
if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
|
||||
array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ),
|
||||
$exception->getDriverDetails() )
|
||||
) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor, creates a new instance of the Association Manager.
|
||||
* The association manager can be used to create and manage
|
||||
* many-to-many relations (for example sharedLists). In a many-to-many relation,
|
||||
* one bean can be associated with many other beans, while each of those beans
|
||||
* can also be related to multiple beans. To create an Association Manager
|
||||
* instance you'll need to pass a ToolBox object.
|
||||
*
|
||||
* @param ToolBox $tools toolbox supplying core RedBeanPHP objects
|
||||
*/
|
||||
public function __construct( ToolBox $tools )
|
||||
{
|
||||
$this->oodb = $tools->getRedBean();
|
||||
$this->adapter = $tools->getDatabaseAdapter();
|
||||
$this->writer = $tools->getWriter();
|
||||
$this->toolbox = $tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table name based on a types array.
|
||||
* Manages the get the correct name for the linking table for the
|
||||
* types provided.
|
||||
*
|
||||
* @param array $types 2 types as strings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTable( $types )
|
||||
{
|
||||
return $this->writer->getAssocTable( $types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates two beans in a many-to-many relation.
|
||||
* This method will associate two beans and store the connection between the
|
||||
* two in a link table. Instead of two single beans this method also accepts
|
||||
* two sets of beans. Returns the ID or the IDs of the linking beans.
|
||||
*
|
||||
* @param OODBBean|array $beans1 one or more beans to form the association
|
||||
* @param OODBBean|array $beans2 one or more beans to form the association
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function associate( $beans1, $beans2 )
|
||||
{
|
||||
if ( !is_array( $beans1 ) ) {
|
||||
$beans1 = array( $beans1 );
|
||||
}
|
||||
|
||||
if ( !is_array( $beans2 ) ) {
|
||||
$beans2 = array( $beans2 );
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach ( $beans1 as $bean1 ) {
|
||||
foreach ( $beans2 as $bean2 ) {
|
||||
$table = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) );
|
||||
$bean = $this->oodb->dispense( $table );
|
||||
$results[] = $this->associateBeans( $bean1, $bean2, $bean );
|
||||
}
|
||||
}
|
||||
|
||||
return ( count( $results ) > 1 ) ? $results : reset( $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of related beans in an N-M relation.
|
||||
* This method returns the number of beans of type $type associated
|
||||
* with reference bean(s) $bean. The query can be tuned using an
|
||||
* SQL snippet for additional filtering.
|
||||
*
|
||||
* @param OODBBean|array $bean a bean object or an array of beans
|
||||
* @param string $type type of bean you're interested in
|
||||
* @param string $sql SQL snippet (optional)
|
||||
* @param array $bindings bindings for your SQL string
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() )
|
||||
{
|
||||
if ( !( $bean instanceof OODBBean ) ) {
|
||||
throw new RedException(
|
||||
'Expected array or OODBBean but got:' . gettype( $bean )
|
||||
);
|
||||
}
|
||||
|
||||
if ( !$bean->id ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$beanType = $bean->getMeta( 'type' );
|
||||
|
||||
try {
|
||||
return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings );
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks the association between two beans. This method unassociates two beans. If the
|
||||
* method succeeds the beans will no longer form an association. In the database
|
||||
* this means that the association record will be removed. This method uses the
|
||||
* OODB trash() method to remove the association links, thus giving FUSE models the
|
||||
* opportunity to hook-in additional business logic. If the $fast parameter is
|
||||
* set to boolean TRUE this method will remove the beans without their consent,
|
||||
* bypassing FUSE. This can be used to improve performance.
|
||||
*
|
||||
* @param OODBBean $beans1 first bean in target association
|
||||
* @param OODBBean $beans2 second bean in target association
|
||||
* @param boolean $fast if TRUE, removes the entries by query without FUSE
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unassociate( $beans1, $beans2, $fast = NULL )
|
||||
{
|
||||
$beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1;
|
||||
$beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2;
|
||||
|
||||
foreach ( $beans1 as $bean1 ) {
|
||||
foreach ( $beans2 as $bean2 ) {
|
||||
try {
|
||||
$this->oodb->store( $bean1 );
|
||||
$this->oodb->store( $bean2 );
|
||||
|
||||
$type1 = $bean1->getMeta( 'type' );
|
||||
$type2 = $bean2->getMeta( 'type' );
|
||||
|
||||
$row = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id );
|
||||
|
||||
if ( !$row ) return;
|
||||
|
||||
$linkType = $this->getTable( array( $type1, $type2 ) );
|
||||
|
||||
if ( $fast ) {
|
||||
$this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$beans = $this->oodb->convertToBeans( $linkType, array( $row ) );
|
||||
|
||||
if ( count( $beans ) > 0 ) {
|
||||
$bean = reset( $beans );
|
||||
$this->oodb->trash( $bean );
|
||||
}
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all relations for a bean. This method breaks every connection between
|
||||
* a certain bean $bean and every other bean of type $type. Warning: this method
|
||||
* is really fast because it uses a direct SQL query however it does not inform the
|
||||
* models about this. If you want to notify FUSE models about deletion use a foreach-loop
|
||||
* with unassociate() instead. (that might be slower though)
|
||||
*
|
||||
* @param OODBBean $bean reference bean
|
||||
* @param string $type type of beans that need to be unassociated
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearRelations( OODBBean $bean, $type )
|
||||
{
|
||||
$this->oodb->store( $bean );
|
||||
try {
|
||||
$this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id );
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
101
vendor/gabordemooij/redbean/RedBeanPHP/BeanCollection.php
vendored
Normal file
101
vendor/gabordemooij/redbean/RedBeanPHP/BeanCollection.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\Cursor as Cursor;
|
||||
use RedBeanPHP\Repository as Repository;
|
||||
|
||||
/**
|
||||
* BeanCollection.
|
||||
*
|
||||
* The BeanCollection represents a collection of beans and
|
||||
* makes it possible to use database cursors. The BeanCollection
|
||||
* has a method next() to obtain the first, next and last bean
|
||||
* in the collection. The BeanCollection does not implement the array
|
||||
* interface nor does it try to act like an array because it cannot go
|
||||
* backward or rewind itself.
|
||||
*
|
||||
* Use the BeanCollection for large datasets where skip/limit is not an
|
||||
* option. Keep in mind that ID-marking (querying a start ID) is a decent
|
||||
* alternative though.
|
||||
*
|
||||
* @file RedBeanPHP/BeanCollection.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 BeanCollection
|
||||
{
|
||||
/**
|
||||
* @var Cursor
|
||||
*/
|
||||
protected $cursor = NULL;
|
||||
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
protected $repository = NULL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = NULL;
|
||||
|
||||
/**
|
||||
* Constructor, creates a new instance of the BeanCollection.
|
||||
*
|
||||
* @param string $type type of beans in this collection
|
||||
* @param Repository $repository repository to use to generate bean objects
|
||||
* @param Cursor $cursor cursor object to use
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $type, Repository $repository, Cursor $cursor )
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->cursor = $cursor;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next bean in the collection.
|
||||
* If called the first time, this will return the first bean in the collection.
|
||||
* If there are no more beans left in the collection, this method
|
||||
* will return NULL.
|
||||
*
|
||||
* @return OODBBean|NULL
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$row = $this->cursor->getNextItem();
|
||||
if ( $row ) {
|
||||
$beans = $this->repository->convertToBeans( $this->type, array( $row ) );
|
||||
return reset( $beans );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the collection from the start, like a fresh() on a bean.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->cursor->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying cursor (needed for some databases).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->cursor->close();
|
||||
}
|
||||
}
|
||||
60
vendor/gabordemooij/redbean/RedBeanPHP/BeanHelper.php
vendored
Normal file
60
vendor/gabordemooij/redbean/RedBeanPHP/BeanHelper.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Bean Helper Interface.
|
||||
*
|
||||
* Interface for Bean Helper.
|
||||
* A little bolt that glues the whole machinery together.
|
||||
* The Bean Helper is passed to the OODB RedBeanPHP Object to
|
||||
* faciliatte the creation of beans and providing them with
|
||||
* a toolbox. The Helper also facilitates the FUSE feature,
|
||||
* determining how beans relate to their models. By overriding
|
||||
* the getModelForBean method you can tune the FUSEing to
|
||||
* fit your business application needs.
|
||||
*
|
||||
* @file RedBeanPHP/IBeanHelper.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.
|
||||
*/
|
||||
interface BeanHelper
|
||||
{
|
||||
/**
|
||||
* Returns a toolbox to empower the bean.
|
||||
* This allows beans to perform OODB operations by themselves,
|
||||
* as such the bean is a proxy for OODB. This allows beans to implement
|
||||
* their magic getters and setters and return lists.
|
||||
*
|
||||
* @return ToolBox
|
||||
*/
|
||||
public function getToolbox();
|
||||
|
||||
/**
|
||||
* Does approximately the same as getToolbox but also extracts the
|
||||
* toolbox for you.
|
||||
* This method returns a list with all toolbox items in Toolbox Constructor order:
|
||||
* OODB, adapter, writer and finally the toolbox itself!.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtractedToolbox();
|
||||
|
||||
/**
|
||||
* Given a certain bean this method will
|
||||
* return the corresponding model.
|
||||
*
|
||||
* @param OODBBean $bean bean to obtain the corresponding model of
|
||||
*
|
||||
* @return SimpleModel|CustomModel|NULL
|
||||
*/
|
||||
public function getModelForBean( OODBBean $bean );
|
||||
}
|
||||
109
vendor/gabordemooij/redbean/RedBeanPHP/BeanHelper/SimpleFacadeBeanHelper.php
vendored
Normal file
109
vendor/gabordemooij/redbean/RedBeanPHP/BeanHelper/SimpleFacadeBeanHelper.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\BeanHelper;
|
||||
|
||||
use RedBeanPHP\BeanHelper as BeanHelper;
|
||||
use RedBeanPHP\Facade as Facade;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\SimpleModelHelper as SimpleModelHelper;
|
||||
|
||||
/**
|
||||
* Bean Helper.
|
||||
*
|
||||
* The Bean helper helps beans to access access the toolbox and
|
||||
* FUSE models. This Bean Helper makes use of the facade to obtain a
|
||||
* reference to the toolbox.
|
||||
*
|
||||
* @file RedBeanPHP/BeanHelperFacade.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* (c) copyright 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 SimpleFacadeBeanHelper implements BeanHelper
|
||||
{
|
||||
/**
|
||||
* Factory function to create instance of Simple Model, if any.
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
private static $factory = null;
|
||||
|
||||
/**
|
||||
* Factory method using a customizable factory function to create
|
||||
* the instance of the Simple Model.
|
||||
*
|
||||
* @param string $modelClassName name of the class
|
||||
*
|
||||
* @return SimpleModel
|
||||
*/
|
||||
public static function factory( $modelClassName )
|
||||
{
|
||||
$factory = self::$factory;
|
||||
return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the factory function to create the model when using FUSE
|
||||
* to connect a bean to a model.
|
||||
*
|
||||
* @param \Closure $factory factory function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setFactoryFunction( $factory )
|
||||
{
|
||||
self::$factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BeanHelper::getToolbox
|
||||
*/
|
||||
public function getToolbox()
|
||||
{
|
||||
return Facade::getToolBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BeanHelper::getModelForBean
|
||||
*/
|
||||
public function getModelForBean( OODBBean $bean )
|
||||
{
|
||||
$model = $bean->getMeta( 'type' );
|
||||
$prefix = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_';
|
||||
|
||||
if ( strpos( $model, '_' ) !== FALSE ) {
|
||||
$modelParts = explode( '_', $model );
|
||||
$modelName = '';
|
||||
foreach( $modelParts as $part ) {
|
||||
$modelName .= ucfirst( $part );
|
||||
}
|
||||
$modelName = $prefix . $modelName;
|
||||
if ( !class_exists( $modelName ) ) {
|
||||
$modelName = $prefix . ucfirst( $model );
|
||||
if ( !class_exists( $modelName ) ) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$modelName = $prefix . ucfirst( $model );
|
||||
if ( !class_exists( $modelName ) ) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
$obj = self::factory( $modelName );
|
||||
$obj->loadBean( $bean );
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BeanHelper::getExtractedToolbox
|
||||
*/
|
||||
public function getExtractedToolbox()
|
||||
{
|
||||
return Facade::getExtractedToolbox();
|
||||
}
|
||||
}
|
||||
49
vendor/gabordemooij/redbean/RedBeanPHP/Cursor.php
vendored
Normal file
49
vendor/gabordemooij/redbean/RedBeanPHP/Cursor.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* Database Cursor Interface.
|
||||
* A cursor is used by Query Writers to fetch Query Result rows
|
||||
* one row at a time. This is useful if you expect the result set to
|
||||
* be quite large. This interface dscribes the API of a database
|
||||
* cursor. There can be multiple implementations of the Cursor,
|
||||
* by default RedBeanPHP offers the PDOCursor for drivers shipping
|
||||
* with RedBeanPHP and the NULLCursor.
|
||||
*
|
||||
* @file RedBeanPHP/Cursor.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.
|
||||
*/
|
||||
interface Cursor
|
||||
{
|
||||
/**
|
||||
* Should retrieve the next row of the result set.
|
||||
* This method is used to iterate over the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNextItem();
|
||||
|
||||
/**
|
||||
* Resets the cursor by closing it and re-executing the statement.
|
||||
* This reloads fresh data from the database for the whole collection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reset();
|
||||
|
||||
/**
|
||||
* Closes the database cursor.
|
||||
* Some databases require a cursor to be closed before executing
|
||||
* another statement/opening a new cursor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close();
|
||||
}
|
||||
48
vendor/gabordemooij/redbean/RedBeanPHP/Cursor/NullCursor.php
vendored
Normal file
48
vendor/gabordemooij/redbean/RedBeanPHP/Cursor/NullCursor.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Cursor;
|
||||
|
||||
use RedBeanPHP\Cursor as Cursor;
|
||||
|
||||
/**
|
||||
* NULL Database Cursor
|
||||
* Implementation of the NULL Cursor.
|
||||
* Used for an empty BeanCollection. This Cursor
|
||||
* can be used for instance if a query fails but the interface
|
||||
* demands a cursor to be returned.
|
||||
*
|
||||
* @file RedBeanPHP/Cursor/NULLCursor.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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 NullCursor implements Cursor
|
||||
{
|
||||
/**
|
||||
* @see Cursor::getNextItem
|
||||
*/
|
||||
public function getNextItem()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Cursor::reset
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Cursor::close
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
74
vendor/gabordemooij/redbean/RedBeanPHP/Cursor/PDOCursor.php
vendored
Normal file
74
vendor/gabordemooij/redbean/RedBeanPHP/Cursor/PDOCursor.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Cursor;
|
||||
|
||||
use RedBeanPHP\Cursor as Cursor;
|
||||
|
||||
/**
|
||||
* PDO Database Cursor
|
||||
* Implementation of PDO Database Cursor.
|
||||
* Used by the BeanCollection to fetch one bean at a time.
|
||||
* The PDO Cursor is used by Query Writers to support retrieval
|
||||
* of large bean collections. For instance, this class is used to
|
||||
* implement the findCollection()/BeanCollection functionality.
|
||||
*
|
||||
* @file RedBeanPHP/Cursor/PDOCursor.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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 PDOCursor implements Cursor
|
||||
{
|
||||
/**
|
||||
* @var PDOStatement
|
||||
*/
|
||||
protected $res;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fetchStyle;
|
||||
|
||||
/**
|
||||
* Constructor, creates a new instance of a PDO Database Cursor.
|
||||
*
|
||||
* @param PDOStatement $res the PDO statement
|
||||
* @param string $fetchStyle fetch style constant to use
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( \PDOStatement $res, $fetchStyle )
|
||||
{
|
||||
$this->res = $res;
|
||||
$this->fetchStyle = $fetchStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Cursor::getNextItem
|
||||
*/
|
||||
public function getNextItem()
|
||||
{
|
||||
return $this->res->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Cursor::reset
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->close();
|
||||
$this->res->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Cursor::close
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->res->closeCursor();
|
||||
}
|
||||
}
|
||||
194
vendor/gabordemooij/redbean/RedBeanPHP/Driver.php
vendored
Normal file
194
vendor/gabordemooij/redbean/RedBeanPHP/Driver.php
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* Interface for database drivers.
|
||||
* The Driver API conforms to the ADODB pseudo standard
|
||||
* for database drivers.
|
||||
*
|
||||
* @file RedBeanPHP/Driver.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.
|
||||
*/
|
||||
interface Driver
|
||||
{
|
||||
/**
|
||||
* Runs a query and fetches results as a multi dimensional array.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetAll( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Runs a query and fetches results as a column.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetCol( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Runs a query and returns results as a single cell.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetOne( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Runs a query and returns results as an associative array
|
||||
* indexed by the first column.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetAssocRow( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Runs a query and returns a flat array containing the values of
|
||||
* one row.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetRow( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Executes SQL code and allows key-value binding.
|
||||
* This function allows you to provide an array with values to bind
|
||||
* to query parameters. For instance you can bind values to question
|
||||
* marks in the query. Each value in the array corresponds to the
|
||||
* question mark in the query that matches the position of the value in the
|
||||
* array. You can also bind values using explicit keys, for instance
|
||||
* array(":key"=>123) will bind the integer 123 to the key :key in the
|
||||
* SQL. This method has no return value.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return array Affected Rows
|
||||
*/
|
||||
public function Execute( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns the latest insert ID if driver does support this
|
||||
* feature.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function GetInsertID();
|
||||
|
||||
/**
|
||||
* Returns the number of rows affected by the most recent query
|
||||
* if the currently selected driver driver supports this feature.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function Affected_Rows();
|
||||
|
||||
/**
|
||||
* Returns a cursor-like object from the database.
|
||||
*
|
||||
* @param string $sql SQL query to execute
|
||||
* @param array $bindings list of values to bind to SQL snippet
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetCursor( $sql, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Toggles debug mode. In debug mode the driver will print all
|
||||
* SQL to the screen together with some information about the
|
||||
* results.
|
||||
*
|
||||
* This method is for more fine-grained control. Normally
|
||||
* you should use the facade to start the query debugger for
|
||||
* you. The facade will manage the object wirings necessary
|
||||
* to use the debugging functionality.
|
||||
*
|
||||
* Usage (through facade):
|
||||
*
|
||||
* <code>
|
||||
* R::debug( TRUE );
|
||||
* ...rest of program...
|
||||
* R::debug( FALSE );
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how to use the RedBeanPHP
|
||||
* query debugger through the facade.
|
||||
*
|
||||
* @param boolean $trueFalse turn on/off
|
||||
* @param Logger $logger logger instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDebugMode( $tf, $customLogger );
|
||||
|
||||
/**
|
||||
* Starts a transaction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function CommitTrans();
|
||||
|
||||
/**
|
||||
* Commits a transaction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function StartTrans();
|
||||
|
||||
/**
|
||||
* Rolls back a transaction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function FailTrans();
|
||||
|
||||
/**
|
||||
* Resets the internal Query Counter.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function resetCounter();
|
||||
|
||||
/**
|
||||
* Returns the number of SQL queries processed.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getQueryCount();
|
||||
|
||||
/**
|
||||
* Sets initialization code for connection.
|
||||
*
|
||||
* @param callable $code code
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInitCode( $code );
|
||||
|
||||
/**
|
||||
* Returns the version string from the database server.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function DatabaseServerVersion();
|
||||
}
|
||||
950
vendor/gabordemooij/redbean/RedBeanPHP/Driver/RPDO.php
vendored
Normal file
950
vendor/gabordemooij/redbean/RedBeanPHP/Driver/RPDO.php
vendored
Normal file
@@ -0,0 +1,950 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Driver;
|
||||
|
||||
use RedBeanPHP\Driver as Driver;
|
||||
use RedBeanPHP\Logger as Logger;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\RedException\SQL as SQL;
|
||||
use RedBeanPHP\Logger\RDefault as RDefault;
|
||||
use RedBeanPHP\PDOCompatible as PDOCompatible;
|
||||
use RedBeanPHP\Cursor\PDOCursor as PDOCursor;
|
||||
|
||||
/**
|
||||
* PDO Driver
|
||||
* This Driver implements the RedBean Driver API.
|
||||
* for RedBeanPHP. This is the standard / default database driver
|
||||
* for RedBeanPHP.
|
||||
*
|
||||
* @file RedBeanPHP/PDO.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* copyright (c) Desfrenes & 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 RPDO implements Driver
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $max;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $dsn;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $loggingEnabled = FALSE;
|
||||
|
||||
/**
|
||||
* @var Logger
|
||||
*/
|
||||
protected $logger = NULL;
|
||||
|
||||
/**
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $affectedRows;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $resultArray;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $connectInfo = array();
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $isConnected = FALSE;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $flagUseStringOnlyBinding = FALSE;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $queryCounter = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $mysqlCharset = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $mysqlCollate = '';
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $stringifyFetches = TRUE;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $initSQL = NULL;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $initCode = NULL;
|
||||
|
||||
/**
|
||||
* Binds parameters. This method binds parameters to a PDOStatement for
|
||||
* Query Execution. This method binds parameters as NULL, INTEGER or STRING
|
||||
* and supports both named keys and question mark keys.
|
||||
*
|
||||
* @param PDOStatement $statement PDO Statement instance
|
||||
* @param array $bindings values that need to get bound to the statement
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function bindParams( $statement, $bindings )
|
||||
{
|
||||
foreach ( $bindings as $key => &$value ) {
|
||||
$k = is_integer( $key ) ? $key + 1 : $key;
|
||||
|
||||
if ( is_array( $value ) && count( $value ) == 2 ) {
|
||||
$paramType = end( $value );
|
||||
$value = reset( $value );
|
||||
} else {
|
||||
$paramType = NULL;
|
||||
}
|
||||
|
||||
if ( is_null( $value ) ) {
|
||||
$statement->bindValue( $k, NULL, \PDO::PARAM_NULL );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $paramType != \PDO::PARAM_INT && $paramType != \PDO::PARAM_STR ) {
|
||||
if ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
|
||||
$paramType = \PDO::PARAM_INT;
|
||||
} else {
|
||||
$paramType = \PDO::PARAM_STR;
|
||||
}
|
||||
}
|
||||
|
||||
$statement->bindParam( $k, $value, $paramType );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method runs the actual SQL query and binds a list of parameters to the query.
|
||||
* slots. The result of the query will be stored in the protected property
|
||||
* $rs (always array). The number of rows affected (result of rowcount, if supported by database)
|
||||
* is stored in protected property $affectedRows. If the debug flag is set
|
||||
* this function will send debugging output to screen buffer.
|
||||
*
|
||||
* @param string $sql the SQL string to be send to database server
|
||||
* @param array $bindings the values that need to get bound to the query slots
|
||||
* @param array $options
|
||||
*
|
||||
* @return mixed
|
||||
* @throws SQL
|
||||
*/
|
||||
protected function runQuery( $sql, $bindings, $options = array() )
|
||||
{
|
||||
$this->connect();
|
||||
if ( $this->loggingEnabled && $this->logger ) {
|
||||
$this->logger->log( $sql, $bindings );
|
||||
}
|
||||
try {
|
||||
if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
|
||||
if (defined('\\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT')) {
|
||||
$statement = @$this->pdo->prepare($sql, array(\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE));
|
||||
} else {
|
||||
$statement = $this->pdo->prepare($sql);
|
||||
}
|
||||
} else {
|
||||
$statement = $this->pdo->prepare( $sql );
|
||||
}
|
||||
$this->bindParams( $statement, $bindings );
|
||||
$statement->execute();
|
||||
$this->queryCounter ++;
|
||||
$this->affectedRows = $statement->rowCount();
|
||||
if ( $statement->columnCount() ) {
|
||||
$fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
|
||||
if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
|
||||
$this->resultArray = array();
|
||||
return $statement;
|
||||
}
|
||||
$this->resultArray = $statement->fetchAll( $fetchStyle );
|
||||
if ( $this->loggingEnabled && $this->logger ) {
|
||||
$this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
|
||||
}
|
||||
} else {
|
||||
$this->resultArray = array();
|
||||
}
|
||||
} catch ( \PDOException $e ) {
|
||||
//Unfortunately the code field is supposed to be int by default (php)
|
||||
//So we need a property to convey the SQL State code.
|
||||
$err = $e->getMessage();
|
||||
if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
|
||||
$exception = new SQL( $err, 0, $e );
|
||||
$exception->setSQLState( $e->getCode() );
|
||||
$exception->setDriverDetails( $e->errorInfo );
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to fix MySQL character encoding problems.
|
||||
* MySQL < 5.5.3 does not support proper 4 byte unicode but they
|
||||
* seem to have added it with version 5.5.3 under a different label: utf8mb4.
|
||||
* We try to select the best possible charset based on your version data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setEncoding()
|
||||
{
|
||||
$driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME );
|
||||
if ($driver === 'mysql') {
|
||||
$charset = $this->hasCap( 'utf8mb4' ) ? 'utf8mb4' : 'utf8';
|
||||
$collate = $this->hasCap( 'utf8mb4_520' ) ? '_unicode_520_ci' : '_unicode_ci';
|
||||
$this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '. $charset ); //on every re-connect
|
||||
/* #624 removed space before SET NAMES because it causes trouble with ProxySQL */
|
||||
$this->pdo->exec('SET NAMES '. $charset); //also for current connection
|
||||
$this->mysqlCharset = $charset;
|
||||
$this->mysqlCollate = $charset . $collate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a database supports a particular feature.
|
||||
* Currently this function can be used to detect the following features:
|
||||
*
|
||||
* - utf8mb4
|
||||
* - utf8mb4 520
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $this->hasCap( 'utf8mb4_520' );
|
||||
* </code>
|
||||
*
|
||||
* By default, RedBeanPHP uses this method under the hood to make sure
|
||||
* you use the latest UTF8 encoding possible for your database.
|
||||
*
|
||||
* @param $db_cap identifier of database capability
|
||||
*
|
||||
* @return int|false Whether the database feature is supported, FALSE otherwise.
|
||||
**/
|
||||
protected function hasCap( $db_cap )
|
||||
{
|
||||
$compare = FALSE;
|
||||
$version = $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION );
|
||||
switch ( strtolower( $db_cap ) ) {
|
||||
case 'utf8mb4':
|
||||
//oneliner, to boost code coverage (coverage does not span versions)
|
||||
if ( version_compare( $version, '5.5.3', '<' ) ) { return FALSE; }
|
||||
$client_version = $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
|
||||
/*
|
||||
* libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
|
||||
* mysqlnd has supported utf8mb4 since 5.0.9.
|
||||
*/
|
||||
if ( strpos( $client_version, 'mysqlnd' ) !== FALSE ) {
|
||||
$client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version );
|
||||
$compare = version_compare( $client_version, '5.0.9', '>=' );
|
||||
} else {
|
||||
$compare = version_compare( $client_version, '5.5.3', '>=' );
|
||||
}
|
||||
break;
|
||||
case 'utf8mb4_520':
|
||||
$compare = version_compare( $version, '5.6', '>=' );
|
||||
break;
|
||||
}
|
||||
|
||||
return $compare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. You may either specify dsn, user and password or
|
||||
* just give an existing PDO connection.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $driver = new RPDO( $dsn, $user, $password );
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how to create a driver
|
||||
* instance from a database connection string (dsn), a username
|
||||
* and a password. It's also possible to pass a PDO object.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $driver = new RPDO( $existingConnection );
|
||||
* </code>
|
||||
*
|
||||
* The second example shows how to create an RPDO instance
|
||||
* from an existing PDO object.
|
||||
*
|
||||
* @param string|object $dsn database connection string
|
||||
* @param string $user optional, usename to sign in
|
||||
* @param string $pass optional, password for connection login
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $dsn, $user = NULL, $pass = NULL, $options = array() )
|
||||
{
|
||||
if ( is_object( $dsn ) ) {
|
||||
$this->pdo = $dsn;
|
||||
$this->isConnected = TRUE;
|
||||
$this->setEncoding();
|
||||
$this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
|
||||
$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
|
||||
// make sure that the dsn at least contains the type
|
||||
$this->dsn = $this->getDatabaseType();
|
||||
} else {
|
||||
$this->dsn = $dsn;
|
||||
$this->connectInfo = array( 'pass' => $pass, 'user' => $user );
|
||||
if (is_array($options)) $this->connectInfo['options'] = $options;
|
||||
}
|
||||
|
||||
//PHP 5.3 PDO SQLite has a bug with large numbers:
|
||||
if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) {
|
||||
$this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
|
||||
} elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
|
||||
$this->max = 2147483647; //bindParam in pdo_cubrid also fails...
|
||||
} else {
|
||||
$this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets PDO in stringify fetch mode.
|
||||
* If set to TRUE, this method will make sure all data retrieved from
|
||||
* the database will be fetched as a string. Default: TRUE.
|
||||
*
|
||||
* To set it to FALSE...
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::getDatabaseAdapter()->getDatabase()->stringifyFetches( FALSE );
|
||||
* </code>
|
||||
*
|
||||
* Important!
|
||||
* Note, this method only works if you set the value BEFORE the connection
|
||||
* has been establish. Also, this setting ONLY works with SOME drivers.
|
||||
* It's up to the driver to honour this setting.
|
||||
*
|
||||
* @param boolean $bool
|
||||
*/
|
||||
public function stringifyFetches( $bool ) {
|
||||
$this->stringifyFetches = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best possible encoding for MySQL based on version data.
|
||||
* This method can be used to obtain the best character set parameters
|
||||
* possible for your database when constructing a table creation query
|
||||
* containing clauses like: CHARSET=... COLLATE=...
|
||||
* This is a MySQL-specific method and not part of the driver interface.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $charset_collate = $this->adapter->getDatabase()->getMysqlEncoding( TRUE );
|
||||
* </code>
|
||||
*
|
||||
* @param boolean $retCol pass TRUE to return both charset/collate
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function getMysqlEncoding( $retCol = FALSE )
|
||||
{
|
||||
if( $retCol )
|
||||
return array( 'charset' => $this->mysqlCharset, 'collate' => $this->mysqlCollate );
|
||||
return $this->mysqlCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to bind all parameters as strings.
|
||||
* If set to TRUE this will cause all integers to be bound as STRINGS.
|
||||
* This will NOT affect NULL values.
|
||||
*
|
||||
* @param boolean $yesNo pass TRUE to bind all parameters as strings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseStringOnlyBinding( $yesNo )
|
||||
{
|
||||
$this->flagUseStringOnlyBinding = (boolean) $yesNo;
|
||||
if ( $this->loggingEnabled && $this->logger && method_exists($this->logger,'setUseStringOnlyBinding')) {
|
||||
$this->logger->setUseStringOnlyBinding( $this->flagUseStringOnlyBinding );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum value to be bound as integer, normally
|
||||
* this value equals PHP's MAX INT constant, however sometimes
|
||||
* PDO driver bindings cannot bind large integers as integers.
|
||||
* This method allows you to manually set the max integer binding
|
||||
* value to manage portability/compatibility issues among different
|
||||
* PHP builds. This method will return the old value.
|
||||
*
|
||||
* @param integer $max maximum value for integer bindings
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function setMaxIntBind( $max )
|
||||
{
|
||||
if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' );
|
||||
$oldMax = $this->max;
|
||||
$this->max = $max;
|
||||
return $oldMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets initialization code to execute upon connecting.
|
||||
*
|
||||
* @param callable $code
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInitCode($code)
|
||||
{
|
||||
$this->initCode= $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database using PHP\PDO
|
||||
* functionality. If a connection has already been established this
|
||||
* method will simply return directly. This method also turns on
|
||||
* UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
|
||||
* PDO-FETCH-ASSOC.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if ( $this->isConnected ) return;
|
||||
try {
|
||||
$user = $this->connectInfo['user'];
|
||||
$pass = $this->connectInfo['pass'];
|
||||
$options = array();
|
||||
if (isset($this->connectInfo['options']) && is_array($this->connectInfo['options'])) {
|
||||
$options = $this->connectInfo['options'];
|
||||
}
|
||||
$this->pdo = new \PDO( $this->dsn, $user, $pass, $options );
|
||||
$this->setEncoding();
|
||||
$this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, $this->stringifyFetches );
|
||||
//cant pass these as argument to constructor, CUBRID driver does not understand...
|
||||
$this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
|
||||
$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
|
||||
$this->isConnected = TRUE;
|
||||
/* run initialisation query if any */
|
||||
if ( $this->initSQL !== NULL ) {
|
||||
$this->Execute( $this->initSQL );
|
||||
$this->initSQL = NULL;
|
||||
}
|
||||
if ( $this->initCode !== NULL ) {
|
||||
$code = $this->initCode;
|
||||
$code( $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION ) );
|
||||
}
|
||||
} catch ( \PDOException $exception ) {
|
||||
$matches = array();
|
||||
$dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
|
||||
throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly sets PDO instance into driver.
|
||||
* This method might improve performance, however since the driver does
|
||||
* not configure this instance terrible things may happen... only use
|
||||
* this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
|
||||
* you know your database server VERY WELL.
|
||||
*
|
||||
* - connected TRUE|FALSE (treat this instance as connected, default: TRUE)
|
||||
* - setEncoding TRUE|FALSE (let RedBeanPHP set encoding for you, default: TRUE)
|
||||
* - setAttributes TRUE|FALSE (let RedBeanPHP set attributes for you, default: TRUE)*
|
||||
* - setDSNString TRUE|FALSE (extract DSN string from PDO instance, default: TRUE)
|
||||
* - stringFetch TRUE|FALSE (whether you want to stringify fetches or not, default: TRUE)
|
||||
* - runInitCode TRUE|FALSE (run init code if any, default: TRUE)
|
||||
*
|
||||
* *attributes:
|
||||
* - RedBeanPHP will ask database driver to throw Exceptions on errors (recommended for compatibility)
|
||||
* - RedBeanPHP will ask database driver to use associative arrays when fetching (recommended for compatibility)
|
||||
*
|
||||
* @param PDO $pdo PDO instance
|
||||
* @param array $options Options to apply
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPDO( \PDO $pdo, $options = array() ) {
|
||||
$this->pdo = $pdo;
|
||||
|
||||
$connected = TRUE;
|
||||
$setEncoding = TRUE;
|
||||
$setAttributes = TRUE;
|
||||
$setDSNString = TRUE;
|
||||
$runInitCode = TRUE;
|
||||
$stringFetch = TRUE;
|
||||
|
||||
if ( isset($options['connected']) ) $connected = $options['connected'];
|
||||
if ( isset($options['setEncoding']) ) $setEncoding = $options['setEncoding'];
|
||||
if ( isset($options['setAttributes']) ) $setAttributes = $options['setAttributes'];
|
||||
if ( isset($options['setDSNString']) ) $setDSNString = $options['setDSNString'];
|
||||
if ( isset($options['runInitCode']) ) $runInitCode = $options['runInitCode'];
|
||||
if ( isset($options['stringFetch']) ) $stringFetch = $options['stringFetch'];
|
||||
|
||||
if ($connected) $this->connected = $connected;
|
||||
if ($setEncoding) $this->setEncoding();
|
||||
if ($setAttributes) {
|
||||
$this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
|
||||
$this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
|
||||
$this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, $stringFetch );
|
||||
}
|
||||
if ($runInitCode) {
|
||||
/* run initialisation query if any */
|
||||
if ( $this->initSQL !== NULL ) {
|
||||
$this->Execute( $this->initSQL );
|
||||
$this->initSQL = NULL;
|
||||
}
|
||||
if ( $this->initCode !== NULL ) {
|
||||
$code = $this->initCode;
|
||||
$code( $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION ) );
|
||||
}
|
||||
}
|
||||
if ($setDSNString) $this->dsn = $this->getDatabaseType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetAll
|
||||
*/
|
||||
public function GetAll( $sql, $bindings = array() )
|
||||
{
|
||||
$this->runQuery( $sql, $bindings );
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetAssocRow
|
||||
*/
|
||||
public function GetAssocRow( $sql, $bindings = array() )
|
||||
{
|
||||
$this->runQuery( $sql, $bindings, array(
|
||||
'fetchStyle' => \PDO::FETCH_ASSOC
|
||||
)
|
||||
);
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetCol
|
||||
*/
|
||||
public function GetCol( $sql, $bindings = array() )
|
||||
{
|
||||
$rows = $this->GetAll( $sql, $bindings );
|
||||
|
||||
if ( empty( $rows ) || !is_array( $rows ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$cols = array();
|
||||
foreach ( $rows as $row ) {
|
||||
$cols[] = reset( $row );
|
||||
}
|
||||
|
||||
return $cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetOne
|
||||
*/
|
||||
public function GetOne( $sql, $bindings = array() )
|
||||
{
|
||||
$arr = $this->GetAll( $sql, $bindings );
|
||||
|
||||
if ( empty( $arr[0] ) || !is_array( $arr[0] ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return reset( $arr[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for getOne().
|
||||
* Backward compatibility.
|
||||
*
|
||||
* @param string $sql SQL
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetCell( $sql, $bindings = array() )
|
||||
{
|
||||
return $this->GetOne( $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetRow
|
||||
*/
|
||||
public function GetRow( $sql, $bindings = array() )
|
||||
{
|
||||
$arr = $this->GetAll( $sql, $bindings );
|
||||
|
||||
if ( is_array( $arr ) && count( $arr ) ) {
|
||||
return reset( $arr );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::Excecute
|
||||
*/
|
||||
public function Execute( $sql, $bindings = array() )
|
||||
{
|
||||
$this->runQuery( $sql, $bindings );
|
||||
return $this->affectedRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetInsertID
|
||||
*/
|
||||
public function GetInsertID()
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
return (int) $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::GetCursor
|
||||
*/
|
||||
public function GetCursor( $sql, $bindings = array() )
|
||||
{
|
||||
$statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
|
||||
$cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
|
||||
return $cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::Affected_Rows
|
||||
*/
|
||||
public function Affected_Rows()
|
||||
{
|
||||
$this->connect();
|
||||
return (int) $this->affectedRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::setDebugMode
|
||||
*/
|
||||
public function setDebugMode( $tf, $logger = NULL )
|
||||
{
|
||||
$this->connect();
|
||||
$this->loggingEnabled = (bool) $tf;
|
||||
if ( $this->loggingEnabled and !$logger ) {
|
||||
$logger = new RDefault();
|
||||
}
|
||||
$this->setLogger( $logger );
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects Logger object.
|
||||
* Sets the logger instance you wish to use.
|
||||
*
|
||||
* This method is for more fine-grained control. Normally
|
||||
* you should use the facade to start the query debugger for
|
||||
* you. The facade will manage the object wirings necessary
|
||||
* to use the debugging functionality.
|
||||
*
|
||||
* Usage (through facade):
|
||||
*
|
||||
* <code>
|
||||
* R::debug( TRUE );
|
||||
* ...rest of program...
|
||||
* R::debug( FALSE );
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how to use the RedBeanPHP
|
||||
* query debugger through the facade.
|
||||
*
|
||||
* @param Logger $logger the logger instance to be used for logging
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLogger( Logger $logger )
|
||||
{
|
||||
$this->logger = $logger;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Logger object.
|
||||
* Returns the currently active Logger instance.
|
||||
*
|
||||
* @return Logger
|
||||
*/
|
||||
public function getLogger()
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::StartTrans
|
||||
*/
|
||||
public function StartTrans()
|
||||
{
|
||||
$this->connect();
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::CommitTrans
|
||||
*/
|
||||
public function CommitTrans()
|
||||
{
|
||||
$this->connect();
|
||||
$this->pdo->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Driver::FailTrans
|
||||
*/
|
||||
public function FailTrans()
|
||||
{
|
||||
$this->connect();
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of database driver for PDO.
|
||||
* Uses the PDO attribute DRIVER NAME to obtain the name of the
|
||||
* PDO driver. Use this method to identify the current PDO driver
|
||||
* used to provide access to the database. Example of a database
|
||||
* driver string:
|
||||
*
|
||||
* <code>
|
||||
* mysql
|
||||
* </code>
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* echo R::getDatabaseAdapter()->getDatabase()->getDatabaseType();
|
||||
* </code>
|
||||
*
|
||||
* The example above prints the current database driver string to
|
||||
* stdout.
|
||||
*
|
||||
* Note that this is a driver-specific method, not part of the
|
||||
* driver interface. This method might not be available in other
|
||||
* drivers since it relies on PDO.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseType()
|
||||
{
|
||||
$this->connect();
|
||||
return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version identifier string of the database client.
|
||||
* This method can be used to identify the currently installed
|
||||
* database client. Note that this method will also establish a connection
|
||||
* (because this is required to obtain the version information).
|
||||
*
|
||||
* Example of a version string:
|
||||
*
|
||||
* <code>
|
||||
* mysqlnd 5.0.12-dev - 20150407 - $Id: b5c5906d452ec590732a93b051f3827e02749b83 $
|
||||
* </code>
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* echo R::getDatabaseAdapter()->getDatabase()->getDatabaseVersion();
|
||||
* </code>
|
||||
*
|
||||
* The example above will print the version string to stdout.
|
||||
*
|
||||
* Note that this is a driver-specific method, not part of the
|
||||
* driver interface. This method might not be available in other
|
||||
* drivers since it relies on PDO.
|
||||
*
|
||||
* To obtain the database server version, use getDatabaseServerVersion()
|
||||
* instead.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDatabaseVersion()
|
||||
{
|
||||
$this->connect();
|
||||
return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying PHP PDO instance.
|
||||
* For some low-level database operations you'll need access to the PDO
|
||||
* object. Not that this method is only available in RPDO and other
|
||||
* PDO based database drivers for RedBeanPHP. Other drivers may not have
|
||||
* a method like this. The following example demonstrates how to obtain
|
||||
* a reference to the PDO instance from the facade:
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
|
||||
* </code>
|
||||
*
|
||||
* @return PDO
|
||||
*/
|
||||
public function getPDO()
|
||||
{
|
||||
$this->connect();
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database connection.
|
||||
* While database connections are closed automatically at the end of the PHP script,
|
||||
* closing database connections is generally recommended to improve performance.
|
||||
* Closing a database connection will immediately return the resources to PHP.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::setup( ... );
|
||||
* ... do stuff ...
|
||||
* R::close();
|
||||
* </code>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->pdo = NULL;
|
||||
$this->isConnected = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the current PDO instance is connected.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isConnected()
|
||||
{
|
||||
return $this->isConnected && $this->pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles logging, enables or disables logging.
|
||||
*
|
||||
* @param boolean $enable TRUE to enable logging
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setEnableLogging( $enable )
|
||||
{
|
||||
$this->loggingEnabled = (boolean) $enable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the query counter.
|
||||
* The query counter can be used to monitor the number
|
||||
* of database queries that have
|
||||
* been processed according to the database driver. You can use this
|
||||
* to monitor the number of queries required to render a page.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::resetQueryCount();
|
||||
* echo R::getQueryCount() . ' queries processed.';
|
||||
* </code>
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function resetCounter()
|
||||
{
|
||||
$this->queryCounter = 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of SQL queries processed.
|
||||
* This method returns the number of database queries that have
|
||||
* been processed according to the database driver. You can use this
|
||||
* to monitor the number of queries required to render a page.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* echo R::getQueryCount() . ' queries processed.';
|
||||
* </code>
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getQueryCount()
|
||||
{
|
||||
return $this->queryCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum value treated as integer parameter
|
||||
* binding.
|
||||
*
|
||||
* This method is mainly for testing purposes but it can help
|
||||
* you solve some issues relating to integer bindings.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getIntegerBindingMax()
|
||||
{
|
||||
return $this->max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query to be executed upon connecting to the database.
|
||||
* This method provides an opportunity to configure the connection
|
||||
* to a database through an SQL-based interface. Objects can provide
|
||||
* an SQL string to be executed upon establishing a connection to
|
||||
* the database. This has been used to solve issues with default
|
||||
* foreign key settings in SQLite3 for instance, see Github issues:
|
||||
* #545 and #548.
|
||||
*
|
||||
* @param string $sql SQL query to run upon connecting to database
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setInitQuery( $sql ) {
|
||||
$this->initSQL = $sql;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version string from the database server.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function DatabaseServerVersion() {
|
||||
return trim( strval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION) ) );
|
||||
}
|
||||
}
|
||||
467
vendor/gabordemooij/redbean/RedBeanPHP/DuplicationManager.php
vendored
Normal file
467
vendor/gabordemooij/redbean/RedBeanPHP/DuplicationManager.php
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\AssociationManager as AssociationManager;
|
||||
use RedBeanPHP\OODB as OODB;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
|
||||
/**
|
||||
* Duplication Manager
|
||||
* The Duplication Manager creates deep copies from beans, this means
|
||||
* it can duplicate an entire bean hierarchy. You can use this feature to
|
||||
* implement versioning for instance. Because duplication and exporting are
|
||||
* closely related this class is also used to export beans recursively
|
||||
* (i.e. we make a duplicate and then convert to array). This class allows
|
||||
* you to tune the duplication process by specifying filters determining
|
||||
* which relations to take into account and by specifying tables
|
||||
* (in which case no reflective queries have to be issued thus improving
|
||||
* performance). This class also hosts the Camelfy function used to
|
||||
* reformat the keys of an array, this method is publicly available and
|
||||
* used internally by exportAll().
|
||||
*
|
||||
* @file RedBeanPHP/DuplicationManager.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 DuplicationManager
|
||||
{
|
||||
/**
|
||||
* @var ToolBox
|
||||
*/
|
||||
protected $toolbox;
|
||||
|
||||
/**
|
||||
* @var AssociationManager
|
||||
*/
|
||||
protected $associationManager;
|
||||
|
||||
/**
|
||||
* @var OODB
|
||||
*/
|
||||
protected $redbean;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tables = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $columns = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $cacheTables = FALSE;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $copyMeta = FALSE;
|
||||
|
||||
/**
|
||||
* Copies the shared beans in a bean, i.e. all the sharedBean-lists.
|
||||
*
|
||||
* @param OODBBean $copy target bean to copy lists to
|
||||
* @param string $shared name of the shared list
|
||||
* @param array $beans array with shared beans to copy
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function copySharedBeans( OODBBean $copy, $shared, $beans )
|
||||
{
|
||||
$copy->$shared = array();
|
||||
|
||||
foreach ( $beans as $subBean ) {
|
||||
array_push( $copy->$shared, $subBean );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the own beans in a bean, i.e. all the ownBean-lists.
|
||||
* Each bean in the own-list belongs exclusively to its owner so
|
||||
* we need to invoke the duplicate method again to duplicate each bean here.
|
||||
*
|
||||
* @param OODBBean $copy target bean to copy lists to
|
||||
* @param string $owned name of the own list
|
||||
* @param array $beans array with shared beans to copy
|
||||
* @param array $trail array with former beans to detect recursion
|
||||
* @param boolean $preserveIDs TRUE means preserve IDs, for export only
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs )
|
||||
{
|
||||
$copy->$owned = array();
|
||||
foreach ( $beans as $subBean ) {
|
||||
array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of bean $bean and copies all primitive properties (not lists)
|
||||
* and the parents beans to the newly created bean. Also sets the ID of the bean
|
||||
* to 0.
|
||||
*
|
||||
* @param OODBBean $bean bean to copy
|
||||
*
|
||||
* @return OODBBean
|
||||
*/
|
||||
private function createCopy( OODBBean $bean )
|
||||
{
|
||||
$type = $bean->getMeta( 'type' );
|
||||
|
||||
$copy = $this->redbean->dispense( $type );
|
||||
$copy->setMeta( 'sys.dup-from-id', $bean->id );
|
||||
$copy->setMeta( 'sys.old-id', $bean->id );
|
||||
$copy->importFrom( $bean );
|
||||
if ($this->copyMeta) $copy->copyMetaFrom($bean);
|
||||
$copy->id = 0;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key from the bean type and its ID and determines if the bean
|
||||
* occurs in the trail, if not the bean will be added to the trail.
|
||||
* Returns TRUE if the bean occurs in the trail and FALSE otherwise.
|
||||
*
|
||||
* @param array $trail list of former beans
|
||||
* @param OODBBean $bean currently selected bean
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function inTrailOrAdd( &$trail, OODBBean $bean )
|
||||
{
|
||||
$type = $bean->getMeta( 'type' );
|
||||
$key = $type . $bean->getID();
|
||||
|
||||
if ( isset( $trail[$key] ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
$trail[$key] = $bean;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the type name of a bean this method returns the canonical names
|
||||
* of the own-list and the shared-list properties respectively.
|
||||
* Returns a list with two elements: name of the own-list, and name
|
||||
* of the shared list.
|
||||
*
|
||||
* @param string $typeName bean type name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getListNames( $typeName )
|
||||
{
|
||||
$owned = 'own' . ucfirst( $typeName );
|
||||
$shared = 'shared' . ucfirst( $typeName );
|
||||
|
||||
return array( $owned, $shared );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the bean has an own list based on
|
||||
* schema inspection from realtime schema or cache.
|
||||
*
|
||||
* @param string $type bean type to get list for
|
||||
* @param string $target type of list you want to detect
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function hasOwnList( $type, $target )
|
||||
{
|
||||
return isset( $this->columns[$target][$type . '_id'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the bea has a shared list based on
|
||||
* schema inspection from realtime schema or cache.
|
||||
*
|
||||
* @param string $type bean type to get list for
|
||||
* @param string $target type of list you are looking for
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function hasSharedList( $type, $target )
|
||||
{
|
||||
return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DuplicationManager::dup
|
||||
*
|
||||
* @param OODBBean $bean bean to be copied
|
||||
* @param array $trail trail to prevent infinite loops
|
||||
* @param boolean $preserveIDs preserve IDs
|
||||
*
|
||||
* @return OODBBean
|
||||
*/
|
||||
protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
|
||||
{
|
||||
if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean;
|
||||
|
||||
$type = $bean->getMeta( 'type' );
|
||||
|
||||
$copy = $this->createCopy( $bean );
|
||||
foreach ( $this->tables as $table ) {
|
||||
|
||||
if ( !empty( $this->filters ) ) {
|
||||
if ( !in_array( $table, $this->filters ) ) continue;
|
||||
}
|
||||
|
||||
list( $owned, $shared ) = $this->getListNames( $table );
|
||||
|
||||
if ( $this->hasSharedList( $type, $table ) ) {
|
||||
if ( $beans = $bean->$shared ) {
|
||||
$this->copySharedBeans( $copy, $shared, $beans );
|
||||
}
|
||||
} elseif ( $this->hasOwnList( $type, $table ) ) {
|
||||
if ( $beans = $bean->$owned ) {
|
||||
$this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs );
|
||||
}
|
||||
|
||||
$copy->setMeta( 'sys.shadow.' . $owned, NULL );
|
||||
}
|
||||
|
||||
$copy->setMeta( 'sys.shadow.' . $shared, NULL );
|
||||
}
|
||||
|
||||
$copy->id = ( $preserveIDs ) ? $bean->id : $copy->id;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively turns the keys of an array into
|
||||
* camelCase.
|
||||
*
|
||||
* @param array $array array to camelize
|
||||
* @param boolean $dolphinMode whether you want the exception for IDs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function camelfy( $array, $dolphinMode = FALSE ) {
|
||||
$newArray = array();
|
||||
foreach( $array as $key => $element ) {
|
||||
$newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){
|
||||
return strtoupper( $matches[1] );
|
||||
}, $key);
|
||||
|
||||
if ( $dolphinMode ) {
|
||||
$newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey );
|
||||
}
|
||||
|
||||
$newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element;
|
||||
}
|
||||
return $newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* For better performance you can pass the tables in an array to this method.
|
||||
* If the tables are available the duplication manager will not query them so
|
||||
* this might be beneficial for performance.
|
||||
*
|
||||
* This method allows two array formats:
|
||||
*
|
||||
* <code>
|
||||
* array( TABLE1, TABLE2 ... )
|
||||
* </code>
|
||||
*
|
||||
* or
|
||||
*
|
||||
* <code>
|
||||
* array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... )
|
||||
* </code>
|
||||
*
|
||||
* @param array $tables a table cache array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTables( $tables )
|
||||
{
|
||||
foreach ( $tables as $key => $value ) {
|
||||
if ( is_numeric( $key ) ) {
|
||||
$this->tables[] = $value;
|
||||
} else {
|
||||
$this->tables[] = $key;
|
||||
$this->columns[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->cacheTables = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a schema array for cache.
|
||||
* You can use the return value of this method as a cache,
|
||||
* store it in RAM or on disk and pass it to setTables later.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSchema()
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether you want the duplication manager to cache the database schema.
|
||||
* If this flag is set to TRUE the duplication manager will query the database schema
|
||||
* only once. Otherwise the duplicationmanager will, by default, query the schema
|
||||
* every time a duplication action is performed (dup()).
|
||||
*
|
||||
* @param boolean $yesNo TRUE to use caching, FALSE otherwise
|
||||
*/
|
||||
public function setCacheTables( $yesNo )
|
||||
{
|
||||
$this->cacheTables = $yesNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter array is an array with table names.
|
||||
* By setting a table filter you can make the duplication manager only take into account
|
||||
* certain bean types. Other bean types will be ignored when exporting or making a
|
||||
* deep copy. If no filters are set all types will be taking into account, this is
|
||||
* the default behavior.
|
||||
*
|
||||
* @param array $filters list of tables to be filtered
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFilters( $filters )
|
||||
{
|
||||
if ( !is_array( $filters ) ) {
|
||||
$filters = array( $filters );
|
||||
}
|
||||
|
||||
$this->filters = $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a copy of a bean. This method makes a deep copy
|
||||
* of the bean.The copy will have the following features.
|
||||
* - All beans in own-lists will be duplicated as well
|
||||
* - All references to shared beans will be copied but not the shared beans themselves
|
||||
* - All references to parent objects (_id fields) will be copied but not the parents themselves
|
||||
* In most cases this is the desired scenario for copying beans.
|
||||
* This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
|
||||
* (i.e. one that already has been processed) the ID of the bean will be returned.
|
||||
* This should not happen though.
|
||||
*
|
||||
* Note:
|
||||
* This function does a reflectional database query so it may be slow.
|
||||
*
|
||||
* Note:
|
||||
* this function actually passes the arguments to a protected function called
|
||||
* duplicate() that does all the work. This method takes care of creating a clone
|
||||
* of the bean to avoid the bean getting tainted (triggering saving when storing it).
|
||||
*
|
||||
* @param OODBBean $bean bean to be copied
|
||||
* @param array $trail for internal usage, pass array()
|
||||
* @param boolean $preserveIDs for internal usage
|
||||
*
|
||||
* @return OODBBean
|
||||
*/
|
||||
public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE )
|
||||
{
|
||||
if ( !count( $this->tables ) ) {
|
||||
$this->tables = $this->toolbox->getWriter()->getTables();
|
||||
}
|
||||
|
||||
if ( !count( $this->columns ) ) {
|
||||
foreach ( $this->tables as $table ) {
|
||||
$this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table );
|
||||
}
|
||||
}
|
||||
|
||||
$rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs );
|
||||
|
||||
if ( !$this->cacheTables ) {
|
||||
$this->tables = array();
|
||||
$this->columns = array();
|
||||
}
|
||||
|
||||
return $rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a collection of beans recursively.
|
||||
* This method will export an array of beans in the first argument to a
|
||||
* set of arrays. This can be used to send JSON or XML representations
|
||||
* of bean hierarchies to the client.
|
||||
*
|
||||
* For every bean in the array this method will export:
|
||||
*
|
||||
* - contents of the bean
|
||||
* - all own bean lists (recursively)
|
||||
* - all shared beans (but not THEIR own lists)
|
||||
*
|
||||
* If the second parameter is set to TRUE the parents of the beans in the
|
||||
* array will be exported as well (but not THEIR parents).
|
||||
*
|
||||
* The third parameter can be used to provide a white-list array
|
||||
* for filtering. This is an array of strings representing type names,
|
||||
* only the type names in the filter list will be exported.
|
||||
*
|
||||
* The fourth parameter can be used to change the keys of the resulting
|
||||
* export arrays. The default mode is 'snake case' but this leaves the
|
||||
* keys as-is, because 'snake' is the default case style used by
|
||||
* RedBeanPHP in the database. You can set this to 'camel' for
|
||||
* camel cased keys or 'dolphin' (same as camelcase but id will be
|
||||
* converted to ID instead of Id).
|
||||
*
|
||||
* @param array|OODBBean $beans beans to be exported
|
||||
* @param boolean $parents also export parents
|
||||
* @param array $filters only these types (whitelist)
|
||||
* @param string $caseStyle case style identifier
|
||||
* @param boolean $meta export meta data as well
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake', $meta = FALSE)
|
||||
{
|
||||
$array = array();
|
||||
if ( !is_array( $beans ) ) {
|
||||
$beans = array( $beans );
|
||||
}
|
||||
$this->copyMeta = $meta;
|
||||
foreach ( $beans as $bean ) {
|
||||
$this->setFilters( $filters );
|
||||
$duplicate = $this->dup( $bean, array(), TRUE );
|
||||
$array[] = $duplicate->export( $meta, $parents, FALSE, $filters );
|
||||
}
|
||||
if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array );
|
||||
if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, TRUE );
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
3354
vendor/gabordemooij/redbean/RedBeanPHP/Facade.php
vendored
Normal file
3354
vendor/gabordemooij/redbean/RedBeanPHP/Facade.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
560
vendor/gabordemooij/redbean/RedBeanPHP/Finder.php
vendored
Normal file
560
vendor/gabordemooij/redbean/RedBeanPHP/Finder.php
vendored
Normal file
@@ -0,0 +1,560 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
|
||||
/**
|
||||
* RedBeanPHP Finder.
|
||||
* Service class to find beans. For the most part this class
|
||||
* offers user friendly utility methods for interacting with the
|
||||
* OODB::find() method, which is rather complex. This class can be
|
||||
* used to find beans using plain old SQL queries.
|
||||
*
|
||||
* @file RedBeanPHP/Finder.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 Finder
|
||||
{
|
||||
/**
|
||||
* @var ToolBox
|
||||
*/
|
||||
protected $toolbox;
|
||||
|
||||
/**
|
||||
* @var OODB
|
||||
*/
|
||||
protected $redbean;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* The Finder requires a toolbox.
|
||||
*
|
||||
* @param ToolBox $toolbox
|
||||
*/
|
||||
public function __construct( ToolBox $toolbox )
|
||||
{
|
||||
$this->toolbox = $toolbox;
|
||||
$this->redbean = $toolbox->getRedBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom record-to-bean mapping function for findMulti.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $collection = R::findMulti( 'shop,product,price',
|
||||
* 'SELECT shop.*, product.*, price.* FROM shop
|
||||
* LEFT JOIN product ON product.shop_id = shop.id
|
||||
* LEFT JOIN price ON price.product_id = product.id', [], [
|
||||
* Finder::map( 'shop', 'product' ),
|
||||
* Finder::map( 'product', 'price' ),
|
||||
* ]);
|
||||
* </code>
|
||||
*
|
||||
* @param string $parentName name of the parent bean
|
||||
* @param string $childName name of the child bean
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function map($parentName,$childName) {
|
||||
return array(
|
||||
'a' => $parentName,
|
||||
'b' => $childName,
|
||||
'matcher' => function( $parent, $child ) use ( $parentName, $childName ) {
|
||||
$propertyName = 'own' . ucfirst( $childName );
|
||||
if (!isset($parent[$propertyName])) {
|
||||
$parent->noLoad()->{$propertyName} = array();
|
||||
}
|
||||
$property = "{$parentName}ID";
|
||||
return ( $child->$property == $parent->id );
|
||||
},
|
||||
'do' => function( $parent, $child ) use ( $childName ) {
|
||||
$list = 'own'.ucfirst( $childName ).'List';
|
||||
$parent->noLoad()->{$list}[$child->id] = $child;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom record-to-bean mapping function for findMulti.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $collection = R::findMulti( 'book,book_tag,tag',
|
||||
* 'SELECT book.*, book_tag.*, tag.* FROM book
|
||||
* LEFT JOIN book_tag ON book_tag.book_id = book.id
|
||||
* LEFT JOIN tag ON book_tag.tag_id = tag.id', [], [
|
||||
* Finder::nmMap( 'book', 'tag' ),
|
||||
* ]);
|
||||
* </code>
|
||||
*
|
||||
* @param string $parentName name of the parent bean
|
||||
* @param string $childName name of the child bean
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function nmMap( $parentName, $childName )
|
||||
{
|
||||
$types = array($parentName, $childName);
|
||||
sort( $types );
|
||||
$link = implode( '_', $types );
|
||||
return array(
|
||||
'a' => $parentName,
|
||||
'b' => $childName,
|
||||
'matcher' => function( $parent, $child, $beans ) use ( $parentName, $childName, $link ) {
|
||||
$propertyName = 'shared' . ucfirst( $childName );
|
||||
if (!isset($parent[$propertyName])) {
|
||||
$parent->noLoad()->{$propertyName} = array();
|
||||
}
|
||||
foreach( $beans[$link] as $linkBean ) {
|
||||
if ( $linkBean["{$parentName}ID"] == $parent->id && $linkBean["{$childName}ID"] == $child->id ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
'do' => function( $parent, $child ) use ( $childName ) {
|
||||
$list = 'shared'.ucfirst( $childName ).'List';
|
||||
$parent->noLoad()->{$list}[$child->id] = $child;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finder::onMap() -> One-to-N mapping.
|
||||
* A custom record-to-bean mapping function for findMulti.
|
||||
* Opposite of Finder::map(). Maps child beans to parents.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $collection = R::findMulti( 'shop,product',
|
||||
* 'SELECT shop.*, product.* FROM shop
|
||||
* LEFT JOIN product ON product.shop_id = shop.id',
|
||||
* [], [
|
||||
* Finder::onmap( 'product', 'shop' ),
|
||||
* ]);
|
||||
* </code>
|
||||
*
|
||||
* Can also be used for instance to attach related beans
|
||||
* in one-go to save some queries:
|
||||
*
|
||||
* Given $users that have a country_id:
|
||||
*
|
||||
* <code>
|
||||
* $all = R::findMulti('country',
|
||||
* R::genSlots( $users,
|
||||
* 'SELECT country.* FROM country WHERE id IN ( %s )' ),
|
||||
* array_column( $users, 'country_id' ),
|
||||
* [Finder::onmap('country', $gebruikers)]
|
||||
* );
|
||||
* </code>
|
||||
*
|
||||
* For your convenience, an even shorter notation has been added:
|
||||
*
|
||||
* $countries = R::loadJoined( $users, 'country' );
|
||||
*
|
||||
* @param string $parentName name of the parent bean
|
||||
* @param string|array $childName name of the child bean
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function onMap($parentName,$childNameOrBeans) {
|
||||
return array(
|
||||
'a' => $parentName,
|
||||
'b' => $childNameOrBeans,
|
||||
'matcher' => array( $parentName, "{$parentName}_id" ),
|
||||
'do' => 'match'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a bean using a type and a where clause (SQL).
|
||||
* As with most Query tools in RedBean you can provide values to
|
||||
* be inserted in the SQL statement by populating the value
|
||||
* array parameter; you can either use the question mark notation
|
||||
* or the slot-notation (:keyname).
|
||||
*
|
||||
* @param string $type type the type of bean you are looking for
|
||||
* @param string $sql 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 find( $type, $sql = NULL, $bindings = array() )
|
||||
{
|
||||
if ( !is_array( $bindings ) ) {
|
||||
throw new RedException(
|
||||
'Expected array, ' . gettype( $bindings ) . ' given.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->redbean->find( $type, array(), $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like find() but also exports the beans as an array.
|
||||
* This method will perform a find-operation. For every bean
|
||||
* in the result collection this method will call the export() method.
|
||||
* This method returns an array containing the array representations
|
||||
* of every bean in the result set.
|
||||
*
|
||||
* @see Finder::find
|
||||
*
|
||||
* @param string $type type the type of bean you are looking for
|
||||
* @param string $sql 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 findAndExport( $type, $sql = NULL, $bindings = array() )
|
||||
{
|
||||
$arr = array();
|
||||
foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) {
|
||||
$arr[] = $item->export();
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like find() but returns just one bean instead of an array of beans.
|
||||
* This method will return only the first bean of the array.
|
||||
* If no beans are found, this method will return NULL.
|
||||
*
|
||||
* @see Finder::find
|
||||
*
|
||||
* @param string $type type the type of bean you are looking for
|
||||
* @param string $sql 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 OODBBean|NULL
|
||||
*/
|
||||
public function findOne( $type, $sql = NULL, $bindings = array() )
|
||||
{
|
||||
$sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
|
||||
|
||||
$items = $this->find( $type, $sql, $bindings );
|
||||
|
||||
if ( empty($items) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return reset( $items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like find() but returns the last bean of the result array.
|
||||
* Opposite of Finder::findLast().
|
||||
* If no beans are found, this method will return NULL.
|
||||
*
|
||||
* @see Finder::find
|
||||
*
|
||||
* @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 OODBBean|NULL
|
||||
*/
|
||||
public function findLast( $type, $sql = NULL, $bindings = array() )
|
||||
{
|
||||
$items = $this->find( $type, $sql, $bindings );
|
||||
|
||||
if ( empty($items) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return end( $items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find beans of a certain type,
|
||||
* if no beans are found, it dispenses a bean of that type.
|
||||
* Note that this function always returns an array.
|
||||
*
|
||||
* @see Finder::find
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a BeanCollection using the repository.
|
||||
* A bean collection can be used to retrieve one bean at a time using
|
||||
* cursors - this is useful for processing large datasets. A bean collection
|
||||
* will not load all beans into memory all at once, just one at a time.
|
||||
*
|
||||
* @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 BeanCollection
|
||||
*/
|
||||
public function findCollection( $type, $sql, $bindings = array() )
|
||||
{
|
||||
return $this->redbean->findCollection( $type, $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a bean.
|
||||
* Tries to find a bean with certain properties specified in the second
|
||||
* parameter ($like). If the bean is found, it will be returned.
|
||||
* If multiple beans are found, only the first will be returned.
|
||||
* If no beans match the criteria, a new bean will be dispensed,
|
||||
* the criteria will be imported as properties and this new bean
|
||||
* will be stored and returned.
|
||||
*
|
||||
* Format of criteria set: property => value
|
||||
* The criteria set also supports OR-conditions: property => array( value1, orValue2 )
|
||||
*
|
||||
* @param string $type type of bean to search for
|
||||
* @param array $like criteria set describing bean to search for
|
||||
* @param boolean $hasBeenCreated set to TRUE if bean has been created
|
||||
*
|
||||
* @return OODBBean
|
||||
*/
|
||||
public function findOrCreate( $type, $like = array(), $sql = '', &$hasBeenCreated = false )
|
||||
{
|
||||
$sql = $this->toolbox->getWriter()->glueLimitOne( $sql );
|
||||
$beans = $this->findLike( $type, $like, $sql );
|
||||
if ( count( $beans ) ) {
|
||||
$bean = reset( $beans );
|
||||
$hasBeenCreated = false;
|
||||
return $bean;
|
||||
}
|
||||
|
||||
$bean = $this->redbean->dispense( $type );
|
||||
$bean->import( $like );
|
||||
$this->redbean->store( $bean );
|
||||
$hasBeenCreated = true;
|
||||
return $bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds beans by its type and a certain criteria set.
|
||||
*
|
||||
* Format of criteria set: property => value
|
||||
* The criteria set also supports OR-conditions: property => array( value1, orValue2 )
|
||||
*
|
||||
* If the additional SQL is a condition, this condition will be glued to the rest
|
||||
* of the query using an AND operator. Note that this is as far as this method
|
||||
* can go, there is no way to glue additional SQL using an OR-condition.
|
||||
* This method provides access to an underlying mechanism in the RedBeanPHP architecture
|
||||
* to find beans using criteria sets. However, please do not use this method
|
||||
* for complex queries, use plain SQL instead ( the regular find method ) as it is
|
||||
* more suitable for the job. This method is
|
||||
* meant for basic search-by-example operations.
|
||||
*
|
||||
* @param string $type type of bean to search for
|
||||
* @param array $conditions criteria set describing the bean to search for
|
||||
* @param string $sql additional SQL (for sorting)
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findLike( $type, $conditions = array(), $sql = '', $bindings = array() )
|
||||
{
|
||||
return $this->redbean->find( $type, $conditions, $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* <code>
|
||||
* $stuff = $finder->findMulti('movie,review', '
|
||||
* SELECT movie.*, review.* FROM movie
|
||||
* LEFT JOIN review ON review.movie_id = movie.id');
|
||||
* </code>
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* <code>
|
||||
* array(
|
||||
* 'a' => TYPE A
|
||||
* 'b' => TYPE B OR BEANS
|
||||
* 'matcher' =>
|
||||
* MATCHING FUNCTION ACCEPTING A, B and ALL BEANS
|
||||
* OR ARRAY
|
||||
* WITH FIELD on B that should match with FIELD on A
|
||||
* AND FIELD on A that should match with FIELD on B
|
||||
* OR TRUE
|
||||
* TO JUST PERFORM THE DO-FUNCTION ON EVERY A-BEAN
|
||||
*
|
||||
* 'do' => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS
|
||||
* (ONLY IF MATCHER IS ALSO A FUNCTION)
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* <code>
|
||||
* 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'.
|
||||
* }
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @note the performance of this function is poor, if you deal with large number of records
|
||||
* please use plain SQL instead. This function has been added as a bridge between plain SQL
|
||||
* and bean oriented approaches but it is really on the edge of both worlds. You can safely
|
||||
* use this function to load additional records as beans in paginated context, let's say
|
||||
* 50-250 records. Anything above that will gradually perform worse. RedBeanPHP was never
|
||||
* intended to replace SQL but offer tooling to integrate SQL with object oriented
|
||||
* designs. If you have come to this function, you have reached the final border between
|
||||
* SQL-oriented design and OOP. Anything after this will be just as good as custom mapping
|
||||
* or plain old database querying. I recommend the latter.
|
||||
*
|
||||
* @param string|array $types a list of types (either array or comma separated string)
|
||||
* @param string|array $sql optional, 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 = NULL, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' )
|
||||
{
|
||||
if ( !is_array( $types ) ) $types = array_map( 'trim', explode( ',', $types ) );
|
||||
if ( is_null( $sql ) ) {
|
||||
$beans = array();
|
||||
foreach( $types as $type ) $beans[$type] = $this->redbean->find( $type );
|
||||
} else {
|
||||
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 ) {
|
||||
$regex = "#( (`?{$type}`?)\.\*)#";
|
||||
if ( preg_match( $regex, $sql, $matches ) ) {
|
||||
$pattern = $matches[1];
|
||||
$table = $matches[2];
|
||||
$newSelectorArray = array();
|
||||
$columns = $writer->getColumns( $type );
|
||||
foreach( $columns as $column => $definition ) {
|
||||
$newSelectorArray[] = sprintf( $queryTemplate, $table, $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'];
|
||||
if (is_array($b)) {
|
||||
$firstBean = reset($b);
|
||||
$type = $firstBean->getMeta('type');
|
||||
$beans[$type] = $b;
|
||||
$b = $type;
|
||||
}
|
||||
$matcher = $remapping['matcher'];
|
||||
if (is_callable($matcher) || $matcher === TRUE) {
|
||||
$do = $remapping['do'];
|
||||
foreach( $beans[$a] as $bean ) {
|
||||
if ( $matcher === TRUE ) {
|
||||
$do( $bean, $beans[$b], $beans, $remapping );
|
||||
continue;
|
||||
}
|
||||
foreach( $beans[$b] as $putBean ) {
|
||||
if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list($field1, $field2) = $matcher;
|
||||
foreach( $beans[$b] as $key => $bean ) {
|
||||
$beans[$b][$key]->{$field1} = $beans[$a][$bean->{$field2}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $beans;
|
||||
}
|
||||
}
|
||||
78
vendor/gabordemooij/redbean/RedBeanPHP/Functions.php
vendored
Normal file
78
vendor/gabordemooij/redbean/RedBeanPHP/Functions.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Support functions for RedBeanPHP.
|
||||
* Additional convenience shortcut functions for RedBeanPHP.
|
||||
*
|
||||
* @file RedBeanPHP/Functions.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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convenience function for ENUM short syntax in queries.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] );
|
||||
* </code>
|
||||
*
|
||||
* If a function called EID() already exists you'll have to write this
|
||||
* wrapper yourself ;)
|
||||
*
|
||||
* @param string $enumName enum code as you would pass to R::enum()
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
if (!function_exists('EID')) {
|
||||
|
||||
function EID($enumName)
|
||||
{
|
||||
return \RedBeanPHP\Facade::enum( $enumName )->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the result of R::dump() to the screen using
|
||||
* print_r.
|
||||
*
|
||||
* @param mixed $data data to dump
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
if ( !function_exists( 'dmp' ) ) {
|
||||
|
||||
function dmp( $list )
|
||||
{
|
||||
print_r( \RedBeanPHP\Facade::dump( $list ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function alias for R::genSlots().
|
||||
*/
|
||||
if ( !function_exists( 'genslots' ) ) {
|
||||
|
||||
function genslots( $slots, $tpl = NULL )
|
||||
{
|
||||
return \RedBeanPHP\Facade::genSlots( $slots, $tpl );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function alias for R::flat().
|
||||
*/
|
||||
if ( !function_exists( 'array_flatten' ) ) {
|
||||
|
||||
function array_flatten( $array )
|
||||
{
|
||||
return \RedBeanPHP\Facade::flat( $array );
|
||||
}
|
||||
}
|
||||
182
vendor/gabordemooij/redbean/RedBeanPHP/LabelMaker.php
vendored
Normal file
182
vendor/gabordemooij/redbean/RedBeanPHP/LabelMaker.php
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Label Maker.
|
||||
* Makes so-called label beans.
|
||||
* A label is a bean with only an id, type and name property.
|
||||
* Labels can be used to create simple entities like categories, tags or enums.
|
||||
* This service class provides convenience methods to deal with this kind of
|
||||
* beans.
|
||||
*
|
||||
* @file RedBeanPHP/LabelMaker.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 LabelMaker
|
||||
{
|
||||
/**
|
||||
* @var ToolBox
|
||||
*/
|
||||
protected $toolbox;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ToolBox $toolbox
|
||||
*/
|
||||
public function __construct( ToolBox $toolbox )
|
||||
{
|
||||
$this->toolbox = $toolbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* A label is a bean with only an id, type and name property.
|
||||
* This function will dispense beans for all entries in the array. The
|
||||
* values of the array will be assigned to the name property of each
|
||||
* individual bean.
|
||||
*
|
||||
* <code>
|
||||
* $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] );
|
||||
* </code>
|
||||
*
|
||||
* @param string $type type of beans you would like to have
|
||||
* @param array $labels list of labels, names for each bean
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function dispenseLabels( $type, $labels )
|
||||
{
|
||||
$labelBeans = array();
|
||||
foreach ( $labels as $label ) {
|
||||
$labelBean = $this->toolbox->getRedBean()->dispense( $type );
|
||||
$labelBean->name = $label;
|
||||
$labelBeans[] = $labelBean;
|
||||
}
|
||||
|
||||
return $labelBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers labels from beans. This function loops through the beans,
|
||||
* collects the value of the name property for each individual bean
|
||||
* and stores the names in a new array. The array then gets sorted using the
|
||||
* default sort function of PHP (sort).
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $o1->name = 'hamburger';
|
||||
* $o2->name = 'pizza';
|
||||
* implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza
|
||||
* </code>
|
||||
*
|
||||
* Note that the return value is an array of strings, not beans.
|
||||
*
|
||||
* @param array $beans list of beans to loop through
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function gatherLabels( $beans )
|
||||
{
|
||||
$labels = array();
|
||||
|
||||
foreach ( $beans as $bean ) {
|
||||
$labels[] = $bean->name;
|
||||
}
|
||||
|
||||
sort( $labels );
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an ENUM from the database and creates it if necessary.
|
||||
* An ENUM has the following format:
|
||||
*
|
||||
* <code>
|
||||
* ENUM:VALUE
|
||||
* </code>
|
||||
*
|
||||
* If you pass 'ENUM' only, this method will return an array of its
|
||||
* values:
|
||||
*
|
||||
* <code>
|
||||
* implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA'
|
||||
* </code>
|
||||
*
|
||||
* If you pass 'ENUM:VALUE' this method will return the specified enum bean
|
||||
* and create it in the database if it does not exist yet:
|
||||
*
|
||||
* <code>
|
||||
* $bananaFlavour = R::enum( 'flavour:banana' );
|
||||
* $bananaFlavour->name;
|
||||
* </code>
|
||||
*
|
||||
* So you can use this method to set an ENUM value in a bean:
|
||||
*
|
||||
* <code>
|
||||
* $shake->flavour = R::enum( 'flavour:banana' );
|
||||
* </code>
|
||||
*
|
||||
* the property flavour now contains the enum bean, a parent bean.
|
||||
* In the database, flavour_id will point to the flavour record with name 'banana'.
|
||||
*
|
||||
* @param string $enum ENUM specification for label
|
||||
*
|
||||
* @return array|OODBBean
|
||||
*/
|
||||
public function enum( $enum )
|
||||
{
|
||||
$oodb = $this->toolbox->getRedBean();
|
||||
|
||||
if ( strpos( $enum, ':' ) === FALSE ) {
|
||||
$type = $enum;
|
||||
$value = FALSE;
|
||||
} else {
|
||||
list( $type, $value ) = explode( ':', $enum );
|
||||
$value = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* We use simply find here, we could use inspect() in fluid mode etc,
|
||||
* but this would be useless. At first sight it looks clean, you could even
|
||||
* bake this into find(), however, find not only has to deal with the primary
|
||||
* search type, people can also include references in the SQL part, so avoiding
|
||||
* find failures does not matter, this is still the quickest way making use
|
||||
* of existing functionality.
|
||||
*
|
||||
* @note There seems to be a bug in XDebug v2.3.2 causing suppressed
|
||||
* exceptions like these to surface anyway, to prevent this use:
|
||||
*
|
||||
* "xdebug.default_enable = 0"
|
||||
*
|
||||
* Also see Github Issue #464
|
||||
*/
|
||||
$values = $oodb->find( $type );
|
||||
|
||||
if ( $value === FALSE ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
foreach( $values as $enumItem ) {
|
||||
if ( $enumItem->name === $value ) return $enumItem;
|
||||
}
|
||||
|
||||
$newEnumItems = $this->dispenseLabels( $type, array( $value ) );
|
||||
$newEnumItem = reset( $newEnumItems );
|
||||
|
||||
$oodb->store( $newEnumItem );
|
||||
|
||||
return $newEnumItem;
|
||||
}
|
||||
}
|
||||
35
vendor/gabordemooij/redbean/RedBeanPHP/Logger.php
vendored
Normal file
35
vendor/gabordemooij/redbean/RedBeanPHP/Logger.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* RedBean Logging interface.
|
||||
* Provides a uniform and convenient logging
|
||||
* interface throughout RedBeanPHP.
|
||||
*
|
||||
* @file RedBean/Logging.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.
|
||||
*/
|
||||
interface Logger
|
||||
{
|
||||
/**
|
||||
* A logger (for PDO or OCI driver) needs to implement the log method.
|
||||
* The log method will receive logging data. Note that the number of parameters is 0, this means
|
||||
* all parameters are optional and the number may vary. This way the logger can be used in a very
|
||||
* flexible way. Sometimes the logger is used to log a simple error message and in other
|
||||
* situations sql and bindings are passed.
|
||||
* The log method should be able to accept all kinds of parameters and data by using
|
||||
* functions like func_num_args/func_get_args.
|
||||
*
|
||||
* @param string $message, ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log();
|
||||
}
|
||||
133
vendor/gabordemooij/redbean/RedBeanPHP/Logger/RDefault.php
vendored
Normal file
133
vendor/gabordemooij/redbean/RedBeanPHP/Logger/RDefault.php
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Logger;
|
||||
|
||||
use RedBeanPHP\Logger as Logger;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
|
||||
/**
|
||||
* Logger. Provides a basic logging function for RedBeanPHP.
|
||||
*
|
||||
* @file RedBeanPHP/Logger.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* copyright (c) G.J.G.T. (Gabor) de Mooij
|
||||
* This source file is subject to the BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class RDefault implements Logger
|
||||
{
|
||||
/**
|
||||
* Logger modes
|
||||
*/
|
||||
const C_LOGGER_ECHO = 0;
|
||||
const C_LOGGER_ARRAY = 1;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $mode = 0;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $logs = array();
|
||||
|
||||
/**
|
||||
* Default logger method logging to STDOUT.
|
||||
* This is the default/reference implementation of a logger.
|
||||
* This method will write the message value to STDOUT (screen) unless
|
||||
* you have changed the mode of operation to C_LOGGER_ARRAY.
|
||||
*
|
||||
* @param $message (optional) message to log (might also be data or output)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log()
|
||||
{
|
||||
if ( func_num_args() < 1 ) return;
|
||||
|
||||
foreach ( func_get_args() as $argument ) {
|
||||
if ( is_array( $argument ) ) {
|
||||
$log = var_export( $argument, TRUE );
|
||||
if ( $this->mode === self::C_LOGGER_ECHO ) {
|
||||
echo $log;
|
||||
} else {
|
||||
$this->logs[] = $log;
|
||||
}
|
||||
} else {
|
||||
if ( $this->mode === self::C_LOGGER_ECHO ) {
|
||||
echo $argument;
|
||||
} else {
|
||||
$this->logs[] = $argument;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->mode === self::C_LOGGER_ECHO ) echo "<br>" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal log array.
|
||||
* The internal log array is where all log messages are stored.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLogs()
|
||||
{
|
||||
return $this->logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the internal log array, removing all
|
||||
* previously stored entries.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->logs = array();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a logging mode.
|
||||
* There are several options available.
|
||||
*
|
||||
* * C_LOGGER_ARRAY - log silently, stores entries in internal log array only
|
||||
* * C_LOGGER_ECHO - also forward log messages directly to STDOUT
|
||||
*
|
||||
* @param integer $mode mode of operation for logging object
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setMode( $mode )
|
||||
{
|
||||
if ($mode !== self::C_LOGGER_ARRAY && $mode !== self::C_LOGGER_ECHO ) {
|
||||
throw new RedException( 'Invalid mode selected for logger, use C_LOGGER_ARRAY or C_LOGGER_ECHO.' );
|
||||
}
|
||||
$this->mode = $mode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for all log entries in internal log array
|
||||
* for $needle and returns those entries.
|
||||
* This method will return an array containing all matches for your
|
||||
* search query.
|
||||
*
|
||||
* @param string $needle phrase to look for in internal log array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function grep( $needle )
|
||||
{
|
||||
$found = array();
|
||||
foreach( $this->logs as $logEntry ) {
|
||||
if ( strpos( $logEntry, $needle ) !== FALSE ) $found[] = $logEntry;
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
270
vendor/gabordemooij/redbean/RedBeanPHP/Logger/RDefault/Debug.php
vendored
Normal file
270
vendor/gabordemooij/redbean/RedBeanPHP/Logger/RDefault/Debug.php
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Logger\RDefault;
|
||||
|
||||
use RedBeanPHP\Logger as Logger;
|
||||
use RedBeanPHP\Logger\RDefault as RDefault;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
|
||||
/**
|
||||
* Debug logger.
|
||||
* A special logger for debugging purposes.
|
||||
* Provides debugging logging functions for RedBeanPHP.
|
||||
*
|
||||
* @file RedBeanPHP/Logger/RDefault/Debug.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* copyright (c) G.J.G.T. (Gabor) de Mooij
|
||||
* This source file is subject to the BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Debug extends RDefault implements Logger
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $strLen = 40;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $noCLI = FALSE;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $flagUseStringOnlyBinding = FALSE;
|
||||
|
||||
/**
|
||||
* Toggles CLI override. By default debugging functions will
|
||||
* output differently based on PHP_SAPI values. This function
|
||||
* allows you to override the PHP_SAPI setting. If you set
|
||||
* this to TRUE, CLI output will be supressed in favour of
|
||||
* HTML output. So, to get HTML on the command line use
|
||||
* setOverrideCLIOutput( TRUE ).
|
||||
*
|
||||
* @param boolean $yesNo CLI-override setting flag
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setOverrideCLIOutput( $yesNo )
|
||||
{
|
||||
self::$noCLI = $yesNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a query for logging with all bindings / params filled
|
||||
* in.
|
||||
*
|
||||
* @param string $newSql the query
|
||||
* @param array $newBindings the bindings to process (key-value pairs)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function writeQuery( $newSql, $newBindings )
|
||||
{
|
||||
//avoid str_replace collisions: slot1 and slot10 (issue 407).
|
||||
uksort( $newBindings, function( $a, $b ) {
|
||||
return ( strlen( $b ) - strlen( $a ) );
|
||||
} );
|
||||
|
||||
$newStr = $newSql;
|
||||
foreach( $newBindings as $slot => $value ) {
|
||||
if ( strpos( $slot, ':' ) === 0 ) {
|
||||
$newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr );
|
||||
}
|
||||
}
|
||||
return $newStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in a value of a binding and truncates the
|
||||
* resulting string if necessary.
|
||||
*
|
||||
* @param mixed $value bound value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function fillInValue( $value )
|
||||
{
|
||||
if ( is_array( $value ) && count( $value ) == 2 ) {
|
||||
$paramType = end( $value );
|
||||
$value = reset( $value );
|
||||
} else {
|
||||
$paramType = NULL;
|
||||
}
|
||||
|
||||
if ( is_null( $value ) ) $value = 'NULL';
|
||||
|
||||
if ( $this->flagUseStringOnlyBinding ) $paramType = \PDO::PARAM_STR;
|
||||
|
||||
if ( $paramType != \PDO::PARAM_INT && $paramType != \PDO::PARAM_STR ) {
|
||||
if ( \RedBeanPHP\QueryWriter\AQueryWriter::canBeTreatedAsInt( $value ) || $value === 'NULL') {
|
||||
$paramType = \PDO::PARAM_INT;
|
||||
} else {
|
||||
$paramType = \PDO::PARAM_STR;
|
||||
}
|
||||
}
|
||||
|
||||
if ( strlen( $value ) > ( $this->strLen ) ) {
|
||||
$value = substr( $value, 0, ( $this->strLen ) ).'... ';
|
||||
}
|
||||
|
||||
if ($paramType === \PDO::PARAM_STR) {
|
||||
$value = '\''.$value.'\'';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependending on the current mode of operation,
|
||||
* this method will either log and output to STDIN or
|
||||
* just log.
|
||||
*
|
||||
* Depending on the value of constant PHP_SAPI this function
|
||||
* will format output for console or HTML.
|
||||
*
|
||||
* @param string $str string to log or output and log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function output( $str )
|
||||
{
|
||||
$this->logs[] = $str;
|
||||
if ( !$this->mode ) {
|
||||
$highlight = FALSE;
|
||||
/* just a quick heuritsic to highlight schema changes */
|
||||
if ( strpos( $str, 'CREATE' ) === 0
|
||||
|| strpos( $str, 'ALTER' ) === 0
|
||||
|| strpos( $str, 'DROP' ) === 0) {
|
||||
$highlight = TRUE;
|
||||
}
|
||||
if (PHP_SAPI === 'cli' && !self::$noCLI) {
|
||||
if ($highlight) echo "\e[91m";
|
||||
echo $str, PHP_EOL;
|
||||
echo "\e[39m";
|
||||
} else {
|
||||
if ($highlight) {
|
||||
echo "<b style=\"color:red\">{$str}</b>";
|
||||
} else {
|
||||
echo $str;
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the slots in an SQL string.
|
||||
* Replaces question mark slots with :slot1 :slot2 etc.
|
||||
*
|
||||
* @param string $sql sql to normalize
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeSlots( $sql )
|
||||
{
|
||||
$newSql = $sql;
|
||||
$i = 0;
|
||||
while(strpos($newSql, '?') !== FALSE ){
|
||||
$pos = strpos( $newSql, '?' );
|
||||
$slot = ':slot'.$i;
|
||||
$begin = substr( $newSql, 0, $pos );
|
||||
$end = substr( $newSql, $pos+1 );
|
||||
if (PHP_SAPI === 'cli' && !self::$noCLI) {
|
||||
$newSql = "{$begin}\e[32m{$slot}\e[39m{$end}";
|
||||
} else {
|
||||
$newSql = "{$begin}<b style=\"color:green\">$slot</b>{$end}";
|
||||
}
|
||||
$i ++;
|
||||
}
|
||||
return $newSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the bindings.
|
||||
* Replaces numeric binding keys with :slot1 :slot2 etc.
|
||||
*
|
||||
* @param array $bindings bindings to normalize
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function normalizeBindings( $bindings )
|
||||
{
|
||||
$i = 0;
|
||||
$newBindings = array();
|
||||
foreach( $bindings as $key => $value ) {
|
||||
if ( is_numeric($key) ) {
|
||||
$newKey = ':slot'.$i;
|
||||
$newBindings[$newKey] = $value;
|
||||
$i++;
|
||||
} else {
|
||||
$newBindings[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $newBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger method.
|
||||
*
|
||||
* Takes a number of arguments tries to create
|
||||
* a proper debug log based on the available data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log()
|
||||
{
|
||||
if ( func_num_args() < 1 ) return;
|
||||
|
||||
$sql = func_get_arg( 0 );
|
||||
|
||||
if ( func_num_args() < 2) {
|
||||
$bindings = array();
|
||||
} else {
|
||||
$bindings = func_get_arg( 1 );
|
||||
}
|
||||
|
||||
if ( !is_array( $bindings ) ) {
|
||||
return $this->output( $sql );
|
||||
}
|
||||
|
||||
$newSql = $this->normalizeSlots( $sql );
|
||||
$newBindings = $this->normalizeBindings( $bindings );
|
||||
$newStr = $this->writeQuery( $newSql, $newBindings );
|
||||
$this->output( $newStr );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max string length for the parameter output in
|
||||
* SQL queries. Set this value to a reasonable number to
|
||||
* keep you SQL queries readable.
|
||||
*
|
||||
* @param integer $len string length
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setParamStringLength( $len = 20 )
|
||||
{
|
||||
$this->strLen = max(0, $len);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to bind all parameters as strings.
|
||||
* If set to TRUE this will cause all integers to be bound as STRINGS.
|
||||
* This will NOT affect NULL values.
|
||||
*
|
||||
* @param boolean $yesNo pass TRUE to bind all parameters as strings.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setUseStringOnlyBinding( $yesNo = false )
|
||||
{
|
||||
$this->flagUseStringOnlyBinding = (boolean) $yesNo;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
596
vendor/gabordemooij/redbean/RedBeanPHP/OODB.php
vendored
Normal file
596
vendor/gabordemooij/redbean/RedBeanPHP/OODB.php
vendored
Normal file
@@ -0,0 +1,596 @@
|
||||
<?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.
|
||||
* Most of the time, you do not need to use this constructor,
|
||||
* since the facade takes care of constructing and wiring the
|
||||
* RedBeanPHP core objects. However if you would like to
|
||||
* assemble an OODB instance yourself, this is how it works:
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $database = new RPDO( $dsn, $user, $pass );
|
||||
* $adapter = new DBAdapter( $database );
|
||||
* $writer = new PostgresWriter( $adapter );
|
||||
* $oodb = new OODB( $writer, FALSE );
|
||||
* $bean = $oodb->dispense( 'bean' );
|
||||
* $bean->name = 'coffeeBean';
|
||||
* $id = $oodb->store( $bean );
|
||||
* $bean = $oodb->load( 'bean', $id );
|
||||
* </code>
|
||||
*
|
||||
* The example above creates the 3 RedBeanPHP core objects:
|
||||
* the Adapter, the Query Writer and the OODB instance and
|
||||
* wires them together. The example also demonstrates some of
|
||||
* the methods that can be used with OODB, as you see, they
|
||||
* closely resemble their facade counterparts.
|
||||
*
|
||||
* The wiring process: create an RPDO instance using your database
|
||||
* connection parameters. Create a database adapter from the RPDO
|
||||
* object and pass that to the constructor of the writer. Next,
|
||||
* create an OODB instance from the writer. Now you have an OODB
|
||||
* object.
|
||||
*
|
||||
* @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 $sql 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 $sql SQL to be used in query
|
||||
* @param array $bindings a list of values to bind to query parameters
|
||||
*
|
||||
* @return BeanCollection
|
||||
*/
|
||||
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 $assocManager 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all function bindings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearAllFuncBindings()
|
||||
{
|
||||
self::$sqlFilters = array();
|
||||
AQueryWriter::setSQLFilters( self::$sqlFilters, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param boolean $isTemplate TRUE if $function is an SQL string, FALSE for just a function name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bindFunc( $mode, $field, $function, $isTemplate = FALSE )
|
||||
{
|
||||
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) {
|
||||
if ($isTemplate) {
|
||||
$code = sprintf( $function, '?' );
|
||||
} else {
|
||||
$code = "{$function}(?)";
|
||||
}
|
||||
self::$sqlFilters[$mode][$type][$property] = $code;
|
||||
} else {
|
||||
if ($isTemplate) {
|
||||
$code = sprintf( $function, $field );
|
||||
} else {
|
||||
$code = "{$function}({$field})";
|
||||
}
|
||||
self::$sqlFilters[$mode][$type][$property] = $code;
|
||||
}
|
||||
}
|
||||
AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) );
|
||||
}
|
||||
}
|
||||
2281
vendor/gabordemooij/redbean/RedBeanPHP/OODBBean.php
vendored
Normal file
2281
vendor/gabordemooij/redbean/RedBeanPHP/OODBBean.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
73
vendor/gabordemooij/redbean/RedBeanPHP/Observable.php
vendored
Normal file
73
vendor/gabordemooij/redbean/RedBeanPHP/Observable.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\Observer as Observer;
|
||||
|
||||
/**
|
||||
* Observable
|
||||
* Base class for Observables
|
||||
*
|
||||
* @file RedBeanPHP/Observable.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.
|
||||
*/
|
||||
abstract class Observable { //bracket must be here - otherwise coverage software does not understand.
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $observers = array();
|
||||
|
||||
/**
|
||||
* Implementation of the Observer Pattern.
|
||||
* Adds an event listener to the observable object.
|
||||
* First argument should be the name of the event you wish to listen for.
|
||||
* Second argument should be the object that wants to be notified in case
|
||||
* the event occurs.
|
||||
*
|
||||
* @param string $eventname event identifier
|
||||
* @param Observer $observer observer instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addEventListener( $eventname, Observer $observer )
|
||||
{
|
||||
if ( !isset( $this->observers[$eventname] ) ) {
|
||||
$this->observers[$eventname] = array();
|
||||
}
|
||||
|
||||
if ( in_array( $observer, $this->observers[$eventname] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->observers[$eventname][] = $observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners.
|
||||
* Sends the signal $eventname, the event identifier and a message object
|
||||
* to all observers that have been registered to receive notification for
|
||||
* this event. Part of the observer pattern implementation in RedBeanPHP.
|
||||
*
|
||||
* @param string $eventname event you want signal
|
||||
* @param mixed $info message object to send along
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function signal( $eventname, $info )
|
||||
{
|
||||
if ( !isset( $this->observers[$eventname] ) ) {
|
||||
$this->observers[$eventname] = array();
|
||||
}
|
||||
|
||||
foreach ( $this->observers[$eventname] as $observer ) {
|
||||
$observer->onEvent( $eventname, $info );
|
||||
}
|
||||
}
|
||||
}
|
||||
35
vendor/gabordemooij/redbean/RedBeanPHP/Observer.php
vendored
Normal file
35
vendor/gabordemooij/redbean/RedBeanPHP/Observer.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* Observer.
|
||||
*
|
||||
* Interface for Observer object. Implementation of the
|
||||
* observer pattern.
|
||||
*
|
||||
* @file RedBeanPHP/Observer.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP community
|
||||
* @license BSD/GPLv2
|
||||
* @desc Part of the observer pattern in RedBean
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
interface Observer
|
||||
{
|
||||
/**
|
||||
* An observer object needs to be capable of receiving
|
||||
* notifications. Therefore the observer needs to implement the
|
||||
* onEvent method with two parameters: the event identifier specifying the
|
||||
* current event and a message object (in RedBeanPHP this can also be a bean).
|
||||
*
|
||||
* @param string $eventname event identifier
|
||||
* @param mixed $bean a message sent along with the notification
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onEvent( $eventname, $bean );
|
||||
}
|
||||
26
vendor/gabordemooij/redbean/RedBeanPHP/Plugin.php
vendored
Normal file
26
vendor/gabordemooij/redbean/RedBeanPHP/Plugin.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* RedBean Plugin.
|
||||
* Marker interface for plugins.
|
||||
* Use this interface when defining new plugins, it's an
|
||||
* easy way for the rest of the application to recognize your
|
||||
* plugin. This plugin interface does not require you to
|
||||
* implement a specific API.
|
||||
*
|
||||
* @file RedBean/Plugin.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.
|
||||
*/
|
||||
interface Plugin
|
||||
{
|
||||
}
|
||||
|
||||
;
|
||||
246
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/Pool.php
vendored
Normal file
246
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/Pool.php
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Plugin;
|
||||
|
||||
use RedBeanPHP\ToolBox;
|
||||
use RedBeanPHP\OODBBean;
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*
|
||||
* PoolDB
|
||||
*
|
||||
* Experimental plugin that makes bean automatically connect
|
||||
* to the database they come from.
|
||||
*/
|
||||
|
||||
/**
|
||||
* NonStaticBeanHelper
|
||||
*
|
||||
* The NonStaticBeanHelper is used by the database pool class PoolDB.
|
||||
*/
|
||||
class NonStaticBeanHelper extends RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper {
|
||||
|
||||
/**
|
||||
* Returns the extracted toolbox.
|
||||
*
|
||||
* @return ToolBox
|
||||
*/
|
||||
public function getExtractedToolbox()
|
||||
{
|
||||
$toolbox = $this->toolbox;
|
||||
return array( $toolbox->getRedbean(), $toolbox->getDatabaseAdapter(), $toolbox->getWriter(), $toolbox );
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Creates a new instance of the NonStaticBeanHelper.
|
||||
* The NonStaticBeanHelper is used by the database pool class PoolDB.
|
||||
*/
|
||||
public function __construct($toolbox)
|
||||
{
|
||||
$this->toolbox = $toolbox;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PoolDB
|
||||
*
|
||||
* Represents a pool of databases that will have persisting
|
||||
* associations with the beans they dispense. Saving a bean from
|
||||
* a pooled database will make sure that the bean will be stored
|
||||
* in the database it originated from instead of the currently
|
||||
* selected database.
|
||||
*
|
||||
* @experimental
|
||||
* This is an experimental plugin, added for testing purposes
|
||||
* only.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* // Let's add some databases
|
||||
* R::addPoolDatabase( 'db1', 'sqlite:/tmp/db1.txt' );
|
||||
* R::nuke();
|
||||
* R::addPoolDatabase( 'db2', 'sqlite:/tmp/db2.txt' );
|
||||
* R::nuke();
|
||||
* R::addPoolDatabase( 'db3', 'sqlite:/tmp/db3.txt' );
|
||||
* R::nuke();
|
||||
*
|
||||
* // create a book and page in db1
|
||||
* R::selectDatabase('db1');
|
||||
* $book = R::dispense(array(
|
||||
* '_type' => 'book',
|
||||
* 'title' => 'Databases for Beans',
|
||||
* 'ownPageList' => array(
|
||||
* 0 => array(
|
||||
* '_type' => 'page',
|
||||
* 'content' => 'Lorem Ipsum'
|
||||
* )
|
||||
* )
|
||||
* ));
|
||||
* R::store($book);
|
||||
*
|
||||
* //switch to db2
|
||||
* R::selectDatabase( 'db2' );
|
||||
* //obtain pages (from db1)
|
||||
* $pages = count($book->ownPageList);
|
||||
* echo "I found {$pages} pages in db1.\n";
|
||||
*
|
||||
* $pages = R::count('page');
|
||||
* echo "There are {$pages} pages in db2.\n";
|
||||
*
|
||||
* // create pizza in db 2
|
||||
* $pizza = R::dispense('pizza');
|
||||
*
|
||||
* // switch to db3
|
||||
* R::selectDatabase( 'db3' );
|
||||
*
|
||||
* // store pizza in db2
|
||||
* $pizza->pepperoni = true;
|
||||
* R::store($pizza);
|
||||
*
|
||||
* $pizzas = R::count('pizza');
|
||||
* echo "There are {$pizzas} in pizzas db3.\n";
|
||||
* R::selectDatabase('db2');
|
||||
* $pizzas = R::count('pizza');
|
||||
* echo "There are {$pizzas} pizzas in db2.\n";
|
||||
* </code>
|
||||
*
|
||||
* @file RedBeanPHP/Plugin/Pool.php
|
||||
* @author RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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 PoolDB extends \RedBeanPHP\OODB {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $pool = array();
|
||||
|
||||
/**
|
||||
* @var ToolBox
|
||||
*/
|
||||
private $toolbox;
|
||||
|
||||
/**
|
||||
* @var OODB
|
||||
*/
|
||||
private $oodb;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var NonStaticBeanHelper
|
||||
*/
|
||||
private $beanHelper;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* creates a new instance of the database pool.
|
||||
*
|
||||
* @param string $key key
|
||||
* @param OODB $oodb oodb instance
|
||||
*/
|
||||
public function __construct( $key, $oodb)
|
||||
{
|
||||
self::$pool[$key] = $oodb;
|
||||
$this->oodb = $oodb;
|
||||
$this->key = $key;
|
||||
parent::__construct( $oodb->writer, $oodb->isFrozen );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the toolbox to be used by the database pool.
|
||||
*
|
||||
* @param ToolBox $toolbox toolbox
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setToolBox( $toolbox )
|
||||
{
|
||||
$this->toolbox = $toolbox;
|
||||
$this->beanHelper = new NonStaticBeanHelper( $this->toolbox );
|
||||
$this->beanHelper->key = $this->key;
|
||||
$this->oodb->setBeanHelper( $this->beanHelper );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bean helper of the database pool.
|
||||
*
|
||||
* @return BeanHelper
|
||||
*/
|
||||
public function getBeanHelper()
|
||||
{
|
||||
return $this->beanHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the find operation.
|
||||
*
|
||||
* @see OODB::find
|
||||
*/
|
||||
public function find( $type, $conditions=array(), $sql=NULL, $bindings=array())
|
||||
{
|
||||
return parent::find($type, $conditions, $sql, $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispenses a new bean from the database pool.
|
||||
* A bean that has been dispensed by the pool will have a special
|
||||
* meta attribute called sys.source containing the key identifying
|
||||
* the database in the pool it originated from.
|
||||
*
|
||||
* @see OODB::dispense
|
||||
*/
|
||||
public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE )
|
||||
{
|
||||
$bean = $this->oodb->dispense( $type, $number, $alwaysReturnArray );
|
||||
foreach( self::$pool as $key => $db ) {
|
||||
if ( $this->oodb === $db ) {
|
||||
$bean->setMeta( 'sys.source',$key );
|
||||
}
|
||||
}
|
||||
return $bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the specified bean in the database in the pool
|
||||
* it originated from by looking up the sys.source attribute.
|
||||
*
|
||||
* @see OODB::store
|
||||
*/
|
||||
public function store( $bean )
|
||||
{
|
||||
$dataSource = $bean->getMeta('sys.source');
|
||||
if ( !is_null( $dataSource ) ) {
|
||||
$result = self::$pool[$dataSource]->store( $bean );
|
||||
} else {
|
||||
$result = parent::store( $bean );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
R::ext( 'addPoolDatabase', function( $dbName, $dsn, $user=NULL, $pass=NULL ) {
|
||||
R::addDatabase( $dbName, $dsn, $user, $pass );
|
||||
R::selectDatabase( $dbName );
|
||||
list($oodb, $adapter, $writer, ) = R::getExtractedToolbox();
|
||||
$poolDB = new PoolDB( $dbName, $oodb );
|
||||
$toolbox = new ToolBox( $poolDB, $adapter, $writer );
|
||||
$poolDB->setToolBox( $toolbox );
|
||||
R::$toolboxes[$dbName]=$toolbox;
|
||||
R::selectDatabase( $dbName, TRUE );
|
||||
} );
|
||||
|
||||
86
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/SQN.php
vendored
Normal file
86
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/SQN.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Short Query Notation Library
|
||||
*
|
||||
* SQN is a small library that allows you to write
|
||||
* convention based SQL queries using a short notation.
|
||||
* SQL is a very flexible and powerful language. Since SQL
|
||||
* does not rely on assumptions you have to specify a lot.
|
||||
* The SQN-library uses some basic assumptions regarding
|
||||
* naming conventions and in return it gives you a shorter notation.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* R::sqn('shop<product<price'); - left joins shop, product and price
|
||||
* R::sqn('book<<tag'); - doubly left joins book, and tag using book_tag
|
||||
*
|
||||
* SQN assumes id fields follow the following conventions:
|
||||
*
|
||||
* Primary key: id
|
||||
* Foreign key: {table}_id
|
||||
* No table prefix.
|
||||
*
|
||||
* SQN can also generate additional aliases:
|
||||
*
|
||||
* R::sqn( ..., 'area/x,y;place/x,y' ) - area_x area_y place_x place_y
|
||||
*
|
||||
* @author Gabor de Mooij
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* copyright (c) G.J.G.T. (Gabor) de Mooij
|
||||
* This source file is subject to the BSD License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
R::ext('sqn', function( $query, $aliases = null, $q = '`' ) {
|
||||
$map = [
|
||||
'|' => 'INNER JOIN',
|
||||
'||' => 'INNER JOIN',
|
||||
'>' => 'RIGHT JOIN',
|
||||
'>>' => 'RIGHT JOIN',
|
||||
'<' => 'LEFT JOIN',
|
||||
'<<' => 'LEFT JOIN',
|
||||
];
|
||||
$select = [];
|
||||
$from = '';
|
||||
$joins = [];
|
||||
$prev = '';
|
||||
$ents = preg_split( '/[^\w_]+/', $query );
|
||||
$tokens = preg_split( '/[\w_]+/', $query );
|
||||
array_pop($tokens);
|
||||
foreach( $ents as $i => $ent ) {
|
||||
$select[] = " {$q}{$ent}{$q}.* ";
|
||||
if (!$i) {
|
||||
$from = $ent;
|
||||
$prev = $ent;
|
||||
continue;
|
||||
}
|
||||
if ( $tokens[$i] == '<' || $tokens[$i] == '>' || $tokens[$i] == '|') {
|
||||
$join[] = " {$map[$tokens[$i]]} {$q}{$ent}{$q} ON {$q}{$ent}{$q}.{$prev}_id = {$q}{$prev}{$q}.id ";
|
||||
}
|
||||
if ( $tokens[$i] == '<<' || $tokens[$i] == '>>' || $tokens[$i] == '||') {
|
||||
$combi = [$prev, $ent];
|
||||
sort( $combi );
|
||||
$combi = implode( '_', $combi );
|
||||
$select[] = " {$q}{$combi}{$q}.* ";
|
||||
$join[] = " {$map[$tokens[$i]]} {$q}{$combi}{$q} ON {$q}{$combi}{$q}.{$prev}_id = {$q}{$prev}{$q}.id ";
|
||||
$join[] = " {$map[$tokens[$i]]} {$q}{$ent}{$q} ON {$q}{$combi}{$q}.{$ent}_id = {$q}{$ent}{$q}.id ";
|
||||
}
|
||||
$prev = $ent;
|
||||
}
|
||||
if (!is_null($aliases)) {
|
||||
$aliases = explode(';', $aliases);
|
||||
foreach($aliases as $alias) {
|
||||
list($table, $cols) = explode('/', $alias);
|
||||
$cols = explode(',', $cols);
|
||||
foreach($cols as $col) {
|
||||
$select[] = " {$q}{$table}{$q}.{$q}{$col}{$q} AS {$q}{$table}_{$col}{$q} ";
|
||||
}
|
||||
}
|
||||
}
|
||||
$selectSQL = implode( ",\n", $select );
|
||||
$joinSQL = implode( "\n", $join );
|
||||
return "SELECT{$selectSQL}\n FROM {$q}{$from}{$q}\n{$joinSQL}";
|
||||
});
|
||||
|
||||
56
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/TinyQueryBuilder.php
vendored
Normal file
56
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/TinyQueryBuilder.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Tiny Query Builder
|
||||
*
|
||||
* <code>
|
||||
* $sql = build_query([
|
||||
* [ 'SELECT * FROM book'],
|
||||
* [$title ,'WHERE','title = ?'],
|
||||
* [$price ,'AND','price < ?'],
|
||||
* [$order ,'ORDER BY ? ASC'],
|
||||
* [$limit ,'LIMIT ?']
|
||||
* ]);
|
||||
* </code>
|
||||
*
|
||||
* Now, if we have a $title and a $price the query will be:
|
||||
* 'SELECT * FROM book WHERE title = ? AND price < ? '
|
||||
* If we only have a $price and a $limit:
|
||||
* 'SELECT * FROM book WHERE price < ? LIMIT ?'
|
||||
* The Query Builder works very easy, it simply loops through the array,
|
||||
* each element is another array inside this main array,
|
||||
* let's call this inner array a 'piece'.
|
||||
* A piece can have one, two or three elements.
|
||||
* If it has one element, the element is simply concatenated to the final query.
|
||||
* If a piece has two elements, the second element will be
|
||||
* concatenated only if the first evaluates to TRUE.
|
||||
* Finally a piece having three elements works the same as a piece with two elements,
|
||||
* except that it will use the glue provided in the second element
|
||||
* to concat the value of the third element. The glue acts as a little tube of glue.
|
||||
* If there is still glue left in the tube (WHERE) it will preserve this
|
||||
* until it can be applied (so the first AND will be ignored in case of a WHERE condition).
|
||||
*/
|
||||
R::ext('buildQuery', function($pieces) {
|
||||
$sql = '';
|
||||
$glue = NULL;
|
||||
foreach( $pieces as $piece ) {
|
||||
$n = count( $piece );
|
||||
switch( $n ) {
|
||||
case 1:
|
||||
$sql .= " {$piece[0]} ";
|
||||
break;
|
||||
case 2:
|
||||
$glue = NULL;
|
||||
if (!is_null($piece[0])) $sql .= " {$piece[1]} ";
|
||||
break;
|
||||
case 3:
|
||||
$glue = ( is_null( $glue ) ) ? $piece[1] : $glue;
|
||||
if (!is_null($piece[0])) {
|
||||
$sql .= " {$glue} {$piece[2]} ";
|
||||
$glue = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $sql;
|
||||
});
|
||||
2
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/put_your_plugins_here.txt
vendored
Normal file
2
vendor/gabordemooij/redbean/RedBeanPHP/Plugin/put_your_plugins_here.txt
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
This folder has been reserved for your plugins
|
||||
|
||||
559
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter.php
vendored
Normal file
559
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter.php
vendored
Normal file
@@ -0,0 +1,559 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* QueryWriter
|
||||
* Interface for QueryWriters.
|
||||
* Describes the API for a QueryWriter.
|
||||
*
|
||||
* Terminology:
|
||||
*
|
||||
* - beautified property (a camelCased property, has to be converted first)
|
||||
* - beautified type (a camelCased type, has to be converted first)
|
||||
* - type (a bean type, corresponds directly to a table)
|
||||
* - property (a bean property, corresponds directly to a column)
|
||||
* - table (a checked and quoted type, ready for use in a query)
|
||||
* - column (a checked and quoted property, ready for use in query)
|
||||
* - tableNoQ (same as type, but in context of a database operation)
|
||||
* - columnNoQ (same as property, but in context of a database operation)
|
||||
*
|
||||
* @file RedBeanPHP/QueryWriter.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.
|
||||
*/
|
||||
interface QueryWriter
|
||||
{
|
||||
/**
|
||||
* SQL filter constants
|
||||
*/
|
||||
const C_SQLFILTER_READ = 'r';
|
||||
const C_SQLFILTER_WRITE = 'w';
|
||||
|
||||
/**
|
||||
* Query Writer constants.
|
||||
*/
|
||||
const C_SQLSTATE_NO_SUCH_TABLE = 1;
|
||||
const C_SQLSTATE_NO_SUCH_COLUMN = 2;
|
||||
const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
|
||||
const C_SQLSTATE_LOCK_TIMEOUT = 4;
|
||||
|
||||
/**
|
||||
* Define data type regions
|
||||
*
|
||||
* 00 - 80: normal data types
|
||||
* 80 - 99: special data types, only scan/code if requested
|
||||
* 99 : specified by user, don't change
|
||||
*/
|
||||
const C_DATATYPE_RANGE_SPECIAL = 80;
|
||||
const C_DATATYPE_RANGE_SPECIFIED = 99;
|
||||
|
||||
/**
|
||||
* Define GLUE types for use with glueSQLCondition methods.
|
||||
* Determines how to prefix a snippet of SQL before appending it
|
||||
* to other SQL (or integrating it, mixing it otherwise).
|
||||
*
|
||||
* WHERE - glue as WHERE condition
|
||||
* AND - glue as AND condition
|
||||
*/
|
||||
const C_GLUE_WHERE = 1;
|
||||
const C_GLUE_AND = 2;
|
||||
|
||||
/**
|
||||
* CTE Select Snippet
|
||||
* Constants specifying select snippets for CTE queries
|
||||
*/
|
||||
const C_CTE_SELECT_NORMAL = FALSE;
|
||||
const C_CTE_SELECT_COUNT = TRUE;
|
||||
|
||||
/**
|
||||
* Parses an sql string to create joins if needed.
|
||||
*
|
||||
* For instance with $type = 'book' and $sql = ' @joined.author.name LIKE ? OR @joined.detail.title LIKE ? '
|
||||
* parseJoin will return the following SQL:
|
||||
* ' LEFT JOIN `author` ON `author`.id = `book`.author_id
|
||||
* LEFT JOIN `detail` ON `detail`.id = `book`.detail_id
|
||||
* WHERE author.name LIKE ? OR detail.title LIKE ? '
|
||||
*
|
||||
* @note this feature requires Narrow Field Mode to be activated (default).
|
||||
*
|
||||
* @note A default implementation is available in AQueryWriter
|
||||
* unless a database uses very different SQL this should suffice.
|
||||
*
|
||||
* @param string $type the source type for the join
|
||||
* @param string $sql the sql string to be parsed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function parseJoin( $type, $sql );
|
||||
|
||||
/**
|
||||
* Writes an SQL Snippet for a JOIN, returns the
|
||||
* SQL snippet string.
|
||||
*
|
||||
* @note A default implementation is available in AQueryWriter
|
||||
* unless a database uses very different SQL this should suffice.
|
||||
*
|
||||
* @param string $type source type
|
||||
* @param string $targetType target type (type to join)
|
||||
* @param string $leftRight type of join (possible: 'LEFT', 'RIGHT' or 'INNER')
|
||||
* @param string $joinType relation between joined tables (possible: 'parent', 'own', 'shared')
|
||||
* @param boolean $firstOfChain is it the join of a chain (or the only join)
|
||||
* @param string $suffix suffix to add for aliasing tables (for joining same table multiple times)
|
||||
*
|
||||
* @return string $joinSQLSnippet
|
||||
*/
|
||||
public function writeJoin( $type, $targetType, $leftRight, $joinType, $firstOfChain, $suffix );
|
||||
|
||||
/**
|
||||
* Glues an SQL snippet to the beginning of a WHERE clause.
|
||||
* This ensures users don't have to add WHERE to their query snippets.
|
||||
*
|
||||
* The snippet gets prefixed with WHERE or AND
|
||||
* if it starts with a condition.
|
||||
*
|
||||
* If the snippet does NOT start with a condition (or this function thinks so)
|
||||
* the snippet is returned as-is.
|
||||
*
|
||||
* The GLUE type determines the prefix:
|
||||
*
|
||||
* * NONE prefixes with WHERE
|
||||
* * WHERE prefixes with WHERE and replaces AND if snippets starts with AND
|
||||
* * AND prefixes with AND
|
||||
*
|
||||
* This method will never replace WHERE with AND since a snippet should never
|
||||
* begin with WHERE in the first place. OR is not supported.
|
||||
*
|
||||
* Only a limited set of clauses will be recognized as non-conditions.
|
||||
* For instance beginning a snippet with complex statements like JOIN or UNION
|
||||
* will not work. This is too complex for use in a snippet.
|
||||
*
|
||||
* @note A default implementation is available in AQueryWriter
|
||||
* unless a database uses very different SQL this should suffice.
|
||||
*
|
||||
* @param string $sql SQL Snippet
|
||||
* @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function glueSQLCondition( $sql, $glue = NULL );
|
||||
|
||||
/**
|
||||
* Determines if there is a LIMIT 1 clause in the SQL.
|
||||
* If not, it will add a LIMIT 1. (used for findOne).
|
||||
*
|
||||
* @note A default implementation is available in AQueryWriter
|
||||
* unless a database uses very different SQL this should suffice.
|
||||
*
|
||||
* @param string $sql query to scan and adjust
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function glueLimitOne( $sql );
|
||||
|
||||
/**
|
||||
* Returns the tables that are in the database.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTables();
|
||||
|
||||
/**
|
||||
* This method will create a table for the bean.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type type of bean you want to create a table for
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function createTable( $type );
|
||||
|
||||
/**
|
||||
* Returns an array containing all the columns of the specified type.
|
||||
* The format of the return array looks like this:
|
||||
* $field => $type where $field is the name of the column and $type
|
||||
* is a database specific description of the datatype.
|
||||
*
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type type of bean you want to obtain a column list of
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns( $type );
|
||||
|
||||
/**
|
||||
* Returns the Column Type Code (integer) that corresponds
|
||||
* to the given value type. This method is used to determine the minimum
|
||||
* column type required to represent the given value. There are two modes of
|
||||
* operation: with or without special types. Scanning without special types
|
||||
* requires the second parameter to be set to FALSE. This is useful when the
|
||||
* column has already been created and prevents it from being modified to
|
||||
* an incompatible type leading to data loss. Special types will be taken
|
||||
* into account when a column does not exist yet (parameter is then set to TRUE).
|
||||
*
|
||||
* Special column types are determines by the AQueryWriter constant
|
||||
* C_DATA_TYPE_ONLY_IF_NOT_EXISTS (usually 80). Another 'very special' type is type
|
||||
* C_DATA_TYPE_MANUAL (usually 99) which represents a user specified type. Although
|
||||
* no special treatment has been associated with the latter for now.
|
||||
*
|
||||
* @param string $value value
|
||||
* @param boolean $alsoScanSpecialForTypes take special types into account
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function scanType( $value, $alsoScanSpecialForTypes = FALSE );
|
||||
|
||||
/**
|
||||
* This method will add a column to a table.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type name of the table
|
||||
* @param string $column name of the column
|
||||
* @param integer $field data type for field
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addColumn( $type, $column, $field );
|
||||
|
||||
/**
|
||||
* Returns the Type Code for a Column Description.
|
||||
* Given an SQL column description this method will return the corresponding
|
||||
* code for the writer. If the include specials flag is set it will also
|
||||
* return codes for special columns. Otherwise special columns will be identified
|
||||
* as specified columns.
|
||||
*
|
||||
* @param string $typedescription description
|
||||
* @param boolean $includeSpecials whether you want to get codes for special columns as well
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function code( $typedescription, $includeSpecials = FALSE );
|
||||
|
||||
/**
|
||||
* This method will widen the column to the specified data type.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type type / table that needs to be adjusted
|
||||
* @param string $column column that needs to be altered
|
||||
* @param integer $datatype target data type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function widenColumn( $type, $column, $datatype );
|
||||
|
||||
/**
|
||||
* Selects records from the database.
|
||||
* This methods selects the records from the database that match the specified
|
||||
* type, conditions (optional) and additional SQL snippet (optional).
|
||||
*
|
||||
* @param string $type name of the table you want to query
|
||||
* @param array $conditions criteria ( $column => array( $values ) )
|
||||
* @param string $addSql additional SQL snippet
|
||||
* @param array $bindings bindings for SQL snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Selects records from the database and returns a cursor.
|
||||
* This methods selects the records from the database that match the specified
|
||||
* type, conditions (optional) and additional SQL snippet (optional).
|
||||
*
|
||||
* @param string $type name of the table you want to query
|
||||
* @param array $conditions criteria ( $column => array( $values ) )
|
||||
* @param string $addSQL additional SQL snippet
|
||||
* @param array $bindings bindings for SQL snippet
|
||||
*
|
||||
* @return Cursor
|
||||
*/
|
||||
public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns records through an intermediate type. This method is used to obtain records using a link table and
|
||||
* allows the SQL snippets to reference columns in the link table for additional filtering or ordering.
|
||||
*
|
||||
* @param string $sourceType source type, the reference type you want to use to fetch related items on the other side
|
||||
* @param string $destType destination type, the target type you want to get beans of
|
||||
* @param mixed $linkID ID to use for the link table
|
||||
* @param string $addSql Additional SQL snippet
|
||||
* @param array $bindings Bindings for SQL snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation.
|
||||
*
|
||||
* @param string $sourceType source type, the first part of the link you're looking for
|
||||
* @param string $destType destination type, the second part of the link you're looking for
|
||||
* @param string $sourceID ID for the source
|
||||
* @param string $destID ID for the destination
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function queryRecordLink( $sourceType, $destType, $sourceID, $destID );
|
||||
|
||||
/**
|
||||
* Counts the number of records in the database that match the
|
||||
* conditions and additional SQL.
|
||||
*
|
||||
* @param string $type name of the table you want to query
|
||||
* @param array $conditions criteria ( $column => array( $values ) )
|
||||
* @param string $addSQL additional SQL snippet
|
||||
* @param array $bindings bindings for SQL snippet
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings.
|
||||
*
|
||||
* @param string $sourceType source type
|
||||
* @param string $targetType the thing you want to count
|
||||
* @param mixed $linkID the of the source type
|
||||
* @param string $addSQL additional SQL snippet
|
||||
* @param array $bindings bindings for SQL snippet
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns all rows of specified type that have been tagged with one of the
|
||||
* strings in the specified tag list array.
|
||||
*
|
||||
* Note that the additional SQL snippet can only be used for pagination,
|
||||
* the SQL snippet will be appended to the end of the query.
|
||||
*
|
||||
* @param string $type the bean type you want to query
|
||||
* @param array $tagList an array of strings, each string containing a tag title
|
||||
* @param boolean $all if TRUE only return records that have been associated with ALL the tags in the list
|
||||
* @param string $addSql addition SQL snippet, for pagination
|
||||
* @param array $bindings parameter bindings for additional SQL snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
|
||||
|
||||
/**
|
||||
* Like queryTagged but only counts.
|
||||
*
|
||||
* @param string $type the bean type you want to query
|
||||
* @param array $tagList an array of strings, each string containing a tag title
|
||||
* @param boolean $all if TRUE only return records that have been associated with ALL the tags in the list
|
||||
* @param string $addSql addition SQL snippet, for pagination
|
||||
* @param array $bindings parameter bindings for additional SQL snippet
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function queryCountTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() );
|
||||
|
||||
/**
|
||||
* Returns all parent rows or child rows of a specified row.
|
||||
* Given a type specifier and a primary key id, this method returns either all child rows
|
||||
* as defined by having <type>_id = id or all parent rows as defined per id = <type>_id
|
||||
* taking into account an optional SQL snippet along with parameters.
|
||||
*
|
||||
* The $select parameter can be used to adjust the select snippet of the query.
|
||||
* Possible values are: C_CTE_SELECT_NORMAL (just select all columns, default), C_CTE_SELECT_COUNT
|
||||
* (count rows) used for countParents and countChildren functions - or you can specify a
|
||||
* string yourself like 'count(distinct brand)'.
|
||||
*
|
||||
* @param string $type the bean type you want to query rows for
|
||||
* @param integer $id id of the reference row
|
||||
* @param boolean $up TRUE to query parent rows, FALSE to query child rows
|
||||
* @param string $addSql optional SQL snippet to embed in the query
|
||||
* @param array $bindings parameter bindings for additional SQL snippet
|
||||
* @param mixed $select Select Snippet to use when querying (optional)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function queryRecursiveCommonTableExpression( $type, $id, $up = TRUE, $addSql = NULL, $bindings = array(), $select = QueryWriter::C_CTE_SELECT_NORMAL );
|
||||
|
||||
/**
|
||||
* This method should update (or insert a record), it takes
|
||||
* a table name, a list of update values ( $field => $value ) and an
|
||||
* primary key ID (optional). If no primary key ID is provided, an
|
||||
* INSERT will take place.
|
||||
* Returns the new ID.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type name of the table to update
|
||||
* @param array $updatevalues list of update values
|
||||
* @param integer $id optional primary key ID value
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function updateRecord( $type, $updatevalues, $id = NULL );
|
||||
|
||||
/**
|
||||
* Deletes records from the database.
|
||||
* @note $addSql is always prefixed with ' WHERE ' or ' AND .'
|
||||
*
|
||||
* @param string $type name of the table you want to query
|
||||
* @param array $conditions criteria ( $column => array( $values ) )
|
||||
* @param string $addSql additional SQL
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() );
|
||||
|
||||
/**
|
||||
* Deletes all links between $sourceType and $destType in an N-M relation.
|
||||
*
|
||||
* @param string $sourceType source type
|
||||
* @param string $destType destination type
|
||||
* @param string $sourceID source ID
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteRelations( $sourceType, $destType, $sourceID );
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addUniqueConstaint
|
||||
*/
|
||||
public function addUniqueIndex( $type, $columns );
|
||||
|
||||
/**
|
||||
* This method will add a UNIQUE constraint index to a table on columns $columns.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type target bean type
|
||||
* @param array $columnsPartOfIndex columns to include in index
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addUniqueConstraint( $type, $columns );
|
||||
|
||||
/**
|
||||
* This method will check whether the SQL state is in the list of specified states
|
||||
* and returns TRUE if it does appear in this list or FALSE if it
|
||||
* does not. The purpose of this method is to translate the database specific state to
|
||||
* a one of the constants defined in this class and then check whether it is in the list
|
||||
* of standard states provided.
|
||||
*
|
||||
* @param string $state SQL state to consider
|
||||
* @param array $list list of standardized SQL state constants to check against
|
||||
* @param array $extraDriverDetails Some databases communicate state information in a driver-specific format
|
||||
* rather than through the main sqlState code. For those databases, this extra
|
||||
* information can be used to determine the standardized state
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function sqlStateIn( $state, $list, $extraDriverDetails = array() );
|
||||
|
||||
/**
|
||||
* This method will remove all beans of a certain type.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type bean type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function wipe( $type );
|
||||
|
||||
/**
|
||||
* This method will add a foreign key from type and field to
|
||||
* target type and target field.
|
||||
* The foreign key is created without an action. On delete/update
|
||||
* no action will be triggered. The FK is only used to allow database
|
||||
* tools to generate pretty diagrams and to make it easy to add actions
|
||||
* later on.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
*
|
||||
* @param string $type type that will have a foreign key field
|
||||
* @param string $targetType points to this type
|
||||
* @param string $property field that contains the foreign key value
|
||||
* @param string $targetProperty field where the fk points to
|
||||
* @param string $isDep whether target is dependent and should cascade on update/delete
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE );
|
||||
|
||||
/**
|
||||
* This method will add an index to a type and field with name
|
||||
* $name.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type type to add index to
|
||||
* @param string $name name of the new index
|
||||
* @param string $property field to index
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addIndex( $type, $name, $property );
|
||||
|
||||
/**
|
||||
* Checks and filters a database structure element like a table of column
|
||||
* for safe use in a query. A database structure has to conform to the
|
||||
* RedBeanPHP DB security policy which basically means only alphanumeric
|
||||
* symbols are allowed. This security policy is more strict than conventional
|
||||
* SQL policies and does therefore not require database specific escaping rules.
|
||||
*
|
||||
* @param string $databaseStructure name of the column/table to check
|
||||
* @param boolean $noQuotes TRUE to NOT put backticks or quotes around the string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function esc( $databaseStructure, $dontQuote = FALSE );
|
||||
|
||||
/**
|
||||
* Removes all tables and views from the database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function wipeAll();
|
||||
|
||||
/**
|
||||
* Renames an association. For instance if you would like to refer to
|
||||
* album_song as: track you can specify this by calling this method like:
|
||||
*
|
||||
* <code>
|
||||
* renameAssociation('album_song','track')
|
||||
* </code>
|
||||
*
|
||||
* This allows:
|
||||
*
|
||||
* <code>
|
||||
* $album->sharedSong
|
||||
* </code>
|
||||
*
|
||||
* to add/retrieve beans from track instead of album_song.
|
||||
* Also works for exportAll().
|
||||
*
|
||||
* This method also accepts a single associative array as
|
||||
* its first argument.
|
||||
*
|
||||
* @param string|array $fromType original type name, or array
|
||||
* @param string $toType new type name (only if 1st argument is string)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function renameAssocTable( $fromType, $toType = NULL );
|
||||
|
||||
/**
|
||||
* Returns the format for link tables.
|
||||
* Given an array containing two type names this method returns the
|
||||
* name of the link table to be used to store and retrieve
|
||||
* association records. For instance, given two types: person and
|
||||
* project, the corresponding link table might be: 'person_project'.
|
||||
*
|
||||
* @param array $types two types array($type1, $type2)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAssocTable( $types );
|
||||
|
||||
}
|
||||
1667
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/AQueryWriter.php
vendored
Normal file
1667
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/AQueryWriter.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
364
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/CUBRID.php
vendored
Normal file
364
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/CUBRID.php
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\QueryWriter;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
|
||||
/**
|
||||
* RedBeanPHP CUBRID Writer.
|
||||
* This is a QueryWriter class for RedBeanPHP.
|
||||
* This QueryWriter provides support for the CUBRID database platform.
|
||||
*
|
||||
* @file RedBeanPHP/QueryWriter/CUBRID.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* (c) copyright 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 CUBRID extends AQueryWriter implements QueryWriter
|
||||
{
|
||||
/**
|
||||
* Data types
|
||||
*/
|
||||
const C_DATATYPE_INTEGER = 0;
|
||||
const C_DATATYPE_DOUBLE = 1;
|
||||
const C_DATATYPE_STRING = 2;
|
||||
const C_DATATYPE_SPECIAL_DATE = 80;
|
||||
const C_DATATYPE_SPECIAL_DATETIME = 81;
|
||||
const C_DATATYPE_SPECIFIED = 99;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $quoteCharacter = '`';
|
||||
|
||||
/**
|
||||
* This method adds a foreign key from type and field to
|
||||
* target type and target field.
|
||||
* The foreign key is created without an action. On delete/update
|
||||
* no action will be triggered. The FK is only used to allow database
|
||||
* tools to generate pretty diagrams and to make it easy to add actions
|
||||
* later on.
|
||||
* This methods accepts a type and infers the corresponding table name.
|
||||
*
|
||||
* @param string $type type that will have a foreign key field
|
||||
* @param string $targetType points to this type
|
||||
* @param string $property field that contains the foreign key value
|
||||
* @param string $targetProperty field where the fk points to
|
||||
* @param boolean $isDep is dependent
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function buildFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$targetTable = $this->esc( $targetType );
|
||||
$targetTableNoQ = $this->esc( $targetType, TRUE );
|
||||
$column = $this->esc( $property );
|
||||
$columnNoQ = $this->esc( $property, TRUE );
|
||||
$targetColumn = $this->esc( $targetProperty );
|
||||
if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $columnNoQ ) ) ) return FALSE;
|
||||
$needsToDropFK = FALSE;
|
||||
$casc = ( $isDep ? 'CASCADE' : 'SET NULL' );
|
||||
$sql = "ALTER TABLE $table ADD CONSTRAINT FOREIGN KEY($column) REFERENCES $targetTable($targetColumn) ON DELETE $casc ";
|
||||
try {
|
||||
$this->adapter->exec( $sql );
|
||||
} catch( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AQueryWriter::getKeyMapForType
|
||||
*/
|
||||
protected function getKeyMapForType( $type )
|
||||
{
|
||||
$sqlCode = $this->adapter->get("SHOW CREATE TABLE `{$type}`");
|
||||
if (!isset($sqlCode[0])) return array();
|
||||
$matches = array();
|
||||
preg_match_all( '/CONSTRAINT\s+\[([\w_]+)\]\s+FOREIGN\s+KEY\s+\(\[([\w_]+)\]\)\s+REFERENCES\s+\[([\w_]+)\](\s+ON\s+DELETE\s+(CASCADE|SET\sNULL|RESTRICT|NO\sACTION)\s+ON\s+UPDATE\s+(SET\sNULL|RESTRICT|NO\sACTION))?/', $sqlCode[0]['CREATE TABLE'], $matches );
|
||||
$list = array();
|
||||
if (!isset($matches[0])) return $list;
|
||||
$max = count($matches[0]);
|
||||
for($i = 0; $i < $max; $i++) {
|
||||
$label = $this->makeFKLabel( $matches[2][$i], $matches[3][$i], 'id' );
|
||||
$list[ $label ] = array(
|
||||
'name' => $matches[1][$i],
|
||||
'from' => $matches[2][$i],
|
||||
'table' => $matches[3][$i],
|
||||
'to' => 'id',
|
||||
'on_update' => $matches[6][$i],
|
||||
'on_delete' => $matches[5][$i]
|
||||
);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Most of the time, you do not need to use this constructor,
|
||||
* since the facade takes care of constructing and wiring the
|
||||
* RedBeanPHP core objects. However if you would like to
|
||||
* assemble an OODB instance yourself, this is how it works:
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $database = new RPDO( $dsn, $user, $pass );
|
||||
* $adapter = new DBAdapter( $database );
|
||||
* $writer = new PostgresWriter( $adapter );
|
||||
* $oodb = new OODB( $writer, FALSE );
|
||||
* $bean = $oodb->dispense( 'bean' );
|
||||
* $bean->name = 'coffeeBean';
|
||||
* $id = $oodb->store( $bean );
|
||||
* $bean = $oodb->load( 'bean', $id );
|
||||
* </code>
|
||||
*
|
||||
* The example above creates the 3 RedBeanPHP core objects:
|
||||
* the Adapter, the Query Writer and the OODB instance and
|
||||
* wires them together. The example also demonstrates some of
|
||||
* the methods that can be used with OODB, as you see, they
|
||||
* closely resemble their facade counterparts.
|
||||
*
|
||||
* The wiring process: create an RPDO instance using your database
|
||||
* connection parameters. Create a database adapter from the RPDO
|
||||
* object and pass that to the constructor of the writer. Next,
|
||||
* create an OODB instance from the writer. Now you have an OODB
|
||||
* object.
|
||||
*
|
||||
* @param Adapter $adapter Database Adapter
|
||||
*/
|
||||
public function __construct( Adapter $adapter )
|
||||
{
|
||||
$this->typeno_sqltype = array(
|
||||
CUBRID::C_DATATYPE_INTEGER => ' INTEGER ',
|
||||
CUBRID::C_DATATYPE_DOUBLE => ' DOUBLE ',
|
||||
CUBRID::C_DATATYPE_STRING => ' STRING ',
|
||||
CUBRID::C_DATATYPE_SPECIAL_DATE => ' DATE ',
|
||||
CUBRID::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
|
||||
);
|
||||
|
||||
$this->sqltype_typeno = array();
|
||||
|
||||
foreach ( $this->typeno_sqltype as $k => $v ) {
|
||||
$this->sqltype_typeno[trim( ( $v ) )] = $k;
|
||||
}
|
||||
|
||||
$this->sqltype_typeno['STRING(1073741823)'] = self::C_DATATYPE_STRING;
|
||||
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the datatype to be used for primary key IDS and
|
||||
* foreign keys. Returns one if the data type constants.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getTypeForID()
|
||||
{
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getTables
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
$rows = $this->adapter->getCol( "SELECT class_name FROM db_class WHERE is_system_class = 'NO';" );
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::createTable
|
||||
*/
|
||||
public function createTable( $table )
|
||||
{
|
||||
$sql = 'CREATE TABLE '
|
||||
. $this->esc( $table )
|
||||
. ' ("id" integer AUTO_INCREMENT, CONSTRAINT "pk_'
|
||||
. $this->esc( $table, TRUE )
|
||||
. '_id" PRIMARY KEY("id"))';
|
||||
|
||||
$this->adapter->exec( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getColumns
|
||||
*/
|
||||
public function getColumns( $table )
|
||||
{
|
||||
$table = $this->esc( $table );
|
||||
|
||||
$columnsRaw = $this->adapter->get( "SHOW COLUMNS FROM $table" );
|
||||
|
||||
$columns = array();
|
||||
foreach ( $columnsRaw as $r ) {
|
||||
$columns[$r['Field']] = $r['Type'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::scanType
|
||||
*/
|
||||
public function scanType( $value, $flagSpecial = FALSE )
|
||||
{
|
||||
$this->svalue = $value;
|
||||
|
||||
if ( is_null( $value ) ) {
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
}
|
||||
|
||||
if ( $flagSpecial ) {
|
||||
if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
|
||||
return self::C_DATATYPE_SPECIAL_DATE;
|
||||
}
|
||||
if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
|
||||
return self::C_DATATYPE_SPECIAL_DATETIME;
|
||||
}
|
||||
}
|
||||
|
||||
$value = strval( $value );
|
||||
|
||||
if ( !$this->startsWithZeros( $value ) ) {
|
||||
if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= -2147483647 && $value <= 2147483647 ) {
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
}
|
||||
if ( is_numeric( $value ) ) {
|
||||
return self::C_DATATYPE_DOUBLE;
|
||||
}
|
||||
}
|
||||
|
||||
return self::C_DATATYPE_STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::code
|
||||
*/
|
||||
public function code( $typedescription, $includeSpecials = FALSE )
|
||||
{
|
||||
$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : self::C_DATATYPE_SPECIFIED );
|
||||
|
||||
if ( $includeSpecials ) {
|
||||
return $r;
|
||||
}
|
||||
|
||||
if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
|
||||
return self::C_DATATYPE_SPECIFIED;
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addColumn
|
||||
*/
|
||||
public function addColumn( $type, $column, $field )
|
||||
{
|
||||
$table = $type;
|
||||
$type = $field;
|
||||
|
||||
$table = $this->esc( $table );
|
||||
$column = $this->esc( $column );
|
||||
|
||||
$type = array_key_exists( $type, $this->typeno_sqltype ) ? $this->typeno_sqltype[$type] : '';
|
||||
|
||||
$this->adapter->exec( "ALTER TABLE $table ADD COLUMN $column $type " );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addUniqueIndex
|
||||
*/
|
||||
public function addUniqueConstraint( $type, $properties )
|
||||
{
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$columns = array();
|
||||
foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
|
||||
$table = $this->esc( $type );
|
||||
sort( $columns ); // else we get multiple indexes due to order-effects
|
||||
$name = 'UQ_' . sha1( implode( ',', $columns ) );
|
||||
$sql = "ALTER TABLE $table ADD CONSTRAINT UNIQUE $name (" . implode( ',', $columns ) . ")";
|
||||
try {
|
||||
$this->adapter->exec( $sql );
|
||||
} catch( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::sqlStateIn
|
||||
*/
|
||||
public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
|
||||
{
|
||||
return ( $state == 'HY000' ) ? ( count( array_diff( array(
|
||||
QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE
|
||||
), $list ) ) !== 3 ) : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addIndex
|
||||
*/
|
||||
public function addIndex( $type, $name, $column )
|
||||
{
|
||||
try {
|
||||
$table = $this->esc( $type );
|
||||
$name = preg_replace( '/\W/', '', $name );
|
||||
$column = $this->esc( $column );
|
||||
$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
|
||||
return TRUE;
|
||||
} catch ( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addFK
|
||||
*/
|
||||
public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
|
||||
{
|
||||
return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDependent );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::wipeAll
|
||||
*/
|
||||
public function wipeAll()
|
||||
{
|
||||
if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
|
||||
foreach ( $this->getTables() as $t ) {
|
||||
foreach ( $this->getKeyMapForType( $t ) as $k ) {
|
||||
$this->adapter->exec( "ALTER TABLE \"$t\" DROP FOREIGN KEY \"{$k['name']}\"" );
|
||||
}
|
||||
}
|
||||
foreach ( $this->getTables() as $t ) {
|
||||
$this->adapter->exec( "DROP TABLE \"$t\"" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::esc
|
||||
*/
|
||||
public function esc( $dbStructure, $noQuotes = FALSE )
|
||||
{
|
||||
return parent::esc( strtolower( $dbStructure ), $noQuotes );
|
||||
}
|
||||
}
|
||||
352
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/Firebird.php
vendored
Normal file
352
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/Firebird.php
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\QueryWriter;
|
||||
|
||||
/* Experimental */
|
||||
|
||||
/**
|
||||
* This driver has been created in 2015 but it has never been distributed
|
||||
* because it was never finished. However, in the true spirit of open source
|
||||
* it is now available in the source of RedBeanPHP.
|
||||
*
|
||||
* Consider this driver experimental or help me finish it to
|
||||
* support the Firebird/Interbase database series.
|
||||
*
|
||||
*/
|
||||
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
|
||||
/**
|
||||
* RedBeanPHP Firebird/Interbase QueryWriter.
|
||||
* This is a QueryWriter class for RedBeanPHP.
|
||||
* This QueryWriter provides support for the Firebird/Interbase database platform.
|
||||
*
|
||||
* *** Warning - Experimental Software | Not ready for Production ****
|
||||
*
|
||||
* @file RedBeanPHP/QueryWriter/Firebird.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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 Firebird extends AQueryWriter implements QueryWriter
|
||||
{
|
||||
/**
|
||||
* Data types
|
||||
*/
|
||||
const C_DATATYPE_INTEGER = 2;
|
||||
const C_DATATYPE_FLOAT = 3;
|
||||
const C_DATATYPE_TEXT = 5;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $quoteCharacter = '"';
|
||||
|
||||
/**
|
||||
* Returns the insert suffix SQL Snippet
|
||||
*
|
||||
* @param string $table table
|
||||
*
|
||||
* @return string $sql SQL Snippet
|
||||
*/
|
||||
protected function getInsertSuffix( $table )
|
||||
{
|
||||
return 'RETURNING id ';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AQueryWriter::getKeyMapForType
|
||||
*/
|
||||
protected function getKeyMapForType( $type )
|
||||
{
|
||||
$table = $this->esc( $type, TRUE );
|
||||
$keys = $this->adapter->get('
|
||||
SELECT
|
||||
information_schema.key_column_usage.constraint_name AS `name`,
|
||||
information_schema.key_column_usage.referenced_table_name AS `table`,
|
||||
information_schema.key_column_usage.column_name AS `from`,
|
||||
information_schema.key_column_usage.referenced_column_name AS `to`,
|
||||
information_schema.referential_constraints.update_rule AS `on_update`,
|
||||
information_schema.referential_constraints.delete_rule AS `on_delete`
|
||||
FROM information_schema.key_column_usage
|
||||
INNER JOIN information_schema.referential_constraints
|
||||
ON (
|
||||
information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name
|
||||
AND information_schema.referential_constraints.constraint_schema = information_schema.key_column_usage.constraint_schema
|
||||
AND information_schema.referential_constraints.constraint_catalog = information_schema.key_column_usage.constraint_catalog
|
||||
)
|
||||
WHERE
|
||||
information_schema.key_column_usage.table_schema IN ( SELECT DATABASE() )
|
||||
AND information_schema.key_column_usage.table_name = ?
|
||||
AND information_schema.key_column_usage.constraint_name != \'PRIMARY\'
|
||||
AND information_schema.key_column_usage.referenced_table_name IS NOT NULL
|
||||
', array($table));
|
||||
$keyInfoList = array();
|
||||
foreach ( $keys as $k ) {
|
||||
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
|
||||
$keyInfoList[$label] = array(
|
||||
'name' => $k['name'],
|
||||
'from' => $k['from'],
|
||||
'table' => $k['table'],
|
||||
'to' => $k['to'],
|
||||
'on_update' => $k['on_update'],
|
||||
'on_delete' => $k['on_delete']
|
||||
);
|
||||
}
|
||||
return $keyInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Adapter $adapter Database Adapter
|
||||
*/
|
||||
public function __construct( Adapter $adapter )
|
||||
{
|
||||
$this->typeno_sqltype = array(
|
||||
Firebird::C_DATATYPE_INTEGER => 'INTEGER',
|
||||
Firebird::C_DATATYPE_FLOAT => 'FLOAT',
|
||||
Firebird::C_DATATYPE_TEXT => 'VARCHAR(8190)',
|
||||
);
|
||||
|
||||
$this->sqltype_typeno = array();
|
||||
|
||||
foreach ( $this->typeno_sqltype as $k => $v ) {
|
||||
$this->sqltype_typeno[trim( strtoupper( $v ) )] = $k;
|
||||
}
|
||||
|
||||
$this->adapter = $adapter;
|
||||
$this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the datatype to be used for primary key IDS and
|
||||
* foreign keys. Returns one if the data type constants.
|
||||
*
|
||||
* @return integer $const data type to be used for IDS.
|
||||
*/
|
||||
public function getTypeForID()
|
||||
{
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getTables
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
return $this->adapter->getCol( 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
|
||||
WHERE RDB$VIEW_BLR IS NULL AND
|
||||
(RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::createTable
|
||||
*/
|
||||
public function createTable( $table )
|
||||
{
|
||||
$tableNoQ = $this->esc( $table );
|
||||
$tableSQL = "CREATE TABLE \"{$table}\" ( id INT )";
|
||||
$dropGeneratorSQL = "DROP GENERATOR gen{$table}";
|
||||
$generatorSQL = "CREATE GENERATOR gen{$table}";
|
||||
$generatorSQL2 = "SET GENERATOR gen{$table} TO 0";
|
||||
$triggerSQL = "
|
||||
CREATE TRIGGER ai{$table} FOR \"{$table}\"
|
||||
ACTIVE BEFORE INSERT POSITION 0
|
||||
AS
|
||||
BEGIN
|
||||
if (NEW.id is NULL) then NEW.id = GEN_ID(gen{$table}, 1);
|
||||
END
|
||||
";
|
||||
|
||||
try { $this->adapter->exec( $dropGeneratorSQL ); }catch( SQLException $e ) {};
|
||||
$this->adapter->exec( $tableSQL );
|
||||
$this->adapter->exec( $generatorSQL );
|
||||
$this->adapter->exec( $generatorSQL2 );
|
||||
$this->adapter->exec( $triggerSQL );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::widenColumn
|
||||
*/
|
||||
public function widenColumn( $type, $property, $dataType )
|
||||
{
|
||||
if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE;
|
||||
|
||||
$table = $this->esc( $type );
|
||||
$column = $this->esc( $property );
|
||||
|
||||
$newType = $this->typeno_sqltype[$dataType];
|
||||
|
||||
$this->adapter->exec( "ALTER TABLE $table ALTER COLUMN $column TYPE $newType " );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getColumns
|
||||
*/
|
||||
public function getColumns( $table )
|
||||
{
|
||||
$columnsRaw = $this->adapter->getAssoc( '
|
||||
SELECT
|
||||
RDB$RELATION_FIELDS.RDB$FIELD_NAME,
|
||||
CASE RDB$FIELDS.RDB$FIELD_TYPE
|
||||
WHEN 10 THEN \'FLOAT\'
|
||||
WHEN 8 THEN \'INTEGER\'
|
||||
WHEN 37 THEN \'VARCHAR\'
|
||||
ELSE RDB$FIELDS.RDB$FIELD_TYPE
|
||||
END AS FTYPE
|
||||
FROM RDB$RELATION_FIELDS
|
||||
LEFT JOIN RDB$FIELDS ON RDB$RELATION_FIELDS.RDB$FIELD_SOURCE = RDB$FIELDS.RDB$FIELD_NAME
|
||||
WHERE RDB$RELATION_FIELDS.RDB$RELATION_NAME = \''.($this->esc($table, true)).'\'
|
||||
ORDER BY RDB$RELATION_FIELDS.RDB$FIELD_POSITION
|
||||
');
|
||||
$columns = array();
|
||||
foreach( $columnsRaw as $rawKey => $columnRaw ) {
|
||||
$columns[ trim( $rawKey ) ] = trim( $columnRaw );
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::scanType
|
||||
*/
|
||||
public function scanType( $value, $flagSpecial = FALSE )
|
||||
{
|
||||
if ( AQueryWriter::canBeTreatedAsInt( $value ) ) {
|
||||
return FireBird::C_DATATYPE_INTEGER;
|
||||
}
|
||||
if ( !$this->startsWithZeros( $value ) && is_numeric( $value ) ) {
|
||||
return FireBird::C_DATATYPE_DOUBLE;
|
||||
}
|
||||
return FireBird::C_DATATYPE_TEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::code
|
||||
*/
|
||||
public function code( $typedescription, $includeSpecials = FALSE )
|
||||
{
|
||||
if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
|
||||
return $this->sqltype_typeno[$typedescription];
|
||||
} else {
|
||||
return self::C_DATATYPE_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addUniqueIndex
|
||||
*/
|
||||
public function addUniqueConstraint( $type, $properties )
|
||||
{
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$columns = array();
|
||||
foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
|
||||
$table = $this->esc( $type );
|
||||
sort( $columns ); // Else we get multiple indexes due to order-effects
|
||||
$name = 'UQ_'.substr( sha1( implode( ',', $columns ) ), 0, 28);
|
||||
try {
|
||||
$sql = "ALTER TABLE $table
|
||||
ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")";
|
||||
$this->adapter->exec( $sql );
|
||||
} catch ( SQLException $e ) {
|
||||
//do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addIndex
|
||||
*/
|
||||
public function addIndex( $type, $name, $property )
|
||||
{
|
||||
try {
|
||||
$table = $this->esc( $type );
|
||||
$name = preg_replace( '/\W/', '', $name );
|
||||
$column = $this->esc( $property );
|
||||
$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
|
||||
return TRUE;
|
||||
} catch ( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addFK
|
||||
*/
|
||||
public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
$targetTable = $this->esc( $targetType );
|
||||
$targetTableNoQ = $this->esc( $targetType, TRUE );
|
||||
$field = $this->esc( $property );
|
||||
$fieldNoQ = $this->esc( $property, TRUE );
|
||||
$targetField = $this->esc( $targetProperty );
|
||||
$targetFieldNoQ = $this->esc( $targetProperty, TRUE );
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$fieldNoQ = $this->esc( $property, TRUE );
|
||||
if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
|
||||
|
||||
//Widen the column if it's incapable of representing a foreign key (at least INT).
|
||||
$columns = $this->getColumns( $tableNoQ );
|
||||
$idType = $this->getTypeForID();
|
||||
if ( $this->code( $columns[$fieldNoQ] ) !== $idType ) {
|
||||
$this->widenColumn( $type, $property, $idType );
|
||||
}
|
||||
|
||||
$fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
|
||||
$cName = 'c_'.$fkName;
|
||||
try {
|
||||
$this->adapter->exec( "
|
||||
ALTER TABLE {$table}
|
||||
ADD CONSTRAINT $cName
|
||||
FOREIGN KEY $fkName ( {$fieldNoQ} ) REFERENCES {$targetTableNoQ}
|
||||
({$targetFieldNoQ}) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';');
|
||||
} catch ( SQLException $e ) {
|
||||
// Failure of fk-constraints is not a problem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::sqlStateIn
|
||||
*/
|
||||
public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
|
||||
{
|
||||
$stateMap = array(
|
||||
'42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
|
||||
'42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
|
||||
'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
|
||||
);
|
||||
|
||||
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::wipeAll
|
||||
*/
|
||||
public function wipeAll()
|
||||
{
|
||||
if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
|
||||
$tables = $this->getTables();
|
||||
foreach( $tables as $table ) {
|
||||
$table = trim( $table );
|
||||
$this->adapter->exec( "DROP TABLE \"{$table}\" " );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
460
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/MySQL.php
vendored
Normal file
460
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/MySQL.php
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\QueryWriter;
|
||||
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
|
||||
/**
|
||||
* RedBeanPHP MySQLWriter.
|
||||
* This is a QueryWriter class for RedBeanPHP.
|
||||
* This QueryWriter provides support for the MySQL/MariaDB database platform.
|
||||
*
|
||||
* @file RedBeanPHP/QueryWriter/MySQL.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @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 MySQL extends AQueryWriter implements QueryWriter
|
||||
{
|
||||
/**
|
||||
* Data types
|
||||
*/
|
||||
const C_DATATYPE_BOOL = 0;
|
||||
const C_DATATYPE_UINT32 = 2;
|
||||
const C_DATATYPE_DOUBLE = 3;
|
||||
const C_DATATYPE_TEXT7 = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
|
||||
const C_DATATYPE_TEXT8 = 5;
|
||||
const C_DATATYPE_TEXT16 = 6;
|
||||
const C_DATATYPE_TEXT32 = 7;
|
||||
const C_DATATYPE_SPECIAL_DATE = 80;
|
||||
const C_DATATYPE_SPECIAL_DATETIME = 81;
|
||||
const C_DATATYPE_SPECIAL_TIME = 83; //MySQL time column (only manual)
|
||||
const C_DATATYPE_SPECIAL_POINT = 90;
|
||||
const C_DATATYPE_SPECIAL_LINESTRING = 91;
|
||||
const C_DATATYPE_SPECIAL_POLYGON = 92;
|
||||
const C_DATATYPE_SPECIAL_MONEY = 93;
|
||||
const C_DATATYPE_SPECIAL_JSON = 94; //JSON support (only manual)
|
||||
|
||||
const C_DATATYPE_SPECIFIED = 99;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $quoteCharacter = '`';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $DDLTemplates = array(
|
||||
'addColumn' => array(
|
||||
'*' => 'ALTER TABLE %s ADD %s %s '
|
||||
),
|
||||
'createTable' => array(
|
||||
'*' => 'CREATE TABLE %s (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET=%s COLLATE=%s '
|
||||
),
|
||||
'widenColumn' => array(
|
||||
'*' => 'ALTER TABLE `%s` CHANGE %s %s %s '
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* @see AQueryWriter::getKeyMapForType
|
||||
*/
|
||||
protected function getKeyMapForType( $type )
|
||||
{
|
||||
$databaseName = $this->adapter->getCell('SELECT DATABASE()');
|
||||
$table = $this->esc( $type, TRUE );
|
||||
$keys = $this->adapter->get('
|
||||
SELECT
|
||||
information_schema.key_column_usage.constraint_name AS `name`,
|
||||
information_schema.key_column_usage.referenced_table_name AS `table`,
|
||||
information_schema.key_column_usage.column_name AS `from`,
|
||||
information_schema.key_column_usage.referenced_column_name AS `to`,
|
||||
information_schema.referential_constraints.update_rule AS `on_update`,
|
||||
information_schema.referential_constraints.delete_rule AS `on_delete`
|
||||
FROM information_schema.key_column_usage
|
||||
INNER JOIN information_schema.referential_constraints
|
||||
ON information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name
|
||||
WHERE
|
||||
information_schema.key_column_usage.table_schema = :database
|
||||
AND information_schema.referential_constraints.constraint_schema = :database
|
||||
AND information_schema.key_column_usage.constraint_schema = :database
|
||||
AND information_schema.key_column_usage.table_name = :table
|
||||
AND information_schema.key_column_usage.constraint_name != \'PRIMARY\'
|
||||
AND information_schema.key_column_usage.referenced_table_name IS NOT NULL
|
||||
', array( ':database' => $databaseName, ':table' => $table ) );
|
||||
$keyInfoList = array();
|
||||
foreach ( $keys as $k ) {
|
||||
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
|
||||
$keyInfoList[$label] = array(
|
||||
'name' => $k['name'],
|
||||
'from' => $k['from'],
|
||||
'table' => $k['table'],
|
||||
'to' => $k['to'],
|
||||
'on_update' => $k['on_update'],
|
||||
'on_delete' => $k['on_delete']
|
||||
);
|
||||
}
|
||||
return $keyInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Most of the time, you do not need to use this constructor,
|
||||
* since the facade takes care of constructing and wiring the
|
||||
* RedBeanPHP core objects. However if you would like to
|
||||
* assemble an OODB instance yourself, this is how it works:
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $database = new RPDO( $dsn, $user, $pass );
|
||||
* $adapter = new DBAdapter( $database );
|
||||
* $writer = new PostgresWriter( $adapter );
|
||||
* $oodb = new OODB( $writer, FALSE );
|
||||
* $bean = $oodb->dispense( 'bean' );
|
||||
* $bean->name = 'coffeeBean';
|
||||
* $id = $oodb->store( $bean );
|
||||
* $bean = $oodb->load( 'bean', $id );
|
||||
* </code>
|
||||
*
|
||||
* The example above creates the 3 RedBeanPHP core objects:
|
||||
* the Adapter, the Query Writer and the OODB instance and
|
||||
* wires them together. The example also demonstrates some of
|
||||
* the methods that can be used with OODB, as you see, they
|
||||
* closely resemble their facade counterparts.
|
||||
*
|
||||
* The wiring process: create an RPDO instance using your database
|
||||
* connection parameters. Create a database adapter from the RPDO
|
||||
* object and pass that to the constructor of the writer. Next,
|
||||
* create an OODB instance from the writer. Now you have an OODB
|
||||
* object.
|
||||
*
|
||||
* @param Adapter $adapter Database Adapter
|
||||
* @param array $options options array
|
||||
*/
|
||||
public function __construct( Adapter $adapter, $options = array() )
|
||||
{
|
||||
$this->typeno_sqltype = array(
|
||||
MySQL::C_DATATYPE_BOOL => ' TINYINT(1) UNSIGNED ',
|
||||
MySQL::C_DATATYPE_UINT32 => ' INT(11) UNSIGNED ',
|
||||
MySQL::C_DATATYPE_DOUBLE => ' DOUBLE ',
|
||||
MySQL::C_DATATYPE_TEXT7 => ' VARCHAR(191) ',
|
||||
MYSQL::C_DATATYPE_TEXT8 => ' VARCHAR(255) ',
|
||||
MySQL::C_DATATYPE_TEXT16 => ' TEXT ',
|
||||
MySQL::C_DATATYPE_TEXT32 => ' LONGTEXT ',
|
||||
MySQL::C_DATATYPE_SPECIAL_DATE => ' DATE ',
|
||||
MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
|
||||
MySQL::C_DATATYPE_SPECIAL_TIME => ' TIME ',
|
||||
MySQL::C_DATATYPE_SPECIAL_POINT => ' POINT ',
|
||||
MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
|
||||
MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
|
||||
MySQL::C_DATATYPE_SPECIAL_MONEY => ' DECIMAL(10,2) ',
|
||||
MYSQL::C_DATATYPE_SPECIAL_JSON => ' JSON '
|
||||
);
|
||||
|
||||
$this->sqltype_typeno = array();
|
||||
|
||||
foreach ( $this->typeno_sqltype as $k => $v ) {
|
||||
$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
|
||||
}
|
||||
|
||||
$this->adapter = $adapter;
|
||||
$this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
|
||||
$me = $this;
|
||||
if (!isset($options['noInitcode']))
|
||||
$this->adapter->setInitCode(function($version) use(&$me) {
|
||||
try {
|
||||
if (strpos($version, 'maria')===FALSE && intval($version)>=8) {
|
||||
$me->useFeature('ignoreDisplayWidth');
|
||||
}
|
||||
} catch( \Exception $e ){}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables certain features/dialects.
|
||||
*
|
||||
* - ignoreDisplayWidth required for MySQL8+
|
||||
* (automatically set by setup() if you pass dsn instead of PDO object)
|
||||
*
|
||||
* @param string $name feature ID
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function useFeature($name) {
|
||||
if ($name == 'ignoreDisplayWidth') {
|
||||
$this->typeno_sqltype[MySQL::C_DATATYPE_BOOL] = ' TINYINT UNSIGNED ';
|
||||
$this->typeno_sqltype[MySQL::C_DATATYPE_UINT32] = ' INT UNSIGNED ';
|
||||
foreach ( $this->typeno_sqltype as $k => $v ) {
|
||||
$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the datatype to be used for primary key IDS and
|
||||
* foreign keys. Returns one if the data type constants.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getTypeForID()
|
||||
{
|
||||
return self::C_DATATYPE_UINT32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getTables
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
return $this->adapter->getCol( 'show tables' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::createTable
|
||||
*/
|
||||
public function createTable( $type )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
|
||||
$charset_collate = $this->adapter->getDatabase()->getMysqlEncoding( TRUE );
|
||||
$charset = $charset_collate['charset'];
|
||||
$collate = $charset_collate['collate'];
|
||||
|
||||
$sql = sprintf( $this->getDDLTemplate( 'createTable', $type ), $table, $charset, $collate );
|
||||
|
||||
$this->adapter->exec( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getColumns
|
||||
*/
|
||||
public function getColumns( $table )
|
||||
{
|
||||
$columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
|
||||
|
||||
$columns = array();
|
||||
foreach ( $columnsRaw as $r ) {
|
||||
$columns[$r['Field']] = $r['Type'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::scanType
|
||||
*/
|
||||
public function scanType( $value, $flagSpecial = FALSE )
|
||||
{
|
||||
$this->svalue = $value;
|
||||
|
||||
if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
|
||||
if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
|
||||
|
||||
if ( $flagSpecial ) {
|
||||
if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
|
||||
return MySQL::C_DATATYPE_SPECIAL_MONEY;
|
||||
}
|
||||
if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
|
||||
return MySQL::C_DATATYPE_SPECIAL_DATE;
|
||||
}
|
||||
if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
|
||||
return MySQL::C_DATATYPE_SPECIAL_DATETIME;
|
||||
}
|
||||
if ( preg_match( '/^POINT\(/', $value ) ) {
|
||||
return MySQL::C_DATATYPE_SPECIAL_POINT;
|
||||
}
|
||||
if ( preg_match( '/^LINESTRING\(/', $value ) ) {
|
||||
return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
|
||||
}
|
||||
if ( preg_match( '/^POLYGON\(/', $value ) ) {
|
||||
return MySQL::C_DATATYPE_SPECIAL_POLYGON;
|
||||
}
|
||||
if ( self::$flagUseJSONColumns && $this->isJSON( $value ) ) {
|
||||
return self::C_DATATYPE_SPECIAL_JSON;
|
||||
}
|
||||
}
|
||||
|
||||
//setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
|
||||
if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' || $value === 0 || $value === 1 ) {
|
||||
return MySQL::C_DATATYPE_BOOL;
|
||||
}
|
||||
|
||||
if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
|
||||
|
||||
if ( !$this->startsWithZeros( $value ) ) {
|
||||
|
||||
if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
|
||||
return MySQL::C_DATATYPE_UINT32;
|
||||
}
|
||||
|
||||
if ( is_numeric( $value ) ) {
|
||||
return MySQL::C_DATATYPE_DOUBLE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
|
||||
return MySQL::C_DATATYPE_TEXT7;
|
||||
}
|
||||
|
||||
if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
|
||||
return MySQL::C_DATATYPE_TEXT8;
|
||||
}
|
||||
|
||||
if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
|
||||
return MySQL::C_DATATYPE_TEXT16;
|
||||
}
|
||||
|
||||
return MySQL::C_DATATYPE_TEXT32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::code
|
||||
*/
|
||||
public function code( $typedescription, $includeSpecials = FALSE )
|
||||
{
|
||||
if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
|
||||
$r = $this->sqltype_typeno[$typedescription];
|
||||
} else {
|
||||
$r = self::C_DATATYPE_SPECIFIED;
|
||||
}
|
||||
|
||||
if ( $includeSpecials ) {
|
||||
return $r;
|
||||
}
|
||||
|
||||
if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
|
||||
return self::C_DATATYPE_SPECIFIED;
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addUniqueIndex
|
||||
*/
|
||||
public function addUniqueConstraint( $type, $properties )
|
||||
{
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$columns = array();
|
||||
foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
|
||||
$table = $this->esc( $type );
|
||||
sort( $columns ); // Else we get multiple indexes due to order-effects
|
||||
$name = 'UQ_' . sha1( implode( ',', $columns ) );
|
||||
try {
|
||||
$sql = "ALTER TABLE $table
|
||||
ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")";
|
||||
$this->adapter->exec( $sql );
|
||||
} catch ( SQLException $e ) {
|
||||
//do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addIndex
|
||||
*/
|
||||
public function addIndex( $type, $name, $property )
|
||||
{
|
||||
try {
|
||||
$table = $this->esc( $type );
|
||||
$name = preg_replace( '/\W/', '', $name );
|
||||
$column = $this->esc( $property );
|
||||
$this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
|
||||
return TRUE;
|
||||
} catch ( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addFK
|
||||
* @return bool
|
||||
*/
|
||||
public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
$targetTable = $this->esc( $targetType );
|
||||
$targetTableNoQ = $this->esc( $targetType, TRUE );
|
||||
$field = $this->esc( $property );
|
||||
$fieldNoQ = $this->esc( $property, TRUE );
|
||||
$targetField = $this->esc( $targetProperty );
|
||||
$targetFieldNoQ = $this->esc( $targetProperty, TRUE );
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$fieldNoQ = $this->esc( $property, TRUE );
|
||||
if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
|
||||
|
||||
//Widen the column if it's incapable of representing a foreign key (at least INT).
|
||||
$columns = $this->getColumns( $tableNoQ );
|
||||
$idType = $this->getTypeForID();
|
||||
if ( $this->code( $columns[$fieldNoQ] ) !== $idType ) {
|
||||
$this->widenColumn( $type, $property, $idType );
|
||||
}
|
||||
|
||||
$fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
|
||||
$cName = 'c_'.$fkName;
|
||||
try {
|
||||
$this->adapter->exec( "
|
||||
ALTER TABLE {$table}
|
||||
ADD CONSTRAINT $cName
|
||||
FOREIGN KEY $fkName ( `{$fieldNoQ}` ) REFERENCES `{$targetTableNoQ}`
|
||||
(`{$targetFieldNoQ}`) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';');
|
||||
} catch ( SQLException $e ) {
|
||||
// Failure of fk-constraints is not a problem
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::sqlStateIn
|
||||
*/
|
||||
public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
|
||||
{
|
||||
$stateMap = array(
|
||||
'42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
|
||||
'42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
|
||||
'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
|
||||
);
|
||||
|
||||
if ( $state == 'HY000' && !empty( $extraDriverDetails[1] ) ) {
|
||||
$driverCode = $extraDriverDetails[1];
|
||||
|
||||
if ( $driverCode == '1205' && in_array( QueryWriter::C_SQLSTATE_LOCK_TIMEOUT, $list ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::wipeAll
|
||||
*/
|
||||
public function wipeAll()
|
||||
{
|
||||
if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
|
||||
$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
|
||||
|
||||
foreach ( $this->getTables() as $t ) {
|
||||
try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
|
||||
try { $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
|
||||
}
|
||||
|
||||
$this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
|
||||
}
|
||||
}
|
||||
436
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/PostgreSQL.php
vendored
Normal file
436
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/PostgreSQL.php
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\QueryWriter;
|
||||
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
|
||||
/**
|
||||
* RedBeanPHP PostgreSQL Query Writer.
|
||||
* This is a QueryWriter class for RedBeanPHP.
|
||||
* This QueryWriter provides support for the PostgreSQL database platform.
|
||||
*
|
||||
* @file RedBeanPHP/QueryWriter/PostgreSQL.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* (c) copyright 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 PostgreSQL extends AQueryWriter implements QueryWriter
|
||||
{
|
||||
/**
|
||||
* Data types
|
||||
*/
|
||||
const C_DATATYPE_INTEGER = 0;
|
||||
const C_DATATYPE_DOUBLE = 1;
|
||||
const C_DATATYPE_TEXT = 3;
|
||||
const C_DATATYPE_SPECIAL_DATE = 80;
|
||||
const C_DATATYPE_SPECIAL_DATETIME = 81;
|
||||
const C_DATATYPE_SPECIAL_TIME = 82; //TIME (no zone) only manual
|
||||
const C_DATATYPE_SPECIAL_TIMEZ = 83; //TIME (plus zone) only manual
|
||||
const C_DATATYPE_SPECIAL_POINT = 90;
|
||||
const C_DATATYPE_SPECIAL_LSEG = 91;
|
||||
const C_DATATYPE_SPECIAL_CIRCLE = 92;
|
||||
const C_DATATYPE_SPECIAL_MONEY = 93;
|
||||
const C_DATATYPE_SPECIAL_POLYGON = 94;
|
||||
const C_DATATYPE_SPECIAL_MONEY2 = 95; //Numbers only money, i.e. fixed point numeric
|
||||
const C_DATATYPE_SPECIAL_JSON = 96; //JSON support (only manual)
|
||||
const C_DATATYPE_SPECIFIED = 99;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $quoteCharacter = '"';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultValue = 'DEFAULT';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $DDLTemplates = array(
|
||||
'addColumn' => array(
|
||||
'*' => 'ALTER TABLE %s ADD %s %s '
|
||||
),
|
||||
'createTable' => array(
|
||||
'*' => 'CREATE TABLE %s (id SERIAL PRIMARY KEY) '
|
||||
),
|
||||
'widenColumn' => array(
|
||||
'*' => 'ALTER TABLE %s ALTER COLUMN %s TYPE %s'
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the insert suffix SQL Snippet
|
||||
*
|
||||
* @param string $table table
|
||||
*
|
||||
* @return string $sql SQL Snippet
|
||||
*/
|
||||
protected function getInsertSuffix( $table )
|
||||
{
|
||||
return 'RETURNING id ';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AQueryWriter::getKeyMapForType
|
||||
*/
|
||||
protected function getKeyMapForType( $type )
|
||||
{
|
||||
$table = $this->esc( $type, TRUE );
|
||||
$keys = $this->adapter->get( '
|
||||
SELECT
|
||||
information_schema.key_column_usage.constraint_name AS "name",
|
||||
information_schema.key_column_usage.column_name AS "from",
|
||||
information_schema.constraint_table_usage.table_name AS "table",
|
||||
information_schema.constraint_column_usage.column_name AS "to",
|
||||
information_schema.referential_constraints.update_rule AS "on_update",
|
||||
information_schema.referential_constraints.delete_rule AS "on_delete"
|
||||
FROM information_schema.key_column_usage
|
||||
INNER JOIN information_schema.constraint_table_usage
|
||||
ON (
|
||||
information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name
|
||||
AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema
|
||||
AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog
|
||||
)
|
||||
INNER JOIN information_schema.constraint_column_usage
|
||||
ON (
|
||||
information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name
|
||||
AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema
|
||||
AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog
|
||||
)
|
||||
INNER JOIN information_schema.referential_constraints
|
||||
ON (
|
||||
information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name
|
||||
AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema
|
||||
AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog
|
||||
)
|
||||
WHERE
|
||||
information_schema.key_column_usage.table_catalog = current_database()
|
||||
AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) )
|
||||
AND information_schema.key_column_usage.table_name = ?
|
||||
', array( $type ) );
|
||||
$keyInfoList = array();
|
||||
foreach ( $keys as $k ) {
|
||||
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
|
||||
$keyInfoList[$label] = array(
|
||||
'name' => $k['name'],
|
||||
'from' => $k['from'],
|
||||
'table' => $k['table'],
|
||||
'to' => $k['to'],
|
||||
'on_update' => $k['on_update'],
|
||||
'on_delete' => $k['on_delete']
|
||||
);
|
||||
}
|
||||
return $keyInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Most of the time, you do not need to use this constructor,
|
||||
* since the facade takes care of constructing and wiring the
|
||||
* RedBeanPHP core objects. However if you would like to
|
||||
* assemble an OODB instance yourself, this is how it works:
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $database = new RPDO( $dsn, $user, $pass );
|
||||
* $adapter = new DBAdapter( $database );
|
||||
* $writer = new PostgresWriter( $adapter );
|
||||
* $oodb = new OODB( $writer, FALSE );
|
||||
* $bean = $oodb->dispense( 'bean' );
|
||||
* $bean->name = 'coffeeBean';
|
||||
* $id = $oodb->store( $bean );
|
||||
* $bean = $oodb->load( 'bean', $id );
|
||||
* </code>
|
||||
*
|
||||
* The example above creates the 3 RedBeanPHP core objects:
|
||||
* the Adapter, the Query Writer and the OODB instance and
|
||||
* wires them together. The example also demonstrates some of
|
||||
* the methods that can be used with OODB, as you see, they
|
||||
* closely resemble their facade counterparts.
|
||||
*
|
||||
* The wiring process: create an RPDO instance using your database
|
||||
* connection parameters. Create a database adapter from the RPDO
|
||||
* object and pass that to the constructor of the writer. Next,
|
||||
* create an OODB instance from the writer. Now you have an OODB
|
||||
* object.
|
||||
*
|
||||
* @param Adapter $adapter Database Adapter
|
||||
*/
|
||||
public function __construct( Adapter $adapter )
|
||||
{
|
||||
$this->typeno_sqltype = array(
|
||||
self::C_DATATYPE_INTEGER => ' integer ',
|
||||
self::C_DATATYPE_DOUBLE => ' double precision ',
|
||||
self::C_DATATYPE_TEXT => ' text ',
|
||||
self::C_DATATYPE_SPECIAL_DATE => ' date ',
|
||||
self::C_DATATYPE_SPECIAL_TIME => ' time ',
|
||||
self::C_DATATYPE_SPECIAL_TIMEZ => ' time with time zone ',
|
||||
self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
|
||||
self::C_DATATYPE_SPECIAL_POINT => ' point ',
|
||||
self::C_DATATYPE_SPECIAL_LSEG => ' lseg ',
|
||||
self::C_DATATYPE_SPECIAL_CIRCLE => ' circle ',
|
||||
self::C_DATATYPE_SPECIAL_MONEY => ' money ',
|
||||
self::C_DATATYPE_SPECIAL_MONEY2 => ' numeric(10,2) ',
|
||||
self::C_DATATYPE_SPECIAL_POLYGON => ' polygon ',
|
||||
self::C_DATATYPE_SPECIAL_JSON => ' json ',
|
||||
);
|
||||
|
||||
$this->sqltype_typeno = array();
|
||||
|
||||
foreach ( $this->typeno_sqltype as $k => $v ) {
|
||||
$this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
|
||||
}
|
||||
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the datatype to be used for primary key IDS and
|
||||
* foreign keys. Returns one if the data type constants.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getTypeForID()
|
||||
{
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getTables
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::createTable
|
||||
*/
|
||||
public function createTable( $type )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
|
||||
$this->adapter->exec( sprintf( $this->getDDLTemplate( 'createTable', $type ), $table ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getColumns
|
||||
*/
|
||||
public function getColumns( $table )
|
||||
{
|
||||
$table = $this->esc( $table, TRUE );
|
||||
|
||||
$columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table' AND table_schema = ANY( current_schemas( FALSE ) )" );
|
||||
|
||||
$columns = array();
|
||||
foreach ( $columnsRaw as $r ) {
|
||||
$columns[$r['column_name']] = $r['data_type'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::scanType
|
||||
*/
|
||||
public function scanType( $value, $flagSpecial = FALSE )
|
||||
{
|
||||
$this->svalue = $value;
|
||||
|
||||
if ( $value === INF ) return self::C_DATATYPE_TEXT;
|
||||
|
||||
if ( $flagSpecial && $value ) {
|
||||
if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
|
||||
return PostgreSQL::C_DATATYPE_SPECIAL_MONEY2;
|
||||
}
|
||||
if ( self::$flagUseJSONColumns && $this->isJSON( $value ) ) {
|
||||
return self::C_DATATYPE_SPECIAL_JSON;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
|
||||
|
||||
if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
|
||||
|
||||
if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value )
|
||||
&& AQueryWriter::canBeTreatedAsInt( $value )
|
||||
&& $value < 2147483648
|
||||
&& $value > -2147483648 )
|
||||
) {
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
} elseif ( is_numeric( $value ) ) {
|
||||
return self::C_DATATYPE_DOUBLE;
|
||||
} else {
|
||||
return self::C_DATATYPE_TEXT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::code
|
||||
*/
|
||||
public function code( $typedescription, $includeSpecials = FALSE )
|
||||
{
|
||||
$r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99;
|
||||
|
||||
if ( $includeSpecials ) return $r;
|
||||
|
||||
if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
|
||||
return self::C_DATATYPE_SPECIFIED;
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::widenColumn
|
||||
*/
|
||||
public function widenColumn( $beanType, $column, $datatype )
|
||||
{
|
||||
$table = $beanType;
|
||||
$type = $datatype;
|
||||
|
||||
$table = $this->esc( $table );
|
||||
$column = $this->esc( $column );
|
||||
|
||||
$newtype = $this->typeno_sqltype[$type];
|
||||
|
||||
$this->adapter->exec( sprintf( $this->getDDLTemplate( 'widenColumn', $beanType, $column ), $table, $column, $newtype ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addUniqueIndex
|
||||
*/
|
||||
public function addUniqueConstraint( $type, $properties )
|
||||
{
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$columns = array();
|
||||
foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
|
||||
$table = $this->esc( $type );
|
||||
sort( $columns ); //else we get multiple indexes due to order-effects
|
||||
$name = "UQ_" . sha1( $table . implode( ',', $columns ) );
|
||||
$sql = "ALTER TABLE {$table}
|
||||
ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")";
|
||||
try {
|
||||
$this->adapter->exec( $sql );
|
||||
} catch( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::sqlStateIn
|
||||
*/
|
||||
public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
|
||||
{
|
||||
$stateMap = array(
|
||||
'42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
|
||||
'42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
|
||||
'23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,
|
||||
'55P03' => QueryWriter::C_SQLSTATE_LOCK_TIMEOUT
|
||||
);
|
||||
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addIndex
|
||||
*/
|
||||
public function addIndex( $type, $name, $property )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
$name = preg_replace( '/\W/', '', $name );
|
||||
$column = $this->esc( $property );
|
||||
|
||||
try {
|
||||
$this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " );
|
||||
return TRUE;
|
||||
} catch ( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addFK
|
||||
*/
|
||||
public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
$targetTable = $this->esc( $targetType );
|
||||
$field = $this->esc( $property );
|
||||
$targetField = $this->esc( $targetProperty );
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$fieldNoQ = $this->esc( $property, TRUE );
|
||||
if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
|
||||
try{
|
||||
$delRule = ( $isDep ? 'CASCADE' : 'SET NULL' );
|
||||
$this->adapter->exec( "ALTER TABLE {$table}
|
||||
ADD FOREIGN KEY ( {$field} ) REFERENCES {$targetTable}
|
||||
({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" );
|
||||
return TRUE;
|
||||
} catch ( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::wipeAll
|
||||
*/
|
||||
public function wipeAll()
|
||||
{
|
||||
if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
|
||||
$this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' );
|
||||
|
||||
foreach ( $this->getTables() as $t ) {
|
||||
$t = $this->esc( $t );
|
||||
//Some plugins (PostGIS have unremovable tables/views), avoid exceptions.
|
||||
try { $this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " ); }catch( \Exception $e ) {}
|
||||
}
|
||||
|
||||
$this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' );
|
||||
}
|
||||
}
|
||||
496
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/SQLiteT.php
vendored
Normal file
496
vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/SQLiteT.php
vendored
Normal file
@@ -0,0 +1,496 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\QueryWriter;
|
||||
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
|
||||
/**
|
||||
* RedBeanPHP SQLiteWriter with support for SQLite types
|
||||
* This is a QueryWriter class for RedBeanPHP.
|
||||
* This QueryWriter provides support for the SQLite database platform.
|
||||
*
|
||||
* @file RedBeanPHP/QueryWriter/SQLiteT.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* (c) copyright 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 SQLiteT extends AQueryWriter implements QueryWriter
|
||||
{
|
||||
/**
|
||||
* Data types
|
||||
*/
|
||||
const C_DATATYPE_INTEGER = 0;
|
||||
const C_DATATYPE_NUMERIC = 1;
|
||||
const C_DATATYPE_TEXT = 2;
|
||||
const C_DATATYPE_SPECIFIED = 99;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $quoteCharacter = '`';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $DDLTemplates = array(
|
||||
'addColumn' => array(
|
||||
'*' => 'ALTER TABLE `%s` ADD `%s` %s'
|
||||
),
|
||||
'createTable' => array(
|
||||
'*' => 'CREATE TABLE %s ( id INTEGER PRIMARY KEY AUTOINCREMENT )'
|
||||
),
|
||||
'widenColumn' => array(
|
||||
'*' => ',`%s` %s '
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets all information about a table (from a type).
|
||||
*
|
||||
* Format:
|
||||
* array(
|
||||
* name => name of the table
|
||||
* columns => array( name => datatype )
|
||||
* indexes => array() raw index information rows from PRAGMA query
|
||||
* keys => array() raw key information rows from PRAGMA query
|
||||
* )
|
||||
*
|
||||
* @param string $type type you want to get info of
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTable( $type )
|
||||
{
|
||||
$tableName = $this->esc( $type, TRUE );
|
||||
$columns = $this->getColumns( $type );
|
||||
$indexes = $this->getIndexes( $type );
|
||||
$keys = $this->getKeyMapForType( $type );
|
||||
|
||||
$table = array(
|
||||
'columns' => $columns,
|
||||
'indexes' => $indexes,
|
||||
'keys' => $keys,
|
||||
'name' => $tableName
|
||||
);
|
||||
|
||||
$this->tableArchive[$tableName] = $table;
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a table. Updates the table structure.
|
||||
* In SQLite we can't change columns, drop columns, change or add foreign keys so we
|
||||
* have a table-rebuild function. You simply load your table with getTable(), modify it and
|
||||
* then store it with putTable()...
|
||||
*
|
||||
* @param array $tableMap information array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function putTable( $tableMap )
|
||||
{
|
||||
$table = $tableMap['name'];
|
||||
$q = array();
|
||||
$q[] = "DROP TABLE IF EXISTS tmp_backup;";
|
||||
|
||||
$oldColumnNames = array_keys( $this->getColumns( $table ) );
|
||||
|
||||
foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`";
|
||||
|
||||
$q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");";
|
||||
$q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
|
||||
$q[] = "PRAGMA foreign_keys = 0 ";
|
||||
$q[] = "DROP TABLE `$table`;";
|
||||
|
||||
$newTableDefStr = '';
|
||||
foreach ( $tableMap['columns'] as $column => $type ) {
|
||||
if ( $column != 'id' ) {
|
||||
$newTableDefStr .= sprintf( $this->getDDLTemplate( 'widenColumn', $table, $column ), $column, $type );
|
||||
}
|
||||
}
|
||||
|
||||
$fkDef = '';
|
||||
foreach ( $tableMap['keys'] as $key ) {
|
||||
$fkDef .= ", FOREIGN KEY(`{$key['from']}`)
|
||||
REFERENCES `{$key['table']}`(`{$key['to']}`)
|
||||
ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}";
|
||||
}
|
||||
|
||||
$q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr $fkDef );";
|
||||
|
||||
foreach ( $tableMap['indexes'] as $name => $index ) {
|
||||
if ( strpos( $name, 'UQ_' ) === 0 ) {
|
||||
$cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) );
|
||||
foreach ( $cols as $k => $v ) $cols[$k] = "`$v`";
|
||||
$q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")";
|
||||
} else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) ";
|
||||
}
|
||||
|
||||
$q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
|
||||
$q[] = "DROP TABLE tmp_backup;";
|
||||
$q[] = "PRAGMA foreign_keys = 1 ";
|
||||
|
||||
foreach ( $q as $sq ) $this->adapter->exec( $sq );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the an array describing the indexes for type $type.
|
||||
*
|
||||
* @param string $type type to describe indexes of
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getIndexes( $type )
|
||||
{
|
||||
$table = $this->esc( $type, TRUE );
|
||||
$indexes = $this->adapter->get( "PRAGMA index_list('$table')" );
|
||||
|
||||
$indexInfoList = array();
|
||||
foreach ( $indexes as $i ) {
|
||||
$indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " );
|
||||
|
||||
$indexInfoList[$i['name']]['unique'] = $i['unique'];
|
||||
}
|
||||
|
||||
return $indexInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a foreign key to a type.
|
||||
* Note: cant put this in try-catch because that can hide the fact
|
||||
* that database has been damaged.
|
||||
*
|
||||
* @param string $type type you want to modify table of
|
||||
* @param string $targetType target type
|
||||
* @param string $field field of the type that needs to get the fk
|
||||
* @param string $targetField field where the fk needs to point to
|
||||
* @param integer $buildopt 0 = NO ACTION, 1 = ON DELETE CASCADE
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE )
|
||||
{
|
||||
$table = $this->esc( $type, TRUE );
|
||||
$targetTable = $this->esc( $targetType, TRUE );
|
||||
$column = $this->esc( $property, TRUE );
|
||||
$targetColumn = $this->esc( $targetProperty, TRUE );
|
||||
|
||||
$tables = $this->getTables();
|
||||
if ( !in_array( $targetTable, $tables ) ) return FALSE;
|
||||
|
||||
if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE;
|
||||
$t = $this->getTable( $table );
|
||||
$consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' );
|
||||
$label = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn;
|
||||
$t['keys'][$label] = array(
|
||||
'table' => $targetTable,
|
||||
'from' => $column,
|
||||
'to' => $targetColumn,
|
||||
'on_update' => $consSQL,
|
||||
'on_delete' => $consSQL
|
||||
);
|
||||
$this->putTable( $t );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AQueryWriter::getKeyMapForType
|
||||
*/
|
||||
protected function getKeyMapForType( $type )
|
||||
{
|
||||
$table = $this->esc( $type, TRUE );
|
||||
$keys = $this->adapter->get( "PRAGMA foreign_key_list('$table')" );
|
||||
$keyInfoList = array();
|
||||
foreach ( $keys as $k ) {
|
||||
$label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
|
||||
$keyInfoList[$label] = array(
|
||||
'name' => $label,
|
||||
'from' => $k['from'],
|
||||
'table' => $k['table'],
|
||||
'to' => $k['to'],
|
||||
'on_update' => $k['on_update'],
|
||||
'on_delete' => $k['on_delete']
|
||||
);
|
||||
}
|
||||
return $keyInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Most of the time, you do not need to use this constructor,
|
||||
* since the facade takes care of constructing and wiring the
|
||||
* RedBeanPHP core objects. However if you would like to
|
||||
* assemble an OODB instance yourself, this is how it works:
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $database = new RPDO( $dsn, $user, $pass );
|
||||
* $adapter = new DBAdapter( $database );
|
||||
* $writer = new PostgresWriter( $adapter );
|
||||
* $oodb = new OODB( $writer, FALSE );
|
||||
* $bean = $oodb->dispense( 'bean' );
|
||||
* $bean->name = 'coffeeBean';
|
||||
* $id = $oodb->store( $bean );
|
||||
* $bean = $oodb->load( 'bean', $id );
|
||||
* </code>
|
||||
*
|
||||
* The example above creates the 3 RedBeanPHP core objects:
|
||||
* the Adapter, the Query Writer and the OODB instance and
|
||||
* wires them together. The example also demonstrates some of
|
||||
* the methods that can be used with OODB, as you see, they
|
||||
* closely resemble their facade counterparts.
|
||||
*
|
||||
* The wiring process: create an RPDO instance using your database
|
||||
* connection parameters. Create a database adapter from the RPDO
|
||||
* object and pass that to the constructor of the writer. Next,
|
||||
* create an OODB instance from the writer. Now you have an OODB
|
||||
* object.
|
||||
*
|
||||
* @param Adapter $adapter Database Adapter
|
||||
*/
|
||||
public function __construct( Adapter $adapter )
|
||||
{
|
||||
$this->typeno_sqltype = array(
|
||||
SQLiteT::C_DATATYPE_INTEGER => 'INTEGER',
|
||||
SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC',
|
||||
SQLiteT::C_DATATYPE_TEXT => 'TEXT',
|
||||
);
|
||||
|
||||
$this->sqltype_typeno = array();
|
||||
|
||||
foreach ( $this->typeno_sqltype as $k => $v ) {
|
||||
$this->sqltype_typeno[$v] = $k;
|
||||
}
|
||||
|
||||
$this->adapter = $adapter;
|
||||
$this->adapter->setOption( 'setInitQuery', ' PRAGMA foreign_keys = 1 ' );
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the datatype to be used for primary key IDS and
|
||||
* foreign keys. Returns one if the data type constants.
|
||||
*
|
||||
* @return integer $const data type to be used for IDS.
|
||||
*/
|
||||
public function getTypeForID()
|
||||
{
|
||||
return self::C_DATATYPE_INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::scanType
|
||||
*/
|
||||
public function scanType( $value, $flagSpecial = FALSE )
|
||||
{
|
||||
$this->svalue = $value;
|
||||
|
||||
if ( $value === NULL ) return self::C_DATATYPE_INTEGER;
|
||||
if ( $value === INF ) return self::C_DATATYPE_TEXT;
|
||||
|
||||
if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT;
|
||||
|
||||
if ( $value === TRUE || $value === FALSE ) return self::C_DATATYPE_INTEGER;
|
||||
|
||||
if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER;
|
||||
|
||||
if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648)
|
||||
|| preg_match( '/\d{4}\-\d\d\-\d\d/', $value )
|
||||
|| preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value )
|
||||
) {
|
||||
return self::C_DATATYPE_NUMERIC;
|
||||
}
|
||||
|
||||
return self::C_DATATYPE_TEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addColumn
|
||||
*/
|
||||
public function addColumn( $table, $column, $type )
|
||||
{
|
||||
$column = $this->check( $column );
|
||||
$table = $this->check( $table );
|
||||
$type = $this->typeno_sqltype[$type];
|
||||
|
||||
$this->adapter->exec( sprintf( $this->getDDLTemplate( 'addColumn', $table, $column ), $table, $column, $type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::code
|
||||
*/
|
||||
public function code( $typedescription, $includeSpecials = FALSE )
|
||||
{
|
||||
$r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 );
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::widenColumn
|
||||
*/
|
||||
public function widenColumn( $type, $column, $datatype )
|
||||
{
|
||||
$t = $this->getTable( $type );
|
||||
|
||||
$t['columns'][$column] = $this->typeno_sqltype[$datatype];
|
||||
|
||||
$this->putTable( $t );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getTables();
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
return $this->adapter->getCol( "SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name!='sqlite_sequence';" );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::createTable
|
||||
*/
|
||||
public function createTable( $type )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
|
||||
$sql = sprintf( $this->getDDLTemplate( 'createTable', $type ), $table );
|
||||
|
||||
$this->adapter->exec( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::getColumns
|
||||
*/
|
||||
public function getColumns( $table )
|
||||
{
|
||||
$table = $this->esc( $table, TRUE );
|
||||
|
||||
$columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" );
|
||||
|
||||
$columns = array();
|
||||
foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type'];
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addUniqueIndex
|
||||
*/
|
||||
public function addUniqueConstraint( $type, $properties )
|
||||
{
|
||||
$tableNoQ = $this->esc( $type, TRUE );
|
||||
$name = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties );
|
||||
$t = $this->getTable( $type );
|
||||
$t['indexes'][$name] = array( 'name' => $name );
|
||||
try {
|
||||
$this->putTable( $t );
|
||||
} catch( SQLException $e ) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::sqlStateIn
|
||||
*/
|
||||
public function sqlStateIn( $state, $list, $extraDriverDetails = array() )
|
||||
{
|
||||
$stateMap = array(
|
||||
'23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
|
||||
);
|
||||
if ( $state == 'HY000'
|
||||
&& isset($extraDriverDetails[1])
|
||||
&& $extraDriverDetails[1] == 1
|
||||
&& ( in_array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, $list )
|
||||
|| in_array( QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, $list )
|
||||
)) {
|
||||
return TRUE;
|
||||
}
|
||||
return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an SQL snippet to be used for the next queryRecord() operation.
|
||||
* SQLite has no SELECT-FOR-UPDATE and filters this.
|
||||
*
|
||||
* @param string $sql SQL snippet to use in SELECT statement.
|
||||
*
|
||||
* return self
|
||||
*/
|
||||
public function setSQLSelectSnippet( $sqlSelectSnippet = '' ) {
|
||||
if ( $sqlSelectSnippet === AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE) $sqlSelectSnippet = '';
|
||||
$this->sqlSelectSnippet = $sqlSelectSnippet;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addIndex
|
||||
*/
|
||||
public function addIndex( $type, $name, $column )
|
||||
{
|
||||
$columns = $this->getColumns( $type );
|
||||
if ( !isset( $columns[$column] ) ) return FALSE;
|
||||
|
||||
$table = $this->esc( $type );
|
||||
$name = preg_replace( '/\W/', '', $name );
|
||||
$column = $this->esc( $column, TRUE );
|
||||
|
||||
try {
|
||||
$t = $this->getTable( $type );
|
||||
$t['indexes'][$name] = array( 'name' => $column );
|
||||
$this->putTable( $t );
|
||||
return TRUE;
|
||||
} catch( SQLException $exception ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::wipe
|
||||
*/
|
||||
public function wipe( $type )
|
||||
{
|
||||
$table = $this->esc( $type );
|
||||
|
||||
$this->adapter->exec( "DELETE FROM $table " );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::addFK
|
||||
*/
|
||||
public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE )
|
||||
{
|
||||
return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep );
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryWriter::wipeAll
|
||||
*/
|
||||
public function wipeAll()
|
||||
{
|
||||
if (AQueryWriter::$noNuke) throw new \Exception('The nuke() command has been disabled using noNuke() or R::feature(novice/...).');
|
||||
$this->adapter->exec( 'PRAGMA foreign_keys = 0 ' );
|
||||
|
||||
foreach ( $this->getTables() as $t ) {
|
||||
try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
|
||||
try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); } catch ( SQLException $e ) { ; }
|
||||
}
|
||||
|
||||
$this->adapter->exec( 'PRAGMA foreign_keys = 1 ' );
|
||||
}
|
||||
}
|
||||
21
vendor/gabordemooij/redbean/RedBeanPHP/R.php
vendored
Normal file
21
vendor/gabordemooij/redbean/RedBeanPHP/R.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* R-Facade (for Composer)
|
||||
*
|
||||
* If you use Composer you don't use the rb.php file which
|
||||
* has the R-facade, so here is a separate, namespaced R-facade for
|
||||
* those that prefer this.
|
||||
*
|
||||
* An alternative option might be to alias RedBeanPHP/Facade.
|
||||
*
|
||||
* @file RedBeanPHP/R.php
|
||||
* @author Simirimia
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
*/
|
||||
class R extends Facade
|
||||
{
|
||||
}
|
||||
20
vendor/gabordemooij/redbean/RedBeanPHP/RedException.php
vendored
Normal file
20
vendor/gabordemooij/redbean/RedBeanPHP/RedException.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
/**
|
||||
* RedBean\Exception Base.
|
||||
* Represents the base class for RedBeanPHP\Exceptions.
|
||||
*
|
||||
* @file RedBeanPHP/Exception.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 RedException extends \Exception
|
||||
{
|
||||
}
|
||||
81
vendor/gabordemooij/redbean/RedBeanPHP/RedException/SQL.php
vendored
Normal file
81
vendor/gabordemooij/redbean/RedBeanPHP/RedException/SQL.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\RedException;
|
||||
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
|
||||
/**
|
||||
* SQL Exception.
|
||||
* Represents a generic database exception independent of the underlying driver.
|
||||
*
|
||||
* @file RedBeanPHP/RedException/SQL.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license BSD/GPLv2
|
||||
*
|
||||
* @copyright
|
||||
* (c) copyright 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 SQL extends RedException
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sqlState;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $driverDetails = array();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDriverDetails()
|
||||
{
|
||||
return $this->driverDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $driverDetails
|
||||
*/
|
||||
public function setDriverDetails($driverDetails)
|
||||
{
|
||||
$this->driverDetails = $driverDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ANSI-92 compliant SQL state.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSQLState()
|
||||
{
|
||||
return $this->sqlState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw SQL STATE, possibly compliant with
|
||||
* ANSI SQL error codes - but this depends on database driver.
|
||||
*
|
||||
* @param string $sqlState SQL state error code
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSQLState( $sqlState )
|
||||
{
|
||||
$this->sqlState = $sqlState;
|
||||
}
|
||||
|
||||
/**
|
||||
* To String prints both code and SQL state.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n".
|
||||
'trace: ' . $this->getTraceAsString();
|
||||
}
|
||||
}
|
||||
741
vendor/gabordemooij/redbean/RedBeanPHP/Repository.php
vendored
Normal file
741
vendor/gabordemooij/redbean/RedBeanPHP/Repository.php
vendored
Normal file
@@ -0,0 +1,741 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\BeanHelper as BeanHelper;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
use RedBeanPHP\Cursor as Cursor;
|
||||
use RedBeanPHP\Cursor\NullCursor as NullCursor;
|
||||
|
||||
/**
|
||||
* Abstract Repository.
|
||||
*
|
||||
* OODB manages two repositories, a fluid one that
|
||||
* adjust the database schema on-the-fly to accomodate for
|
||||
* new bean types (tables) and new properties (columns) and
|
||||
* a frozen one for use in a production environment. OODB
|
||||
* allows you to swap the repository instances using the freeze()
|
||||
* method.
|
||||
*
|
||||
* @file RedBeanPHP/Repository.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.
|
||||
*/
|
||||
abstract class Repository
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $stash = NULL;
|
||||
|
||||
/*
|
||||
* @var integer
|
||||
*/
|
||||
protected $nesting = 0;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $partialBeans = FALSE;
|
||||
|
||||
/**
|
||||
* Toggles 'partial bean mode'. If this mode has been
|
||||
* selected the repository will only update the fields of a bean that
|
||||
* have been changed rather than the entire bean.
|
||||
* Pass the value TRUE to select 'partial mode' for all beans.
|
||||
* Pass the value FALSE to disable 'partial mode'.
|
||||
* Pass an array of bean types if you wish to use partial mode only
|
||||
* for some types.
|
||||
* This method will return the previous value.
|
||||
*
|
||||
* @param boolean|array $yesNoBeans List of type names or 'all'
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function usePartialBeans( $yesNoBeans )
|
||||
{
|
||||
$oldValue = $this->partialBeans;
|
||||
$this->partialBeans = $yesNoBeans;
|
||||
return $oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully processes a bean and updates the associated records in the database.
|
||||
* First the bean properties will be grouped as 'embedded' bean,
|
||||
* addition, deleted 'trash can' or residue. Next, the different groups
|
||||
* of beans will be processed accordingly and the reference bean (i.e.
|
||||
* the one that was passed to the method as an argument) will be stored.
|
||||
* Each type of list (own/shared) has 3 bean processors:
|
||||
*
|
||||
* - trashCanProcessor : removes the bean or breaks its association with the current bean
|
||||
* - additionProcessor : associates the bean with the current one
|
||||
* - residueProcessor : manages beans in lists that 'remain' but may need to be updated
|
||||
*
|
||||
* This method first groups the beans and then calls the
|
||||
* internal processing methods.
|
||||
*
|
||||
* @param OODBBean $bean bean to process
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function storeBeanWithLists( OODBBean $bean )
|
||||
{
|
||||
$sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups
|
||||
foreach ( $bean as $property => $value ) {
|
||||
$value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value;
|
||||
if ( $value instanceof OODBBean ) {
|
||||
$this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value );
|
||||
$bean->setMeta("sys.typeof.{$property}", $value->getMeta('type'));
|
||||
} elseif ( is_array( $value ) ) {
|
||||
foreach($value as &$item) {
|
||||
$item = ( $item instanceof SimpleModel ) ? $item->unbox() : $item;
|
||||
}
|
||||
$originals = $bean->moveMeta( 'sys.shadow.' . $property, array() );
|
||||
if ( strpos( $property, 'own' ) === 0 ) {
|
||||
list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue );
|
||||
$listName = lcfirst( substr( $property, 3 ) );
|
||||
if ($bean->moveMeta( 'sys.exclusive-'. $listName ) ) {
|
||||
OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE );
|
||||
OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) );
|
||||
}
|
||||
unset( $bean->$property );
|
||||
} elseif ( strpos( $property, 'shared' ) === 0 ) {
|
||||
list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue );
|
||||
unset( $bean->$property );
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->storeBean( $bean );
|
||||
$this->processTrashcan( $bean, $ownTrashcan );
|
||||
$this->processAdditions( $bean, $ownAdditions );
|
||||
$this->processResidue( $ownresidue );
|
||||
$this->processSharedTrashcan( $bean, $sharedTrashcan );
|
||||
$this->processSharedAdditions( $bean, $sharedAdditions );
|
||||
$this->processSharedResidue( $bean, $sharedresidue );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process groups. Internal function. Processes different kind of groups for
|
||||
* storage function. Given a list of original beans and a list of current beans,
|
||||
* this function calculates which beans remain in the list (residue), which
|
||||
* have been deleted (are in the trashcan) and which beans have been added
|
||||
* (additions).
|
||||
*
|
||||
* @param array $originals originals
|
||||
* @param array $current the current beans
|
||||
* @param array $additions beans that have been added
|
||||
* @param array $trashcan beans that have been deleted
|
||||
* @param array $residue beans that have been left untouched
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function processGroups( $originals, $current, $additions, $trashcan, $residue )
|
||||
{
|
||||
return array(
|
||||
array_merge( $additions, array_diff( $current, $originals ) ),
|
||||
array_merge( $trashcan, array_diff( $originals, $current ) ),
|
||||
array_merge( $residue, array_intersect( $current, $originals ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a list of beans from a bean.
|
||||
* A bean may contain lists. This
|
||||
* method handles shared addition lists; i.e.
|
||||
* the $bean->sharedObject properties.
|
||||
* Shared beans will be associated with eachother using the
|
||||
* Association Manager.
|
||||
*
|
||||
* @param OODBBean $bean the bean
|
||||
* @param array $sharedAdditions list with shared additions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processSharedAdditions( $bean, $sharedAdditions )
|
||||
{
|
||||
foreach ( $sharedAdditions as $addition ) {
|
||||
if ( $addition instanceof OODBBean ) {
|
||||
$this->oodb->getAssociationManager()->associate( $addition, $bean );
|
||||
} else {
|
||||
throw new RedException( 'Array may only contain OODBBeans' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a list of beans from a bean.
|
||||
* A bean may contain lists. This
|
||||
* method handles own lists; i.e.
|
||||
* the $bean->ownObject properties.
|
||||
* A residue is a bean in an own-list that stays
|
||||
* where it is. This method checks if there have been any
|
||||
* modification to this bean, in that case
|
||||
* the bean is stored once again, otherwise the bean will be left untouched.
|
||||
*
|
||||
* @param array $ownresidue list to process
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processResidue( $ownresidue )
|
||||
{
|
||||
foreach ( $ownresidue as $residue ) {
|
||||
if ( $residue->getMeta( 'tainted' ) ) {
|
||||
$this->store( $residue );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a list of beans from a bean. A bean may contain lists. This
|
||||
* method handles own lists; i.e. the $bean->ownObject properties.
|
||||
* A trash can bean is a bean in an own-list that has been removed
|
||||
* (when checked with the shadow). This method
|
||||
* checks if the bean is also in the dependency list. If it is the bean will be removed.
|
||||
* If not, the connection between the bean and the owner bean will be broken by
|
||||
* setting the ID to NULL.
|
||||
*
|
||||
* @param OODBBean $bean bean to process
|
||||
* @param array $ownTrashcan list to process
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processTrashcan( $bean, $ownTrashcan )
|
||||
{
|
||||
foreach ( $ownTrashcan as $trash ) {
|
||||
|
||||
$myFieldLink = $bean->getMeta( 'type' ) . '_id';
|
||||
$alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) );
|
||||
if ( $alias ) $myFieldLink = $alias . '_id';
|
||||
|
||||
if ( $trash->getMeta( 'sys.garbage' ) === TRUE ) {
|
||||
$this->trash( $trash );
|
||||
} else {
|
||||
$trash->$myFieldLink = NULL;
|
||||
$this->store( $trash );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassociates the list items in the trashcan.
|
||||
* This bean processor processes the beans in the shared trash can.
|
||||
* This group of beans has been deleted from a shared list.
|
||||
* The affected beans will no longer be associated with the bean
|
||||
* that contains the shared list.
|
||||
*
|
||||
* @param OODBBean $bean bean to process
|
||||
* @param array $sharedTrashcan list to process
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processSharedTrashcan( $bean, $sharedTrashcan )
|
||||
{
|
||||
foreach ( $sharedTrashcan as $trash ) {
|
||||
$this->oodb->getAssociationManager()->unassociate( $trash, $bean );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores all the beans in the residue group.
|
||||
* This bean processor processes the beans in the shared residue
|
||||
* group. This group of beans 'remains' in the list but might need
|
||||
* to be updated or synced. The affected beans will be stored
|
||||
* to perform the required database queries.
|
||||
*
|
||||
* @param OODBBean $bean bean to process
|
||||
* @param array $sharedresidue list to process
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processSharedResidue( $bean, $sharedresidue )
|
||||
{
|
||||
foreach ( $sharedresidue as $residue ) {
|
||||
$this->store( $residue );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the bean has 'loaded lists' or
|
||||
* 'loaded embedded beans' that need to be processed
|
||||
* by the store() method.
|
||||
*
|
||||
* @param OODBBean $bean bean to be examined
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function hasListsOrObjects( OODBBean $bean )
|
||||
{
|
||||
$processLists = FALSE;
|
||||
foreach ( $bean as $value ) {
|
||||
if ( is_array( $value ) || is_object( $value ) ) {
|
||||
$processLists = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $processLists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an embedded bean to an ID, removes the bean property and
|
||||
* stores the bean in the embedded beans array. The id will be
|
||||
* assigned to the link field property, i.e. 'bean_id'.
|
||||
*
|
||||
* @param array $embeddedBeans destination array for embedded bean
|
||||
* @param OODBBean $bean target bean to process
|
||||
* @param string $property property that contains the embedded bean
|
||||
* @param OODBBean $value embedded bean itself
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value )
|
||||
{
|
||||
$linkField = $property . '_id';
|
||||
if ( !$value->id || $value->getMeta( 'tainted' ) ) {
|
||||
$this->store( $value );
|
||||
}
|
||||
$id = $value->id;
|
||||
if ($bean->$linkField != $id) $bean->$linkField = $id;
|
||||
$bean->setMeta( 'cast.' . $linkField, 'id' );
|
||||
$embeddedBeans[$linkField] = $value;
|
||||
unset( $bean->$property );
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor, requires a query writer and OODB.
|
||||
* Creates a new instance of the bean respository class.
|
||||
*
|
||||
* @param OODB $oodb instance of object database
|
||||
* @param QueryWriter $writer the Query Writer to use for this repository
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( OODB $oodb, QueryWriter $writer )
|
||||
{
|
||||
$this->writer = $writer;
|
||||
$this->oodb = $oodb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. To be valid a bean
|
||||
* must abide to the following rules:
|
||||
*
|
||||
* - It must have an primary key id property named: id
|
||||
* - It must have a type
|
||||
* - The type must conform to the RedBeanPHP naming policy
|
||||
* - All properties must be valid
|
||||
* - All values must be valid
|
||||
*
|
||||
* @param OODBBean $bean the bean that needs to be checked
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check( OODBBean $bean )
|
||||
{
|
||||
//Is all meta information present?
|
||||
if ( !isset( $bean->id ) ) {
|
||||
throw new RedException( 'Bean has incomplete Meta Information id ' );
|
||||
}
|
||||
if ( !( $bean->getMeta( 'type' ) ) ) {
|
||||
throw new RedException( 'Bean has incomplete Meta Information II' );
|
||||
}
|
||||
//Pattern of allowed characters
|
||||
$pattern = '/[^a-z0-9_]/i';
|
||||
//Does the type contain invalid characters?
|
||||
if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) {
|
||||
throw new RedException( 'Bean Type is invalid' );
|
||||
}
|
||||
//Are the properties and values valid?
|
||||
foreach ( $bean as $prop => $value ) {
|
||||
if (
|
||||
is_array( $value )
|
||||
|| ( is_object( $value ) )
|
||||
) {
|
||||
throw new RedException( "Invalid Bean value: property $prop" );
|
||||
} else if (
|
||||
strlen( $prop ) < 1
|
||||
|| preg_match( $pattern, $prop )
|
||||
) {
|
||||
throw new RedException( "Invalid Bean property: property $prop" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* To use a different class for beans (instead of OODBBean) set:
|
||||
* REDBEAN_OODBBEAN_CLASS to the name of the class to be used.
|
||||
*
|
||||
* @param string $type type of bean you want to dispense
|
||||
* @param int $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 )
|
||||
{
|
||||
$OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean';
|
||||
$beans = array();
|
||||
for ( $i = 0; $i < $number; $i++ ) {
|
||||
$bean = new $OODBBEAN;
|
||||
$bean->initializeForDispense( $type, $this->oodb->getBeanHelper() );
|
||||
$this->check( $bean );
|
||||
$this->oodb->signal( 'dispense', $bean );
|
||||
$beans[] = $bean;
|
||||
}
|
||||
|
||||
return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $sql SQL to be used in query
|
||||
* @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() )
|
||||
{
|
||||
//for backward compatibility, allow mismatch arguments:
|
||||
if ( is_array( $sql ) ) {
|
||||
if ( isset( $sql[1] ) ) {
|
||||
$bindings = $sql[1];
|
||||
}
|
||||
$sql = $sql[0];
|
||||
}
|
||||
try {
|
||||
$beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) );
|
||||
|
||||
return $beans;
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a BeanCollection.
|
||||
* Given a type, an SQL snippet and optionally some parameter bindings
|
||||
* this methods returns a BeanCollection for your query.
|
||||
*
|
||||
* The BeanCollection represents a collection of beans and
|
||||
* makes it possible to use database cursors. The BeanCollection
|
||||
* has a method next() to obtain the first, next and last bean
|
||||
* in the collection. The BeanCollection does not implement the array
|
||||
* interface nor does it try to act like an array because it cannot go
|
||||
* backward or rewind itself.
|
||||
*
|
||||
* @param string $type type of beans you are looking for
|
||||
* @param string $sql SQL to be used in query
|
||||
* @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not)
|
||||
*
|
||||
* @return BeanCollection
|
||||
*/
|
||||
public function findCollection( $type, $sql, $bindings = array() )
|
||||
{
|
||||
try {
|
||||
$cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings );
|
||||
return new BeanCollection( $type, $this, $cursor );
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
}
|
||||
return new BeanCollection( $type, $this, new NullCursor );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
$processLists = $this->hasListsOrObjects( $bean );
|
||||
if ( !$processLists && !$bean->getMeta( 'tainted' ) ) {
|
||||
return $bean->getID(); //bail out!
|
||||
}
|
||||
$this->oodb->signal( 'update', $bean );
|
||||
$processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model!
|
||||
if ( $processLists ) {
|
||||
$this->storeBeanWithLists( $bean );
|
||||
} else {
|
||||
$this->storeBean( $bean );
|
||||
}
|
||||
$this->oodb->signal( 'after_update', $bean );
|
||||
|
||||
return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
if ( !$ids ) {
|
||||
return array();
|
||||
}
|
||||
$collection = array();
|
||||
try {
|
||||
$rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) );
|
||||
} catch ( SQLException $e ) {
|
||||
$this->handleException( $e );
|
||||
$rows = FALSE;
|
||||
}
|
||||
$this->stash[$this->nesting] = array();
|
||||
if ( !$rows ) {
|
||||
return array();
|
||||
}
|
||||
foreach ( $rows as $row ) {
|
||||
$this->stash[$this->nesting][$row['id']] = $row;
|
||||
}
|
||||
foreach ( $ids as $id ) {
|
||||
$collection[$id] = $this->load( $type, $id );
|
||||
}
|
||||
$this->stash[$this->nesting] = NULL;
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* New in 4.3.2: meta mask. The meta mask is a special mask to send
|
||||
* data from raw result rows to the meta store of the bean. This is
|
||||
* useful for bundling additional information with custom queries.
|
||||
* Values of every column whos name starts with $mask will be
|
||||
* transferred to the meta section of the bean under key 'data.bundle'.
|
||||
*
|
||||
* @param string $type type of beans you would like to have
|
||||
* @param array $rows rows from the database result
|
||||
* @param string $mask meta mask to apply (optional)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function convertToBeans( $type, $rows, $mask = '__meta' )
|
||||
{
|
||||
$masktype = gettype( $mask );
|
||||
switch ( $masktype ) {
|
||||
case 'string':
|
||||
break;
|
||||
case 'array':
|
||||
$maskflip = array();
|
||||
foreach ( $mask as $m ) {
|
||||
if ( !is_string( $m ) ) {
|
||||
$mask = NULL;
|
||||
$masktype = 'NULL';
|
||||
break 2;
|
||||
}
|
||||
$maskflip[$m] = TRUE;
|
||||
}
|
||||
$mask = $maskflip;
|
||||
break;
|
||||
default:
|
||||
$mask = NULL;
|
||||
$masktype = 'NULL';
|
||||
}
|
||||
|
||||
$collection = array();
|
||||
$this->stash[$this->nesting] = array();
|
||||
foreach ( $rows as $row ) {
|
||||
if ( $mask !== NULL ) {
|
||||
$meta = array();
|
||||
foreach( $row as $key => $value ) {
|
||||
if ( $masktype === 'string' ) {
|
||||
if ( strpos( $key, $mask ) === 0 ) {
|
||||
unset( $row[$key] );
|
||||
$meta[$key] = $value;
|
||||
}
|
||||
} elseif ( $masktype === 'array' ) {
|
||||
if ( isset( $mask[$key] ) ) {
|
||||
unset( $row[$key] );
|
||||
$meta[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$id = $row['id'];
|
||||
$this->stash[$this->nesting][$id] = $row;
|
||||
$collection[$id] = $this->load( $type, $id );
|
||||
|
||||
if ( $mask !== NULL ) {
|
||||
$collection[$id]->setMeta( 'data.bundle', $meta );
|
||||
}
|
||||
}
|
||||
$this->stash[$this->nesting] = NULL;
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() )
|
||||
{
|
||||
$type = AQueryWriter::camelsSnake( $type );
|
||||
if ( count( explode( '_', $type ) ) > 2 ) {
|
||||
throw new RedException( 'Invalid type for count.' );
|
||||
}
|
||||
|
||||
try {
|
||||
$count = (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings );
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
$count = 0;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
$this->oodb->signal( 'delete', $bean );
|
||||
foreach ( $bean as $property => $value ) {
|
||||
if ( $value instanceof OODBBean ) {
|
||||
unset( $bean->$property );
|
||||
}
|
||||
if ( is_array( $value ) ) {
|
||||
if ( strpos( $property, 'own' ) === 0 ) {
|
||||
unset( $bean->$property );
|
||||
} elseif ( strpos( $property, 'shared' ) === 0 ) {
|
||||
unset( $bean->$property );
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
$deleted = $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL );
|
||||
} catch ( SQLException $exception ) {
|
||||
$this->handleException( $exception );
|
||||
}
|
||||
$bean->id = 0;
|
||||
$this->oodb->signal( 'after_delete', $bean );
|
||||
return isset($deleted) ? $deleted : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->writer->tableExists( $table );
|
||||
}
|
||||
|
||||
/**
|
||||
* Trash all beans of a given type.
|
||||
* Wipes an entire type of bean. After this operation there
|
||||
* will be no beans left of the specified type.
|
||||
* This method will ignore exceptions caused by database
|
||||
* tables that do not exist.
|
||||
*
|
||||
* @param string $type type of bean you wish to delete all instances of
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function wipe( $type )
|
||||
{
|
||||
try {
|
||||
$this->writer->wipe( $type );
|
||||
|
||||
return TRUE;
|
||||
} catch ( SQLException $exception ) {
|
||||
if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ), $exception->getDriverDetails() ) ) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
323
vendor/gabordemooij/redbean/RedBeanPHP/Repository/Fluid.php
vendored
Normal file
323
vendor/gabordemooij/redbean/RedBeanPHP/Repository/Fluid.php
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Repository;
|
||||
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\BeanHelper as BeanHelper;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
use RedBeanPHP\Repository as Repository;
|
||||
|
||||
/**
|
||||
* Fluid Repository.
|
||||
* OODB manages two repositories, a fluid one that
|
||||
* adjust the database schema on-the-fly to accomodate for
|
||||
* new bean types (tables) and new properties (columns) and
|
||||
* a frozen one for use in a production environment. OODB
|
||||
* allows you to swap the repository instances using the freeze()
|
||||
* method.
|
||||
*
|
||||
* @file RedBeanPHP/Repository/Fluid.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 Fluid extends Repository
|
||||
{
|
||||
/**
|
||||
* Figures out the desired type given the cast string ID.
|
||||
* Given a cast ID, this method will return the associated
|
||||
* type (INT(10) or VARCHAR for instance). The returned type
|
||||
* can be processed by the Query Writer to build the specified
|
||||
* column for you in the database. The Cast ID is actually just
|
||||
* a superset of the QueryWriter types. In addition to default
|
||||
* Query Writer column types you can pass the following 'cast types':
|
||||
* 'id' and 'string'. These will map to Query Writer specific
|
||||
* column types (probably INT and VARCHAR).
|
||||
*
|
||||
* @param string $cast cast identifier
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function getTypeFromCast( $cast )
|
||||
{
|
||||
if ( $cast == 'string' ) {
|
||||
$typeno = $this->writer->scanType( 'STRING' );
|
||||
} elseif ( $cast == 'id' ) {
|
||||
$typeno = $this->writer->getTypeForID();
|
||||
} elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) {
|
||||
$typeno = $this->writer->sqltype_typeno[$cast];
|
||||
} else {
|
||||
throw new RedException( 'Invalid Cast' );
|
||||
}
|
||||
|
||||
return $typeno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders the Query Writer to create a table if it does not exist already and
|
||||
* adds a note in the build report about the creation.
|
||||
*
|
||||
* @param OODBBean $bean bean to update report of
|
||||
* @param string $table table to check and create if not exists
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function createTableIfNotExists( OODBBean $bean, $table )
|
||||
{
|
||||
//Does table exist? If not, create
|
||||
if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) {
|
||||
$this->writer->createTable( $table );
|
||||
$bean->setMeta( 'buildreport.flags.created', TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the table to fit the bean data.
|
||||
* Given a property and a value and the bean, this method will
|
||||
* adjust the table structure to fit the requirements of the property and value.
|
||||
* This may include adding a new column or widening an existing column to hold a larger
|
||||
* or different kind of value. This method employs the writer to adjust the table
|
||||
* structure in the database. Schema updates are recorded in meta properties of the bean.
|
||||
*
|
||||
* This method will also apply indexes, unique constraints and foreign keys.
|
||||
*
|
||||
* @param OODBBean $bean bean to get cast data from and store meta in
|
||||
* @param string $property property to store
|
||||
* @param mixed $value value to store
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function modifySchema( OODBBean $bean, $property, $value, &$columns = NULL )
|
||||
{
|
||||
$doFKStuff = FALSE;
|
||||
$table = $bean->getMeta( 'type' );
|
||||
if ($columns === NULL) {
|
||||
$columns = $this->writer->getColumns( $table );
|
||||
}
|
||||
$columnNoQ = $this->writer->esc( $property, TRUE );
|
||||
if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) {
|
||||
if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types
|
||||
$cast = $bean->getMeta( "cast.$property" );
|
||||
$typeno = $this->getTypeFromCast( $cast );
|
||||
} else {
|
||||
$cast = FALSE;
|
||||
$typeno = $this->writer->scanType( $value, TRUE );
|
||||
}
|
||||
if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ?
|
||||
if ( !$cast ) { //rescan without taking into account special types >80
|
||||
$typeno = $this->writer->scanType( $value, FALSE );
|
||||
}
|
||||
$sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] );
|
||||
if ( $typeno > $sqlt ) { //no, we have to widen the database column type
|
||||
$this->writer->widenColumn( $table, $property, $typeno );
|
||||
$bean->setMeta( 'buildreport.flags.widen', TRUE );
|
||||
$doFKStuff = TRUE;
|
||||
}
|
||||
} else {
|
||||
$this->writer->addColumn( $table, $property, $typeno );
|
||||
$bean->setMeta( 'buildreport.flags.addcolumn', TRUE );
|
||||
$doFKStuff = TRUE;
|
||||
}
|
||||
if ($doFKStuff) {
|
||||
if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) {
|
||||
$destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3);
|
||||
$indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}";
|
||||
$this->writer->addIndex($table, $indexName, $columnNoQ);
|
||||
$typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ);
|
||||
$isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE );
|
||||
//Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean
|
||||
$isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) );
|
||||
$result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep );
|
||||
//If this is a link bean and all unique columns have been added already, then apply unique constraint
|
||||
if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) {
|
||||
$this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') );
|
||||
$bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of the store() functionality.
|
||||
* Handles all new additions after the bean has been saved.
|
||||
* Stores addition bean in own-list, extracts the id and
|
||||
* adds a foreign key. Also adds a constraint in case the type is
|
||||
* in the dependent list.
|
||||
*
|
||||
* Note that this method raises a custom exception if the bean
|
||||
* is not an instance of OODBBean. Therefore it does not use
|
||||
* a type hint. This allows the user to take action in case
|
||||
* invalid objects are passed in the list.
|
||||
*
|
||||
* @param OODBBean $bean bean to process
|
||||
* @param array $ownAdditions list of addition beans in own-list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processAdditions( $bean, $ownAdditions )
|
||||
{
|
||||
$beanType = $bean->getMeta( 'type' );
|
||||
|
||||
foreach ( $ownAdditions as $addition ) {
|
||||
if ( $addition instanceof OODBBean ) {
|
||||
|
||||
$myFieldLink = $beanType . '_id';
|
||||
$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
|
||||
if ( $alias ) $myFieldLink = $alias . '_id';
|
||||
|
||||
$addition->$myFieldLink = $bean->id;
|
||||
$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
|
||||
|
||||
if ($alias) {
|
||||
$addition->setMeta( "sys.typeof.{$alias}", $beanType );
|
||||
} else {
|
||||
$addition->setMeta( "sys.typeof.{$beanType}", $beanType );
|
||||
}
|
||||
|
||||
$this->store( $addition );
|
||||
} else {
|
||||
throw new RedException( 'Array may only contain OODBBeans' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
|
||||
* method. When all lists and embedded beans (parent objects) have been processed and
|
||||
* removed from the original bean the bean is passed to this method to be stored
|
||||
* in the database.
|
||||
*
|
||||
* @param OODBBean $bean the clean bean
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function storeBean( OODBBean $bean )
|
||||
{
|
||||
if ( $bean->getMeta( 'changed' ) ) {
|
||||
$this->check( $bean );
|
||||
$table = $bean->getMeta( 'type' );
|
||||
$this->createTableIfNotExists( $bean, $table );
|
||||
|
||||
$updateValues = array();
|
||||
|
||||
$partial = ( $this->partialBeans === TRUE || ( is_array( $this->partialBeans ) && in_array( $table, $this->partialBeans ) ) );
|
||||
if ( $partial ) {
|
||||
$mask = $bean->getMeta( 'changelist' );
|
||||
$bean->setMeta( 'changelist', array() );
|
||||
}
|
||||
|
||||
$columnCache = NULL;
|
||||
foreach ( $bean as $property => $value ) {
|
||||
if ( $partial && !in_array( $property, $mask ) ) continue;
|
||||
if ( $property !== 'id' ) {
|
||||
$this->modifySchema( $bean, $property, $value, $columnCache );
|
||||
}
|
||||
if ( $property !== 'id' ) {
|
||||
$updateValues[] = array( 'property' => $property, 'value' => $value );
|
||||
}
|
||||
}
|
||||
|
||||
$bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id );
|
||||
$bean->setMeta( 'changed', FALSE );
|
||||
}
|
||||
$bean->setMeta( 'tainted', FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception handler.
|
||||
* Fluid and Frozen mode have different ways of handling
|
||||
* exceptions. Fluid mode (using the fluid repository) ignores
|
||||
* exceptions caused by the following:
|
||||
*
|
||||
* - missing tables
|
||||
* - missing column
|
||||
*
|
||||
* In these situations, the repository will behave as if
|
||||
* no beans could be found. This is because in fluid mode
|
||||
* it might happen to query a table or column that has not been
|
||||
* created yet. In frozen mode, this is not supposed to happen
|
||||
* and the corresponding exceptions will be thrown.
|
||||
*
|
||||
* @param \Exception $exception exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleException( \Exception $exception )
|
||||
{
|
||||
if ( !$this->writer->sqlStateIn( $exception->getSQLState(),
|
||||
array(
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ),
|
||||
$exception->getDriverDetails() )
|
||||
) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
$rows = array();
|
||||
$bean = $this->dispense( $type );
|
||||
if ( isset( $this->stash[$this->nesting][$id] ) ) {
|
||||
$row = $this->stash[$this->nesting][$id];
|
||||
} else {
|
||||
try {
|
||||
$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
|
||||
} catch ( SQLException $exception ) {
|
||||
if (
|
||||
$this->writer->sqlStateIn(
|
||||
$exception->getSQLState(),
|
||||
array(
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
|
||||
QueryWriter::C_SQLSTATE_NO_SUCH_TABLE
|
||||
),
|
||||
$exception->getDriverDetails()
|
||||
)
|
||||
) {
|
||||
$rows = array();
|
||||
} else {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
if ( !count( $rows ) ) {
|
||||
return $bean;
|
||||
}
|
||||
$row = array_pop( $rows );
|
||||
}
|
||||
$bean->importRow( $row );
|
||||
$this->nesting++;
|
||||
$this->oodb->signal( 'open', $bean );
|
||||
$this->nesting--;
|
||||
|
||||
return $bean->setMeta( 'tainted', FALSE );
|
||||
}
|
||||
}
|
||||
176
vendor/gabordemooij/redbean/RedBeanPHP/Repository/Frozen.php
vendored
Normal file
176
vendor/gabordemooij/redbean/RedBeanPHP/Repository/Frozen.php
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP\Repository;
|
||||
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\BeanHelper as BeanHelper;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
use RedBeanPHP\Repository as Repository;
|
||||
|
||||
/**
|
||||
* Frozen Repository.
|
||||
* OODB manages two repositories, a fluid one that
|
||||
* adjust the database schema on-the-fly to accomodate for
|
||||
* new bean types (tables) and new properties (columns) and
|
||||
* a frozen one for use in a production environment. OODB
|
||||
* allows you to swap the repository instances using the freeze()
|
||||
* method.
|
||||
*
|
||||
* @file RedBeanPHP/Repository/Frozen.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 Frozen extends Repository
|
||||
{
|
||||
/**
|
||||
* Exception handler.
|
||||
* Fluid and Frozen mode have different ways of handling
|
||||
* exceptions. Fluid mode (using the fluid repository) ignores
|
||||
* exceptions caused by the following:
|
||||
*
|
||||
* - missing tables
|
||||
* - missing column
|
||||
*
|
||||
* In these situations, the repository will behave as if
|
||||
* no beans could be found. This is because in fluid mode
|
||||
* it might happen to query a table or column that has not been
|
||||
* created yet. In frozen mode, this is not supposed to happen
|
||||
* and the corresponding exceptions will be thrown.
|
||||
*
|
||||
* @param \Exception $exception exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleException( \Exception $exception )
|
||||
{
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
|
||||
* method. When all lists and embedded beans (parent objects) have been processed and
|
||||
* removed from the original bean the bean is passed to this method to be stored
|
||||
* in the database.
|
||||
*
|
||||
* @param OODBBean $bean the clean bean
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function storeBean( OODBBean $bean )
|
||||
{
|
||||
if ( $bean->getMeta( 'changed' ) ) {
|
||||
|
||||
list( $properties, $table ) = $bean->getPropertiesAndType();
|
||||
$id = $properties['id'];
|
||||
unset($properties['id']);
|
||||
$updateValues = array();
|
||||
$k1 = 'property';
|
||||
$k2 = 'value';
|
||||
|
||||
$partial = ( $this->partialBeans === TRUE || ( is_array( $this->partialBeans ) && in_array( $table, $this->partialBeans ) ) );
|
||||
if ( $partial ) {
|
||||
$mask = $bean->getMeta( 'changelist' );
|
||||
$bean->setMeta( 'changelist', array() );
|
||||
}
|
||||
|
||||
foreach( $properties as $key => $value ) {
|
||||
if ( $partial && !in_array( $key, $mask ) ) continue;
|
||||
$updateValues[] = array( $k1 => $key, $k2 => $value );
|
||||
}
|
||||
$bean->id = $this->writer->updateRecord( $table, $updateValues, $id );
|
||||
$bean->setMeta( 'changed', FALSE );
|
||||
}
|
||||
$bean->setMeta( 'tainted', FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of the store() functionality.
|
||||
* Handles all new additions after the bean has been saved.
|
||||
* Stores addition bean in own-list, extracts the id and
|
||||
* adds a foreign key. Also adds a constraint in case the type is
|
||||
* in the dependent list.
|
||||
*
|
||||
* Note that this method raises a custom exception if the bean
|
||||
* is not an instance of OODBBean. Therefore it does not use
|
||||
* a type hint. This allows the user to take action in case
|
||||
* invalid objects are passed in the list.
|
||||
*
|
||||
* @param OODBBean $bean bean to process
|
||||
* @param array $ownAdditions list of addition beans in own-list
|
||||
*
|
||||
* @return void
|
||||
* @throws RedException
|
||||
*/
|
||||
protected function processAdditions( $bean, $ownAdditions )
|
||||
{
|
||||
$beanType = $bean->getMeta( 'type' );
|
||||
|
||||
$cachedIndex = array();
|
||||
foreach ( $ownAdditions as $addition ) {
|
||||
if ( $addition instanceof OODBBean ) {
|
||||
|
||||
$myFieldLink = $beanType . '_id';
|
||||
$alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) );
|
||||
if ( $alias ) $myFieldLink = $alias . '_id';
|
||||
|
||||
$addition->$myFieldLink = $bean->id;
|
||||
$addition->setMeta( 'cast.' . $myFieldLink, 'id' );
|
||||
$this->store( $addition );
|
||||
|
||||
} else {
|
||||
throw new RedException( 'Array may only contain OODBBeans' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws SQLException
|
||||
*/
|
||||
public function load( $type, $id )
|
||||
{
|
||||
$rows = array();
|
||||
$bean = $this->dispense( $type );
|
||||
if ( isset( $this->stash[$this->nesting][$id] ) ) {
|
||||
$row = $this->stash[$this->nesting][$id];
|
||||
} else {
|
||||
$rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) );
|
||||
if ( !count( $rows ) ) {
|
||||
return $bean;
|
||||
}
|
||||
$row = array_pop( $rows );
|
||||
}
|
||||
$bean->importRow( $row );
|
||||
$this->nesting++;
|
||||
$this->oodb->signal( 'open', $bean );
|
||||
$this->nesting--;
|
||||
|
||||
return $bean->setMeta( 'tainted', FALSE );
|
||||
}
|
||||
}
|
||||
127
vendor/gabordemooij/redbean/RedBeanPHP/SimpleModel.php
vendored
Normal file
127
vendor/gabordemooij/redbean/RedBeanPHP/SimpleModel.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* SimpleModel
|
||||
* Base Model For All RedBeanPHP Models using FUSE.
|
||||
*
|
||||
* RedBeanPHP FUSE is a mechanism to connect beans to posthoc
|
||||
* models. Models are connected to beans by naming conventions.
|
||||
* Actions on beans will result in actions on models.
|
||||
*
|
||||
* @file RedBeanPHP/SimpleModel.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Team
|
||||
* @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 SimpleModel
|
||||
{
|
||||
/**
|
||||
* @var OODBBean
|
||||
*/
|
||||
protected $bean;
|
||||
|
||||
/**
|
||||
* Used by FUSE: the ModelHelper class to connect a bean to a model.
|
||||
* This method loads a bean in the model.
|
||||
*
|
||||
* @param OODBBean $bean bean to load
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadBean( OODBBean $bean )
|
||||
{
|
||||
$this->bean = $bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Getter to make the bean properties available from
|
||||
* the $this-scope.
|
||||
*
|
||||
* @note this method returns a value, not a reference!
|
||||
* To obtain a reference unbox the bean first!
|
||||
*
|
||||
* @param string $prop property to get
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get( $prop )
|
||||
{
|
||||
return $this->bean->$prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Setter.
|
||||
* Sets the value directly as a bean property.
|
||||
*
|
||||
* @param string $prop property to set value of
|
||||
* @param mixed $value value to set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __set( $prop, $value )
|
||||
{
|
||||
$this->bean->$prop = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Isset implementation.
|
||||
* Implements the isset function for array-like access.
|
||||
*
|
||||
* @param string $key key to check
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function __isset( $key )
|
||||
{
|
||||
return isset( $this->bean->$key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Box the bean using the current model.
|
||||
* This method wraps the current bean in this model.
|
||||
* This method can be reached using FUSE through a simple
|
||||
* OODBBean. The method returns a RedBeanPHP Simple Model.
|
||||
* This is useful if you would like to rely on PHP type hinting.
|
||||
* You can box your beans before passing them to functions or methods
|
||||
* with typed parameters.
|
||||
*
|
||||
* Note about beans vs models:
|
||||
* Use unbox to obtain the bean powering the model. If you want to use bean functionality,
|
||||
* you should -always- unbox first. While some functionality (like magic get/set) is
|
||||
* available in the model, this is just read-only. To use a model as a typical RedBean
|
||||
* OODBBean you should always unbox the model to a bean. Models are meant to
|
||||
* expose only domain logic added by the developer (business logic, no ORM logic).
|
||||
*
|
||||
* @return SimpleModel
|
||||
*/
|
||||
public function box()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbox the bean from the model.
|
||||
* This method returns the bean inside the model.
|
||||
*
|
||||
* Note about beans vs models:
|
||||
* Use unbox to obtain the bean powering the model. If you want to use bean functionality,
|
||||
* you should -always- unbox first. While some functionality (like magic get/set) is
|
||||
* available in the model, this is just read-only. To use a model as a typical RedBean
|
||||
* OODBBean you should always unbox the model to a bean. Models are meant to
|
||||
* expose only domain logic added by the developer (business logic, no ORM logic).
|
||||
*
|
||||
* @return OODBBean
|
||||
*/
|
||||
public function unbox()
|
||||
{
|
||||
return $this->bean;
|
||||
}
|
||||
}
|
||||
71
vendor/gabordemooij/redbean/RedBeanPHP/SimpleModelHelper.php
vendored
Normal file
71
vendor/gabordemooij/redbean/RedBeanPHP/SimpleModelHelper.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\Observer as Observer;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\Observable as Observable;
|
||||
|
||||
/**
|
||||
* RedBean Model Helper.
|
||||
*
|
||||
* Connects beans to models.
|
||||
* This is the core of so-called FUSE.
|
||||
*
|
||||
* @file RedBeanPHP/ModelHelper.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 SimpleModelHelper implements Observer
|
||||
{
|
||||
/**
|
||||
* Gets notified by an observable.
|
||||
* This method decouples the FUSE system from the actual beans.
|
||||
* If a FUSE event happens 'update', this method will attempt to
|
||||
* invoke the corresponding method on the bean.
|
||||
*
|
||||
* @param string $eventName i.e. 'delete', 'after_delete'
|
||||
* @param OODBean $bean affected bean
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onEvent( $eventName, $bean )
|
||||
{
|
||||
$bean->$eventName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the FUSE event listeners. Now the Model Helper will listen for
|
||||
* CRUD events. If a CRUD event occurs it will send a signal to the model
|
||||
* that belongs to the CRUD bean and this model will take over control from
|
||||
* there. This method will attach the following event listeners to the observable:
|
||||
*
|
||||
* - 'update' (gets called by R::store, before the records gets inserted / updated)
|
||||
* - 'after_update' (gets called by R::store, after the records have been inserted / updated)
|
||||
* - 'open' (gets called by R::load, after the record has been retrieved)
|
||||
* - 'delete' (gets called by R::trash, before deletion of record)
|
||||
* - 'after_delete' (gets called by R::trash, after deletion)
|
||||
* - 'dispense' (gets called by R::dispense)
|
||||
*
|
||||
* For every event type, this method will register this helper as a listener.
|
||||
* The observable will notify the listener (this object) with the event ID and the
|
||||
* affected bean. This helper will then process the event (onEvent) by invoking
|
||||
* the event on the bean. If a bean offers a method with the same name as the
|
||||
* event ID, this method will be invoked.
|
||||
*
|
||||
* @param Observable $observable object to observe
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function attachEventListeners( Observable $observable )
|
||||
{
|
||||
foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $eventID ) {
|
||||
$observable->addEventListener( $eventID, $this );
|
||||
}
|
||||
}
|
||||
}
|
||||
370
vendor/gabordemooij/redbean/RedBeanPHP/TagManager.php
vendored
Normal file
370
vendor/gabordemooij/redbean/RedBeanPHP/TagManager.php
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\AssociationManager as AssociationManager;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* RedBeanPHP Tag Manager.
|
||||
*
|
||||
* The tag manager offers an easy way to quickly implement basic tagging
|
||||
* functionality.
|
||||
*
|
||||
* Provides methods to tag beans and perform tag-based searches in the
|
||||
* bean database.
|
||||
*
|
||||
* @file RedBeanPHP/TagManager.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 TagManager
|
||||
{
|
||||
/**
|
||||
* @var ToolBox
|
||||
*/
|
||||
protected $toolbox;
|
||||
|
||||
/**
|
||||
* @var AssociationManager
|
||||
*/
|
||||
protected $associationManager;
|
||||
|
||||
/**
|
||||
* @var OODBBean
|
||||
*/
|
||||
protected $redbean;
|
||||
|
||||
/**
|
||||
* Checks if the argument is a comma separated string, in this case
|
||||
* it will split the string into words and return an array instead.
|
||||
* In case of an array the argument will be returned 'as is'.
|
||||
*
|
||||
* @param array|string $tagList list of tags
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function extractTagsIfNeeded( $tagList )
|
||||
{
|
||||
if ( $tagList !== FALSE && !is_array( $tagList ) ) {
|
||||
$tags = explode( ',', (string) $tagList );
|
||||
} else {
|
||||
$tags = $tagList;
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a tag bean by it's title.
|
||||
* Internal method.
|
||||
*
|
||||
* @param string $title title to search for
|
||||
*
|
||||
* @return OODBBean
|
||||
*/
|
||||
protected function findTagByTitle( $title )
|
||||
{
|
||||
$beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) );
|
||||
|
||||
if ( $beans ) {
|
||||
$bean = reset( $beans );
|
||||
|
||||
return $bean;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* The tag manager offers an easy way to quickly implement basic tagging
|
||||
* functionality.
|
||||
*
|
||||
* @param ToolBox $toolbox toolbox object
|
||||
*/
|
||||
public function __construct( ToolBox $toolbox )
|
||||
{
|
||||
$this->toolbox = $toolbox;
|
||||
$this->redbean = $toolbox->getRedBean();
|
||||
|
||||
$this->associationManager = $this->redbean->getAssociationManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a bean has been associated with one ore more
|
||||
* of the listed tags. If the third parameter is TRUE this method
|
||||
* will return TRUE only if all tags that have been specified are indeed
|
||||
* associated with the given bean, otherwise FALSE.
|
||||
* If the third parameter is FALSE this
|
||||
* method will return TRUE if one of the tags matches, FALSE if none
|
||||
* match.
|
||||
*
|
||||
* Tag list can be either an array with tag names or a comma separated list
|
||||
* of tag names.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::hasTag( $blog, 'horror,movie', TRUE );
|
||||
* </code>
|
||||
*
|
||||
* The example above returns TRUE if the $blog bean has been tagged
|
||||
* as BOTH horror and movie. If the post has only been tagged as 'movie'
|
||||
* or 'horror' this operation will return FALSE because the third parameter
|
||||
* has been set to TRUE.
|
||||
*
|
||||
* @param OODBBean $bean bean to check for tags
|
||||
* @param array|string $tags list of tags
|
||||
* @param boolean $all whether they must all match or just some
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasTag( $bean, $tags, $all = FALSE )
|
||||
{
|
||||
$foundtags = $this->tag( $bean );
|
||||
|
||||
$tags = $this->extractTagsIfNeeded( $tags );
|
||||
$same = array_intersect( $tags, $foundtags );
|
||||
|
||||
if ( $all ) {
|
||||
return ( implode( ',', $same ) === implode( ',', $tags ) );
|
||||
}
|
||||
|
||||
return (bool) ( count( $same ) > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all specified tags from the bean. The tags specified in
|
||||
* the second parameter will no longer be associated with the bean.
|
||||
*
|
||||
* Tag list can be either an array with tag names or a comma separated list
|
||||
* of tag names.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::untag( $blog, 'smart,interesting' );
|
||||
* </code>
|
||||
*
|
||||
* In the example above, the $blog bean will no longer
|
||||
* be associated with the tags 'smart' and 'interesting'.
|
||||
*
|
||||
* @param OODBBean $bean tagged bean
|
||||
* @param array $tagList list of tags (names)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function untag( $bean, $tagList )
|
||||
{
|
||||
$tags = $this->extractTagsIfNeeded( $tagList );
|
||||
|
||||
foreach ( $tags as $tag ) {
|
||||
if ( $t = $this->findTagByTitle( $tag ) ) {
|
||||
$this->associationManager->unassociate( $bean, $t );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of RedBeanPHP Tagging API.
|
||||
* Tags a bean or returns tags associated with a bean.
|
||||
* If $tagList is NULL or omitted this method will return a
|
||||
* comma separated list of tags associated with the bean provided.
|
||||
* If $tagList is a comma separated list (string) of tags all tags will
|
||||
* be associated with the bean.
|
||||
* You may also pass an array instead of a string.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::tag( $meal, "TexMex,Mexican" );
|
||||
* $tags = R::tag( $meal );
|
||||
* </code>
|
||||
*
|
||||
* The first line in the example above will tag the $meal
|
||||
* as 'TexMex' and 'Mexican Cuisine'. The second line will
|
||||
* retrieve all tags attached to the meal object.
|
||||
*
|
||||
* @param OODBBean $bean bean to tag
|
||||
* @param mixed $tagList tags to attach to the specified bean
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function tag( OODBBean $bean, $tagList = NULL )
|
||||
{
|
||||
if ( is_null( $tagList ) ) {
|
||||
|
||||
$tags = $bean->sharedTag;
|
||||
$foundTags = array();
|
||||
|
||||
foreach ( $tags as $tag ) {
|
||||
$foundTags[] = $tag->title;
|
||||
}
|
||||
|
||||
return $foundTags;
|
||||
}
|
||||
|
||||
$this->associationManager->clearRelations( $bean, 'tag' );
|
||||
$this->addTags( $bean, $tagList );
|
||||
|
||||
return $tagList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of RedBeanPHP Tagging API.
|
||||
* Adds tags to a bean.
|
||||
* If $tagList is a comma separated list of tags all tags will
|
||||
* be associated with the bean.
|
||||
* You may also pass an array instead of a string.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* R::addTags( $blog, ["halloween"] );
|
||||
* </code>
|
||||
*
|
||||
* The example adds the tag 'halloween' to the $blog
|
||||
* bean.
|
||||
*
|
||||
* @param OODBBean $bean bean to tag
|
||||
* @param array $tagList list of tags to add to bean
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTags( OODBBean $bean, $tagList )
|
||||
{
|
||||
$tags = $this->extractTagsIfNeeded( $tagList );
|
||||
|
||||
if ( $tagList === FALSE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $tags as $tag ) {
|
||||
if ( !$t = $this->findTagByTitle( $tag ) ) {
|
||||
$t = $this->redbean->dispense( 'tag' );
|
||||
$t->title = $tag;
|
||||
|
||||
$this->redbean->store( $t );
|
||||
}
|
||||
|
||||
$this->associationManager->associate( $bean, $t );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all beans that have been tagged with one or more
|
||||
* of the specified tags.
|
||||
*
|
||||
* Tag list can be either an array with tag names or a comma separated list
|
||||
* of tag names.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $watchList = R::tagged(
|
||||
* 'movie',
|
||||
* 'horror,gothic',
|
||||
* ' ORDER BY movie.title DESC LIMIT ?',
|
||||
* [ 10 ]
|
||||
* );
|
||||
* </code>
|
||||
*
|
||||
* The example uses R::tagged() to find all movies that have been
|
||||
* tagged as 'horror' or 'gothic', order them by title and limit
|
||||
* the number of movies to be returned to 10.
|
||||
*
|
||||
* @param string $beanType type of bean you are looking for
|
||||
* @param array|string $tagList list of tags to match
|
||||
* @param string $sql additional SQL (use only for pagination)
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function tagged( $beanType, $tagList, $sql = '', $bindings = array() )
|
||||
{
|
||||
$tags = $this->extractTagsIfNeeded( $tagList );
|
||||
$records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings );
|
||||
|
||||
return $this->redbean->convertToBeans( $beanType, $records );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all beans that have been tagged with ALL of the tags given.
|
||||
* This method works the same as R::tagged() except that this method only returns
|
||||
* beans that have been tagged with all the specified labels.
|
||||
*
|
||||
* Tag list can be either an array with tag names or a comma separated list
|
||||
* of tag names.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $watchList = R::taggedAll(
|
||||
* 'movie',
|
||||
* [ 'gothic', 'short' ],
|
||||
* ' ORDER BY movie.id DESC LIMIT ? ',
|
||||
* [ 4 ]
|
||||
* );
|
||||
* </code>
|
||||
*
|
||||
* The example above returns at most 4 movies (due to the LIMIT clause in the SQL
|
||||
* Query Snippet) that have been tagged as BOTH 'short' AND 'gothic'.
|
||||
*
|
||||
* @param string $beanType type of bean you are looking for
|
||||
* @param array|string $tagList list of tags to match
|
||||
* @param string $sql additional sql snippet
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
|
||||
{
|
||||
$tags = $this->extractTagsIfNeeded( $tagList );
|
||||
$records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings );
|
||||
|
||||
return $this->redbean->convertToBeans( $beanType, $records );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like taggedAll() but only counts.
|
||||
*
|
||||
* @see taggedAll
|
||||
*
|
||||
* @param string $beanType type of bean you are looking for
|
||||
* @param array|string $tagList list of tags to match
|
||||
* @param string $sql additional sql snippet
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function countTaggedAll( $beanType, $tagList, $sql = '', $bindings = array() )
|
||||
{
|
||||
$tags = $this->extractTagsIfNeeded( $tagList );
|
||||
return $this->toolbox->getWriter()->queryCountTagged( $beanType, $tags, TRUE, $sql, $bindings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like tagged() but only counts.
|
||||
*
|
||||
* @see tagged
|
||||
*
|
||||
* @param string $beanType type of bean you are looking for
|
||||
* @param array|string $tagList list of tags to match
|
||||
* @param string $sql additional sql snippet
|
||||
* @param array $bindings bindings
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function countTagged( $beanType, $tagList, $sql = '', $bindings = array() )
|
||||
{
|
||||
$tags = $this->extractTagsIfNeeded( $tagList );
|
||||
return $this->toolbox->getWriter()->queryCountTagged( $beanType, $tags, FALSE, $sql, $bindings );
|
||||
}
|
||||
}
|
||||
167
vendor/gabordemooij/redbean/RedBeanPHP/ToolBox.php
vendored
Normal file
167
vendor/gabordemooij/redbean/RedBeanPHP/ToolBox.php
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace RedBeanPHP;
|
||||
|
||||
use RedBeanPHP\OODB as OODB;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
|
||||
/**
|
||||
* ToolBox.
|
||||
*
|
||||
* The toolbox is an integral part of RedBeanPHP providing the basic
|
||||
* architectural building blocks to manager objects, helpers and additional tools
|
||||
* like plugins. A toolbox contains the three core components of RedBeanPHP:
|
||||
* the adapter, the query writer and the core functionality of RedBeanPHP in
|
||||
* OODB.
|
||||
*
|
||||
* @file RedBeanPHP/ToolBox.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 ToolBox
|
||||
{
|
||||
/**
|
||||
* @var OODB
|
||||
*/
|
||||
protected $oodb;
|
||||
|
||||
/**
|
||||
* @var QueryWriter
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
/**
|
||||
* @var DBAdapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* The toolbox is an integral part of RedBeanPHP providing the basic
|
||||
* architectural building blocks to manager objects, helpers and additional tools
|
||||
* like plugins. A toolbox contains the three core components of RedBeanPHP:
|
||||
* the adapter, the query writer and the core functionality of RedBeanPHP in
|
||||
* OODB.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $toolbox = new ToolBox( $oodb, $adapter, $writer );
|
||||
* $plugin = new MyPlugin( $toolbox );
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how the toolbox is used.
|
||||
* The core objects are passed to the ToolBox constructor to
|
||||
* assemble a toolbox instance. The toolbox is then passed to
|
||||
* the plugin, helper or manager object. Instances of
|
||||
* TagManager, AssociationManager and so on are examples of
|
||||
* this, they all require a toolbox. The toolbox can also
|
||||
* be obtained from the facade using: R::getToolBox();
|
||||
*
|
||||
* @param OODB $oodb Object Database, OODB
|
||||
* @param DBAdapter $adapter Database Adapter
|
||||
* @param QueryWriter $writer Query Writer
|
||||
*/
|
||||
public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer )
|
||||
{
|
||||
$this->oodb = $oodb;
|
||||
$this->adapter = $adapter;
|
||||
$this->writer = $writer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query writer in this toolbox.
|
||||
* The Query Writer is responsible for building the queries for a
|
||||
* specific database and executing them through the adapter.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $toolbox = R::getToolBox();
|
||||
* $redbean = $toolbox->getRedBean();
|
||||
* $adapter = $toolbox->getDatabaseAdapter();
|
||||
* $writer = $toolbox->getWriter();
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how to obtain the core objects
|
||||
* from a toolbox instance. If you are working with the R-object
|
||||
* only, the following shortcuts exist as well:
|
||||
*
|
||||
* - R::getRedBean()
|
||||
* - R::getDatabaseAdapter()
|
||||
* - R::getWriter()
|
||||
*
|
||||
* @return QueryWriter
|
||||
*/
|
||||
public function getWriter()
|
||||
{
|
||||
return $this->writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OODB instance in this toolbox.
|
||||
* OODB is responsible for creating, storing, retrieving and deleting
|
||||
* single beans. Other components rely
|
||||
* on OODB for their basic functionality.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $toolbox = R::getToolBox();
|
||||
* $redbean = $toolbox->getRedBean();
|
||||
* $adapter = $toolbox->getDatabaseAdapter();
|
||||
* $writer = $toolbox->getWriter();
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how to obtain the core objects
|
||||
* from a toolbox instance. If you are working with the R-object
|
||||
* only, the following shortcuts exist as well:
|
||||
*
|
||||
* - R::getRedBean()
|
||||
* - R::getDatabaseAdapter()
|
||||
* - R::getWriter()
|
||||
*
|
||||
* @return OODB
|
||||
*/
|
||||
public function getRedBean()
|
||||
{
|
||||
return $this->oodb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database adapter in this toolbox.
|
||||
* The adapter is responsible for executing the query and binding the values.
|
||||
* The adapter also takes care of transaction handling.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* $toolbox = R::getToolBox();
|
||||
* $redbean = $toolbox->getRedBean();
|
||||
* $adapter = $toolbox->getDatabaseAdapter();
|
||||
* $writer = $toolbox->getWriter();
|
||||
* </code>
|
||||
*
|
||||
* The example above illustrates how to obtain the core objects
|
||||
* from a toolbox instance. If you are working with the R-object
|
||||
* only, the following shortcuts exist as well:
|
||||
*
|
||||
* - R::getRedBean()
|
||||
* - R::getDatabaseAdapter()
|
||||
* - R::getWriter()
|
||||
*
|
||||
* @return DBAdapter
|
||||
*/
|
||||
public function getDatabaseAdapter()
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
}
|
||||
89
vendor/gabordemooij/redbean/RedBeanPHP/Util/ArrayTool.php
vendored
Normal file
89
vendor/gabordemooij/redbean/RedBeanPHP/Util/ArrayTool.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
116
vendor/gabordemooij/redbean/RedBeanPHP/Util/Diff.php
vendored
Normal file
116
vendor/gabordemooij/redbean/RedBeanPHP/Util/Diff.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
209
vendor/gabordemooij/redbean/RedBeanPHP/Util/DispenseHelper.php
vendored
Normal file
209
vendor/gabordemooij/redbean/RedBeanPHP/Util/DispenseHelper.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
70
vendor/gabordemooij/redbean/RedBeanPHP/Util/Dump.php
vendored
Normal file
70
vendor/gabordemooij/redbean/RedBeanPHP/Util/Dump.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
108
vendor/gabordemooij/redbean/RedBeanPHP/Util/Feature.php
vendored
Normal file
108
vendor/gabordemooij/redbean/RedBeanPHP/Util/Feature.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
109
vendor/gabordemooij/redbean/RedBeanPHP/Util/Look.php
vendored
Normal file
109
vendor/gabordemooij/redbean/RedBeanPHP/Util/Look.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
118
vendor/gabordemooij/redbean/RedBeanPHP/Util/MatchUp.php
vendored
Normal file
118
vendor/gabordemooij/redbean/RedBeanPHP/Util/MatchUp.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
58
vendor/gabordemooij/redbean/RedBeanPHP/Util/MultiLoader.php
vendored
Normal file
58
vendor/gabordemooij/redbean/RedBeanPHP/Util/MultiLoader.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
126
vendor/gabordemooij/redbean/RedBeanPHP/Util/QuickExport.php
vendored
Normal file
126
vendor/gabordemooij/redbean/RedBeanPHP/Util/QuickExport.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
vendor/gabordemooij/redbean/RedBeanPHP/Util/Transaction.php
vendored
Normal file
90
vendor/gabordemooij/redbean/RedBeanPHP/Util/Transaction.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
212
vendor/gabordemooij/redbean/RedBeanPHP/Util/Tree.php
vendored
Normal file
212
vendor/gabordemooij/redbean/RedBeanPHP/Util/Tree.php
vendored
Normal 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));
|
||||
}
|
||||
}
|
||||
316
vendor/gabordemooij/redbean/RedBeanPHP/license.txt
vendored
Normal file
316
vendor/gabordemooij/redbean/RedBeanPHP/license.txt
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
|
||||
RedBeanPHP
|
||||
Written by Gabor de Mooij
|
||||
|
||||
RedBean is DUAL Licensed New BSD and GPLv2. You may choose the license that fits
|
||||
best for your project.
|
||||
|
||||
New BSD License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of RedBeanPHP nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY GABOR DE MOOIJ ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL GABOR DE MOOIJ BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
RedBeanPHP is Written by Gabor de Mooij (G.J.G.T de Mooij) Copyright (c) 2018.
|
||||
|
||||
|
||||
GPLv2 LICENSE
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
85
vendor/gabordemooij/redbean/RedBeanPHP/loader.php
vendored
Normal file
85
vendor/gabordemooij/redbean/RedBeanPHP/loader.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
//Set the directory path
|
||||
define('REDBEANPHP_MAIN_DIR', 'phar://rb.phar/RedBeanPHP/');
|
||||
|
||||
//Load Database drivers
|
||||
require( REDBEANPHP_MAIN_DIR . 'Logger.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Logger/RDefault.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Logger/RDefault/Debug.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Driver.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Driver/RPDO.php' );
|
||||
|
||||
//Load Infrastructure
|
||||
require( REDBEANPHP_MAIN_DIR . 'OODBBean.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Observable.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Observer.php' );
|
||||
|
||||
//Load Database Adapters
|
||||
require( REDBEANPHP_MAIN_DIR . 'Adapter.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Adapter/DBAdapter.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Cursor.php');
|
||||
require( REDBEANPHP_MAIN_DIR . 'Cursor/PDOCursor.php');
|
||||
require( REDBEANPHP_MAIN_DIR . 'Cursor/NullCursor.php');
|
||||
require( REDBEANPHP_MAIN_DIR . 'BeanCollection.php' );
|
||||
|
||||
//Load SQL drivers
|
||||
require( REDBEANPHP_MAIN_DIR . 'QueryWriter.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'QueryWriter/AQueryWriter.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'QueryWriter/MySQL.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'QueryWriter/SQLiteT.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'QueryWriter/PostgreSQL.php' );
|
||||
|
||||
//Load required Exceptions
|
||||
require( REDBEANPHP_MAIN_DIR . 'RedException.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'RedException/SQL.php' );
|
||||
|
||||
//Load Repository Classes
|
||||
require( REDBEANPHP_MAIN_DIR . 'Repository.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Repository/Fluid.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Repository/Frozen.php' );
|
||||
|
||||
//Load Core functionality
|
||||
require( REDBEANPHP_MAIN_DIR . 'OODB.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'ToolBox.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Finder.php' );
|
||||
|
||||
//Load extended functionality
|
||||
require( REDBEANPHP_MAIN_DIR . 'AssociationManager.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'BeanHelper.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'BeanHelper/SimpleFacadeBeanHelper.php' );
|
||||
|
||||
/* Developer Comfort */
|
||||
require( REDBEANPHP_MAIN_DIR . 'SimpleModel.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'SimpleModelHelper.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'TagManager.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'LabelMaker.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Facade.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'DuplicationManager.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Plugin.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Functions.php' );
|
||||
|
||||
/* Facade Utilities */
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/ArrayTool.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/DispenseHelper.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/Dump.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/MultiLoader.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/Transaction.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/QuickExport.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/MatchUp.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/Look.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/Diff.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/Tree.php' );
|
||||
require( REDBEANPHP_MAIN_DIR . 'Util/Feature.php' );
|
||||
|
||||
//Allow users to mount the plugin folder.
|
||||
if ( defined( 'REDBEANPHP_PLUGINS' ) ) {
|
||||
Phar::mount( 'RedBeanPHP/Plugin', REDBEANPHP_PLUGINS );
|
||||
}
|
||||
|
||||
//make some classes available for backward compatibility
|
||||
class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {};
|
||||
|
||||
if (!class_exists('R')) {
|
||||
class R extends \RedBeanPHP\Facade{};
|
||||
}
|
||||
22
vendor/gabordemooij/redbean/composer.json
vendored
Normal file
22
vendor/gabordemooij/redbean/composer.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "gabordemooij/redbean",
|
||||
"description": "RedBeanPHP ORM",
|
||||
"keywords": ["orm"],
|
||||
"homepage": "https://redbeanphp.com/",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gabor de Mooij",
|
||||
"email": "gabor@redbeanphp.com",
|
||||
"homepage": "https://redbeanphp.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"RedBeanPHP\\" : "RedBeanPHP"
|
||||
}
|
||||
}
|
||||
}
|
||||
19
vendor/gabordemooij/redbean/p533patch.php
vendored
Normal file
19
vendor/gabordemooij/redbean/p533patch.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
echo "Running Patch P533...";
|
||||
echo PHP_EOL;
|
||||
|
||||
$code = file_get_contents('rb.php');
|
||||
$code = str_replace('&offsetGet', 'offsetGet', $code);
|
||||
|
||||
$bytes = file_put_contents('rb-p533.php', $code);
|
||||
|
||||
if ($bytes > 0) {
|
||||
echo 'Applied patch for PHP < 5.3.3';
|
||||
echo PHP_EOL;
|
||||
exit;
|
||||
} else {
|
||||
echo 'Somthing went wrong.';
|
||||
echo PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
34
vendor/gabordemooij/redbean/replica2-win.php
vendored
Normal file
34
vendor/gabordemooij/redbean/replica2-win.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
echo "Welcome to Replica 2 Build Script for RedBeanPHP\n";
|
||||
echo "Now building your beans!\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
echo "Cleaning up... ";
|
||||
@exec('del /q rb.phar');
|
||||
@exec('del /q build');
|
||||
echo "Done.\n";
|
||||
|
||||
echo "Trying to create a directory called build to build the PHAR... ";
|
||||
@mkdir('build');
|
||||
@mkdir('build\RedBeanPHP');
|
||||
echo "Done.\n";
|
||||
|
||||
echo "Trying to copy RedBeanPHP to build/RedBeanPHP... ";
|
||||
@exec('xcopy .\RedBeanPHP .\build\RedBeanPHP /e /y');
|
||||
echo "Done.\n";
|
||||
|
||||
echo "Moving loader to build folder... ";
|
||||
@exec('move .\build\RedBeanPHP\loader.php .\build\loader.php');
|
||||
echo "Done.\n";
|
||||
|
||||
echo "Creating PHAR archive... ";
|
||||
$phar = new Phar("rb.phar", 0, "rb.phar");
|
||||
$phar->buildFromDirectory('./build');
|
||||
echo "Done.\n";
|
||||
|
||||
echo "Adding stub... ";
|
||||
$phar->setStub($phar->createDefaultStub("loader.php"));
|
||||
echo "Done.\n";
|
||||
|
||||
echo "Your PHAR file has been generated.\n";
|
||||
121
vendor/gabordemooij/redbean/replica2.php
vendored
Normal file
121
vendor/gabordemooij/redbean/replica2.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
echo "Welcome to Replica 2 Build Script for RedBeanPHP\n";
|
||||
echo "Now building your beans!\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
|
||||
$code = '';
|
||||
$codeMySQL = '';
|
||||
$codePostgres = '';
|
||||
$codeSQLite = '';
|
||||
|
||||
function addFile($file, $only = null) {
|
||||
global $code;
|
||||
global $codeMySQL;
|
||||
global $codePostgres;
|
||||
global $codeSQLite;
|
||||
echo 'Added ', $file , ' to package... ',PHP_EOL;
|
||||
$raw = file_get_contents($file);
|
||||
$raw = preg_replace('/namespace\s+([a-zA-Z0-9\\\;]+);/m', 'namespace $1 {', $raw);
|
||||
$raw .= '}';
|
||||
$code .= $raw;
|
||||
if ( $only == null || $only == 'mysql' ) $codeMySQL .= $raw;
|
||||
if ( $only == null || $only == 'postgres' ) $codePostgres .= $raw;
|
||||
if ( $only == null || $only == 'sqlite' ) $codeSQLite .= $raw;
|
||||
}
|
||||
|
||||
define('DIR', 'RedBeanPHP/');
|
||||
|
||||
addFile( DIR . 'Logger.php' );
|
||||
addFile( DIR . 'Logger/RDefault.php' );
|
||||
addFile( DIR . 'Logger/RDefault/Debug.php' );
|
||||
addFile( DIR . 'Driver.php' );
|
||||
addFile( DIR . 'Driver/RPDO.php' );
|
||||
addFile( DIR . 'OODBBean.php' );
|
||||
addFile( DIR . 'Observable.php' );
|
||||
addFile( DIR . 'Observer.php' );
|
||||
addFile( DIR . 'Adapter.php' );
|
||||
addFile( DIR . 'Adapter/DBAdapter.php' );
|
||||
addFile( DIR . 'Cursor.php');
|
||||
addFile( DIR . 'Cursor/PDOCursor.php');
|
||||
addFile( DIR . 'Cursor/NullCursor.php');
|
||||
addFile( DIR . 'BeanCollection.php' );
|
||||
addFile( DIR . 'QueryWriter.php' );
|
||||
addFile( DIR . 'QueryWriter/AQueryWriter.php' );
|
||||
addFile( DIR . 'QueryWriter/MySQL.php', 'mysql' );
|
||||
addFile( DIR . 'QueryWriter/SQLiteT.php', 'sqlite' );
|
||||
addFile( DIR . 'QueryWriter/PostgreSQL.php', 'postgres' );
|
||||
addFile( DIR . 'QueryWriter/CUBRID.php' );
|
||||
addFile( DIR . 'RedException.php' );
|
||||
addFile( DIR . 'RedException/SQL.php' );
|
||||
addFile( DIR . 'Repository.php' );
|
||||
addFile( DIR . 'Repository/Fluid.php' );
|
||||
addFile( DIR . 'Repository/Frozen.php' );
|
||||
addFile( DIR . 'OODB.php' );
|
||||
addFile( DIR . 'ToolBox.php' );
|
||||
addFile( DIR . 'Finder.php' );
|
||||
addFile( DIR . 'AssociationManager.php' );
|
||||
addFile( DIR . 'BeanHelper.php' );
|
||||
addFile( DIR . 'BeanHelper/SimpleFacadeBeanHelper.php' );
|
||||
addFile( DIR . 'SimpleModel.php' );
|
||||
addFile( DIR . 'SimpleModelHelper.php' );
|
||||
addFile( DIR . 'TagManager.php' );
|
||||
addFile( DIR . 'LabelMaker.php' );
|
||||
addFile( DIR . 'Facade.php' );
|
||||
addFile( DIR . 'DuplicationManager.php' );
|
||||
addFile( DIR . 'Util/ArrayTool.php' );
|
||||
addFile( DIR . 'Util/DispenseHelper.php' );
|
||||
addFile( DIR . 'Util/Dump.php' );
|
||||
addFile( DIR . 'Util/MultiLoader.php' );
|
||||
addFile( DIR . 'Util/Transaction.php' );
|
||||
addFile( DIR . 'Util/QuickExport.php' );
|
||||
addFile( DIR . 'Util/MatchUp.php' );
|
||||
addFile( DIR . 'Util/Look.php' );
|
||||
addFile( DIR . 'Util/Diff.php' );
|
||||
addFile( DIR . 'Util/Tree.php' );
|
||||
addFile( DIR . 'Util/Feature.php' );
|
||||
addFile( DIR . 'Plugin.php' );
|
||||
|
||||
$func = file_get_contents(DIR . 'Functions.php');
|
||||
|
||||
$extra = "
|
||||
namespace {
|
||||
|
||||
//make some classes available for backward compatibility
|
||||
class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {};
|
||||
|
||||
if (!class_exists('R')) {
|
||||
class R extends \RedBeanPHP\Facade{};
|
||||
}
|
||||
|
||||
$func
|
||||
|
||||
}
|
||||
";
|
||||
|
||||
$code .= $extra;
|
||||
$codeMySQL .= $extra;
|
||||
$codePostgres .= $extra;
|
||||
$codeSQLite .= $extra;
|
||||
|
||||
$code = '<?php'.str_replace('<?php', '', $code);
|
||||
$codeMySQL = '<?php'.str_replace('<?php', '', $codeMySQL);
|
||||
$codePostgres = '<?php'.str_replace('<?php', '', $codePostgres);
|
||||
$codeSQLite = '<?php'.str_replace('<?php', '', $codeSQLite);
|
||||
|
||||
$files = array( 'rb.php' => $code, 'rb-mysql.php' => $codeMySQL, 'rb-postgres.php' => $codePostgres, 'rb-sqlite.php' => $codeSQLite );
|
||||
foreach( $files as $file => $content ) {
|
||||
echo 'Okay, seems we have all the code.. now writing file: ', $file ,PHP_EOL;
|
||||
$b = file_put_contents($file, $content);
|
||||
echo 'Written: ',$b,' bytes.',PHP_EOL;
|
||||
if ($b > 0) {
|
||||
echo 'Done!' ,PHP_EOL;
|
||||
} else {
|
||||
echo 'Hm, something seems to have gone wrong... ',PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
6
vendor/gabordemooij/redbean/run_all_tests.sh
vendored
Executable file
6
vendor/gabordemooij/redbean/run_all_tests.sh
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
php replica2.php
|
||||
cp rb.php testing/cli/testcontainer/rb.php
|
||||
cd testing
|
||||
cd cli
|
||||
php runtests.php
|
||||
11
vendor/gabordemooij/redbean/run_single_test.sh
vendored
Executable file
11
vendor/gabordemooij/redbean/run_single_test.sh
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Please enter the name of a test suite, example: Blackhole/Version"
|
||||
exit
|
||||
fi
|
||||
php replica2.php
|
||||
cp rb.php testing/cli/testcontainer/rb.php
|
||||
cd testing
|
||||
cd cli
|
||||
php runtests.php $1
|
||||
17
vendor/gabordemooij/redbean/test-dist.ini
vendored
Normal file
17
vendor/gabordemooij/redbean/test-dist.ini
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
; Test suite database config
|
||||
; Rename this file to test.ini if you are done
|
||||
|
||||
[mysql]
|
||||
host = "localhost"
|
||||
schema = "oodb"
|
||||
user = "root"
|
||||
pass = "password"
|
||||
|
||||
[pgsql]
|
||||
host = "localhost"
|
||||
schema = "oodb"
|
||||
user = "postgres"
|
||||
pass = "password"
|
||||
|
||||
[sqlite]
|
||||
file = "/tmp/database.db"
|
||||
162
vendor/gabordemooij/redbean/testing/RedUNIT.php
vendored
Normal file
162
vendor/gabordemooij/redbean/testing/RedUNIT.php
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* RedUNIT
|
||||
* Base class for RedUNIT, the micro unit test suite for RedBeanPHP
|
||||
*
|
||||
* @file RedUNIT/RedUNIT.php
|
||||
* @description Provides the basic logic for any unit test in RedUNIT.
|
||||
* @author Gabor de Mooij
|
||||
* @license BSD
|
||||
*
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij
|
||||
* This source file is subject to the BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
abstract class RedUNIT
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $round;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $currentlyActiveDriverID = 'unknown';
|
||||
|
||||
/**
|
||||
* What drivers should be loaded for this test pack ?
|
||||
* This method should be implemented by the test to tell the
|
||||
* test controller system what drivers are supposed to be tested.
|
||||
* Each driver will be fed to the test in a 'round' (explained below).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getTargetDrivers();
|
||||
|
||||
/**
|
||||
* Prepare test pack (mostly: nuke the entire database).
|
||||
* This method prepares the test for a run.
|
||||
*/
|
||||
public function prepare()
|
||||
{
|
||||
R::getDatabaseAdapter()->getDatabase()->connect();
|
||||
try {
|
||||
R::freeze( FALSE );
|
||||
R::debug( FALSE );
|
||||
R::nuke();
|
||||
} catch( \Exception $e ) {
|
||||
R::freeze( FALSE );
|
||||
R::debug( FALSE );
|
||||
R::nuke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test. This method will run the tests implemented
|
||||
* by the RedUNIT instance. The run method scans its class for
|
||||
* all public instance methods except:
|
||||
* run (to avoid recursion), getTargetDrivers, onEvent
|
||||
* and prepare. -- added cleanUp/prepare just in case they get overridden.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$old_error_handler = set_error_handler('redunit_error_handler');
|
||||
$class = new \ReflectionClass( $this );
|
||||
$skip = array( 'run', 'getTargetDrivers', 'onEvent', 'cleanUp', 'prepare' );
|
||||
// Call all methods except run automatically
|
||||
foreach ( $class->getMethods( \ReflectionMethod::IS_PUBLIC ) as $method ) {
|
||||
// Skip methods inherited from parent class
|
||||
if ( $method->class != $class->getName() ) continue;
|
||||
if ( in_array( $method->name, $skip ) ) continue;
|
||||
$classname = str_replace( $class->getParentClass()->getName().'_', '', $method->class );
|
||||
printtext( "\n\t" . $classname."->".$method->name." [".$method->class."->".$method->name."]" . " \n\t" );
|
||||
$call = $method->name;
|
||||
$this->$call();
|
||||
try {
|
||||
R::nuke();
|
||||
} catch( \PDOException $e ) {
|
||||
// Some tests use a broken database on purpose, so an exception is ok
|
||||
}
|
||||
}
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean-up method, to be invoked after running the test.
|
||||
* This is an empty implementation, it does nothing. However this method
|
||||
* should be overridden by tests that require clean-up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function cleanUp()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current driver.
|
||||
* This method is called by a test controller, runner or manager
|
||||
* to activate the mode associated with the specified driver
|
||||
* identifier. This mechanism allows a test to run slightly different
|
||||
* in the context of another driver, for instance SQLite might not need
|
||||
* some tests, or MySQL might need some additional tests etc...
|
||||
*
|
||||
* @param string $driver the driver identifier
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCurrentDriver( $driver )
|
||||
{
|
||||
$this->currentlyActiveDriverID = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the round number.
|
||||
* Each test can have a varying number of flavors.
|
||||
* A test flavor is 'that same test' but for a different driver.
|
||||
* Each 'run on a specific driver' is called a round.
|
||||
* Some tests might want to know what the current round is.
|
||||
* This method can be used to set the current round number.
|
||||
*
|
||||
* @param integer $roundNumber round, the current round number
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRound( $roundNumber )
|
||||
{
|
||||
$this->round = (int) $roundNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current round number.
|
||||
* The current round number indicates how many times
|
||||
* this test has been applied (to various drivers).
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getRound()
|
||||
{
|
||||
return $this->round;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the current round is the first one.
|
||||
* If the current round is indeed the first round, this method
|
||||
* will return boolean TRUE, otherwise it will return FALSE. Note that
|
||||
* the rounds are 0-based, so - if the current round equals 0, this
|
||||
* method will return TRUE.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isFirstRound()
|
||||
{
|
||||
return ( $this->round === 0 );
|
||||
}
|
||||
}
|
||||
55
vendor/gabordemooij/redbean/testing/RedUNIT/Base.php
vendored
Normal file
55
vendor/gabordemooij/redbean/testing/RedUNIT/Base.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT;
|
||||
|
||||
/**
|
||||
* Base
|
||||
*
|
||||
* This is the base class for all multi-driver Unit Tests.
|
||||
* By default base class derived tests will offer 'test rounds' for
|
||||
* all well known RedBeanPHP drivers: mysql (MySQL/MariaDB), pgsql (PostgreSQL),
|
||||
* sqlite (SQLite3) and CUBRID (CUBRID).
|
||||
*
|
||||
* @file RedUNIT/Base.php
|
||||
* @desc Base class for all drivers that support all database systems.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Base extends RedUNIT
|
||||
{
|
||||
|
||||
/**
|
||||
* List of DB drivers.
|
||||
* Contains the list of database drivers as returned by getTargetDrivers().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $driverList = array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
|
||||
|
||||
/**
|
||||
* Adds a driver to the list.
|
||||
*
|
||||
* @param string $driverID driver identifier.
|
||||
*/
|
||||
public static function addToDriverList( $driverID )
|
||||
{
|
||||
self::$driverList[] = $driverID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of drivers for which this driver supports
|
||||
* 'test rounds'. This class only supports all base drivers.
|
||||
*
|
||||
* @see RedUNIT::getTargetDrivers() for details.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTargetDrivers()
|
||||
{
|
||||
return self::$driverList;
|
||||
}
|
||||
}
|
||||
387
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Aliasing.php
vendored
Normal file
387
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Aliasing.php
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Aliasing
|
||||
*
|
||||
* Tests aliasing functionality, i.e. fetching beans as,
|
||||
* inferring correct type and retrieving lists as alias.
|
||||
*
|
||||
* @file RedUNIT/Base/Aliasing.php
|
||||
* @desc Tests for nested beans with aliases, i.e. teacher alias for person etc.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Aliasing extends Base
|
||||
{
|
||||
/**
|
||||
* Test for aliasing issue for LTS version.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssueAliasingForLTSVersion() {
|
||||
$person = R::dispense('person');
|
||||
$pro = R::dispense('project');
|
||||
$c = R::dispense('course');
|
||||
$person->name = 'x';
|
||||
$person->alias('teacher')->ownProject[] = $pro;
|
||||
$person->alias('student')->ownCourse[] = $c;
|
||||
R::store($person);
|
||||
asrt($c->fresh()->fetchAs('person')->student->name, 'x');
|
||||
asrt($pro->fresh()->fetchAs('person')->teacher->name, 'x');
|
||||
$person = $person->fresh();
|
||||
$person->alias('teacher')->ownProject = array();
|
||||
$person->alias('student')->ownCourse = array();
|
||||
R::store($person);
|
||||
asrt($c->fresh()->fetchAs('person')->student, NULL);
|
||||
asrt($pro->fresh()->fetchAs('person')->teacher, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describing how clearing state of bean works.
|
||||
* Every method returning somthing (except getID)
|
||||
* clears prefix-method-state (anything set by withCond,with,alias,fetchAs).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearStateAdditionalTests()
|
||||
{
|
||||
list( $project1, $project2 ) = R::dispense( 'project', 2 );
|
||||
list( $irene, $ilse ) = R::dispense('person', 2);
|
||||
$project1->developer = $ilse;
|
||||
$project1->designer = $irene;
|
||||
$ilse->name = 'Ilse';
|
||||
$irene->name = 'Irene';
|
||||
$project2->developer = $ilse;
|
||||
R::storeAll( array( $project1, $project2 ) );
|
||||
$ilse = R::load( 'person', $ilse->id );
|
||||
asrt( count( $ilse->alias( 'developer' )->ownProject ), 2);
|
||||
//cached - same list
|
||||
asrt( count( $ilse->ownProject ), 2);
|
||||
asrt( count( $ilse->alias( 'designer' )->ownProject ), 0);
|
||||
//cached - same list
|
||||
asrt( count( $ilse->ownProject ), 0);
|
||||
//now test state
|
||||
asrt( count( $ilse->setAttr( 'a', 'b' )->alias( 'developer' )->ownProject ), 2);
|
||||
//now test state
|
||||
$ilse = $ilse->fresh();
|
||||
//attr clears state...
|
||||
asrt( count( $ilse->alias( 'developer' )->setAttr( 'a', 'b' )->ownProject ), 0);
|
||||
//but getID() does not!
|
||||
$ilse = $ilse->fresh();
|
||||
$ilse->alias('developer');
|
||||
$ilse->getID();
|
||||
asrt( count( $ilse->ownProject ), 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can switch fetchAs().
|
||||
* Also checks shadow by storing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function canSwitchParentBean()
|
||||
{
|
||||
list( $project1, $project2 ) = R::dispense( 'project', 2 );
|
||||
list( $irene, $ilse ) = R::dispense('person', 2);
|
||||
$project1->developer = $ilse;
|
||||
$project1->designer = $irene;
|
||||
$ilse->name = 'Ilse';
|
||||
$irene->name = 'Irene';
|
||||
$project2->developer = $ilse;
|
||||
R::storeAll( array( $project1, $project2 ) );
|
||||
$project1 = R::load( 'project', $project1->id );
|
||||
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
|
||||
asrt( $project1->fetchAs('person')->designer->name, 'Irene' );
|
||||
R::store( $project1 );
|
||||
$project1 = R::load( 'project', $project1->id );
|
||||
asrt( $project1->fetchAs('person')->designer->name, 'Irene' );
|
||||
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
|
||||
R::store( $project1 );
|
||||
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
|
||||
asrt( $project1->fetchAs('person')->designer->name, 'Irene' );
|
||||
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switching aliases (->alias) should not change other list during
|
||||
* storage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testShadow()
|
||||
{
|
||||
list( $project1, $project2 ) = R::dispense( 'project', 2 );
|
||||
list( $irene, $ilse ) = R::dispense('person', 2);
|
||||
$project1->developer = $ilse;
|
||||
$project1->designer = $irene;
|
||||
$project2->developer = $ilse;
|
||||
R::storeAll( array( $project1, $project2 ) );
|
||||
$ilse = R::load( 'person', $ilse->id );
|
||||
$irene = R::load( 'person', $irene->id );
|
||||
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
|
||||
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
|
||||
R::store( $ilse );
|
||||
$ilse = R::load( 'person', $ilse->id );
|
||||
$irene = R::load( 'person', $irene->id );
|
||||
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
|
||||
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
|
||||
R::storeAll( array( $ilse, $irene) );
|
||||
$ilse = R::load( 'person', $ilse->id );
|
||||
$irene = R::load( 'person', $irene->id );
|
||||
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
|
||||
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
|
||||
asrt( count( $irene->alias('designer')->ownProject), 1 );
|
||||
asrt( count( $irene->alias('developer')->ownProject), 0 );
|
||||
R::storeAll( array( $ilse, $irene) );
|
||||
$ilse = R::load( 'person', $ilse->id );
|
||||
$irene = R::load( 'person', $irene->id );
|
||||
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
|
||||
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
|
||||
asrt( count( $irene->alias('designer')->ownProject), 1 );
|
||||
asrt( count( $irene->alias('developer')->ownProject), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue 291. State not cleared.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFetchTypeConfusionIssue291()
|
||||
{
|
||||
list( $teacher, $student ) = R::dispense( 'person', 2 ) ;
|
||||
$teacher->name = 'jimmy' ;
|
||||
$student->name = 'jacko' ;
|
||||
R::store( $teacher ) ;
|
||||
R::store( $student ) ;
|
||||
$client = R::dispense( 'client' ) ;
|
||||
$client->firm = 'bean AG' ;
|
||||
R::store( $client ) ;
|
||||
$project = R::dispense( 'project' ) ;
|
||||
$project->teacher = $teacher ;
|
||||
$project->student = $student ;
|
||||
$project->client = $client ;
|
||||
R::store( $project ) ;
|
||||
unset( $project->student ) ;
|
||||
R::store( $project ) ;
|
||||
$project = R::load( 'project', 1 ) ;
|
||||
$teacher = $project->fetchAs( 'person' )->teacher ;
|
||||
$student = $project->fetchAs( 'person' )->student ;
|
||||
$client = $project->client ; // this will select from "person" instead of "client"
|
||||
asrt( $client->firm, 'bean AG' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test switching alias (also issue #291).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasSwitch()
|
||||
{
|
||||
$student = R::dispense( 'person' );
|
||||
$project = R::dispense( 'project' );
|
||||
$project->student = $student;
|
||||
R::store( $project );
|
||||
$person = R::load( 'person', $student->id );
|
||||
asrt( count( $person->alias( 'student' )->ownProject ), 1);
|
||||
asrt( count( $person->alias( 'teacher' )->ownProject ), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associating two beans, then loading the associated bean
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAssociated()
|
||||
{
|
||||
$person = R::dispense( 'person' );
|
||||
$person->name = 'John';
|
||||
R::store( $person );
|
||||
$course = R::dispense( 'course' );
|
||||
$course->name = 'Math';
|
||||
|
||||
R::store( $course );
|
||||
$course->teacher = $person;
|
||||
$id = R::store( $course );
|
||||
$course = R::load( 'course', $id );
|
||||
$teacher = $course->fetchAs( 'person' )->teacher;
|
||||
asrt( $teacher->name, 'John' );
|
||||
|
||||
//Trying to load a property that has an invalid name
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->wrongProperty = array( $page );
|
||||
try {
|
||||
$book->wrongProperty[] = $page;
|
||||
R::store( $book );
|
||||
fail();
|
||||
} catch ( RedException $e ) {
|
||||
pass();
|
||||
} catch ( \Exception $e ) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for quick detect change.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function basic()
|
||||
{
|
||||
$book = R::dispense( 'book' );
|
||||
|
||||
asrt( isset( $book->prop ), FALSE ); //not a very good test
|
||||
asrt( in_array( 'prop', array_keys( $book->export() ) ), FALSE ); //better...
|
||||
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
|
||||
$book->paper = $page;
|
||||
|
||||
$id = R::store( $book );
|
||||
$book = R::load( 'book', $id );
|
||||
|
||||
asrt( FALSE, ( isset( $book->paper ) ) );
|
||||
asrt( FALSE, ( isset( $book->page ) ) );
|
||||
|
||||
/**
|
||||
* The following tests try to store various things that aren't
|
||||
* beans (which is expected) with the own* and shared* properties
|
||||
* which only accept beans as assignments, so they're expected to fail
|
||||
*/
|
||||
foreach ( array( 'a string', 1928, TRUE, NULL, array()) as $value ) {
|
||||
try {
|
||||
$book->ownPage[] = $value;
|
||||
R::store( $book );
|
||||
$book->sharedPage[] = $value;
|
||||
R::store( $book );
|
||||
fail();
|
||||
} catch ( RedException $e ) {
|
||||
pass();
|
||||
} catch ( \Exception $e ) {
|
||||
if (strpos($e->getMessage(),'Array to string conversion')===FALSE) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finding $person beans that have been aliased into various roles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasedFinder()
|
||||
{
|
||||
$message = R::dispense( 'message' );
|
||||
$message->subject = 'Roommate agreement';
|
||||
list( $sender, $recipient ) = R::dispense( 'person', 2 );
|
||||
$sender->name = 'Sheldon';
|
||||
$recipient->name = 'Leonard';
|
||||
$message->sender = $sender;
|
||||
$message->recipient = $recipient;
|
||||
$id = R::store( $message );
|
||||
$message = R::load( 'message', $id );
|
||||
asrt( $message->fetchAs( 'person' )->sender->name, 'Sheldon' );
|
||||
asrt( $message->fetchAs( 'person' )->recipient->name, 'Leonard' );
|
||||
$otherRecipient = R::dispense( 'person' );
|
||||
$otherRecipient->name = 'Penny';
|
||||
$message->recipient = $otherRecipient;
|
||||
R::store( $message );
|
||||
$message = R::load( 'message', $id );
|
||||
asrt( $message->fetchAs( 'person' )->sender->name, 'Sheldon' );
|
||||
asrt( $message->fetchAs( 'person' )->recipient->name, 'Penny' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Basic Fetch AS functionality.
|
||||
*/
|
||||
public function testBasicFetchAs()
|
||||
{
|
||||
$project = R::dispense( 'project' );
|
||||
$project->name = 'Mutant Project';
|
||||
list( $teacher, $student ) = R::dispense( 'person', 2 );
|
||||
$teacher->name = 'Charles Xavier';
|
||||
$project->student = $student;
|
||||
$project->student->name = 'Wolverine';
|
||||
$project->teacher = $teacher;
|
||||
$id = R::store( $project );
|
||||
$project = R::load( 'project', $id );
|
||||
asrt( $project->fetchAs( 'person' )->teacher->name, 'Charles Xavier' );
|
||||
asrt( $project->fetchAs( 'person' )->student->name, 'Wolverine' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Basic list variations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBasicListVariations()
|
||||
{
|
||||
$farm = R::dispense( 'building' );
|
||||
$village = R::dispense( 'village' );
|
||||
$farm->name = 'farm';
|
||||
$village->name = 'Dusty Mountains';
|
||||
$farm->village = $village;
|
||||
$id = R::store( $farm );
|
||||
$farm = R::load( 'building', $id );
|
||||
asrt( $farm->name, 'farm' );
|
||||
asrt( $farm->village->name, 'Dusty Mountains' );
|
||||
$village = R::dispense( 'village' );
|
||||
list( $mill, $tavern ) = R::dispense( 'building', 2 );
|
||||
$mill->name = 'Mill';
|
||||
$tavern->name = 'Tavern';
|
||||
$village->ownBuilding = array( $mill, $tavern );
|
||||
$id = R::store( $village );
|
||||
$village = R::load( 'village', $id );
|
||||
asrt( count( $village->ownBuilding ), 2 );
|
||||
$village2 = R::dispense( 'village' );
|
||||
$army = R::dispense( 'army' );
|
||||
$village->sharedArmy[] = $army;
|
||||
$village2->sharedArmy[] = $army;
|
||||
$id1 = R::store( $village );
|
||||
$id2 = R::store( $village2 );
|
||||
$village1 = R::load( 'village', $id1 );
|
||||
$village2 = R::load( 'village', $id2 );
|
||||
asrt( count( $village1->sharedArmy ), 1 );
|
||||
asrt( count( $village2->sharedArmy ), 1 );
|
||||
asrt( count( $village1->ownArmy ), 0 );
|
||||
asrt( count( $village2->ownArmy ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether aliasing plays nice with beautification.
|
||||
* Ensure that aliased column aren't beautified
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasWithBeautify()
|
||||
{
|
||||
$points = R::dispense( 'point', 2 );
|
||||
$line = R::dispense( 'line' );
|
||||
$line->pointA = $points[0];
|
||||
$line->pointB = $points[1];
|
||||
R::store( $line );
|
||||
$line2 = R::dispense( 'line' );
|
||||
$line2->pointA = $line->fetchAs('point')->pointA;
|
||||
$line2->pointB = R::dispense( 'point' );
|
||||
R::store( $line2 );
|
||||
|
||||
//now we have two points per line (1-to-x)
|
||||
//I want to know which lines cross A:
|
||||
$a = R::load( 'point', $line->pointA->id ); //reload A
|
||||
$lines = $a->alias( 'pointA' )->ownLine;
|
||||
asrt( count( $lines ), 2 );
|
||||
}
|
||||
}
|
||||
273
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Arrays.php
vendored
Normal file
273
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Arrays.php
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Arrays
|
||||
*
|
||||
* Beans can also be treated like arrays, this test verifies
|
||||
* whether the bean array interface works correctly in various
|
||||
* scenarios (combination with lists and so on).
|
||||
*
|
||||
* @file RedUNIT/Base/Arrays.php
|
||||
* @desc Tests the array interface of beans
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Arrays extends Base
|
||||
{
|
||||
/**
|
||||
* Tests basic array access.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayAccess()
|
||||
{
|
||||
$bean = R::dispense('bean');
|
||||
$bean->name = 'bean';
|
||||
$bean->taste = 'salty';
|
||||
$properties = array();
|
||||
foreach($bean as $key => $value) {
|
||||
$properties[ $key ] = $value;
|
||||
}
|
||||
asrt( count( $bean ), 3 );
|
||||
asrt( count( $properties ), 3 );
|
||||
asrt( isset( $properties['id'] ), TRUE );
|
||||
asrt( isset( $properties['name'] ), TRUE );
|
||||
asrt( isset( $properties['taste'] ), TRUE );
|
||||
$bean = R::dispense('bean');
|
||||
$bean['name'] = 'bean';
|
||||
$bean['taste'] = 'salty';
|
||||
$properties = array();
|
||||
foreach($bean as $key => $value) {
|
||||
$properties[ $key ] = $value;
|
||||
}
|
||||
asrt( count( $bean ), 3 );
|
||||
asrt( count( $properties ), 3 );
|
||||
asrt( isset( $properties['id'] ), TRUE );
|
||||
asrt( isset( $properties['name'] ), TRUE );
|
||||
asrt( isset( $properties['taste'] ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests array access with lists.
|
||||
* Tests whether list properties behave as arrays correctly.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayAccessAndLists()
|
||||
{
|
||||
$book = R::dispense('book');
|
||||
$book['title'] = 'My Book';
|
||||
//Can we add a bean in list with array access?
|
||||
$book['ownPage'][] = R::dispense('page');
|
||||
$book['ownPage'][] = R::dispense('page');
|
||||
asrt( count( $book ), 3 );
|
||||
$properties = array();
|
||||
foreach($book as $key => $value) {
|
||||
$properties[ $key ] = $value;
|
||||
}
|
||||
asrt( count( $properties ), 3 );
|
||||
//Dont reveal aliased x-own and -List in foreach-loop
|
||||
asrt( isset( $properties['id'] ), TRUE );
|
||||
asrt( isset( $properties['title'] ), TRUE );
|
||||
asrt( isset( $properties['ownPage'] ), TRUE );
|
||||
asrt( isset( $properties['ownPageList'] ), FALSE );
|
||||
asrt( isset( $properties['xownPage'] ), FALSE );
|
||||
asrt( isset( $properties['xownPageList'] ), FALSE );
|
||||
//But keep them countable
|
||||
asrt( count( $book['ownPage'] ), 2 );
|
||||
asrt( count( $book['ownPageList'] ), 2 );
|
||||
asrt( count( $book['xownPage'] ), 2 );
|
||||
asrt( count( $book['xownPageList'] ), 2 );
|
||||
//And reveal state of items with isset()
|
||||
asrt( isset( $book['id'] ), TRUE );
|
||||
asrt( isset( $book['title'] ), TRUE );
|
||||
asrt( isset( $book['ownPage'] ), TRUE );
|
||||
asrt( isset( $book['ownPageList'] ), TRUE );
|
||||
asrt( isset( $book['xownPage'] ), TRUE );
|
||||
asrt( isset( $book['xownPageList'] ), TRUE );
|
||||
//Can we add using the List alias?
|
||||
$book['ownPageList'][] = R::dispense('page');
|
||||
asrt( count( $book['ownPage'] ), 3 );
|
||||
asrt( count( $book['ownPageList'] ), 3 );
|
||||
asrt( count( $book['xownPage'] ), 3 );
|
||||
asrt( count( $book['xownPageList'] ), 3 );
|
||||
//Can we add using the x-mode alias?
|
||||
$book['ownPageList'][] = R::dispense('page');
|
||||
asrt( count( $book['ownPage'] ), 4 );
|
||||
asrt( count( $book['ownPageList'] ), 4 );
|
||||
asrt( count( $book['xownPage'] ), 4 );
|
||||
asrt( count( $book['xownPageList'] ), 4 );
|
||||
//Can we unset using array access?
|
||||
unset( $book['ownPage'] );
|
||||
asrt( isset( $book['ownPage'] ), FALSE );
|
||||
asrt( isset( $book['ownPageList'] ), FALSE );
|
||||
asrt( isset( $book['xownPage'] ), FALSE );
|
||||
asrt( isset( $book['xownPageList'] ), FALSE );
|
||||
$book['ownPage'] = array( R::dispense('page') );
|
||||
unset( $book['xownPage'] );
|
||||
asrt( isset( $book['ownPage'] ), FALSE );
|
||||
asrt( isset( $book['ownPageList'] ), FALSE );
|
||||
asrt( isset( $book['xownPage'] ), FALSE );
|
||||
asrt( isset( $book['xownPageList'] ), FALSE );
|
||||
$book['ownPage'] = array( R::dispense('page') );
|
||||
unset( $book['ownPageList'] );
|
||||
asrt( isset( $book['ownPage'] ), FALSE );
|
||||
asrt( isset( $book['ownPageList'] ), FALSE );
|
||||
asrt( isset( $book['xownPage'] ), FALSE );
|
||||
asrt( isset( $book['xownPageList'] ), FALSE );
|
||||
$book['ownPage'] = array( R::dispense('page') );
|
||||
unset( $book['xownPageList'] );
|
||||
asrt( isset( $book['ownPage'] ), FALSE );
|
||||
asrt( isset( $book['ownPageList'] ), FALSE );
|
||||
asrt( isset( $book['xownPage'] ), FALSE );
|
||||
asrt( isset( $book['xownPageList'] ), FALSE );
|
||||
//does it work with shared lists as well?
|
||||
$book['sharedCategory'] = array( R::dispense('category') );
|
||||
asrt( count( $book ), 3 );
|
||||
$properties = array();
|
||||
foreach($book as $key => $value) {
|
||||
$properties[ $key ] = $value;
|
||||
}
|
||||
asrt( isset( $properties['sharedCategory'] ), TRUE );
|
||||
asrt( isset( $properties['sharedCategoryList'] ), FALSE );
|
||||
asrt( isset( $book['sharedCategory'] ), TRUE );
|
||||
asrt( isset( $book['sharedCategoryList'] ), TRUE );
|
||||
asrt( count( $book['sharedCategory'] ), 1 );
|
||||
asrt( count( $book['sharedCategoryList'] ), 1 );
|
||||
$book['sharedCategory'][] = R::dispense( 'category' );
|
||||
asrt( count( $book['sharedCategory'] ), 2 );
|
||||
asrt( count( $book['sharedCategoryList'] ), 2 );
|
||||
$book['sharedCategoryList'][] = R::dispense( 'category' );
|
||||
asrt( count( $book['sharedCategory'] ), 3 );
|
||||
asrt( count( $book['sharedCategoryList'] ), 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests array access with parent beans.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayAccessWithBeans()
|
||||
{
|
||||
$book = R::dispense( 'bean' );
|
||||
$book['author'] = R::dispense( 'author' );
|
||||
asrt( isset( $book['author'] ), TRUE );
|
||||
asrt( count( $book ), 2 );
|
||||
$book['author']['name'] = 'me';
|
||||
asrt( $book['author']['name'], 'me' );
|
||||
$book['author']['address'] = R::dispense( 'address' );
|
||||
$book['author']['ownTagList'][] = R::dispense( 'tag' );
|
||||
asrt( isset( $book['author']['address'] ), TRUE );
|
||||
asrt( isset( $book['author']['ownTag'] ), TRUE );
|
||||
asrt( count( $book['author']['ownTag'] ), 1 );
|
||||
asrt( isset( $book['author']['xownTag'] ), TRUE );
|
||||
asrt( count( $book['author']['xownTag'] ), 1 );
|
||||
asrt( isset( $book['author']['ownTagList'] ), TRUE );
|
||||
asrt( count( $book['author']['ownTagList'] ), 1 );
|
||||
asrt( isset( $book['author']['xownTagList'] ), TRUE );
|
||||
asrt( count( $book['author']['xownTagList'] ), 1 );
|
||||
unset( $book['author'] );
|
||||
asrt( isset( $book['author'] ), FALSE );
|
||||
asrt( count( $book ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests array access with CRUD operations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayAccessWithCRUD()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book['ownPageList'] = R::dispense( 'page', 3 );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
//note that isset first returns FALSE, so you can check if a list is loaded
|
||||
asrt( isset( $book['ownPage'] ), FALSE );
|
||||
asrt( isset( $book['ownPageList'] ), FALSE );
|
||||
asrt( isset( $book['xownPage'] ), FALSE );
|
||||
asrt( isset( $book['xownPageList'] ), FALSE );
|
||||
//count triggers load...
|
||||
asrt( count( $book['ownPage'] ), 3 );
|
||||
asrt( isset( $book['ownPage'] ), TRUE );
|
||||
asrt( isset( $book['ownPageList'] ), TRUE );
|
||||
asrt( isset( $book['xownPage'] ), TRUE );
|
||||
asrt( isset( $book['xownPageList'] ), TRUE );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['xownPage'] ), 3 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['ownPageList'] ), 3 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['xownPageList'] ), 3 );
|
||||
$book['ownPage'][] = R::dispense( 'page' );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['ownPage'] ), 4 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['xownPage'] ), 4 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['ownPageList'] ), 4 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['xownPageList'] ), 4 );
|
||||
//does dependency still work?
|
||||
$book['xownPageList'] = array();
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['ownPage'] ), 0 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['xownPage'] ), 0 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['ownPageList'] ), 0 );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['xownPageList'] ), 0 );
|
||||
//does shared list work as well?
|
||||
$book['sharedTag'] = R::dispense( 'tag', 2 );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
//note that isset first returns FALSE, so you can check if a list is loaded
|
||||
asrt( isset( $book['sharedTagList'] ), FALSE );
|
||||
asrt( isset( $book['sharedTag'] ), FALSE );
|
||||
//count triggers load...
|
||||
asrt( count( $book['sharedTagList'] ), 2 );
|
||||
asrt( count( $book['sharedTag'] ), 2 );
|
||||
asrt( isset( $book['sharedTagList'] ), TRUE );
|
||||
asrt( isset( $book['sharedTag'] ), TRUE );
|
||||
$book['sharedTag'][] = R::dispense( 'tag' );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['sharedTagList'] ), 3 );
|
||||
asrt( count( $book['sharedTag'] ), 3 );
|
||||
$book['sharedTagList'][] = R::dispense( 'tag' );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( count( $book['sharedTagList'] ), 4 );
|
||||
asrt( count( $book['sharedTag'] ), 4 );
|
||||
//does it also work with cross-shared
|
||||
$book['sharedBookList'][] = R::dispense( 'book' );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( isset( $book['sharedBookList'] ), FALSE );
|
||||
asrt( count( $book['sharedBookList'] ), 1 );
|
||||
$first = reset( $book['sharedBookList'] );
|
||||
$id = $first['id'];
|
||||
asrt( count( $book['sharedBookList'][$id]['sharedBookList'] ), 1 );
|
||||
$properties = array();
|
||||
foreach($book as $key => $value) {
|
||||
$properties[ $key ] = $value;
|
||||
}
|
||||
asrt( count( $properties ), 2 );
|
||||
$keys = array_keys( $properties );
|
||||
sort( $keys );
|
||||
asrt( implode( ',', $keys ), 'id,sharedBook' );
|
||||
}
|
||||
}
|
||||
296
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Association.php
vendored
Normal file
296
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Association.php
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\OODB as OODB;
|
||||
use RedBeanPHP\AssociationManager as AssociationManager;
|
||||
use RedBeanPHP\RedException\SQL as SQL;
|
||||
|
||||
/**
|
||||
* Association
|
||||
*
|
||||
* Originally meant to test R::associate - which is no longer
|
||||
* used, this class tests all kinds of relations from
|
||||
* one-to-one to polymorph relations using the poly() method.
|
||||
*
|
||||
* @file RedUNIT/Base/Association.php
|
||||
* @desc Tests Association API (N:N associations)
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Association extends Base
|
||||
{
|
||||
/**
|
||||
* MySQL specific tests.
|
||||
* Test MySQL specific issues with constraints.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fast-track deletion, i.e. bypassing FUSE.
|
||||
* For link beans.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFastTrackDeletion()
|
||||
{
|
||||
testpack( 'Test fast-track deletion' );
|
||||
$ghost = R::dispense( 'ghost' );
|
||||
$house = R::dispense( 'house' );
|
||||
$house->sharedGhost[] = $ghost;
|
||||
R::store($house);
|
||||
\Model_Ghost_House::$deleted = FALSE;
|
||||
R::getRedBean()->getAssociationManager()->unassociate( $house, $ghost );
|
||||
// No fast-track, assoc bean got trashed
|
||||
asrt( \Model_Ghost_House::$deleted, TRUE );
|
||||
\Model_Ghost_House::$deleted = FALSE;
|
||||
$house->sharedGhost[] = $ghost;
|
||||
R::store($house);
|
||||
R::getRedBean()->getAssociationManager()->unassociate( $house, $ghost, TRUE );
|
||||
// Fast-track, assoc bean got deleted right away
|
||||
asrt( \Model_Ghost_House::$deleted, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test self-referential associations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCrossAssociation()
|
||||
{
|
||||
$ghost = R::dispense( 'ghost' );
|
||||
$ghost2 = R::dispense( 'ghost' );
|
||||
R::getRedBean()->getAssociationManager()->associate( $ghost, $ghost2 );
|
||||
\Model_Ghost_Ghost::$deleted = FALSE;
|
||||
R::getRedBean()->getAssociationManager()->unassociate( $ghost, $ghost2 );
|
||||
// No fast-track, assoc bean got trashed
|
||||
asrt( \Model_Ghost_Ghost::$deleted, TRUE );
|
||||
\Model_Ghost_Ghost::$deleted = FALSE;
|
||||
R::getRedBean()->getAssociationManager()->unassociate( $ghost, $ghost2, TRUE );
|
||||
// Fast-track, assoc bean got deleted right away
|
||||
asrt( \Model_Ghost_Ghost::$deleted, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test limited support for polymorph associations.
|
||||
* RedBeanPHP does not really feature polymorph relations since
|
||||
* they are not really compatible with traditional relational databases.
|
||||
* However a light-weight, basic implementation has been added for
|
||||
* those circumstances where you can't live without...
|
||||
* i.e... possible legacy systems and so on.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPoly()
|
||||
{
|
||||
testpack( 'Testing poly' );
|
||||
$shoe = R::dispense( 'shoe' );
|
||||
$lace = R::dispense( 'lace' );
|
||||
$lace->color = 'white';
|
||||
$id = R::store( $lace );
|
||||
$shoe->itemType = 'lace';
|
||||
$shoe->item = $lace;
|
||||
$id = R::store( $shoe );
|
||||
$shoe = R::load( 'shoe', $id );
|
||||
$x = $shoe->poly( 'itemType' )->item;
|
||||
asrt( $x->color, 'white' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test limited support for 1-to-1 associations.
|
||||
* The rule is, one-to-ones are supposes to be in the same table,
|
||||
* this is just for some legacy tables not designed to work
|
||||
* with RedBeanPHP at all.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOneToOne()
|
||||
{
|
||||
testpack( 'Testing one-to-ones' );
|
||||
$author = R::dispense( 'author' )->setAttr( 'name', 'a' );;
|
||||
$bio = R::dispense( 'bio' )->setAttr( 'name', 'a' );
|
||||
R::storeAll( array( $author, $bio ) );
|
||||
$id1 = $author->id;
|
||||
$author = R::dispense( 'author' )->setAttr( 'name', 'b' );;
|
||||
$bio = R::dispense( 'bio' )->setAttr( 'name', 'b' );
|
||||
R::storeAll( array( $author, $bio ) );
|
||||
$x = $author->one( 'bio' );
|
||||
$y = $bio->one('author');
|
||||
asrt( $x->name, $bio->name );
|
||||
asrt( $y->name, $author->name );
|
||||
asrt( $x->id, $bio->id );
|
||||
asrt( $y->id, $author->id );
|
||||
$id2 = $author->id;
|
||||
list( $a, $b ) = R::loadMulti( 'author,bio', $id1 );
|
||||
asrt( $a->name, $b->name );
|
||||
asrt( $a->name, 'a' );
|
||||
list( $a, $b ) = R::loadMulti( 'author,bio', $id2 );
|
||||
asrt( $a->name, $b->name );
|
||||
asrt( $a->name, 'b' );
|
||||
list( $a, $b ) = R::loadMulti( array( 'author', 'bio' ), $id1 );
|
||||
asrt( $a->name, $b->name );
|
||||
asrt( $a->name, 'a' );
|
||||
list( $a, $b ) = R::loadMulti( array( 'author', 'bio' ), $id2 );
|
||||
asrt( $a->name, $b->name );
|
||||
asrt( $a->name, 'b' );
|
||||
asrt( is_array( R::loadMulti( NULL, 1 ) ), TRUE );
|
||||
asrt( ( count( R::loadMulti( NULL, 1 ) ) === 0 ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test single column bases unique constraints.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSingleColUniqueConstraint()
|
||||
{
|
||||
testpack( 'Testing unique constraint on single column' );
|
||||
$book = R::dispense( 'book' );
|
||||
$book->title = 'bla';
|
||||
$book->extra = 2;
|
||||
$id = R::store( $book );
|
||||
R::getWriter()->addUniqueIndex( 'book', array( 'title' ) );
|
||||
$book = R::dispense( 'book' );
|
||||
$book->title = 'bla';
|
||||
$expected = NULL;
|
||||
try {
|
||||
R::store( $book );
|
||||
|
||||
fail();
|
||||
} catch ( SQL $e ) {
|
||||
$expected = $e;
|
||||
}
|
||||
asrt( ( $expected instanceof SQL ), TRUE );
|
||||
asrt( R::count( 'book' ), 1 );
|
||||
$book = R::load( 'book', $id );
|
||||
// Causes failure, table will be rebuild
|
||||
$book->extra = 'CHANGE';
|
||||
$id2 = R::store( $book );
|
||||
$book2 = R::load( 'book', $id2 );
|
||||
$book = R::dispense( 'book' );
|
||||
$book->title = 'bla';
|
||||
try {
|
||||
R::store( $book );
|
||||
|
||||
fail();
|
||||
} catch ( SQL $e ) {
|
||||
$expected = $e;
|
||||
}
|
||||
asrt( ( $expected instanceof SQL ), TRUE );
|
||||
asrt( R::count( 'book' ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multiple assiociation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMultiAssociationDissociation()
|
||||
{
|
||||
$wines = R::dispense( 'wine', 3 );
|
||||
$cheese = R::dispense( 'cheese', 3 );
|
||||
$olives = R::dispense( 'olive', 3 );
|
||||
R::getRedBean()->getAssociationManager()->associate( $wines, array_merge( $cheese, $olives ) );
|
||||
asrt( R::count( 'cheese' ), 3 );
|
||||
asrt( R::count( 'olive' ), 3 );
|
||||
asrt( R::count( 'wine' ), 3 );
|
||||
asrt( count( $wines[0]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[0]->sharedOlive ), 3 );
|
||||
asrt( count( $wines[1]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[1]->sharedOlive ), 3 );
|
||||
asrt( count( $wines[2]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[2]->sharedOlive ), 3 );
|
||||
R::getRedBean()->getAssociationManager()->unassociate( $wines, $olives );
|
||||
asrt( count( $wines[0]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[0]->sharedOlive ), 0 );
|
||||
asrt( count( $wines[1]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[1]->sharedOlive ), 0 );
|
||||
asrt( count( $wines[2]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[2]->sharedOlive ), 0 );
|
||||
R::getRedBean()->getAssociationManager()->unassociate( array( $wines[1] ), $cheese );
|
||||
asrt( count( $wines[0]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[0]->sharedOlive ), 0 );
|
||||
asrt( count( $wines[1]->sharedCheese ), 0 );
|
||||
asrt( count( $wines[1]->sharedOlive ), 0 );
|
||||
asrt( count( $wines[2]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[2]->sharedOlive ), 0 );
|
||||
R::getRedBean()->getAssociationManager()->unassociate( array( $wines[2] ), $cheese );
|
||||
asrt( count( $wines[0]->sharedCheese ), 3 );
|
||||
asrt( count( $wines[0]->sharedOlive ), 0 );
|
||||
asrt( count( $wines[1]->sharedCheese ), 0 );
|
||||
asrt( count( $wines[1]->sharedOlive ), 0 );
|
||||
asrt( count( $wines[2]->sharedCheese ), 0 );
|
||||
asrt( count( $wines[2]->sharedOlive ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests error handling related to association.
|
||||
* On database systems providing informative SQL STATE error codes
|
||||
* RedBeanPHP should not mind non-existing tables or columns in
|
||||
* fluid mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testErrorHandling()
|
||||
{
|
||||
R::nuke();
|
||||
list( $book, $page ) = R::dispenseAll( 'book,page' );
|
||||
$book->sharedPage[] = $page;
|
||||
R::store( $page );
|
||||
$redbean = R::getRedBean();
|
||||
$am = $redbean->getAssociationManager();
|
||||
//SQLite and CUBRID do not comply with ANSI SQLState codes.
|
||||
$catchAll = ( $this->currentlyActiveDriverID == 'sqlite' || $this->currentlyActiveDriverID === 'CUBRID' );
|
||||
try {
|
||||
$am->related( $book, 'page', 'invalid SQL' );
|
||||
if ($catchAll) pass(); else fail();
|
||||
} catch ( SQL $e ) {
|
||||
if ($catchAll) fail(); else pass();
|
||||
}
|
||||
try {
|
||||
$am->related( $book, 'cover');
|
||||
pass();
|
||||
} catch ( SQL $e ) {
|
||||
fail();
|
||||
}
|
||||
try {
|
||||
$am->related( R::dispense('cover'), 'book' );
|
||||
pass();
|
||||
} catch ( SQL $e ) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
151
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Batch.php
vendored
Normal file
151
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Batch.php
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\RedException\SQL as SQL;
|
||||
|
||||
/**
|
||||
* Batch
|
||||
*
|
||||
* Test whether we can load a group of beans in one go, i.e.
|
||||
* the batch loading functionality of RedBeanPHP, also in combination
|
||||
* with aliasing.
|
||||
*
|
||||
* @file RedUNIT/Base/Batch.php
|
||||
* @desc Tests batch loading of beans, i.e. loading large collections of beans in optimized way.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Batch extends Base
|
||||
{
|
||||
/**
|
||||
* Can we delete beans by find-query?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testHunt()
|
||||
{
|
||||
R::nuke();
|
||||
$books = R::dispenseAll( 'book*3' );
|
||||
R::storeAll( $books[0] );
|
||||
pass();
|
||||
asrt( ( R::count( 'book' ) === 3 ), TRUE );
|
||||
$ids = R::getCol( 'SELECT id FROM book' );
|
||||
R::hunt( 'book', ' id IN ( '. R::genSlots( $ids ) .' ) ', $ids );
|
||||
asrt( ( R::count( 'book' ) === 0 ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests batch trashing. Can we trash beans using
|
||||
* IDs only?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBatchTrash()
|
||||
{
|
||||
R::nuke();
|
||||
$books = R::dispenseAll( 'book*3' );
|
||||
R::storeAll( $books[0] );
|
||||
pass();
|
||||
asrt( ( R::count( 'book' ) === 3 ), TRUE );
|
||||
R::trashBatch( 'book', R::getCol( 'SELECT id FROM book' ) );
|
||||
asrt( ( R::count( 'book' ) === 0 ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin testing.
|
||||
* This method runs the actual test pack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBatch()
|
||||
{
|
||||
R::freeze( FALSE );
|
||||
$toolbox = R::getToolBox();
|
||||
$adapter = $toolbox->getDatabaseAdapter();
|
||||
$writer = $toolbox->getWriter();
|
||||
$redbean = $toolbox->getRedBean();
|
||||
$pdo = $adapter->getDatabase();
|
||||
$page = $redbean->dispense( "page" );
|
||||
$page->name = "page no. 1";
|
||||
$page->rating = 1;
|
||||
$id1 = $redbean->store( $page );
|
||||
$page = $redbean->dispense( "page" );
|
||||
$page->name = "page no. 2";
|
||||
$id2 = $redbean->store( $page );
|
||||
$batch = $redbean->batch( "page", array( $id1, $id2 ) );
|
||||
asrt( count( $batch ), 2 );
|
||||
asrt( $batch[$id1]->getMeta( "type" ), "page" );
|
||||
asrt( $batch[$id2]->getMeta( "type" ), "page" );
|
||||
asrt( (int) $batch[$id1]->id, $id1 );
|
||||
asrt( (int) $batch[$id2]->id, $id2 );
|
||||
$book = $redbean->dispense( "book" );
|
||||
$book->name = "book 1";
|
||||
$redbean->store( $book );
|
||||
$book = $redbean->dispense( "book" );
|
||||
$book->name = "book 2";
|
||||
$redbean->store( $book );
|
||||
$book = $redbean->dispense( "book" );
|
||||
$book->name = "book 3";
|
||||
$redbean->store( $book );
|
||||
$books = $redbean->batch( "book", $adapter->getCol( "SELECT id FROM book" ) );
|
||||
asrt( count( $books ), 3 );
|
||||
$a = $redbean->batch( 'book', 9919 );
|
||||
asrt( is_array( $a ), TRUE );
|
||||
asrt( count( $a ), 0 );
|
||||
$a = $redbean->batch( 'triangle', 1 );
|
||||
asrt( is_array( $a ), TRUE );
|
||||
asrt( count( $a ), 0 );
|
||||
R::freeze( TRUE );
|
||||
$a = $redbean->batch( 'book', 9919 );
|
||||
asrt( is_array( $a ), TRUE );
|
||||
asrt( count( $a ), 0 );
|
||||
try {
|
||||
$a = $redbean->batch( 'triangle', 1 );
|
||||
fail();
|
||||
} catch(SQL $e) {
|
||||
pass();
|
||||
}
|
||||
R::freeze( FALSE );
|
||||
asrt( R::wipe( 'spaghettimonster' ), FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test missing bean scenarios.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMissingBeans()
|
||||
{
|
||||
testpack( 'deal with missing beans' );
|
||||
|
||||
$id = R::store( R::dispense( 'beer' ) );
|
||||
$bottles = R::batch( 'beer', array( $id, $id + 1, $id + 2 ) );
|
||||
|
||||
asrt( count( $bottles ), 3 );
|
||||
asrt( (int) $bottles[$id]->id, (int) $id );
|
||||
asrt( (int) $bottles[$id + 1]->id, 0 );
|
||||
asrt( (int) $bottles[$id + 2]->id, 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test batch alias loadAll.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBatchAliasLoadAll()
|
||||
{
|
||||
$ids = R::storeAll( R::dispense( 'page', 2 ) );
|
||||
$pages = R::loadAll( 'page', $ids );
|
||||
asrt( is_array( $pages ), TRUE );
|
||||
asrt( count( $pages ), 2 );
|
||||
asrt( ( $pages[$ids[0]] instanceof OODBBean ), TRUE );
|
||||
}
|
||||
}
|
||||
1276
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Bean.php
vendored
Normal file
1276
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Bean.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
70
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Boxing.php
vendored
Normal file
70
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Boxing.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\SimpleModel as SimpleModel;
|
||||
|
||||
/**
|
||||
* Boxing
|
||||
*
|
||||
* Test boxing and unboxing of beans.
|
||||
*
|
||||
* @file RedUNIT/Base/Boxing.php
|
||||
* @desc Tests bean boxing and unboxing functionality.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Boxing extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* Test boxing beans.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBoxing()
|
||||
{
|
||||
R::nuke();
|
||||
$bean = R::dispense( 'boxedbean' )->box();
|
||||
R::trash( $bean );
|
||||
pass();
|
||||
$bean = R::dispense( 'boxedbean' );
|
||||
$bean->sharedBoxbean = R::dispense( 'boxedbean' )->box();
|
||||
R::store( $bean );
|
||||
pass();
|
||||
$bean = R::dispense( 'boxedbean' );
|
||||
$bean->ownBoxedbean = R::dispense( 'boxedbean' )->box();
|
||||
R::store( $bean );
|
||||
pass();
|
||||
$bean = R::dispense( 'boxedbean' );
|
||||
$bean->other = R::dispense( 'boxedbean' )->box();
|
||||
R::store( $bean );
|
||||
pass();
|
||||
$bean = R::dispense( 'boxedbean' );
|
||||
$bean->title = 'MyBean';
|
||||
$box = $bean->box();
|
||||
asrt( ( $box instanceof \Model_Boxedbean ), TRUE );
|
||||
R::store( $box );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fix for issue #512 - thanks for reporting Bernhard H.
|
||||
* OODBBean::__toString() implementation only works with C_ERR_IGNORE
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testToStringIssue512()
|
||||
{
|
||||
R::setErrorHandlingFUSE( \RedBeanPHP\OODBBean::C_ERR_FATAL );
|
||||
$boxedBean = R::dispense( 'boxedbean' );
|
||||
$str = (string) $boxedBean;
|
||||
asrt( $str, '{"id":0}' ); //no fatal error
|
||||
R::setErrorHandlingFUSE( FALSE );
|
||||
}
|
||||
}
|
||||
135
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Chill.php
vendored
Normal file
135
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Chill.php
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Chill
|
||||
*
|
||||
* Tests 'chill' mode. In this mode some bean types are frozen,
|
||||
* their schemas cannot be modified while others are fluid and
|
||||
* can still be adjusted.
|
||||
*
|
||||
* @file RedUNIT/Base/Chill.php
|
||||
* @desc Tests chill list functionality, i.e. freezing a subset of all types.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Chill extends Base
|
||||
{
|
||||
/**
|
||||
* Test Chill mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testChill()
|
||||
{
|
||||
$bean = R::dispense( 'bean' );
|
||||
$bean->col1 = '1';
|
||||
$bean->col2 = '2';
|
||||
R::store( $bean );
|
||||
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 3 );
|
||||
$bean->col3 = '3';
|
||||
R::store( $bean );
|
||||
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 4 );
|
||||
R::freeze( array( 'umbrella' ) );
|
||||
$bean->col4 = '4';
|
||||
R::store( $bean );
|
||||
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 5 );
|
||||
R::freeze( array( 'bean' ) );
|
||||
$bean->col5 = '5';
|
||||
try {
|
||||
R::store( $bean );
|
||||
fail();
|
||||
} catch (\Exception $e ) {
|
||||
pass();
|
||||
}
|
||||
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 5 );
|
||||
R::freeze( array() );
|
||||
$bean->col5 = '5';
|
||||
R::store( $bean );
|
||||
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 6 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we cannot add unique constraints on chilled tables,
|
||||
* otherwise you cannot avoid this from happening when adding beans to the
|
||||
* shared list :) -- this is almost a theoretical issue however we want it
|
||||
* to work according to specifications!
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDontAddUniqueConstraintForChilledBeanTypes()
|
||||
{
|
||||
R::nuke();
|
||||
$person = R::dispense( 'person' );
|
||||
$role = R::dispense( 'role' );
|
||||
$person->sharedRole[] = $role;
|
||||
R::store( $person );
|
||||
$person->sharedRole[] = R::dispense( 'role' );
|
||||
R::store( $person );
|
||||
$bean = R::getRedBean()->dispense('person_role');
|
||||
$bean->personId = $person->id;
|
||||
$bean->roleId = $role->id;
|
||||
try {
|
||||
R::store( $bean );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
asrt(R::count('person_role'), 2);
|
||||
R::nuke();
|
||||
$link = R::getRedBean()->dispense('person_role');
|
||||
$person = R::dispense( 'person' );
|
||||
$role = R::dispense( 'role' );
|
||||
$link->person = $person;
|
||||
$link->role = $role;
|
||||
R::store( $link );
|
||||
R::freeze(array('person_role'));
|
||||
$person->sharedRole[] = R::dispense( 'role' );
|
||||
R::store( $person );
|
||||
$bean = R::getRedBean()->dispense('person_role');
|
||||
$bean->personId = $person->id;
|
||||
$bean->roleId = $role->id;
|
||||
try {
|
||||
R::store( $bean );
|
||||
pass();
|
||||
} catch(\Exception $e) {
|
||||
fail();
|
||||
}
|
||||
asrt(R::count('person_role'), 3);
|
||||
R::freeze( array() ); //set freeze to FALSE and clear CHILL LIST!
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can set and reset the chill list and check the contents
|
||||
* of the chill list.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testChillTest()
|
||||
{
|
||||
R::freeze( array( 'beer' ) );
|
||||
$oodb = R::getRedBean();
|
||||
asrt( $oodb->isChilled( 'beer' ), TRUE );
|
||||
asrt( $oodb->isChilled( 'wine' ), FALSE );
|
||||
R::freeze( FALSE );
|
||||
$oodb = R::getRedBean();
|
||||
asrt( $oodb->isChilled( 'beer' ), TRUE );
|
||||
asrt( $oodb->isChilled( 'wine' ), FALSE );
|
||||
R::freeze( TRUE );
|
||||
$oodb = R::getRedBean();
|
||||
asrt( $oodb->isChilled( 'beer' ), TRUE );
|
||||
asrt( $oodb->isChilled( 'wine' ), FALSE );
|
||||
R::freeze( array() );
|
||||
$oodb = R::getRedBean();
|
||||
asrt( $oodb->isChilled( 'beer' ), FALSE );
|
||||
asrt( $oodb->isChilled( 'wine' ), FALSE );
|
||||
}
|
||||
}
|
||||
62
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Close.php
vendored
Normal file
62
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Close.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\QueryWriter\SQLiteT as SQLiteT;
|
||||
|
||||
/**
|
||||
* Close
|
||||
*
|
||||
* Tests whether we can close the database connection.
|
||||
*
|
||||
* @file RedUNIT/Base/Close.php
|
||||
* @desc Tests database closing functionality.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Close extends Base
|
||||
{
|
||||
/**
|
||||
* Test closing database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testClose()
|
||||
{
|
||||
// Test whether we can select a specific feature set
|
||||
R::useFeatureSet('novice/latest');
|
||||
pass();
|
||||
R::useFeatureSet('latest');
|
||||
pass();
|
||||
R::useFeatureSet('5.3');
|
||||
pass();
|
||||
R::useFeatureSet('novice/5.3');
|
||||
pass();
|
||||
try {
|
||||
R::useFeatureSet('5.2');
|
||||
fail();
|
||||
} catch ( \Exception $e ) {
|
||||
asrt( $e->getMessage(), 'Unknown feature set label.' );
|
||||
}
|
||||
try {
|
||||
R::nuke();
|
||||
fail();
|
||||
} catch( \Exception $e ) {
|
||||
asrt( $e->getMessage(), 'The nuke() command has been disabled using noNuke() or R::feature(novice/...).' );
|
||||
}
|
||||
R::useFeatureSet('latest');
|
||||
//Close
|
||||
R::getDatabaseAdapter()->setOption( 'setInitQuery', NULL );
|
||||
asrt( R::getDatabaseAdapter()->getDatabase()->isConnected(), TRUE );
|
||||
R::close();
|
||||
asrt( R::getDatabaseAdapter()->getDatabase()->isConnected(), FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
314
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Concurrency.php
vendored
Normal file
314
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Concurrency.php
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter;
|
||||
|
||||
/**
|
||||
* Concurrency
|
||||
*
|
||||
* Tests whether we can lock beans.
|
||||
*
|
||||
* @file RedUNIT/Base/Concurrency.php
|
||||
* @desc Tests concurrency scenarios
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Concurrency extends Base
|
||||
{
|
||||
/**
|
||||
* Returns the target drivers for this test.
|
||||
* This test only works with Postgres and MySQL/MariaDB.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTargetDrivers()
|
||||
{
|
||||
return array( 'pgsql','mysql' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepare()
|
||||
{
|
||||
try{
|
||||
R::close();
|
||||
} catch( \Exception $e ) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test has to be run manually.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function testLockException()
|
||||
{
|
||||
R::nuke();
|
||||
$lock = R::dispense('lock');
|
||||
$lock->value = 123;
|
||||
R::store($lock);
|
||||
$c = pcntl_fork();
|
||||
if ($c == -1) exit(1);
|
||||
if (!$c) {
|
||||
R::selectDatabase($this->currentlyActiveDriverID . 'c2');
|
||||
R::freeze(TRUE);
|
||||
try { R::exec('SET SESSION innodb_lock_wait_timeout=5');}catch( \Exception $e ){}
|
||||
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
|
||||
R::begin();
|
||||
$lock = R::loadForUpdate('lock', $lock->id);
|
||||
$lock->value = 4;
|
||||
sleep(10);
|
||||
R::store($lock);
|
||||
exit(0);
|
||||
} else {
|
||||
R::selectDatabase($this->currentlyActiveDriverID . 'c2');
|
||||
sleep(1);
|
||||
R::freeze(TRUE);
|
||||
try{ R::exec('SET SESSION innodb_lock_wait_timeout=5');}catch( \Exception $e ){}
|
||||
try{R::exec("SET lock_timeout = '1s';");}catch( \Exception $e ){}
|
||||
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
|
||||
R::begin();
|
||||
$exception = NULL;
|
||||
try {
|
||||
$lock = R::loadForUpdate('lock', $lock->id);
|
||||
} catch( \Exception $e ) {
|
||||
$exception = $e;
|
||||
}
|
||||
if ( !$exception ) fail();
|
||||
pass();
|
||||
$details = $exception->getDriverDetails();
|
||||
asrt( ($details[1]===1205 || $details[0]==='55P03'), TRUE );
|
||||
var_dump($lock);
|
||||
}
|
||||
try { R::exec('SET autocommit = 1'); }catch( \Exception $e ){}
|
||||
pcntl_wait($status);
|
||||
try { R::exec('SET SESSION innodb_lock_wait_timeout=50');}catch( \Exception $e ){}
|
||||
try{R::exec("SET lock_timeout = '50s';");}catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic locking scenarios using fork().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testConcurrency()
|
||||
{
|
||||
$c = pcntl_fork();
|
||||
if ($c == -1) exit(1);
|
||||
if (!$c) {
|
||||
R::selectDatabase($this->currentlyActiveDriverID . 'c');
|
||||
try{ R::exec('SET SESSION innodb_lock_wait_timeout=51');}catch( \Exception $e ){}
|
||||
try{R::exec("SET lock_timeout = '51s';");}catch( \Exception $e ){}
|
||||
R::exec('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
|
||||
sleep(2);
|
||||
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
|
||||
R::freeze(TRUE);
|
||||
R::begin();
|
||||
echo "CHILD: SUBTRACTING 2 START\n";
|
||||
$i = R::loadForUpdate('inventory', 1);
|
||||
$i->apples -= 2;
|
||||
sleep(4);
|
||||
R::store($i);
|
||||
R::commit();
|
||||
echo "CHILD: SUBTRACTING 2 DONE\n";
|
||||
echo (R::load('inventory', 1));
|
||||
echo "\n";
|
||||
exit(0);
|
||||
} else {
|
||||
R::selectDatabase($this->currentlyActiveDriverID . 'c');
|
||||
try{ R::exec('SET SESSION innodb_lock_wait_timeout=51');}catch( \Exception $e ){}
|
||||
try{R::exec("SET lock_timeout = '51s';");}catch( \Exception $e ){}
|
||||
echo "PARENT: PREP START\n";
|
||||
R::nuke();
|
||||
$i = R::dispense('inventory');
|
||||
$i->apples = 10;
|
||||
R::store($i);
|
||||
R::exec('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
|
||||
echo "PARENT: PREP DONE\n";
|
||||
sleep(3);
|
||||
echo "PARENT: ADDING 5 START\n";
|
||||
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
|
||||
R::freeze( TRUE );
|
||||
R::begin();
|
||||
$i = R::findForUpdate('inventory', ' id = ? ', array(1));
|
||||
$i = reset( $i );
|
||||
print_r($i);
|
||||
$i->apples += 5;
|
||||
R::store($i);
|
||||
R::commit();
|
||||
echo "PARENT ADDING 5 DONE\n";
|
||||
$i = R::getAll('select * from inventory where id = 1');
|
||||
print_r($i);
|
||||
asrt((int)$i[0]['apples'], 13);
|
||||
R::freeze( FALSE );
|
||||
try { R::exec('SET autocommit = 1'); }catch( \Exception $e ){}
|
||||
pcntl_wait($status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can use setSQLSnippet with find() and batch().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBatchAndFind()
|
||||
{
|
||||
/* baseline */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::batch( 'bean', $ids );
|
||||
asrt( count( $beans ), 10 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 0 );
|
||||
$logs->clear();
|
||||
|
||||
/* findOneForUpdate */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::findOneForUpdate( 'bean' );
|
||||
asrt( count( $beans ), 1 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 0 );
|
||||
$logs->clear();
|
||||
|
||||
/* findForUpdate */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::findForUpdate( 'bean' );
|
||||
asrt( count( $beans ), 10 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 0 );
|
||||
$logs->clear();
|
||||
|
||||
/* batch + snippet */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
R::getWriter()->setSQLSelectSnippet('for update');
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::batch( 'bean', $ids );
|
||||
asrt( count( $beans ), 10 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 1 );
|
||||
print_r( $entries );
|
||||
$logs->clear();
|
||||
|
||||
/* baseline */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::batch( 'bean', $ids );
|
||||
asrt( count( $beans ), 10 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 0 );
|
||||
$logs->clear();
|
||||
|
||||
/* find + snippet */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
R::getWriter()->setSQLSelectSnippet('for update');
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::find( 'bean' );
|
||||
asrt( count( $beans ), 10 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 1 );
|
||||
print_r( $entries );
|
||||
$logs->clear();
|
||||
|
||||
/* baseline */
|
||||
R::nuke();
|
||||
$beans = R::dispenseAll('bean*10');
|
||||
R::storeAll($beans[0]);
|
||||
$ids = array();
|
||||
foreach($beans[0] as $bean) $ids[] = $bean->id;
|
||||
R::debug( TRUE, 1 );
|
||||
$logs = R::getDatabaseAdapter()
|
||||
->getDatabase()
|
||||
->getLogger();
|
||||
$beans = R::batch( 'bean', $ids );
|
||||
asrt( count( $beans ), 10 );
|
||||
$entries = $logs->grep('for update');
|
||||
asrt( count( $entries ), 0 );
|
||||
$logs->clear();
|
||||
|
||||
R::debug( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* loadForUpdate/findForUpdate should be applied even if caching is on.
|
||||
* Caching may not interfere with locking beans.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testLockAndCache()
|
||||
{
|
||||
R::nuke();
|
||||
$bean = R::dispense('lock');
|
||||
$bean->title = 'lock';
|
||||
$id = R::store( $bean );
|
||||
R::getWriter()->setUseCache( TRUE );
|
||||
$lock = R::loadForUpdate( 'lock', $id );
|
||||
R::debug( TRUE, 1 );
|
||||
$lock = R::loadForUpdate( 'lock', $id );
|
||||
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
|
||||
asrt( count($logs), 1 ); //if no cache clear, then would have been 2
|
||||
R::debug( FALSE );
|
||||
$lock = R::findForUpdate( 'lock', 'id = ?', array( $id ) );
|
||||
R::debug( TRUE, 1 );
|
||||
$lock = R::findForUpdate( 'lock', 'id = ?', array( $id ) );
|
||||
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
|
||||
asrt( count($logs), 1 ); //if no cache clear, then would have been 2
|
||||
R::getWriter()->setUseCache( FALSE );
|
||||
R::debug( FALSE );
|
||||
R::nuke();
|
||||
}
|
||||
}
|
||||
224
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Copy.php
vendored
Normal file
224
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Copy.php
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Copy
|
||||
*
|
||||
* Tests whether we can make a copy or a deep copy of a bean
|
||||
* and whether recursion is handled well. Also tests
|
||||
* versioning: copying can be used to implement a versioning feature,
|
||||
* some test cases will reflect this particular use case.
|
||||
*
|
||||
* @file RedUNIT/Base/Copy.php
|
||||
* @desc Tests whether we can make a deep copy of a bean.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Copy extends Base
|
||||
{
|
||||
/**
|
||||
* Test whether recursion happens
|
||||
*/
|
||||
public function testCopyRecursion()
|
||||
{
|
||||
$document = R::dispense( 'document' );
|
||||
$id = R::store( $document );
|
||||
$document->ownDocument[] = $document;
|
||||
R::store( $document );
|
||||
$duplicate = R::dup( $document );
|
||||
pass(); //if RB cant handle this is will crash (nesting level error from PHP).
|
||||
$id2 = R::store( $duplicate );
|
||||
$duplicate = R::load( 'document', $id );
|
||||
asrt( (int) $document->document_id, $id );
|
||||
asrt( (int) $duplicate->document_id, $id2 );
|
||||
// Export variant
|
||||
$duplicate = R::exportAll( $document );
|
||||
asrt( (int) $duplicate[0]['document_id'], $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test real world scenario: Versioning
|
||||
*/
|
||||
public function testVersioning()
|
||||
{
|
||||
$document = R::dispense( 'document' );
|
||||
$page = R::dispense( 'page' );
|
||||
$document->title = 'test';
|
||||
$page->content = 'lorem ipsum';
|
||||
$user = R::dispense( 'user' );
|
||||
$user->name = 'Leo';
|
||||
$document->sharedUser[] = $user;
|
||||
$document->ownPage[] = $page;
|
||||
$document->starship_id = 3;
|
||||
$document->planet = R::dispense( 'planet' );
|
||||
R::store( $document );
|
||||
$duplicate = R::dup( $document );
|
||||
R::store( $duplicate );
|
||||
$duplicate = R::dup( $document );
|
||||
R::store( $duplicate );
|
||||
asrt( R::count( 'planet' ), 1 );
|
||||
asrt( R::count( 'user' ), 1 );
|
||||
asrt( R::count( 'document' ), 3 );
|
||||
asrt( R::count( 'page' ), 3 );
|
||||
asrt( R::count( 'spaceship' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above but now with intermediate save, counts must be same
|
||||
*/
|
||||
public function testVersioningIntermediateSaves()
|
||||
{
|
||||
$document = R::dispense( 'document' );
|
||||
$page = R::dispense( 'page' );
|
||||
$document->title = 'test';
|
||||
$page->content = 'lorem ipsum';
|
||||
$user = R::dispense( 'user' );
|
||||
$user->name = 'Leo';
|
||||
$document->sharedUser[] = $user;
|
||||
$document->ownPage[] = $page;
|
||||
$document->starship_id = 3;
|
||||
$document->planet = R::dispense( 'planet' );
|
||||
R::store( $document );
|
||||
$duplicate = R::dup( $document );
|
||||
R::store( $document );
|
||||
R::store( $duplicate );
|
||||
R::store( $document );
|
||||
$duplicate = R::dup( $document );
|
||||
R::store( $document );
|
||||
R::store( $duplicate );
|
||||
asrt( R::count( 'planet' ), 1 );
|
||||
asrt( R::count( 'user' ), 1 );
|
||||
asrt( R::count( 'document' ), 3 );
|
||||
asrt( R::count( 'page' ), 3 );
|
||||
asrt( R::count( 'spaceship' ), 0 );
|
||||
// same, but now with intermediate save, counts must be same
|
||||
R::freeze( TRUE );
|
||||
$document = R::dispense( 'document' );
|
||||
$page = R::dispense( 'page' );
|
||||
$document->title = 'test';
|
||||
$page->content = 'lorem ipsum';
|
||||
$user = R::dispense( 'user' );
|
||||
$user->name = 'Leo';
|
||||
$document->sharedUser[] = $user;
|
||||
$document->ownPage[] = $page;
|
||||
$document->starship_id = 3;
|
||||
$document->planet = R::dispense( 'planet' );
|
||||
R::store( $document );
|
||||
$duplicate = R::dup( $document );
|
||||
R::store( $document );
|
||||
R::store( $duplicate );
|
||||
R::store( $document );
|
||||
$duplicate = R::dup( $document );
|
||||
R::store( $document );
|
||||
R::store( $duplicate );
|
||||
asrt( R::count( 'planet' ), 2 );
|
||||
asrt( R::count( 'user' ), 2 );
|
||||
asrt( R::count( 'document' ), 6 );
|
||||
asrt( R::count( 'page' ), 6 );
|
||||
try { asrt( R::count( 'spaceship' ), 0 ); }catch(\Exception $e){pass();}
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Recursion
|
||||
*/
|
||||
public function testRecursion()
|
||||
{
|
||||
list( $d1, $d2 ) = R::dispense( 'document', 2 );
|
||||
$page = R::dispense( 'page' );
|
||||
list( $p1, $p2 ) = R::dispense( 'paragraph', 2 );
|
||||
list( $e1, $e2 ) = R::dispense( 'excerpt', 2 );
|
||||
$id2 = R::store( $d2 );
|
||||
$p1->name = 'a';
|
||||
$p2->name = 'b';
|
||||
$page->title = 'my page';
|
||||
$page->ownParagraph = array( $p1, $p2 );
|
||||
$p1->ownExcerpt[] = $e1;
|
||||
$p2->ownExcerpt[] = $e2;
|
||||
$e1->ownDocument[] = $d2;
|
||||
$e2->ownDocument[] = $d1;
|
||||
$d1->ownPage[] = $page;
|
||||
$id1 = R::store( $d1 );
|
||||
$d1 = R::load( 'document', $id1 );
|
||||
$d = R::dup( $d1 );
|
||||
$ids = array();
|
||||
asrt( ( $d instanceof OODBBean ), TRUE );
|
||||
asrt( count( $d->ownPage ), 1 );
|
||||
foreach ( end( $d->ownPage )->ownParagraph as $p ) {
|
||||
foreach ( $p->ownExcerpt as $e ) {
|
||||
$ids[] = end( $e->ownDocument )->id;
|
||||
}
|
||||
}
|
||||
sort( $ids );
|
||||
asrt( (int) $ids[0], 0 );
|
||||
asrt( (int) $ids[1], $id1 );
|
||||
R::store( $d );
|
||||
pass();
|
||||
$phillies = R::dispense( 'diner' );
|
||||
list( $lonelyman, $man, $woman ) = R::dispense( 'guest', 3 );
|
||||
$attendant = R::dispense( 'employee' );
|
||||
$lonelyman->name = 'Bennie Moten';
|
||||
$man->name = 'Daddy Stovepipe';
|
||||
$woman->name = 'Mississippi Sarah';
|
||||
$attendant->name = 'Gus Cannon';
|
||||
$phillies->sharedGuest = array( $lonelyman, $man, $woman );
|
||||
$phillies->ownEmployee[] = $attendant;
|
||||
$props = R::dispense( 'prop', 2 );
|
||||
$props[0]->kind = 'cigarette';
|
||||
$props[1]->kind = 'coffee';
|
||||
$thought = R::dispense( 'thought' );
|
||||
$thought->content = 'Blues';
|
||||
$thought2 = R::dispense( 'thought' );
|
||||
$thought2->content = 'Jazz';
|
||||
$woman->ownProp[] = $props[0];
|
||||
$man->sharedProp[] = $props[1];
|
||||
$attendant->ownThought = array( $thought, $thought2 );
|
||||
R::store( $phillies );
|
||||
$diner = R::findOne( 'diner' );
|
||||
$diner2 = R::dup( $diner );
|
||||
$id2 = R::store( $diner2 );
|
||||
$diner2 = R::load( 'diner', $id2 );
|
||||
asrt( count( $diner->ownEmployee ), 1 );
|
||||
asrt( count( $diner2->ownEmployee ), 1 );
|
||||
asrt( count( $diner->sharedGuest ), 3 );
|
||||
asrt( count( $diner2->sharedGuest ), 3 );
|
||||
$employee = reset( $diner->ownEmployee );
|
||||
asrt( count( $employee->ownThought ), 2 );
|
||||
$employee = reset( $diner2->ownEmployee );
|
||||
asrt( count( $employee->ownThought ), 2 );
|
||||
// Can we change something in the duplicate without changing the original?
|
||||
$employee->name = 'Marvin';
|
||||
$thought = R::dispense( 'thought' );
|
||||
$thought->content = 'depression';
|
||||
$employee->ownThought[] = $thought;
|
||||
array_pop( $diner2->sharedGuest );
|
||||
$guest = reset( $diner2->sharedGuest );
|
||||
$guest->name = 'Arthur Dent';
|
||||
$id2 = R::store( $diner2 );
|
||||
$diner2 = R::load( 'diner', $id2 );
|
||||
asrt( count( $diner->ownEmployee ), 1 );
|
||||
asrt( count( $diner2->ownEmployee ), 1 );
|
||||
asrt( count( $diner->sharedGuest ), 3 );
|
||||
asrt( count( $diner2->sharedGuest ), 2 );
|
||||
$employeeOld = reset( $diner->ownEmployee );
|
||||
asrt( count( $employeeOld->ownThought ), 2 );
|
||||
$employee = reset( $diner2->ownEmployee );
|
||||
asrt( count( $employee->ownThought ), 3 );
|
||||
asrt( $employee->name, 'Marvin' );
|
||||
asrt( $employeeOld->name, 'Gus Cannon' );
|
||||
// However the shared beans must not be copied
|
||||
asrt( R::count( 'guest' ), 3 );
|
||||
asrt( R::count( 'guest_prop' ), 1 );
|
||||
$arthur = R::findOne( 'guest', ' ' . R::getWriter()->esc( 'name' ) . ' = ? ', array( 'Arthur Dent' ) );
|
||||
asrt( $arthur->name, 'Arthur Dent' );
|
||||
}
|
||||
}
|
||||
226
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Count.php
vendored
Normal file
226
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Count.php
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\RedException\SQL as SQL;
|
||||
use RedBeanPHP\OODBBean;
|
||||
|
||||
/**
|
||||
* Count
|
||||
*
|
||||
* Tests whether we can count beans with or without
|
||||
* additional conditions and whether we can count associated
|
||||
* beans (relationCount).
|
||||
*
|
||||
* @file RedUNIT/Base/Count.php
|
||||
* @desc Tests for simple bean counting.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Count extends Base
|
||||
{
|
||||
/**
|
||||
* Tests type check and conversion in
|
||||
* OODB for count().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCountType()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->sharedPage = R::dispense( 'page', 10 );
|
||||
R::store( $book );
|
||||
asrt( R::count('bookPage'), 10 );
|
||||
try {
|
||||
R::count( 'WrongTypeName' );
|
||||
fail();
|
||||
} catch ( RedException $ex ) {
|
||||
pass();
|
||||
}
|
||||
try {
|
||||
R::count( 'wrong_type_name' );
|
||||
fail();
|
||||
} catch ( RedException $ex ) {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test count and wipe.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCountAndWipe()
|
||||
{
|
||||
testpack( "Test count and wipe" );
|
||||
$page = R::dispense( "page" );
|
||||
$page->name = "ABC";
|
||||
R::store( $page );
|
||||
$n1 = R::count( "page" );
|
||||
$page = R::dispense( "page" );
|
||||
$page->name = "DEF";
|
||||
R::store( $page );
|
||||
$n2 = R::count( "page" );
|
||||
asrt( $n1 + 1, $n2 );
|
||||
R::wipe( "page" );
|
||||
asrt( R::count( "page" ), 0 );
|
||||
asrt( R::getRedBean()->count( "page" ), 0 );
|
||||
asrt( R::getRedBean()->count( "kazoo" ), 0 ); // non existing table
|
||||
R::freeze( TRUE );
|
||||
try {
|
||||
asrt( R::getRedBean()->count( "kazoo" ), 0 ); // non existing table
|
||||
fail();
|
||||
} catch( \Exception $e ) {
|
||||
pass();
|
||||
}
|
||||
R::freeze( FALSE );
|
||||
$page = R::dispense( 'page' );
|
||||
$page->name = 'foo';
|
||||
R::store( $page );
|
||||
$page = R::dispense( 'page' );
|
||||
$page->name = 'bar';
|
||||
R::store( $page );
|
||||
asrt( R::count( 'page', ' name = ? ', array( 'foo' ) ), 1 );
|
||||
// Now count something that does not exist, this should return 0. (just be polite)
|
||||
asrt( R::count( 'teapot', ' name = ? ', array( 'flying' ) ), 0 );
|
||||
asrt( R::count( 'teapot' ), 0 );
|
||||
$currentDriver = $this->currentlyActiveDriverID;
|
||||
// Some drivers don't support that many error codes.
|
||||
if ( $currentDriver === 'mysql' || $currentDriver === 'postgres' ) {
|
||||
try {
|
||||
R::count( 'teaport', ' for tea ' );
|
||||
fail();
|
||||
} catch ( SQL $e ) {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we count the number of shared beans?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCountShared()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->sharedPageList = R::dispense( 'page', 5 );
|
||||
R::store( $book );
|
||||
asrt( $book->countShared('page'), 5 );
|
||||
asrt( $book->countShared('leaflet'), 0 );
|
||||
asrt( R::dispense( 'book' )->countShared('page'), 0 );
|
||||
$am = R::getRedBean()->getAssociationManager();
|
||||
asrt( $am->relatedCount( R::dispense( 'book' ), 'page' ), 0);
|
||||
try {
|
||||
$am->relatedCount( 'not a bean', 'type' );
|
||||
fail();
|
||||
} catch( RedException $e ) {
|
||||
pass();
|
||||
}
|
||||
R::getWriter()->setUseCache(TRUE);
|
||||
asrt( $book->countShared('page'), 5 );
|
||||
R::exec('DELETE FROM book_page WHERE book_id > 0 -- keep-cache');
|
||||
asrt( $book->countShared('page'), 5 );
|
||||
R::getWriter()->setUseCache(FALSE);
|
||||
asrt( $book->countShared('page'), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test $bean->countOwn($type);
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCountOwn()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$empty = R::dispense( 'book' );
|
||||
$nothing = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->ownPageList[] = $page;
|
||||
R::store( $book );
|
||||
R::store( $empty );
|
||||
OODBBean::useFluidCount( FALSE );
|
||||
asrt( $book->countOwn('page'), 1 );
|
||||
asrt( $empty->countOwn('page'), 0 );
|
||||
asrt( $nothing->countOwn('page'), 0 );
|
||||
$old = OODBBean::useFluidCount( TRUE );
|
||||
asrt( $old, FALSE );
|
||||
asrt( $book->countOwn('page'), 1 );
|
||||
asrt( $empty->countOwn('page'), 0 );
|
||||
asrt( $nothing->countOwn('page'), 0 );
|
||||
R::freeze( TRUE );
|
||||
asrt( $book->countOwn('page'), 1 );
|
||||
asrt( $empty->countOwn('page'), 0 );
|
||||
asrt( $nothing->countOwn('page'), 0 );
|
||||
R::freeze( FALSE );
|
||||
R::nuke();
|
||||
asrt( $empty->countOwn('page'), 0 );
|
||||
asrt( $nothing->countOwn('page'), 0 );
|
||||
R::freeze( TRUE );
|
||||
asrt( $nothing->countOwn('page'), 0 );
|
||||
try { asrt( $empty->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
|
||||
try { asrt( $book->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
|
||||
R::freeze( FALSE );
|
||||
OODBBean::useFluidCount( FALSE );
|
||||
try { asrt( $empty->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
|
||||
try { asrt( $book->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
|
||||
OODBBean::useFluidCount( TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test $bean->withCondition( ... )->countOwn( $type );
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCountWithCondition()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->ownPageList[] = R::dispense( 'page' );
|
||||
R::store( $book );
|
||||
OODBBean::useFluidCount( FALSE );
|
||||
$count = $book
|
||||
->withCondition(' id > :id ', array( ':id' => 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 1 );
|
||||
$count = $book
|
||||
->withCondition(' id > ? ', array( 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 1 );
|
||||
$count = $book
|
||||
->withCondition(' id < :id ', array( ':id' => 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 0 );
|
||||
$count = $book
|
||||
->withCondition(' id < ? ', array( 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 0 );
|
||||
OODBBean::useFluidCount( TRUE );
|
||||
$count = $book
|
||||
->withCondition(' id > :id ', array( ':id' => 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 1 );
|
||||
$count = $book
|
||||
->withCondition(' id > ? ', array( 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 1 );
|
||||
$count = $book
|
||||
->withCondition(' id < :id ', array( ':id' => 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 0 );
|
||||
$count = $book
|
||||
->withCondition(' id < ? ', array( 0 ) )
|
||||
->countOwn('page');
|
||||
asrt( $count, 0 );
|
||||
}
|
||||
}
|
||||
474
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Cross.php
vendored
Normal file
474
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Cross.php
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\AssociationManager as AssociationManager;
|
||||
use RedBeanPHP\RedException\SQL as SQL;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Cross
|
||||
*
|
||||
* Tests self referential many-to-many relations, including the
|
||||
* aggr feature.
|
||||
*
|
||||
* @file RedUNIT/Base/Cross.php
|
||||
* @desc Tests associations within the same table (i.e. page_page2 alike)
|
||||
* Cross tables, self referential many-to-many relations
|
||||
* and aggregation techniques
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
|
||||
class Cross extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* Test how well aggr handles fields with no
|
||||
* values.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAggrNullHandling()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$page->name = 'Page 3';
|
||||
$book->xownPageList[] = $page;
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
$texts = $book->aggr( 'ownPageList', 'text' );
|
||||
pass();
|
||||
asrt( count( $texts ), 0 );
|
||||
asrt( is_array( $texts ), TRUE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page1 = R::dispense( 'page' );
|
||||
$page1->name = 'Page 1';
|
||||
$text1 = R::dispense('text');
|
||||
$text1->content = 'Text 1';
|
||||
$page1->text = $text1;
|
||||
$book->xownPageList[] = $page1;
|
||||
$page2 = R::dispense( 'page' );
|
||||
$page2->name = 'Page 2';
|
||||
$text2 = R::dispense( 'text' );
|
||||
$text2->content = 'Text 2';
|
||||
$page2->text = $text2;
|
||||
$book->xownPageList[] = $page2;
|
||||
$page3 = R::dispense( 'page' );
|
||||
$page3->name = 'Page 3';
|
||||
$book->xownPageList[] = $page3;
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
$texts = $book->aggr( 'ownPageList', 'text' );
|
||||
pass();
|
||||
asrt( count( $texts ), 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test many different scenarios with self referential
|
||||
* many-to-many relations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSelfReferentialCRUD()
|
||||
{
|
||||
R::nuke();
|
||||
$buddies = R::dispense( 'buddy', 4 );
|
||||
$buddies[0]->name = 'A';
|
||||
$buddies[1]->name = 'B';
|
||||
$buddies[2]->name = 'C';
|
||||
$buddies[3]->name = 'D';
|
||||
$buddies[0]->sharedBuddyList = array( $buddies[1], $buddies[2] );
|
||||
$buddies[3]->sharedBuddyList = array( $buddies[2] );
|
||||
R::storeAll( $buddies );
|
||||
$buddies[0] = $buddies[0]->fresh();
|
||||
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
|
||||
//does this yield valid combinations - cross relations / self ref n-m
|
||||
//need to symmetric....
|
||||
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'B,C' );
|
||||
unset( $buddies[0]->sharedBuddy );
|
||||
R::storeAll( $buddies );
|
||||
$buddies[0] = $buddies[0]->fresh();
|
||||
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
|
||||
$buddies[3] = $buddies[3]->fresh();
|
||||
asrt( count( $buddies[3]->sharedBuddyList ), 1 );
|
||||
$names = R::gatherLabels( $buddies[3]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'C' );
|
||||
$buddies[2] = $buddies[2]->fresh();
|
||||
asrt( count( $buddies[2]->sharedBuddyList ), 2 );
|
||||
$names = R::gatherLabels( $buddies[2]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'A,D' );
|
||||
$buddies[1] = $buddies[1]->fresh();
|
||||
asrt( count( $buddies[1]->sharedBuddyList ), 1 );
|
||||
$names = R::gatherLabels( $buddies[1]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'A' );
|
||||
//Can we add one?
|
||||
$buddies[1]->sharedBuddyList[] = R::dispense('buddy')->setAttr('name', 'E');
|
||||
R::store( $buddies[1] );
|
||||
$buddies[0] = $buddies[0]->fresh();
|
||||
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
|
||||
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'B,C' );
|
||||
$buddies[1] = $buddies[1]->fresh();
|
||||
asrt( count( $buddies[1]->sharedBuddyList ), 2 );
|
||||
$names = R::gatherLabels( $buddies[1]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'A,E' );
|
||||
$all = R::find( 'buddy' );
|
||||
asrt( count( $all ), 5 );
|
||||
foreach( $buddies[1]->sharedBuddy as $buddy ) {
|
||||
if ( $buddy->name === 'E' ) {
|
||||
$buddyE = $buddy;
|
||||
}
|
||||
}
|
||||
asrt( isset( $buddyE ), TRUE );
|
||||
asrt( $buddyE->name, 'E' );
|
||||
//can we update?
|
||||
foreach( $buddies[0]->sharedBuddy as $buddy ) {
|
||||
if ( $buddy->name === 'C' ) {
|
||||
$buddy->name = 'C2';
|
||||
}
|
||||
}
|
||||
R::store( $buddies[0] );
|
||||
$buddies[0] = $buddies[0]->fresh();
|
||||
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
|
||||
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'B,C2' );
|
||||
$buddies[2] = $buddies[2]->fresh();
|
||||
asrt( count( $buddies[2]->sharedBuddyList ), 2 );
|
||||
$names = R::gatherLabels( $buddies[2]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'A,D' );
|
||||
//can we delete?
|
||||
foreach( $buddies[0]->sharedBuddyList as $id => $buddy ) {
|
||||
if ( $buddy->name === 'B' ) {
|
||||
unset( $buddies[0]->sharedBuddyList[$id] );
|
||||
}
|
||||
}
|
||||
R::store( $buddies[0] );
|
||||
$buddies[0] = $buddies[0]->fresh();
|
||||
asrt( count( $buddies[0]->sharedBuddyList ), 1 );
|
||||
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'C2' );
|
||||
$buddies[1] = $buddies[1]->fresh();
|
||||
asrt( count( $buddies[1]->sharedBuddyList ), 1 );
|
||||
$names = R::gatherLabels( $buddies[1]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'E' );
|
||||
asrt( R::count( 'buddy' ), 5 );
|
||||
asrt( R::count( 'buddyBuddy' ), 3 );
|
||||
$buddies[3] = $buddies[3]->fresh();
|
||||
asrt( count( $buddies[3]->sharedBuddyList ), 1 );
|
||||
$names = R::gatherLabels( $buddies[3]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'C2' );
|
||||
$buddies[2] = $buddies[2]->fresh();
|
||||
asrt( count( $buddies[2]->sharedBuddyList ), 2 );
|
||||
$names = R::gatherLabels( $buddies[2]->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'A,D' );
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->sharedBuddyList ), 1 );
|
||||
$names = R::gatherLabels( $buddyE->sharedBuddyList );
|
||||
sort( $names );
|
||||
$names = implode( ',', $names );
|
||||
asrt( $names, 'B' );
|
||||
if ( $this->currentlyActiveDriverID === 'mysql' ) {
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->with(' HAVING linked_by > 0 ')->sharedBuddyList ), 1 );
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->with(' HAVING linked_by < 0 ')->sharedBuddyList ), 0 );
|
||||
}
|
||||
if ( $this->currentlyActiveDriverID === 'sqlite' ) {
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->withCondition(' linked_by > 0 ')->sharedBuddyList ), 1 );
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->withCondition(' linked_by < 0 ')->sharedBuddyList ), 0 );
|
||||
}
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->withCondition(' buddy_buddy.buddy_id > 0 AND buddy_buddy.buddy2_id > 0 ')->sharedBuddyList ), 1 );
|
||||
$buddyE = $buddyE->fresh();
|
||||
asrt( count( $buddyE->withCondition(' buddy_buddy.buddy_id < 0 AND buddy_buddy.buddy2_id < 0 ')->sharedBuddyList ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test self referential N-M relations (page_page).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSelfReferential()
|
||||
{
|
||||
$page = R::dispense( 'page' )->setAttr( 'title', 'a' );
|
||||
$page->sharedPage[] = R::dispense( 'page' )->setAttr( 'title', 'b' );
|
||||
R::store( $page );
|
||||
$page = $page->fresh();
|
||||
$page = reset( $page->sharedPage );
|
||||
asrt( $page->title, 'b' );
|
||||
$tables = array_flip( R::inspect() );
|
||||
asrt( isset( $tables['page_page'] ), TRUE );
|
||||
$columns = R::inspect( 'page_page' );
|
||||
asrt( isset( $columns['page2_id'] ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the unique constraint.
|
||||
* Just want to make sure it is not too limiting and functions
|
||||
* properly for typical RedBeanPHP usage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUnique()
|
||||
{
|
||||
R::nuke();
|
||||
$page1 = R::dispense( 'page' );
|
||||
$tag1 = R::dispense( 'tag' );
|
||||
$page2 = R::dispense( 'page' );
|
||||
$tag2 = R::dispense( 'tag' );
|
||||
$page3 = R::dispense( 'page' );
|
||||
$tag3 = R::dispense( 'tag' );
|
||||
$page1->sharedTag[] = $tag1;
|
||||
R::store( $page1 );
|
||||
//can we save all combinations with unique?
|
||||
asrt( R::count( 'pageTag' ), 1);
|
||||
$page1->sharedTag[] = $tag2;
|
||||
R::store( $page1 );
|
||||
asrt( R::count( 'pageTag' ), 2 );
|
||||
$page1->sharedTag[] = $tag3;
|
||||
$page2->sharedTag[] = $tag1;
|
||||
$page2->sharedTag[] = $tag2;
|
||||
$page2->sharedTag[] = $tag3;
|
||||
$page3->sharedTag[] = $tag1;
|
||||
$page3->sharedTag[] = $tag2;
|
||||
$page3->sharedTag[] = $tag3;
|
||||
R::storeAll( array( $page1, $page2, $page3 ) );
|
||||
asrt( R::count('pageTag'), 9 );
|
||||
$page1 = $page1->fresh();
|
||||
$page1->sharedTag[] = $tag3;
|
||||
R::store( $page1 );
|
||||
//cant add violates unique
|
||||
asrt( R::count( 'pageTag' ), 9 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared lists can only be formed using types.
|
||||
* If you happen to have two beans of the same type you can still
|
||||
* have a shared list but not with a sense of direction.
|
||||
* I.e. quest->sharedQuest returns all the quests that follow the first one,
|
||||
* but also the ones that are followed by the first one.
|
||||
* If you want to have some sort of direction; i.e. one quest follows another one
|
||||
* you'll have to use an alias: quest->target, but now you can't use the shared list
|
||||
* anymore because it will start looking for a type named 'target' (which is just an alias)
|
||||
* for quest, but it cant find that table and it's not possible to 'keep remembering'
|
||||
* the alias throughout the system.
|
||||
*
|
||||
* The aggr() method solves this inconvenience.
|
||||
* Aggr iterates through the list identified by its first argument ('target' -> ownQuestTargetList)
|
||||
* and fetches every ID of the target (quest_target.target_id), loads these beans in batch for
|
||||
* optimal performance, puts them back in the beans (questTarget->target) and returns the
|
||||
* references.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAggregationInSelfRefNM()
|
||||
{
|
||||
R::nuke();
|
||||
$quest1 = R::dispense( 'quest' );
|
||||
$quest1->name = 'Quest 1';
|
||||
$quest2 = R::dispense( 'quest' );
|
||||
$quest2->name = 'Quest 2';
|
||||
$quest3 = R::dispense( 'quest' );
|
||||
$quest3->name = 'Quest 3';
|
||||
$quest4 = R::dispense( 'quest' );
|
||||
$quest4->name = 'Quest 4';
|
||||
$quest1->link( 'questTarget' )->target = $quest2;
|
||||
$quest1->link( 'questTarget' )->target = $quest3;
|
||||
$quest3->link( 'questTarget' )->target = $quest4;
|
||||
$quest3->link( 'questTarget' )->target = $quest1;
|
||||
R::storeAll( array( $quest1, $quest3 ) );
|
||||
//There should be 4 links
|
||||
asrt( (int) R::count('questTarget'), 4 );
|
||||
$quest1 = $quest1->fresh();
|
||||
$targets = $quest1->aggr( 'ownQuestTargetList', 'target', 'quest' );
|
||||
//can we aggregate the targets over the link type?
|
||||
asrt( count( $targets), 2 );
|
||||
//are the all valid beans?
|
||||
foreach( $targets as $target ) {
|
||||
//are they beans?
|
||||
asrt( ( $target instanceof OODBBean ), TRUE );
|
||||
//are they fetched as quest?
|
||||
asrt( ( $target->getMeta( 'type' ) ), 'quest' );
|
||||
}
|
||||
//list target should already have been loaded, nuke has no effect
|
||||
R::nuke();
|
||||
$links = $quest1->ownQuestTargetList;
|
||||
//are the links still there, have they been set in the beans as well?
|
||||
asrt( count( $links ), 2);
|
||||
//are they references instead of copies, changes in the aggregation set should affect the beans in links!
|
||||
foreach( $targets as $target ) {
|
||||
$target->name .= 'b';
|
||||
asrt( substr( $target->name, -1 ), 'b' );
|
||||
}
|
||||
//do the names end with a 'b' here as well ? i.e. have they been changed through references?
|
||||
foreach( $links as $link ) {
|
||||
asrt( substr( $target->name, -1 ), 'b' );
|
||||
}
|
||||
//now test the effect on existing shadow...
|
||||
R::nuke();
|
||||
$quest1 = R::dispense('quest');
|
||||
$quest1->name = 'Quest 1';
|
||||
$quest2 = R::dispense('quest');
|
||||
$quest2->name = 'Quest 2';
|
||||
$quest3 = R::dispense('quest');
|
||||
$quest3->name = 'Quest 3';
|
||||
$quest4 = R::dispense('quest');
|
||||
$quest4->name = 'Quest 4';
|
||||
$quest1->link( 'questTarget' )->target = $quest2;
|
||||
$quest1->link( 'questTarget' )->target = $quest3;
|
||||
R::store($quest1);
|
||||
asrt( (int) R::count( 'questTarget' ), 2 );
|
||||
//now lets first build a shadow
|
||||
$quest1->link( 'questTarget' )->target = $quest4;
|
||||
//$quest1 = $quest1->fresh();
|
||||
$targets = $quest1->aggr( 'ownQuestTargetList', 'target', 'quest' );
|
||||
//targets should not include the new bean...
|
||||
asrt( count($targets), 2 );
|
||||
//this should not overwrite the existing beans
|
||||
R::store($quest1);
|
||||
asrt( (int) R::count( 'questTarget' ), 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test aggr without the aliasing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAggrBasic()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page1 = R::dispense( 'page' );
|
||||
$page1->name = 'Page 1';
|
||||
$text1 = R::dispense('text');
|
||||
$text1->content = 'Text 1';
|
||||
$page1->text = $text1;
|
||||
$book->xownPageList[] = $page1;
|
||||
$page2 = R::dispense( 'page' );
|
||||
$page2->name = 'Page 2';
|
||||
$text2 = R::dispense( 'text' );
|
||||
$text2->content = 'Text 2';
|
||||
$page2->text = $text2;
|
||||
$book->xownPageList[] = $page2;
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
$texts = $book->aggr( 'ownPageList', 'text' );
|
||||
R::nuke();
|
||||
asrt( count( $texts ), 2 );
|
||||
foreach( $texts as $text ) {
|
||||
asrt( ( $text instanceof OODBBean ), TRUE );
|
||||
}
|
||||
$pages = $book->ownPageList;
|
||||
asrt( count( $pages ), 2 );
|
||||
asrt( R::count( 'page' ), 0 );
|
||||
foreach( $pages as $page ) {
|
||||
asrt( ( $page instanceof OODBBean ), TRUE );
|
||||
$text = $page->text;
|
||||
asrt( ( $text instanceof OODBBean ), TRUE );
|
||||
$text->content = 'CHANGED';
|
||||
}
|
||||
foreach( $texts as $text ) {
|
||||
asrt( $text->content, 'CHANGED' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test aggr with basic aliasing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAggrWithOnlyAlias()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page1 = R::dispense( 'page' );
|
||||
$page1->name = 'Page 1';
|
||||
$text1 = R::dispense( 'text' );
|
||||
$text1->content = 'Text 1';
|
||||
$page1->content = $text1;
|
||||
$book->xownPageList[] = $page1;
|
||||
$page2 = R::dispense( 'page' );
|
||||
$page2->name = 'Page 2';
|
||||
$text2 = R::dispense( 'text' );
|
||||
$text2->content = 'Text 2';
|
||||
$page2->content = $text2;
|
||||
$book->xownPageList[] = $page2;
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
$texts = $book->aggr( 'ownPageList', 'content', 'text' );
|
||||
R::nuke();
|
||||
asrt( count( $texts ), 2 );
|
||||
foreach( $texts as $text ) {
|
||||
asrt( ( $text instanceof OODBBean), TRUE );
|
||||
}
|
||||
$pages = $book->ownPageList;
|
||||
asrt( count( $pages ), 2 );
|
||||
asrt( R::count( 'page' ), 0 );
|
||||
foreach( $pages as $page ) {
|
||||
asrt( ( $page instanceof OODBBean ), TRUE );
|
||||
$text = $page->content;
|
||||
asrt( ( $text instanceof OODBBean ), TRUE );
|
||||
$text->content = 'CHANGED';
|
||||
}
|
||||
foreach( $texts as $text ) {
|
||||
asrt( $text->content, 'CHANGED' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The aggr method can only be used with own-list.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testErrorHandlingAggr()
|
||||
{
|
||||
$wrongLists = array( 'not-an-own-list', 'OWNlist', 'Ownpage', 'ownbook', 'own book', '!', 'sharedBook' );
|
||||
foreach( $wrongLists as $wrongList ) {
|
||||
$bean = R::dispense( 'bean' );
|
||||
try {
|
||||
$bean->aggr( $wrongList, 'field' );
|
||||
fail();
|
||||
} catch ( \Exception $exception ) {
|
||||
pass();
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
160
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Cursors.php
vendored
Normal file
160
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Cursors.php
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\QueryWriter;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter;
|
||||
|
||||
/**
|
||||
* Cursors
|
||||
*
|
||||
* Tests whether RedBeanPHP can use cursors (using the
|
||||
* findCollection method) to iterate over large data sets.
|
||||
*
|
||||
* @file RedUNIT/Base/Cursors.php
|
||||
* @desc Tests whether we can use cursors
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Cursors extends Base
|
||||
{
|
||||
/**
|
||||
* Test whether we can use cursors with raw SQL
|
||||
* from the facade (#656).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSQLCursors()
|
||||
{
|
||||
R::nuke();
|
||||
for( $i=0; $i<20; $i++ ) {
|
||||
$page = R::dispense( 'page' );
|
||||
$page->number = $i;
|
||||
$page->content = sha1( $i );
|
||||
R::store( $page );
|
||||
}
|
||||
$cursor = R::getCursor( 'SELECT * FROM page ORDER BY page.number ASC' );
|
||||
asrt( get_class( $cursor ), 'RedBeanPHP\Cursor\PDOCursor');
|
||||
$i = 0;
|
||||
$list = array();
|
||||
while( $row = $cursor->getNextItem() ) {
|
||||
asrt( is_array( $row ), TRUE );
|
||||
asrt( (string) $row['number'], strval( $i ) );
|
||||
asrt( $row['content'], sha1( $i ) );
|
||||
$list[] = $row['content'];
|
||||
$i ++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic cursor functionality.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBasicCursors()
|
||||
{
|
||||
R::nuke();
|
||||
for( $i=0; $i<20; $i++ ) {
|
||||
$page = R::dispense( 'page' );
|
||||
$page->number = $i;
|
||||
$page->content = sha1( $i );
|
||||
R::store( $page );
|
||||
}
|
||||
$collection = R::findCollection( 'page', 'ORDER BY page.number ASC' );
|
||||
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
|
||||
$i = 0;
|
||||
$list = array();
|
||||
while( $bean = $collection->next() ) {
|
||||
asrt( ( $bean instanceof OODBBean ), TRUE );
|
||||
asrt( (string) $bean->number, strval( $i ) );
|
||||
asrt( $bean->content, sha1( $i ) );
|
||||
$list[] = $bean->content;
|
||||
$i ++;
|
||||
}
|
||||
$collection->reset();
|
||||
$i = 0;
|
||||
while( $bean = $collection->next() ) {
|
||||
asrt( ( $bean instanceof OODBBean ), TRUE );
|
||||
asrt( (string) $bean->number, strval( $i ) );
|
||||
asrt( $bean->content, sha1( $i ) );
|
||||
$i ++;
|
||||
}
|
||||
$collection = R::findCollection( 'page', ' ORDER BY content ASC ' );
|
||||
sort( $list );
|
||||
$i = 0;
|
||||
while( $bean = $collection->next() ) {
|
||||
asrt( $bean->content, $list[$i] );
|
||||
$i ++;
|
||||
}
|
||||
$collection = R::findCollection( 'page', ' ORDER BY content ASC LIMIT 5 ' );
|
||||
sort( $list );
|
||||
$i = 0;
|
||||
while( $bean = $collection->next() ) {
|
||||
asrt( $bean->content, $list[$i] );
|
||||
$i ++;
|
||||
if ( $i > 5 ) break;
|
||||
}
|
||||
$key = array_rand( $list );
|
||||
$content = $list[ $key ];
|
||||
$collection = R::findCollection( 'page', ' content = ? ', array( $content ) );
|
||||
$bean = $collection->next();
|
||||
asrt( $bean->content, $content );
|
||||
$collection->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use a filtered cursor?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCursorWithFilter()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->title = 'Title';
|
||||
R::store( $book );
|
||||
$filter = array(
|
||||
QueryWriter::C_SQLFILTER_READ => array(
|
||||
'book' => array('title' => ' LOWER(book.title) ' )
|
||||
)
|
||||
);
|
||||
AQueryWriter::setSQLFilters( $filter );
|
||||
$books = R::findCollection( 'book' );
|
||||
$book = $books->next();
|
||||
asrt( $book->title, 'title' );
|
||||
AQueryWriter::setSQLFilters( array() );
|
||||
$books = R::findCollection( 'book' );
|
||||
$book = $books->next();
|
||||
asrt( $book->title, 'Title' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test empty collections (NULLCursor).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEmptyCollection()
|
||||
{
|
||||
R::nuke();
|
||||
$page = R::dispense( 'page' );
|
||||
$page->content = 'aaa';
|
||||
R::store( $page );
|
||||
$collection = R::findCollection( 'page' );
|
||||
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
|
||||
$collection = R::findCollection( 'page', ' content = ?', array( 'bbb' ) );
|
||||
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
|
||||
asrt( is_null( $collection->next() ), TRUE );
|
||||
$collection = R::findCollection( 'something' );
|
||||
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
|
||||
asrt( is_null( $collection->next() ), TRUE );
|
||||
asrt( is_null( $collection->reset() ), TRUE );
|
||||
$collection->close();
|
||||
}
|
||||
}
|
||||
461
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Database.php
vendored
Normal file
461
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Database.php
vendored
Normal file
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\QueryWriter\SQLiteT as SQLiteT;
|
||||
use RedBeanPHP\OODB as OODB;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\AssociationManager as AssociationManager;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\RedException\SQL as SQL;
|
||||
use RedBeanPHP\QueryWriter\MySQL as MySQL;
|
||||
use RedBeanPHP\QueryWriter\PostgreSQL as PostgreSQL;
|
||||
use RedBeanPHP\QueryWriter\CUBRID as CUBRID;
|
||||
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
|
||||
|
||||
/**
|
||||
* Database
|
||||
*
|
||||
* Tests basic RedBeanPHP database functionality.
|
||||
*
|
||||
* @file RedUNIT/Base/Database.php
|
||||
* @desc Tests basic database behaviors
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Database extends Base
|
||||
{
|
||||
/**
|
||||
* What drivers should be loaded for this test pack?
|
||||
*/
|
||||
public function getTargetDrivers()
|
||||
{
|
||||
return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use meta mask with find() ?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSelectFindOne()
|
||||
{
|
||||
R::store(R::dispense('book'));
|
||||
R::store(R::dispense('book'));
|
||||
if ($this->currentlyActiveDriverID == 'pgsql') {
|
||||
R::getWriter()->setSQLFilters(array('r'=>array('book'=>array('__meta_total'=>'COUNT(*) OVER()'))), FALSE);
|
||||
} else {
|
||||
R::getWriter()->setSQLFilters(array('r'=>array('book'=>array('__meta_total'=>'2'))), FALSE);
|
||||
}
|
||||
$books = R::find('book', 'LIMIT 1');
|
||||
$book = reset($books);
|
||||
$bundle = $book->getMeta('data.bundle');
|
||||
asrt(intval($bundle['__meta_total']),2);
|
||||
R::getWriter()->setSQLFilters(array(), FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we cannot just bind function names but
|
||||
* also function templates, i.e. little SQL snippets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBindFuncFunctionTemplates()
|
||||
{
|
||||
R::bindFunc('read', 'xbean.lucky', '111 * %s', TRUE);
|
||||
$bean = R::dispense('xbean');
|
||||
$bean->lucky = 7;
|
||||
$id = R::store( $bean );
|
||||
$bean = R::load( 'xbean', $id );
|
||||
asrt( intval($bean->lucky), 777 );
|
||||
R::bindFunc('write', 'xbean.triple', '3 * %s', TRUE);
|
||||
$bean->triple = 3;
|
||||
R::store($bean);
|
||||
$bean = $bean->fresh();
|
||||
asrt( intval($bean->triple), 9);
|
||||
R::bindFunc('read', 'xbean.lucky', NULL);
|
||||
R::bindFunc('write', 'xbean.triple', NULL);
|
||||
R::getRedBean()->clearAllFuncBindings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make ConvertToBean work together with getRow #759.
|
||||
* When no results are found for getRow it returns []
|
||||
* Then when you give that to convertToBean it wraps your
|
||||
* single row into an array of multiple rows, so you get [[]].
|
||||
* Then this loop has something to
|
||||
* iterate on foreach ( $rows as $row ) { ...
|
||||
* And then it crashes on: $id = $row['id'];
|
||||
*/
|
||||
public function testHarmonizeConvertToBeanAndGetRow()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::convertToBean( 'book', R::getRow( 'SELECT * FROM book' ) );
|
||||
asrt( is_null( $book ), TRUE );
|
||||
$book = R::convertToBean( 'book', array() );
|
||||
asrt( is_null( $book ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for bugfix:
|
||||
* adhere to return type specification for R::getRow #728
|
||||
* public static function getRow is documented as a function
|
||||
* that returns an array. However, in a situation
|
||||
* where the resultset is empty, this returns a boolean
|
||||
* and causes an unexpected failure in
|
||||
* code like this because it is expecting an array.
|
||||
*/
|
||||
public function testReturnTypeGetRow()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
R::store( $book );
|
||||
$row = R::getRow('SELECT * FROM book');
|
||||
asrt( is_array( $row ), TRUE );
|
||||
R::trash( $book );
|
||||
$row = R::getRow('SELECT * FROM book');
|
||||
asrt( is_array( $row ), TRUE );
|
||||
R::nuke();
|
||||
$row = R::getRow('SELECT * FROM book');
|
||||
asrt( is_array( $row ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the (protected) database capability checker method
|
||||
* of the RedBeanPHP PDO driver (RPDO).
|
||||
*/
|
||||
public function testDatabaseCapabilityChecker()
|
||||
{
|
||||
$capChecker = new \DatabaseCapabilityChecker( R::getDatabaseAdapter()->getDatabase()->getPDO() );
|
||||
$result = $capChecker->checkCapability('creativity');
|
||||
asrt( $result, FALSE ); /* nope, no strong AI yet.. */
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can obtain the PDO object from the
|
||||
* database driver for custom database operations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetPDO()
|
||||
{
|
||||
$driver = R::getDatabaseAdapter();
|
||||
asrt( ( $driver instanceof DBAdapter), TRUE );
|
||||
$pdo = $driver->getDatabase()->getPDO();
|
||||
asrt( ( $pdo instanceof \PDO ), TRUE );
|
||||
$pdo2 = R::getPDO();
|
||||
asrt( ( $pdo2 instanceof \PDO ), TRUE );
|
||||
asrt( ( $pdo === $pdo2 ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setter maximum integer bindings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSetMaxBind()
|
||||
{
|
||||
$driver = R::getDatabaseAdapter()->getDatabase();
|
||||
$old = $driver->setMaxIntBind( 10 );
|
||||
//use SQLite to confirm...
|
||||
if ( $this->currentlyActiveDriverID === 'sqlite' ) {
|
||||
$type = R::getCell( 'SELECT typeof( ? ) ', array( 11 ) );
|
||||
asrt( $type, 'text' );
|
||||
$type = R::getCell( 'SELECT typeof( ? ) ', array( 10 ) );
|
||||
asrt( $type, 'integer' );
|
||||
$type = R::getCell( 'SELECT typeof( ? ) ', array( 9 ) );
|
||||
asrt( $type, 'integer' );
|
||||
}
|
||||
$new = $driver->setMaxIntBind( $old );
|
||||
asrt( $new, 10 );
|
||||
try {
|
||||
$driver->setMaxIntBind( '10' );
|
||||
fail();
|
||||
} catch( RedException $e ) {
|
||||
pass();
|
||||
}
|
||||
$new = $driver->setMaxIntBind( $old );
|
||||
asrt( $new, $old );
|
||||
$new = $driver->setMaxIntBind( $old );
|
||||
asrt( $new, $old );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use colons in SQL?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testColonsInSQL()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->title = 'About :';
|
||||
R::store( $book );
|
||||
pass();
|
||||
$book = R::findOne( 'book', ' title LIKE :this ', array(
|
||||
':this' => 'About :'
|
||||
) );
|
||||
asrt( ( $book instanceof OODBBean ), TRUE );
|
||||
//without the colon?
|
||||
$book = R::findOne( 'book', ' title LIKE :this ', array(
|
||||
'this' => 'About :'
|
||||
) );
|
||||
asrt( ( $book instanceof OODBBean ), TRUE );
|
||||
$book = R::findOne( 'book', ' title LIKE :this ', array(
|
||||
':this' => '%:%'
|
||||
) );
|
||||
asrt( ( $book instanceof OODBBean ), TRUE );
|
||||
$book = R::findOne( 'book', ' title LIKE :this OR title LIKE :that', array(
|
||||
'this' => '%:%', ':that' => 'That'
|
||||
) );
|
||||
asrt( ( $book instanceof OODBBean ), TRUE );
|
||||
$records = R::getAll('SELECT * FROM book WHERE title LIKE :this', array( ':this' => 'About :' ) );
|
||||
asrt( count( $records ), 1 );
|
||||
$records = R::getAll('SELECT * FROM book WHERE title LIKE :this', array( 'this' => 'About :' ) );
|
||||
asrt( count( $records ), 1 );
|
||||
$records = R::getAll('SELECT * FROM book WHERE title LIKE :this OR title LIKE :that', array( ':this' => 'About :', ':that' => 'That' ) );
|
||||
asrt( count( $records ), 1 );
|
||||
$records = R::getRow('SELECT * FROM book WHERE title LIKE :this', array( ':this' => 'About :' ) );
|
||||
asrt( count( $records ), 2 );
|
||||
$records = R::getRow('SELECT * FROM book WHERE title LIKE :this', array( 'this' => 'About :' ) );
|
||||
asrt( count( $records ), 2 );
|
||||
$records = R::getRow('SELECT * FROM book WHERE title LIKE :this OR title LIKE :that', array( ':this' => 'About :', ':that' => 'That' ) );
|
||||
asrt( count( $records ), 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting direct PDO.
|
||||
* Not much to test actually.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDirectPDO()
|
||||
{
|
||||
$pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
|
||||
R::getDatabaseAdapter()->getDatabase()->setPDO( $pdo );
|
||||
pass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for testConnection() method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testConnectionTester()
|
||||
{
|
||||
asrt( R::testConnection(), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the various ways to fetch (select queries)
|
||||
* data using adapter methods in the facade.
|
||||
* Also tests the new R::getAssocRow() method,
|
||||
* as requested in issue #324.
|
||||
*/
|
||||
public function testFetchTypes()
|
||||
{
|
||||
R::nuke();
|
||||
$page = R::dispense( 'page' );
|
||||
$page->a = 'a';
|
||||
$page->b = 'b';
|
||||
R::store( $page );
|
||||
$page = R::dispense( 'page' );
|
||||
$page->a = 'c';
|
||||
$page->b = 'd';
|
||||
R::store( $page );
|
||||
$expect = '[{"id":"1","a":"a","b":"b"},{"id":"2","a":"c","b":"d"}]';
|
||||
asrt( json_encode( R::getAll( 'SELECT * FROM page' ) ), $expect );
|
||||
$expect = '{"1":"a","2":"c"}';
|
||||
asrt( json_encode( R::getAssoc( 'SELECT id, a FROM page' ) ), $expect );
|
||||
$expect = '{"1":{"a":"a","b":"b"},"2":{"a":"c","b":"d"}}';
|
||||
asrt( json_encode( R::getAssoc( 'SELECT id, a, b FROM page' ) ), $expect );
|
||||
$expect = '[{"id":"1","a":"a"},{"id":"2","a":"c"}]';
|
||||
asrt( json_encode( R::getAssocRow( 'SELECT id, a FROM page' ) ), $expect );
|
||||
$expect = '[{"id":"1","a":"a","b":"b"},{"id":"2","a":"c","b":"d"}]';
|
||||
asrt( json_encode( R::getAssocRow( 'SELECT id, a, b FROM page' ) ), $expect );
|
||||
$expect = '{"id":"1","a":"a","b":"b"}';
|
||||
asrt( json_encode( R::getRow( 'SELECT * FROM page WHERE id = 1' ) ), $expect );
|
||||
$expect = '"a"';
|
||||
asrt( json_encode( R::getCell( 'SELECT a FROM page WHERE id = 1' ) ), $expect );
|
||||
$expect = '"b"';
|
||||
asrt( json_encode( R::getCell( 'SELECT b FROM page WHERE id = 1') ), $expect );
|
||||
$expect = '"c"';
|
||||
asrt( json_encode( R::getCell('SELECT a FROM page WHERE id = 2') ), $expect );
|
||||
$expect = '["a","c"]';
|
||||
asrt( json_encode( R::getCol( 'SELECT a FROM page' ) ), $expect );
|
||||
$expect = '["b","d"]';
|
||||
asrt( json_encode( R::getCol('SELECT b FROM page') ), $expect );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether we can store an empty bean.
|
||||
* An empty bean has no properties, only ID. Normally we would
|
||||
* skip the ID field in an INSERT, this test forces the driver
|
||||
* to specify a value for the ID field. Different writers have to
|
||||
* use different values: Mysql uses NULL to insert a new auto-generated ID,
|
||||
* while Postgres has to use DEFAULT.
|
||||
*/
|
||||
public function testEmptyBean()
|
||||
{
|
||||
testpack( 'Test Empty Bean Storage.' );
|
||||
R::nuke();
|
||||
$bean = R::dispense( 'emptybean' );
|
||||
$id = R::store( $bean );
|
||||
asrt( ( $id > 0 ), TRUE );
|
||||
asrt( R::count( 'emptybean' ), 1 );
|
||||
$bean = R::dispense( 'emptybean' );
|
||||
$id = R::store( $bean );
|
||||
asrt( ( $id > 0 ), TRUE );
|
||||
asrt( R::count( 'emptybean' ), 2 );
|
||||
//also test in frozen mode
|
||||
R::freeze( TRUE );
|
||||
$bean = R::dispense( 'emptybean' );
|
||||
$id = R::store( $bean );
|
||||
asrt( ( $id > 0 ), TRUE );
|
||||
asrt( R::count( 'emptybean' ), 3 );
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test selecting.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSelects()
|
||||
{
|
||||
$rooms = R::dispense( 'room', 2 );
|
||||
$rooms[0]->kind = 'suite';
|
||||
$rooms[1]->kind = 'classic';
|
||||
$rooms[0]->number = 6;
|
||||
$rooms[1]->number = 7;
|
||||
R::store( $rooms[0] );
|
||||
R::store( $rooms[1] );
|
||||
$rooms = R::getAssoc('SELECT * FROM room WHERE id < -999');
|
||||
asrt(is_array($rooms), TRUE);
|
||||
asrt(count($rooms), 0);
|
||||
$rooms = R::getAssoc( 'SELECT ' . R::getWriter()->esc( 'number' ) . ', kind FROM room ORDER BY kind ASC' );
|
||||
foreach ( $rooms as $key => $room ) {
|
||||
asrt( ( $key === 6 || $key === 7 ), TRUE );
|
||||
asrt( ( $room == 'classic' || $room == 'suite' ), TRUE );
|
||||
}
|
||||
$rooms = R::getDatabaseAdapter()->getAssoc( 'SELECT kind FROM room' );
|
||||
foreach ( $rooms as $key => $room ) {
|
||||
asrt( ( $room == 'classic' || $room == 'suite' ), TRUE );
|
||||
asrt( $room, $key );
|
||||
}
|
||||
$rooms = R::getAssoc( 'SELECT `number`, kind FROM rooms2 ORDER BY kind ASC' );
|
||||
asrt( count( $rooms ), 0 );
|
||||
asrt( is_array( $rooms ), TRUE );
|
||||
// GetCell should return NULL in case of exception
|
||||
asrt( NULL, R::getCell( 'SELECT dream FROM fantasy' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Malfunctioning database adapter to test exceptions.
|
||||
*/
|
||||
class TroubleDapter extends DBAdapter
|
||||
{
|
||||
private $sqlState;
|
||||
public function setSQLState( $sqlState )
|
||||
{
|
||||
$this->sqlState = $sqlState;
|
||||
}
|
||||
public function get( $sql, $values = array() )
|
||||
{
|
||||
$exception = new SQL( 'Just a trouble maker' );
|
||||
$exception->setSQLState( $this->sqlState );
|
||||
$exception->setDriverDetails( array(0,1,0) );
|
||||
throw $exception;
|
||||
}
|
||||
public function getRow( $sql, $aValues = array() )
|
||||
{
|
||||
$this->get( $sql, $aValues );
|
||||
}
|
||||
public function exec( $sql, $aValues = array(), $noEvent = FALSE )
|
||||
{
|
||||
$this->get( $sql, $aValues );
|
||||
}
|
||||
}
|
||||
|
||||
182
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Dispense.php
vendored
Normal file
182
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Dispense.php
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\Facade as Facade;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\Util\DispenseHelper as DispenseHelper;
|
||||
|
||||
/**
|
||||
* Dispense
|
||||
*
|
||||
* Tests whether we can dispense beans and tests all
|
||||
* features of the dispense/dispenseAll functions.
|
||||
*
|
||||
* @file RedUNIT/Base/Dispense.php
|
||||
* @desc Tests bean dispensing functionality.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Dispense extends Base
|
||||
{
|
||||
/**
|
||||
* Test whether findOrDispense and findOneOrDispense
|
||||
* will trigger same validation Exception for invalid
|
||||
* bean types as R::dispense(). Github issue #546.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssue546()
|
||||
{
|
||||
try {
|
||||
R::findOrDispense( 'invalid_type' );
|
||||
fail();
|
||||
} catch ( RedException $exception ) {
|
||||
pass();
|
||||
}
|
||||
try {
|
||||
R::findOneOrDispense( 'invalid_type' );
|
||||
fail();
|
||||
} catch ( RedException $exception ) {
|
||||
pass();
|
||||
}
|
||||
try {
|
||||
DispenseHelper::checkType( 'invalid_type' );
|
||||
fail();
|
||||
} catch ( RedException $exception ) {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dispense.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBasicsDispense()
|
||||
{
|
||||
$redbean = R::getRedBean();
|
||||
// Can we dispense a bean?
|
||||
$page = $redbean->dispense( "page" );
|
||||
// Does it have a meta type?
|
||||
asrt( ( (bool) $page->getMeta( "type" ) ), TRUE );
|
||||
// Does it have an ID?
|
||||
asrt( isset( $page->id ), TRUE );
|
||||
// Type should be 'page'
|
||||
asrt( ( $page->getMeta( "type" ) ), "page" );
|
||||
// ID should be 0 because bean does not exist in database yet.
|
||||
asrt( ( $page->id ), 0 );
|
||||
// Try some faulty dispense actions.
|
||||
foreach ( array( "", ".", "-") as $value ) {
|
||||
try {
|
||||
$redbean->dispense( $value );
|
||||
fail();
|
||||
} catch (RedException $e ) {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
$bean = $redbean->dispense( "testbean" );
|
||||
$bean["property"] = 123;
|
||||
$bean["abc"] = "def";
|
||||
asrt( $bean["property"], 123 );
|
||||
asrt( $bean["abc"], "def" );
|
||||
asrt( $bean->abc, "def" );
|
||||
asrt( isset( $bean["abd"] ), FALSE );
|
||||
asrt( isset( $bean["abc"] ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the facade-only dispenseAll method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDispenseAll()
|
||||
{
|
||||
list( $book, $page ) = Facade::dispenseAll( 'book,page' );
|
||||
asrt( ( $book instanceof OODBBean ), TRUE );
|
||||
asrt( ( $page instanceof OODBBean ), TRUE );
|
||||
asrt( $book->getMeta( 'type' ), 'book');
|
||||
asrt( $page->getMeta( 'type' ), 'page');
|
||||
list( $book, $page, $texts, $mark ) = R::dispenseAll( 'book,page,text*2,mark' );
|
||||
asrt( ( $book instanceof OODBBean ), TRUE );
|
||||
asrt( ( $page instanceof OODBBean ), TRUE );
|
||||
asrt( is_array( $texts ), TRUE );
|
||||
asrt( ( $mark instanceof OODBBean ), TRUE );
|
||||
asrt( $book->getMeta( 'type'), 'book' );
|
||||
asrt( $page->getMeta( 'type'), 'page' );
|
||||
asrt( $mark->getMeta( 'type'), 'mark' );
|
||||
asrt( $texts[0]->getMeta( 'type'), 'text' );
|
||||
asrt( $texts[1]->getMeta( 'type'), 'text' );
|
||||
list( $eggs, $milk, $butter ) = R::dispenseAll( 'eggs*3,milk*1,butter*9' );
|
||||
asrt( count( $eggs ), 3 );
|
||||
asrt( ( $milk instanceof OODBBean ), TRUE );
|
||||
asrt( count( $butter ), 9 );
|
||||
list( $eggs, $milk, $butter ) = R::dispenseAll( 'eggs*3,milk*1,butter*9', TRUE );
|
||||
asrt( count( $eggs ), 3 );
|
||||
asrt( count( $milk ), 1 );
|
||||
asrt( count( $eggs ), 3 );
|
||||
list( $beer ) = R::dispenseAll( 'beer*0', TRUE );
|
||||
asrt( is_array( $beer ), TRUE );
|
||||
asrt( count( $beer ), 0 );
|
||||
list( $beer ) = R::dispenseAll( 'beer*0', FALSE );
|
||||
asrt( is_array( $beer ), FALSE );
|
||||
asrt( is_null( $beer ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests different return values of dispense().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDispenseArray()
|
||||
{
|
||||
$oodb = R::getRedBean();
|
||||
$array = $oodb->dispense( 'book', 0, TRUE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 1, TRUE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 2, TRUE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = R::dispense( 'book', 0, TRUE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = R::dispense( 'book', 1, TRUE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = R::dispense( 'book', 2, TRUE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 0, FALSE );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
asrt( is_null( $array ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 1, FALSE );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
asrt( ( $array instanceof OODBBean ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 2, FALSE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = R::dispense( 'book', 0, FALSE );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
$array = R::dispense( 'book', 1, FALSE );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
$array = R::dispense( 'book', 2, FALSE );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 0 );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
asrt( is_null( $array ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 1 );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
asrt( ( $array instanceof OODBBean ), TRUE );
|
||||
$array = $oodb->dispense( 'book', 2 );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
$array = R::dispense( 'book', 0 );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
$array = R::dispense( 'book', 1 );
|
||||
asrt( is_array( $array ), FALSE );
|
||||
$array = R::dispense( 'book', 2 );
|
||||
asrt( is_array( $array ), TRUE );
|
||||
}
|
||||
}
|
||||
647
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Dup.php
vendored
Normal file
647
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Dup.php
vendored
Normal file
@@ -0,0 +1,647 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\DuplicationManager as DuplicationManager;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
|
||||
/**
|
||||
* Dup
|
||||
*
|
||||
* Tests duplication. Like the 'copy' test suite but
|
||||
* focuses on more complex scenarios.
|
||||
*
|
||||
* @file RedUNIT/Base/Dup.php
|
||||
* @desc Intensive test for dup()
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Dup extends Base
|
||||
{
|
||||
/**
|
||||
* Tests basic export functionality
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testExport()
|
||||
{
|
||||
$bean = R::dispense('bean');
|
||||
$bean->name = R::dispense('book');
|
||||
$export = $bean->export( FALSE, FALSE, FALSE, array( 'book' ) );
|
||||
asrt( isset( $export['name'] ), TRUE );
|
||||
$export = $bean->export( FALSE, FALSE, FALSE, array( 'book2' ) );
|
||||
asrt( isset( $export['name'] ), FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the original ID is stored
|
||||
* in meta data (quite handy for ID mappings).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeepOldID()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->xownPageList[] = R::dispense( 'page' );
|
||||
R::store( $book );
|
||||
$bookID = $book->id;
|
||||
$page = reset( $book->xownPageList );
|
||||
$pageID = $page->id;
|
||||
$book = $book->fresh();
|
||||
$copy = R::duplicate( $book );
|
||||
asrt( $copy->getMeta( 'sys.dup-from-id' ), $bookID );
|
||||
$copyPage = reset( $copy->xownPageList );
|
||||
asrt( $copyPage->getMeta( 'sys.dup-from-id' ), $pageID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test export camelCase.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testExportCamelCase()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->isCheap = TRUE;
|
||||
$book->hasISBNCode = FALSE;
|
||||
$page = R::dispense('page');
|
||||
$page->isWrittenWell = TRUE;
|
||||
$page->containsInterestingText = TRUE;
|
||||
$book->ownPageList[] = $page;
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
$export = R::exportAll( $book );
|
||||
asrt( isset( $export[0]['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['is_cheap'] ), TRUE );
|
||||
asrt( isset( $export[0]['has_isbn_code'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['is_written_well'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['contains_interesting_text'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['book_id'] ), TRUE );
|
||||
R::useExportCase( 'camel' );
|
||||
$export = R::exportAll( $book );
|
||||
asrt( isset( $export[0]['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['isCheap'] ), TRUE );
|
||||
asrt( isset( $export[0]['hasIsbnCode'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['isWrittenWell'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['containsInterestingText'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['bookId'] ), TRUE );
|
||||
R::useExportCase( 'dolphin' );
|
||||
$export = R::exportAll( $book );
|
||||
asrt( isset( $export[0]['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['isCheap'] ), TRUE );
|
||||
asrt( isset( $export[0]['hasIsbnCode'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['isWrittenWell'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['containsInterestingText'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['bookID'] ), TRUE );
|
||||
R::useExportCase( 'default' );
|
||||
$export = R::exportAll( $book );
|
||||
asrt( isset( $export[0]['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['is_cheap'] ), TRUE );
|
||||
asrt( isset( $export[0]['has_isbn_code'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['is_written_well'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['contains_interesting_text'] ), TRUE );
|
||||
asrt( isset( $export[0]['ownPage']['0']['book_id'] ), TRUE );
|
||||
try {
|
||||
R::useExportCase( 'invalid' );
|
||||
fail();
|
||||
} catch ( RedException $exception ) {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can duplicate part of a tree
|
||||
* without infinite loops.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDupPortionOfATree()
|
||||
{
|
||||
R::nuke();
|
||||
$article = R::dispense( 'article' );
|
||||
$article->name = 'article 1';
|
||||
list( $article2, $article3 ) = R::dispense( 'article', 2 );
|
||||
$article2->name = 'article 2';
|
||||
$article3->name = 'article 3';
|
||||
list( $article4, $article5 ) = R::dispense( 'article' , 2);
|
||||
$article4->name = 'article 4';
|
||||
$article5->name = 'article 5';
|
||||
list( $article6, $article7 ) = R::dispense( 'article' , 2);
|
||||
$article6->name = 'article 6';
|
||||
$article7->name = 'article 7';
|
||||
$article3->xownArticleList[] = $article7;
|
||||
$article4->xownArticleList[] = $article6;
|
||||
$article2->xownArticleList = array( $article5, $article4 );
|
||||
$article->xownArticleList = array( $article2, $article3 );
|
||||
R::store( $article );
|
||||
asrt( R::count( 'article' ), 7 );
|
||||
$article2 = $article2->fresh();
|
||||
$dupArticle2 = R::duplicate( $article2 );
|
||||
$dupArticle2->name = 'article 2b';
|
||||
$dupBeans = $dupArticle2->xownArticleList;
|
||||
foreach( $dupBeans as $dupBean ) {
|
||||
$list[] = $dupBean->name;
|
||||
}
|
||||
sort( $list );
|
||||
$listStr = implode( ',', $list );
|
||||
asrt( $listStr, 'article 4,article 5' );
|
||||
foreach( $dupBeans as $dupBean ) {
|
||||
if ( $dupBean->name === 'article 4' ) {
|
||||
$dup4 = $dupBean;
|
||||
}
|
||||
}
|
||||
asrt( isset( $dup4 ), TRUE );
|
||||
$dupBeans = $dup4->xownArticleList;
|
||||
foreach( $dupBeans as $dupBean ) {
|
||||
asrt( $dupBean->name, 'article 6' );
|
||||
}
|
||||
//so we have extracted part of the tree, can we store it?
|
||||
$id = R::store( $dupArticle2 );
|
||||
asrt( ( $id > 0 ), TRUE );
|
||||
asrt( R::count( 'article' ), 11 );
|
||||
$originalArticle = $article->fresh();
|
||||
asrt( $originalArticle->name, 'article 1' );
|
||||
$subArticles = $originalArticle->xownArticleList;
|
||||
$list = array();
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
$list[] = $subArticle->name;
|
||||
}
|
||||
sort( $list );
|
||||
$listStr = implode( ',', $list );
|
||||
asrt( $listStr, 'article 2,article 2b,article 3' );
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
if ( $subArticle->name === 'article 2' ) {
|
||||
$sub2 = $subArticle;
|
||||
}
|
||||
if ( $subArticle->name === 'article 3' ) {
|
||||
$sub3 = $subArticle;
|
||||
}
|
||||
}
|
||||
$subArticles = $sub2->xownArticleList;
|
||||
$list = array();
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
$list[] = $subArticle->name;
|
||||
}
|
||||
sort( $list );
|
||||
$listStr = implode( ',', $list );
|
||||
asrt( $listStr, 'article 4,article 5' );
|
||||
$subArticles = $sub3->xownArticleList;
|
||||
$list = array();
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
$list[] = $subArticle->name;
|
||||
}
|
||||
sort( $list );
|
||||
$listStr = implode( ',', $list );
|
||||
asrt( $listStr, 'article 7' );
|
||||
$subArticles = $sub2->xownArticleList;
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
if ( $subArticle->name === 'article 4' ) {
|
||||
$sub4 = $subArticle;
|
||||
}
|
||||
if ( $subArticle->name === 'article 5' ) {
|
||||
$sub5 = $subArticle;
|
||||
}
|
||||
}
|
||||
asrt( count( $sub4->xownArticleList ), 1 );
|
||||
$subBeans = $sub4->xownArticleList;
|
||||
$subBean = reset( $subBeans );
|
||||
asrt( $subBean->name, 'article 6');
|
||||
asrt( count( $sub5->xownArticleList ), 0 );
|
||||
$dupArticle2 = $dupArticle2->fresh();
|
||||
$subArticles = $dupArticle2->xownArticleList;
|
||||
$list = array();
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
$list[] = $subArticle->name;
|
||||
}
|
||||
sort( $list );
|
||||
$listStr = implode( ',', $list );
|
||||
asrt( $listStr, 'article 4,article 5' );
|
||||
foreach( $subArticles as $subArticle ) {
|
||||
if ( $subArticle->name === 'article 4' ) {
|
||||
$sub4 = $subArticle;
|
||||
}
|
||||
if ( $subArticle->name === 'article 5' ) {
|
||||
$sub5 = $subArticle;
|
||||
}
|
||||
}
|
||||
asrt( count( $sub4->xownArticleList ), 1 );
|
||||
$subBeans = $sub4->xownArticleList;
|
||||
$subBean = reset( $subBeans );
|
||||
asrt( $subBean->name, 'article 6');
|
||||
asrt( count( $sub5->xownArticleList ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exportAll and caching.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testExportAllAndCache()
|
||||
{
|
||||
testpack( 'exportAll() and Cache' );
|
||||
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
|
||||
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
|
||||
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
|
||||
$id = R::store( $can );
|
||||
R::debug( TRUE );
|
||||
ob_start();
|
||||
$can = R::load( 'can', $id );
|
||||
$cache = $this->getCache();
|
||||
$data = R::exportAll( array( $can ), TRUE );
|
||||
$queries = ob_get_contents();
|
||||
R::debug( FALSE );
|
||||
ob_end_clean();
|
||||
$len1 = strlen( $queries );
|
||||
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
|
||||
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
|
||||
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
|
||||
$id = R::store( $can );
|
||||
R::debug( TRUE );
|
||||
ob_start();
|
||||
$can = R::load( 'can', $id );
|
||||
$cache = $this->getCache();
|
||||
$data = R::exportAll( array( $can ), TRUE );
|
||||
$queries = ob_get_contents();
|
||||
R::debug( FALSE );
|
||||
ob_end_clean();
|
||||
$len2 = strlen( $queries );
|
||||
asrt( ( $len1 ), ( $len2 ) );
|
||||
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
|
||||
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
|
||||
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
|
||||
$id = R::store( $can );
|
||||
R::debug( TRUE );
|
||||
ob_start();
|
||||
$can = R::load( 'can', $id );
|
||||
$cache = $this->getCache();
|
||||
R::getDuplicationManager()->setTables( $cache );
|
||||
$data = R::exportAll( array( $can ), TRUE );
|
||||
$queries = ob_get_contents();
|
||||
R::debug( FALSE );
|
||||
ob_end_clean();
|
||||
$len3 = strlen( $queries );
|
||||
asrt( ( ( $len3 ) < ( $len2 ) ), TRUE );
|
||||
asrt( count( $data ), 1 );
|
||||
asrt( $data[0]['ownCoffee'][0]['color'], 'black' );
|
||||
R::getDuplicationManager()->setCacheTables( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test duplication and caching.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function DupAndCache()
|
||||
{
|
||||
testpack( 'Dup() and Cache' );
|
||||
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
|
||||
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
|
||||
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
|
||||
$can = R::load( 'can', R::store( $can ) );
|
||||
$d = new DuplicationManager( R::getToolBox() );
|
||||
$d->setCacheTables( TRUE );
|
||||
ob_start();
|
||||
R::debug( 1 );
|
||||
$x = $d->dup( $can );
|
||||
$queries = ob_get_contents();
|
||||
R::debug( 0 );
|
||||
ob_end_clean();
|
||||
$len1 = strlen( $queries );
|
||||
asrt( ( $len1 > 40 ), TRUE );
|
||||
asrt( isset( $x->ownCoffee ), TRUE );
|
||||
asrt( count( $x->ownCoffee ), 1 );
|
||||
asrt( isset( $x->sharedTag ), TRUE );
|
||||
asrt( count( $x->sharedTag ), 1 );
|
||||
$cache = $d->getSchema();
|
||||
R::nuke();
|
||||
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
|
||||
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
|
||||
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
|
||||
$can = R::load( 'can', R::store( $can ) );
|
||||
$d = new DuplicationManager( R::getToolBox() );
|
||||
|
||||
/**
|
||||
* $cache = '{"book": {
|
||||
* "id": "INTEGER",
|
||||
* "title": "TEXT"
|
||||
* }, "bean": {
|
||||
* "id": "INTEGER",
|
||||
* "prop": "INTEGER"
|
||||
* }, "pessoa": {
|
||||
* "id": "INTEGER",
|
||||
* "nome": "TEXT",
|
||||
* "nome_meio": "TEXT",
|
||||
* "sobrenome": "TEXT",
|
||||
* "nascimento": "NUMERIC",
|
||||
* "reg_owner": "TEXT"
|
||||
* }, "documento": {
|
||||
* "id": "INTEGER",
|
||||
* "nome_documento": "TEXT",
|
||||
* "numero_documento": "TEXT",
|
||||
* "reg_owner": "TEXT",
|
||||
* "ownPessoa_id": "INTEGER"
|
||||
* }, "can": {
|
||||
* "id": "INTEGER",
|
||||
* "size": "INTEGER"
|
||||
* }, "coffee": {
|
||||
* "id": "INTEGER",
|
||||
* "color": "TEXT",
|
||||
* "can_id": "INTEGER"
|
||||
* }, "tag": {
|
||||
* "id": "INTEGER",
|
||||
* "name": "TEXT"
|
||||
* }, "can_tag": {
|
||||
* "id": "INTEGER",
|
||||
* "tag_id": "INTEGER",
|
||||
* "can_id": "INTEGER"
|
||||
* }}'
|
||||
*/
|
||||
|
||||
$d->setTables( $cache );
|
||||
ob_start();
|
||||
R::debug( 1 );
|
||||
$x = $d->dup( $can );
|
||||
$queries = ob_get_contents();
|
||||
ob_end_clean();
|
||||
R::debug( 0 );
|
||||
$len2 = strlen( $queries );
|
||||
asrt( isset( $x->ownCoffee ), TRUE );
|
||||
asrt( count( $x->ownCoffee ), 1 );
|
||||
asrt( isset( $x->sharedTag ), TRUE );
|
||||
asrt( count( $x->sharedTag ), 1 );
|
||||
asrt( json_encode( $cache ), json_encode( $d->getSchema() ) );
|
||||
asrt( ( $len1 > $len2 ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test duplication and tainting.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDupAndExportNonTainting()
|
||||
{
|
||||
testpack( 'Dup() and Export() should not taint beans' );
|
||||
$p = R::dispense( 'page' );
|
||||
$b = R::dispense( 'book' );
|
||||
$b->ownPage[] = $p;
|
||||
$b->title = 'a';
|
||||
$id = R::store( $b );
|
||||
$b = R::load( 'book', $id );
|
||||
asrt( ( !$b->getMeta( 'tainted' ) ), TRUE );
|
||||
R::exportAll( $b );
|
||||
asrt( ( !$b->getMeta( 'tainted' ) ), TRUE );
|
||||
R::dup( $b );
|
||||
asrt( ( !$b->getMeta( 'tainted' ) ), TRUE );
|
||||
testpack( 'Test issue with ownItems and stealing Ids.' );
|
||||
R::nuke();
|
||||
$bill = R::dispense( 'bill' );
|
||||
$item = R::dispense( 'item' );
|
||||
$element = R::dispense( 'element' );
|
||||
$bill->ownItem[] = $item;
|
||||
$bill->sharedElement[] = $element;
|
||||
R::store( $bill );
|
||||
$bill = R::load( 'bill', 1 );
|
||||
$bill->ownItem;
|
||||
$bill->sharedElement;
|
||||
$copy = R::dup( $bill );
|
||||
R::store( $copy );
|
||||
$rows = ( R::getAll( 'select * from bill_element' ) );
|
||||
asrt( count( $rows ), 2 );
|
||||
$rows = ( R::getAll( 'select * from item' ) );
|
||||
foreach ( $rows as $row ) {
|
||||
asrt( ( $row['bill_id'] > 0 ), TRUE );
|
||||
}
|
||||
R::nuke();
|
||||
$this->runOnce();
|
||||
R::freeze( TRUE );
|
||||
$this->runOnce( FALSE );
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exporting with filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ExportWithFilters()
|
||||
{
|
||||
testpack( 'Export with filters' );
|
||||
$book = R::dispense( 'book' );
|
||||
$pages = R::dispense( 'page', 2 );
|
||||
$texts = R::dispense( 'text', 2 );
|
||||
$images = R::dispense( 'image', 2 );
|
||||
$author = R::dispense( 'author' );
|
||||
$pub = R::dispense( 'publisher' );
|
||||
$bookmarks = R::dispense( 'bookmark', 2 );
|
||||
$pages[0]->ownText = array( $texts[0] );
|
||||
$pages[0]->ownImage = array( $images[0] );
|
||||
$pages[1]->ownText = array( $texts[1] );
|
||||
$pages[1]->ownImage = array( $images[1] );
|
||||
$pages[0]->sharedBookmark[] = $bookmarks[0];
|
||||
$pages[1]->sharedBookmark[] = $bookmarks[1];
|
||||
$bookmarks[0]->ownNote[] = R::dispense( 'note' )->setAttr( 'text', 'a note' );
|
||||
$bookmarks[1]->ownNote[] = R::dispense( 'note' )->setAttr( 'text', 'a note' );
|
||||
$book->ownPage = $pages;
|
||||
$book->author = $author;
|
||||
$author->publisher = $pub;
|
||||
$bookID = R::store( $book );
|
||||
R::getDuplicationManager()->setTables( R::getWriter()->getTables() );
|
||||
$objects = ( R::exportAll( array( $book ), TRUE, array() ) );
|
||||
asrt( isset( $objects[0]['ownPage'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'] ), 2 );
|
||||
asrt( isset( $objects[0]['author'] ), TRUE );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'][0]['ownText'] ), 1 );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownImage'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'][0]['ownImage'] ), 1 );
|
||||
$objects = ( R::exportAll( array( $book ), TRUE, array( 'page', 'author', 'text', 'image' ) ) );
|
||||
asrt( isset( $objects[0]['ownPage'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'] ), 2 );
|
||||
asrt( isset( $objects[0]['author'] ), TRUE );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'][0]['ownText'] ), 1 );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownImage'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'][0]['ownImage'] ), 1 );
|
||||
$objects = ( R::exportAll( array( $book ), TRUE, 'author' ) );
|
||||
asrt( isset( $objects[0]['ownPage'] ), FALSE );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), FALSE );
|
||||
$objects = ( R::exportAll( array( $book ), TRUE, array( 'page' ) ) );
|
||||
asrt( isset( $objects[0]['author'] ), FALSE );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), FALSE );
|
||||
$objects = ( R::exportAll( array( $book ), TRUE, array( 'page', 'text' ) ) );
|
||||
asrt( isset( $objects[0]['author'] ), FALSE );
|
||||
asrt( isset( $objects[0]['ownPage'] ), TRUE );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), TRUE );
|
||||
asrt( count( $objects[0]['ownPage'][0]['ownText'] ), 1 );
|
||||
asrt( isset( $objects[0]['ownPage'][0]['ownImage'] ), FALSE );
|
||||
$objects = ( R::exportAll( array( $book ), TRUE, array( 'none' ) ) );
|
||||
asrt( isset( $objects[0]['author'] ), FALSE );
|
||||
asrt( isset( $objects[0]['ownPage'] ), FALSE );
|
||||
$texts = R::find( 'text' );
|
||||
R::getDuplicationManager()->setCacheTables( FALSE );
|
||||
testpack( 'Keyless export' );
|
||||
$book = R::load( 'book', $bookID );
|
||||
$book->ownPage;
|
||||
$export = $book->export();
|
||||
asrt( isset( $export['ownPage'][0] ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function getCache().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getCache()
|
||||
{
|
||||
return array(
|
||||
'coffee' => array(
|
||||
'color' => 'color',
|
||||
'id' => 'id',
|
||||
'can_id' => 'can_id'
|
||||
),
|
||||
'can' => array(
|
||||
'size' => 'size',
|
||||
'id' => 'id'
|
||||
),
|
||||
'can_tag' => array(
|
||||
'id' => 'id',
|
||||
'can_id' => 'can_id',
|
||||
'tag_id' => 'tag_id'
|
||||
),
|
||||
'tag' => array(
|
||||
'id' => 'id',
|
||||
'name' => 'name' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares object with export
|
||||
*
|
||||
* @param type $object
|
||||
* @param type $array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function compare( $object, $array )
|
||||
{
|
||||
foreach ( $object as $property => $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
foreach ( $value as $index => $nestedObject ) {
|
||||
if ( $nestedObject->id ) {
|
||||
$foundMatch = FALSE;
|
||||
//order might be different
|
||||
foreach ( $array[$property] as $k => $a ) {
|
||||
if ( $a['id'] == $nestedObject->id ) {
|
||||
$foundMatch = TRUE;
|
||||
$index = $k;
|
||||
}
|
||||
}
|
||||
if ( !$foundMatch ) throw new\Exception( 'failed to find match for object ' . $nestedObject->id );
|
||||
}
|
||||
$this->compare( $nestedObject, $array[$property][$index] );
|
||||
}
|
||||
} elseif ( !is_object( $value ) ) {
|
||||
asrt( strval( $array[$property] ), strval( $value ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run tests
|
||||
*/
|
||||
private function runOnce( $n = TRUE )
|
||||
{
|
||||
$books = R::dispense( 'book', 10 );
|
||||
$pages = R::dispense( 'page', 10 );
|
||||
$readers = R::dispense( 'reader', 10 );
|
||||
$texts = R::dispense( 'text', 10 );
|
||||
$i = 0;
|
||||
foreach ( $books as $book ) $book->name = 'book-' . ( $i++ );
|
||||
$i = 0;
|
||||
foreach ( $pages as $page ) $page->name = 'page-' . ( $i++ );
|
||||
$i = 0;
|
||||
foreach ( $readers as $reader ) $reader->name = 'reader-' . ( $i++ );
|
||||
$i = 0;
|
||||
foreach ( $texts as $text ) $text->content = 'lorem ipsum -' . ( $i++ );
|
||||
|
||||
foreach ( $texts as $text ) {
|
||||
$pages[array_rand( $pages )]->ownText[] = $text;
|
||||
}
|
||||
foreach ( $pages as $page ) {
|
||||
$books[array_rand( $books )]->ownPage[] = $page;
|
||||
}
|
||||
foreach ( $readers as $reader ) {
|
||||
$books[array_rand( $books )]->sharedReader[] = $reader;
|
||||
}
|
||||
$i = $noOfReaders = $noOfPages = $noOfTexts = 0;
|
||||
foreach ( $books as $key => $book ) {
|
||||
$i++;
|
||||
$noOfPages += count( $book->ownPage );
|
||||
$noOfReaders += count( $book->sharedReader );
|
||||
foreach ( $book->ownPage as $page ) $noOfTexts += count( $page->ownText );
|
||||
$arr = R::exportAll( $book );
|
||||
echo "\nIntermediate info: " . json_encode( $arr ) . ": Totals = $i,$noOfPages,$noOfReaders,$noOfTexts ";
|
||||
$this->compare( $book, $arr[0] );
|
||||
$copiedBook = R::dup( $book );
|
||||
$copiedBookArray = R::exportAll( $copiedBook );
|
||||
$this->compare( $book, $copiedBookArray[0] );
|
||||
$copiedBookArrayII = $copiedBook->export();
|
||||
$this->compare( $book, $copiedBookArrayII );
|
||||
$copyFromCopy = R::dup( $copiedBook );
|
||||
$copyFromCopyArray = R::exportAll( $copyFromCopy );
|
||||
$this->compare( $book, $copyFromCopyArray[0] );
|
||||
$copyFromCopyArrayII = $copyFromCopy->export();
|
||||
$this->compare( $book, $copyFromCopyArrayII );
|
||||
$id = R::store( $book );
|
||||
$copiedBook = R::dup( $book );
|
||||
R::store( $book ); //should not be damaged
|
||||
$copiedBookArray = R::exportAll( $copiedBook );
|
||||
$originalBookArray = R::exportAll( $book );
|
||||
$this->compare( $copiedBook, $copiedBookArray[0] );
|
||||
$this->compare( $book, $originalBookArray[0] );
|
||||
$book = R::load( 'book', $id );
|
||||
$this->compare( $book, $originalBookArray[0] );
|
||||
$copiedBook = R::dup( $book );
|
||||
$this->compare( $copiedBook, $copiedBook->export() );
|
||||
R::store( $copiedBook );
|
||||
$this->compare( $copiedBook, $copiedBook->export() );
|
||||
$copyFromCopy = R::dup( $copiedBook );
|
||||
$this->compare( $copyFromCopy, $copyFromCopy->export() );
|
||||
R::store( $copyFromCopy );
|
||||
$newPage = R::dispense( 'page' );
|
||||
$newPage->name = 'new';
|
||||
$copyFromCopy->ownPage[] = $newPage;
|
||||
$modifiedCopy = R::dup( $copyFromCopy );
|
||||
$exportMod = R::exportAll( $modifiedCopy );
|
||||
$this->compare( $modifiedCopy, $exportMod[0] );
|
||||
asrt( count( $modifiedCopy->ownPage ), count( $copiedBook->ownPage ) + 1 );
|
||||
R::store( $modifiedCopy );
|
||||
if ( $n ) {
|
||||
asrt( (int) R::getCell( 'SELECT count(*) FROM book' ), $i * 4 );
|
||||
asrt( (int) R::getCell( 'SELECT count(*) FROM page' ), ( $noOfPages * 4 ) + $i );
|
||||
asrt( (int) R::getCell( 'SELECT count(*) FROM text' ), $noOfTexts * 4 );
|
||||
asrt( (int) R::getCell( 'SELECT count(*) FROM book_reader' ), $noOfReaders * 4 );
|
||||
asrt( (int) R::getCell( 'SELECT count(*) FROM reader' ), $noOfReaders );
|
||||
}
|
||||
}
|
||||
if ( $n ) {
|
||||
asrt( $noOfTexts, 10 );
|
||||
asrt( $noOfReaders, 10 );
|
||||
asrt( $noOfPages, 10 );
|
||||
asrt( $i, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
166
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Exceptions.php
vendored
Normal file
166
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Exceptions.php
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\RedException\SQL as SQLException;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\Adapter\DBAdapter;
|
||||
use RedBeanPHP\ToolBox;
|
||||
use RedBeanPHP\QueryWriter\SQLiteT;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter;
|
||||
use RedBeanPHP\QueryWriter;
|
||||
use RedBeanPHP\OODB;
|
||||
use RedBeanPHP\Driver\RPDO;
|
||||
|
||||
/**
|
||||
* Exceptions
|
||||
*
|
||||
* Tests exception handling in various scenarios as well
|
||||
* as exception related functionalities.
|
||||
*
|
||||
* @file RedUNIT/Base/Exceptions.php
|
||||
* @desc Tests exception handling
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Exceptions extends Base {
|
||||
|
||||
/**
|
||||
* Tests load exceptions.
|
||||
* Load Exceptions (version 5+)
|
||||
*
|
||||
* - If a bean does not exist,
|
||||
* R::load() and R::loadForUpdate() will return an empty bean.
|
||||
* - If there is an error because of a missing table or column,
|
||||
* both methods will return an empty bean in fluid mode
|
||||
* and throw an exception in frozen mode.
|
||||
* - If something else happens (lock timeout for instance)
|
||||
* both methods will always throw an exception, even in fluid mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testLoadExceptions()
|
||||
{
|
||||
/* bean does not exist, and table does not exist */
|
||||
R::nuke();
|
||||
$book = R::load( 'book', 1 );
|
||||
pass();
|
||||
asrt( $book->id, 0 );
|
||||
R::freeze( TRUE );
|
||||
$exception = NULL;
|
||||
try {
|
||||
$book = R::load( 'book', 1 );
|
||||
} catch( RedException $exception ) {}
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
R::freeze( FALSE );
|
||||
R::store( $book );
|
||||
/* bean does not exist - table exists */
|
||||
$book = R::load( 'book', 2 );
|
||||
pass();
|
||||
asrt( $book->id, 0 );
|
||||
R::freeze( TRUE );
|
||||
$book = R::load( 'book', 2 );
|
||||
pass();
|
||||
asrt( $book->id, 0 );
|
||||
/* other error */
|
||||
if ( !( R::getWriter() instanceof SQLiteT ) ) {
|
||||
R::freeze( FALSE );
|
||||
$exception = NULL;
|
||||
try {
|
||||
$book = R::load( 'book', 1, 'invalid sql' );
|
||||
} catch( RedException $exception ) {}
|
||||
//not supported for CUBRID
|
||||
if ($this->currentlyActiveDriverID !== 'CUBRID') {
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
}
|
||||
} else {
|
||||
/* error handling in SQLite is suboptimal */
|
||||
R::freeze( FALSE );
|
||||
$book = R::load( 'book', 1, 'invalid sql' );
|
||||
pass();
|
||||
asrt( $book->id, 0 );
|
||||
}
|
||||
R::freeze( TRUE );
|
||||
$exception = NULL;
|
||||
try {
|
||||
$book = R::load( 'book', 1, 'invalid sql' );
|
||||
} catch( RedException $exception ) {}
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
R::freeze( FALSE );
|
||||
R::nuke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete exceptions
|
||||
*
|
||||
* - in fluid mode no complaining about missing structures
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleteExceptions()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
R::store( $book );
|
||||
R::nuke();
|
||||
R::trash( $book );
|
||||
R::freeze( TRUE );
|
||||
$exception = NULL;
|
||||
try {
|
||||
R::trash( $book );
|
||||
} catch( RedException $exception ) {}
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
R::freeze( FALSE );
|
||||
$adapter = R::getDatabaseAdapter();
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
R::store( $book );
|
||||
$broken = new BrokenWriter( $adapter );
|
||||
$redbean = R::getRedBean();
|
||||
$oodb = new OODB( $broken, $redbean->isFrozen() );
|
||||
R::setRedBean( $oodb );
|
||||
$exception = NULL;
|
||||
try {
|
||||
R::trash( $book );
|
||||
} catch( RedException $exception ) {}
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
R::freeze( TRUE );
|
||||
$exception = NULL;
|
||||
try {
|
||||
R::trash( $book );
|
||||
} catch( RedException $exception ) {}
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
R::setRedBean( $redbean );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test chaining of exceptions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testChainingExceptions()
|
||||
{
|
||||
R::freeze( TRUE );
|
||||
$exception = NULL;
|
||||
try {
|
||||
$book = R::load( 'book', 1, 'invalid sql' );
|
||||
} catch( RedException $exception ) {}
|
||||
pass();
|
||||
asrt( ( $exception instanceof RedException ), TRUE );
|
||||
asrt( ( $exception->getPrevious() instanceof \Exception ), TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
class BrokenWriter extends SQLiteT {
|
||||
public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
|
||||
{
|
||||
throw new SQLException('oops');
|
||||
}
|
||||
}
|
||||
137
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Facade.php
vendored
Normal file
137
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Facade.php
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\AssociationManager as AssociationManager;
|
||||
use RedBeanPHP\OODB as OODB;
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Facade
|
||||
*
|
||||
* Tests the facade. The facade is a single class (often aliased with R)
|
||||
* that provides access to all (or most) RedBeanPHP functionality without
|
||||
* the need to interact with all the objects involved. The facade has
|
||||
* been designed to be as 'straightfoward' as possible, many methods
|
||||
* of the facade class should be 'almost identical' to simple calls
|
||||
* to objects behind the facade. This test focuses on basic usage
|
||||
* scenarios. Complex scenarios are tested in other suites because
|
||||
* we often test 'the complete route' as the facade code path always
|
||||
* includes the non-facade code path as well (as proven by the coverage numbers).
|
||||
*
|
||||
* @file RedUNIT/Base/Facade.php
|
||||
* @desc Tests basic functions through facade.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Facade extends Base
|
||||
{
|
||||
/**
|
||||
* What drivers should be loaded for this test pack?
|
||||
* This pack contains some SQL incomp. with OCI
|
||||
*/
|
||||
public function getTargetDrivers()
|
||||
{
|
||||
return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests quick trash method: R::trash( type, id ).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testQuickTrash()
|
||||
{
|
||||
R::nuke();
|
||||
$bean = R::dispense( 'bean' );
|
||||
$id = R::store( $bean );
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( 'bean', $id );
|
||||
asrt( R::count( 'bean' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test common Facade usage scenarios.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCommonUsageFacade()
|
||||
{
|
||||
$toolbox = R::getToolBox();
|
||||
$adapter = $toolbox->getDatabaseAdapter();
|
||||
$writer = $toolbox->getWriter();
|
||||
$redbean = $toolbox->getRedBean();
|
||||
$pdo = $adapter->getDatabase();
|
||||
$a = new AssociationManager( $toolbox );
|
||||
asrt( R::getRedBean() instanceof OODB, TRUE );
|
||||
asrt( R::getToolBox() instanceof ToolBox, TRUE );
|
||||
asrt( R::getDatabaseAdapter() instanceof Adapter, TRUE );
|
||||
asrt( R::getWriter() instanceof QueryWriter, TRUE );
|
||||
$book = R::dispense( "book" );
|
||||
asrt( $book instanceof OODBBean, TRUE );
|
||||
$book->title = "a nice book";
|
||||
$id = R::store( $book );
|
||||
asrt( ( $id > 0 ), TRUE );
|
||||
$book = R::load( "book", (int) $id );
|
||||
asrt( $book->title, "a nice book" );
|
||||
asrt( R::load( 'book', 999 )->title, NULL );
|
||||
R::freeze( TRUE );
|
||||
try {
|
||||
R::load( 'bookies', 999 );
|
||||
fail();
|
||||
} catch (\Exception $e ) {
|
||||
pass();
|
||||
}
|
||||
R::freeze( FALSE );
|
||||
$author = R::dispense( "author" );
|
||||
$author->name = "me";
|
||||
R::store( $author );
|
||||
$book9 = R::dispense( "book" );
|
||||
$author9 = R::dispense( "author" );
|
||||
$author9->name = "mr Nine";
|
||||
$a9 = R::store( $author9 );
|
||||
$book9->author_id = $a9;
|
||||
$bk9 = R::store( $book9 );
|
||||
$book9 = R::load( "book", $bk9 );
|
||||
$author = R::load( "author", $book9->author_id );
|
||||
asrt( $author->name, "mr Nine" );
|
||||
R::trash( $author );
|
||||
R::trash( $book9 );
|
||||
pass();
|
||||
$book2 = R::dispense( "book" );
|
||||
$book2->title = "second";
|
||||
R::store( $book2 );
|
||||
$book3 = R::dispense( "book" );
|
||||
$book3->title = "third";
|
||||
R::store( $book3 );
|
||||
asrt( count( R::find( "book" ) ), 3 );
|
||||
asrt( count( R::findAll( "book" ) ), 3 );
|
||||
asrt( count( R::findAll( "book", " LIMIT 2" ) ), 2 );
|
||||
asrt( count( R::find( "book", " id=id " ) ), 3 );
|
||||
asrt( count( R::find( "book", " title LIKE ?", array( "third" ) ) ), 1 );
|
||||
asrt( count( R::find( "book", " title LIKE ?", array( "%d%" ) ) ), 2 );
|
||||
// Find without where clause
|
||||
asrt( count( R::findAll( 'book', ' order by id' ) ), 3 );
|
||||
R::trash( $book3 );
|
||||
R::trash( $book2 );
|
||||
asrt( count( R::getAll( "SELECT * FROM book " ) ), 1 );
|
||||
asrt( count( R::getCol( "SELECT title FROM book " ) ), 1 );
|
||||
asrt( (int) R::getCell( "SELECT 123 " ), 123 );
|
||||
$book = R::dispense( "book" );
|
||||
$book->title = "not so original title";
|
||||
$author = R::dispense( "author" );
|
||||
$author->name = "Bobby";
|
||||
R::store( $book );
|
||||
$aid = R::store( $author );
|
||||
$author = R::findOne( "author", " name = ? ", array( "Bobby" ) );
|
||||
}
|
||||
}
|
||||
1336
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Finding.php
vendored
Normal file
1336
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Finding.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
329
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Foreignkeys.php
vendored
Normal file
329
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Foreignkeys.php
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\Observer as Observer;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
|
||||
|
||||
/**
|
||||
* Foreignkeys
|
||||
*
|
||||
* Tests whether foreign keys are correctly generated and whether
|
||||
* depending beans are correctly removed. Also tests auto resolving
|
||||
* types inferred by inspecting foreign keys.
|
||||
*
|
||||
* @file RedUNIT/Base/Foreignkeys.php
|
||||
* @desc Tests foreign key handling and dynamic foreign keys with
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Foreignkeys extends Base implements Observer
|
||||
{
|
||||
/**
|
||||
* To log the queries
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $queries = array();
|
||||
|
||||
/**
|
||||
* Test whether unique constraints are properly created using
|
||||
* reflection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUniqueInspect()
|
||||
{
|
||||
$writer = R::getWriter();
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$category = R::dispense( 'category' );
|
||||
$book->sharedCategory[] = $category;
|
||||
R::store( $book );
|
||||
asrt( count( get_uniques_for_type('book_category') ), 1 );
|
||||
asrt( are_cols_in_unique( 'book_category', array( 'book_id', 'category_id' ) ), TRUE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$category = R::dispense( 'category' );
|
||||
$book->via( 'library' )->sharedCategory[] = $category;
|
||||
R::store( $book );
|
||||
asrt( count( get_uniques_for_type('book_category') ), 0 );
|
||||
asrt( are_cols_in_unique( 'book_category', array( 'book_id', 'category_id' ) ), FALSE );
|
||||
asrt( count( get_uniques_for_type('library') ), 1 );
|
||||
asrt( are_cols_in_unique( 'library', array( 'book_id', 'category_id' ) ), TRUE );
|
||||
AQueryWriter::clearRenames();
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$category = R::dispense( 'category' );
|
||||
$book->sharedCategory[] = $category;
|
||||
R::store( $book );
|
||||
asrt( count( get_uniques_for_type('book_category') ), 1 );
|
||||
asrt( are_cols_in_unique( 'book_category', array( 'book_id', 'category_id' ) ), TRUE );
|
||||
asrt( count( get_uniques_for_type('library') ), 0 );
|
||||
asrt( are_cols_in_unique( 'library', array( 'book_id', 'category_id' ) ), FALSE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book2 = R::dispense( 'book' );
|
||||
$book->sharedBook[] = $book2;
|
||||
R::store( $book );
|
||||
asrt( count( get_uniques_for_type('book_book') ), 1 );
|
||||
asrt( are_cols_in_unique( 'book_book', array( 'book_id', 'book2_id' ) ), TRUE );
|
||||
try {
|
||||
$result = R::getWriter()->addUniqueConstraint( 'nonexistant', array( 'a', 'b' ) );
|
||||
} catch( \Exception $e ) {
|
||||
print_r( $e ); exit;
|
||||
}
|
||||
pass(); //dont crash!
|
||||
asrt( $result, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests foreign keys but checks using ProxyWriter.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFKInspect()
|
||||
{
|
||||
$faultyWriter = new \FaultyWriter( R::getDatabaseAdapter() );
|
||||
try {
|
||||
$null = \ProxyWriter::callMethod( $faultyWriter, 'getForeignKeyForTypeProperty', 'test', 'test' );
|
||||
pass();
|
||||
} catch( \Exception $e ) {
|
||||
fail();
|
||||
}
|
||||
asrt( is_null( $null ), TRUE );
|
||||
$writer = R::getWriter();
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->xownPage[] = $page;
|
||||
R::store( $book );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'book_id' );
|
||||
asrt( is_array( $keys ), TRUE );
|
||||
asrt( $keys['on_delete'], 'CASCADE' );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->ownPage[] = $page;
|
||||
R::store( $book );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'book_id' );
|
||||
asrt( is_array( $keys ), TRUE );
|
||||
asrt( $keys['on_delete'], 'SET NULL' );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->alias('magazine')->xownPage[] = $page;
|
||||
R::store( $book );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'magazine_id' );
|
||||
asrt( is_array( $keys ), TRUE );
|
||||
asrt( $keys['on_delete'], 'CASCADE' );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'book_id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->cover= $page;
|
||||
R::store( $book );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book', 'cover_id' );
|
||||
asrt( is_array( $keys ), TRUE );
|
||||
asrt( $keys['on_delete'], 'SET NULL' );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book', 'page_id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book', 'id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$category = R::dispense( 'category' );
|
||||
$book->sharedTag[] = $category;
|
||||
R::store( $book );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book_category', 'book_id' );
|
||||
asrt( is_array( $keys ), TRUE );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book_category', 'category_id' );
|
||||
asrt( is_array( $keys ), TRUE );
|
||||
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book_category', 'id' );
|
||||
asrt( is_null( $keys ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dependencies.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDependency()
|
||||
{
|
||||
$can = $this->createBeanInCan( FALSE );
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
// Bean stays
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dependencies (variation).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDependency2()
|
||||
{
|
||||
$can = $this->createBeanInCan( TRUE );
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
// Bean gone
|
||||
asrt( R::count( 'bean' ), 0 );
|
||||
$can = $this->createBeanInCan( FALSE );
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
// Bean stays, constraint removed
|
||||
asrt( R::count( 'bean' ), 0 );
|
||||
//need to recreate table to get rid of constraint!
|
||||
R::nuke();
|
||||
$can = $this->createBeanInCan( FALSE );
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
// Bean stays, constraint removed
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dependencies (variation).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDependency3()
|
||||
{
|
||||
R::nuke();
|
||||
$can = $this->createCanForBean();
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dependencies (variation).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDependency4()
|
||||
{
|
||||
R::nuke();
|
||||
$can = $this->createBeanInCan( TRUE );
|
||||
R::store( $can );
|
||||
R::trash( $can );
|
||||
$can = $this->createCanForBean();
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
asrt( R::count( 'bean' ), 0 );
|
||||
$can = $this->createBeanInCan( TRUE );
|
||||
R::store( $can );
|
||||
R::trash( $can );
|
||||
$can = $this->createCanForBean();
|
||||
asrt( R::count( 'bean' ), 1 );
|
||||
R::trash( $can );
|
||||
asrt( R::count( 'bean' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue #171
|
||||
* The index name argument is not unique in processEmbeddedBean etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssue171()
|
||||
{
|
||||
R::getDatabaseAdapter()->addEventListener( 'sql_exec', $this );
|
||||
$account = R::dispense( 'account' );
|
||||
$user = R::dispense( 'user' );
|
||||
$player = R::dispense( 'player' );
|
||||
$account->ownUser[] = $user;
|
||||
R::store( $account );
|
||||
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_user_account' ) !== FALSE, TRUE );
|
||||
$this->queries = array();
|
||||
$account->ownPlayer[] = $player;
|
||||
R::store( $account );
|
||||
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_player_accou' ) !== FALSE, TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether foreign keys are created correctly for certain
|
||||
* relations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreationOfForeignKeys()
|
||||
{
|
||||
$this->queries = array();
|
||||
$account = R::dispense( 'account' );
|
||||
$user = R::dispense( 'user' );
|
||||
$player = R::dispense( 'player' );
|
||||
$user->account = $account;
|
||||
R::store( $user );
|
||||
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_user_account' ) !== FALSE, TRUE );
|
||||
$this->queries = array();
|
||||
$player->account = $account;
|
||||
R::store( $player );
|
||||
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_player_accou' ) !== FALSE, TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test helper method.
|
||||
* Creates a bean in a can. The bean will get a reference
|
||||
* to the can and can be made dependent.
|
||||
*
|
||||
* @return OODBBean $can
|
||||
*/
|
||||
private function createBeanInCan( $isExcl )
|
||||
{
|
||||
$can = R::dispense( 'can' );
|
||||
$bean = R::dispense( 'bean' );
|
||||
$can->name = 'bakedbeans';
|
||||
$bean->taste = 'salty';
|
||||
if ($isExcl) {
|
||||
$can->xownBean[] = $bean;
|
||||
} else {
|
||||
$can->ownBean[] = $bean;
|
||||
}
|
||||
R::store( $can );
|
||||
return $can;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test helper method.
|
||||
* Creates a bean in a can beginning with the bean. The bean will get a reference
|
||||
* to the can and can be made dependent.
|
||||
*
|
||||
* @return OODBBean $can
|
||||
*/
|
||||
private function createCanForBean()
|
||||
{
|
||||
$can = R::dispense( 'can' );
|
||||
$bean = R::dispense( 'bean' );
|
||||
$bean->can = $can;
|
||||
R::store( $bean );
|
||||
return $can;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log queries
|
||||
*
|
||||
* @param string $event
|
||||
* @param Adapter $info
|
||||
*/
|
||||
public function onEvent( $event, $info )
|
||||
{
|
||||
$this->queries[] = $info->getSQL();
|
||||
}
|
||||
}
|
||||
104
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Frozen.php
vendored
Normal file
104
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Frozen.php
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Frozen
|
||||
*
|
||||
* Frozen mode tests
|
||||
* When I split the repositories in frozen and fluid I discovered some missed
|
||||
* code-paths in the tests.
|
||||
* These tests are here to make sure the following scenarios work properly
|
||||
* in frozen mode as well.
|
||||
*
|
||||
* @file RedUNIT/Base/Frozen.php
|
||||
* @desc Test some scenarios we haven't covered for frozen mode.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Frozen extends Base
|
||||
{
|
||||
/**
|
||||
* Tests the handling of trashed beans in frozen mode.
|
||||
* Are the lists unset etc?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTrash()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->xownPageList[] = R::dispense( 'page' );
|
||||
$book->sharedTagList[] = R::dispense( 'tag' );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
R::freeze( TRUE );
|
||||
$book->xownPageList = array();
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( R::count('page'), 0 );
|
||||
$book->xownPageList[] = R::dispense( 'page' );
|
||||
R::store( $book );
|
||||
$book = $book->fresh();
|
||||
asrt( R::count('page'), 1 );
|
||||
$book->xownPageList;
|
||||
$book->sharedTagList;
|
||||
R::trash( $book );
|
||||
asrt( R::count('book'), 0 );
|
||||
asrt( R::count('page'), 0 );
|
||||
asrt( R::count('tag'), 1 );
|
||||
asrt( R::count('book_tag'), 0 );
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether invalid list checks are
|
||||
* operational in frozen mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidList()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->xownPageList[] = R::dispense( 'page' );
|
||||
$book->sharedTagList[] = R::dispense( 'tag' );
|
||||
R::store( $book );
|
||||
R::freeze( TRUE );
|
||||
$book = R::dispense( 'book' );
|
||||
$book->xownPageList[] = 'nonsense';
|
||||
try {
|
||||
R::store( $book );
|
||||
fail();
|
||||
} catch( \Exception $e ) {
|
||||
pass();
|
||||
}
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether loading non-existant beans
|
||||
* returns the same results in frozen mode.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function testLoadNonExistant()
|
||||
{
|
||||
R::nuke();
|
||||
R::store( R::dispense( 'bean' ) );
|
||||
R::freeze( TRUE );
|
||||
$bean = R::load( 'bean', 123 );
|
||||
R::freeze( FALSE );
|
||||
asrt( ( $bean instanceof OODBBean ), TRUE );
|
||||
asrt( $bean->id, 0 );
|
||||
}
|
||||
}
|
||||
420
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Fuse.php
vendored
Normal file
420
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Fuse.php
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\ModelHelper as ModelHelper;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
use RedBeanPHP\OODB as OODB;
|
||||
use RedBeanPHP\ToolBox as ToolBox;
|
||||
use RedBeanPHP\QueryWriter as QueryWriter;
|
||||
use RedBeanPHP\Adapter as Adapter;
|
||||
use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper;
|
||||
|
||||
/**
|
||||
* FUSE
|
||||
*
|
||||
* Tests whether we can associate model logic on-the-fly
|
||||
* by defining models extending from SimpleModel. Tests
|
||||
* whether the calls to facade trigger the corresponding
|
||||
* methods on the model.
|
||||
*
|
||||
* @file RedUNIT/Base/Fuse.php
|
||||
* @desc Tests Fuse feature; coupling beans to models.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Fuse extends Base
|
||||
{
|
||||
/**
|
||||
* Hook into the jsonSerialize function #651
|
||||
* Allow models to provide a jsonSerialize return.
|
||||
* This test uses the Coffee Helper Model to add
|
||||
* a description to the JSON representation of the bean.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFUSEJSonSerialize()
|
||||
{
|
||||
if ( phpversion() < 5.4 ) return;
|
||||
$coffee = R::dispense( 'coffee' );
|
||||
$coffee->variant = 'Tropical';
|
||||
$coffee->strength = 4;
|
||||
$json = json_encode( $coffee );
|
||||
$array = json_decode( $json, TRUE );
|
||||
asrt( isset( $array['description'] ), TRUE );
|
||||
asrt( $array['description'], 'Tropical.4' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can override the getModelForBean() method
|
||||
* of the BeanHelper and use a custom BeanHelper to attach a model
|
||||
* based on type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCustomBeanHelper()
|
||||
{
|
||||
$customBeanHelper = new \SoupBeanHelper( R::getToolbox() );
|
||||
$oldBeanHelper = R::getRedBean()->getBeanHelper();
|
||||
asrt( ( $oldBeanHelper instanceof SimpleFacadeBeanHelper ), TRUE );
|
||||
R::getRedBean()->setBeanHelper( $customBeanHelper );
|
||||
$meal = R::dispense( 'meal' );
|
||||
asrt( ( $meal->box() instanceof \Model_Soup ), TRUE );
|
||||
$cake = R::dispense( 'cake' );
|
||||
asrt( is_null( $cake->box() ), TRUE );
|
||||
$bean = R::dispense( 'coffee' );
|
||||
asrt( ( $bean->box() instanceof \Model_Coffee ), TRUE );
|
||||
$meal->setFlavour( 'tomato' );
|
||||
asrt( $meal->getFlavour(), 'tomato' );
|
||||
$meal->rating = 5;
|
||||
R::store( $meal );
|
||||
asrt( $meal->getFlavour(), 'tomato' );
|
||||
$meal = $meal->unbox();
|
||||
asrt( $meal->getFlavour(), 'tomato' );
|
||||
$meal = R::findOne( 'meal' );
|
||||
asrt( ( $meal->box() instanceof \Model_Soup ), TRUE );
|
||||
asrt( $meal->getFlavour(), '' );
|
||||
$meal->setFlavour( 'tomato' );
|
||||
asrt( $meal->getFlavour(), 'tomato' );
|
||||
$meal = $meal->unbox();
|
||||
asrt( $meal->getFlavour(), 'tomato' );
|
||||
R::getRedBean()->setBeanHelper( $oldBeanHelper );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test FUSE hooks (i.e. open, update, update_after etc..)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testHooks()
|
||||
{
|
||||
R::nuke();
|
||||
$probe = R::dispense( 'probe' );
|
||||
$probe->name = 'test';
|
||||
asrt( $probe->getLogActionCount(), 1 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
|
||||
R::store( $probe );
|
||||
asrt( $probe->getLogActionCount(), 3 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
|
||||
$probe = R::load( 'probe', $probe->id );
|
||||
asrt( $probe->getLogActionCount(), 2 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
|
||||
asrt( ( $probe->getDataFromLog( 1, 'id' ) === $probe->id ), TRUE );
|
||||
$probe->clearLog();
|
||||
R::trash( $probe );
|
||||
asrt( $probe->getLogActionCount(), 2 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 1 );
|
||||
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
|
||||
asrt( ( $probe->getDataFromLog( 1, 'bean' ) === $probe ), TRUE );
|
||||
//less 'normal scenarios'
|
||||
$probe = R::dispense( 'probe' );
|
||||
$probe->name = 'test';
|
||||
asrt( $probe->getLogActionCount(), 1 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
|
||||
R::store( $probe );
|
||||
asrt( $probe->getLogActionCount(), 3 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
|
||||
asrt( $probe->getMeta( 'tainted' ), FALSE );
|
||||
asrt( $probe->getMeta( 'changed' ), FALSE );
|
||||
R::store( $probe ); //not tainted, no FUSE save!
|
||||
asrt( $probe->getLogActionCount(), 3 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
|
||||
$probe->xownProbeList[] = R::dispense( 'probe' );
|
||||
//tainted, not changed, triggers FUSE
|
||||
asrt( $probe->getMeta( 'tainted' ), TRUE );
|
||||
asrt( $probe->getMeta( 'changed' ), FALSE );
|
||||
R::store( $probe );
|
||||
asrt( $probe->getMeta( 'tainted' ), FALSE );
|
||||
asrt( $probe->getMeta( 'changed' ), FALSE );
|
||||
asrt( $probe->getLogActionCount(), 5 );
|
||||
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
|
||||
asrt( $probe->getLogActionCount( 'open' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'update' ), 2 );
|
||||
asrt( $probe->getLogActionCount( 'after_update' ), 2 );
|
||||
asrt( $probe->getLogActionCount( 'delete' ), 0 );
|
||||
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
|
||||
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the SimpleFacadeBeanHelper factory setter.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFactory()
|
||||
{
|
||||
SimpleFacadeBeanHelper::setFactoryFunction( function( $name ) {
|
||||
$model = new $name();
|
||||
$model->setNote( 'injected', 'dependency' );
|
||||
return $model;
|
||||
} );
|
||||
$bean = R::dispense( 'band' )->box();
|
||||
asrt( ( $bean instanceof \Model_Band ), TRUE );
|
||||
asrt( ( $bean->getNote('injected') ), 'dependency' );
|
||||
SimpleFacadeBeanHelper::setFactoryFunction( NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that beans of type book_page can be fused with
|
||||
* models like BookPage (beautified) as well as Book_Page (non-beautified).
|
||||
*/
|
||||
public function testBeutificationOfLinkModel()
|
||||
{
|
||||
$page = R::dispense( 'page' );
|
||||
$widget = R::dispense( 'widget' );
|
||||
$page->sharedWidgetList[] = $widget;
|
||||
R::store( $page );
|
||||
$testReport = \Model_PageWidget::getTestReport();
|
||||
asrt( $testReport, 'didSave' );
|
||||
$page = R::dispense( 'page' );
|
||||
$gadget = R::dispense( 'gadget' );
|
||||
$page->sharedGadgetList[] = $gadget;
|
||||
R::store( $page );
|
||||
$testReport = \Model_Gadget_Page::getTestReport();
|
||||
asrt( $testReport, 'didSave' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Only theoretical.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTheoreticalBeautifications()
|
||||
{
|
||||
$bean = R::dispense('bean');
|
||||
$bean->setMeta('type', 'a_b_c');
|
||||
R::store($bean);
|
||||
$testReport = \Model_A_B_C::getTestReport();
|
||||
asrt( $testReport, 'didSave' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test extraction of toolbox.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetExtractedToolBox()
|
||||
{
|
||||
$helper = new SimpleFacadeBeanHelper;
|
||||
list( $redbean, $database, $writer, $toolbox ) = $helper->getExtractedToolbox();
|
||||
asrt( ( $redbean instanceof OODB ), TRUE );
|
||||
asrt( ( $database instanceof Adapter ), TRUE );
|
||||
asrt( ( $writer instanceof QueryWriter ), TRUE );
|
||||
asrt( ( $toolbox instanceof ToolBox ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test FUSE and model formatting.
|
||||
*
|
||||
* @todo move tagging tests to tag tester.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFUSE()
|
||||
{
|
||||
$toolbox = R::getToolBox();
|
||||
$adapter = $toolbox->getDatabaseAdapter();
|
||||
$blog = R::dispense( 'blog' );
|
||||
$blog->title = 'testing';
|
||||
$blog->blog = 'tesing';
|
||||
R::store( $blog );
|
||||
$blogpost = R::load( "blog", 1 );
|
||||
$post = R::dispense( "post" );
|
||||
$post->message = "hello";
|
||||
$blog->sharedPost[] = $post;
|
||||
R::store($blog);
|
||||
$a = R::getAll( "select * from blog " );
|
||||
R::tag( $post, "lousy,smart" );
|
||||
asrt( implode( ',', R::tag( $post ) ), "lousy,smart" );
|
||||
R::tag( $post, "clever,smart" );
|
||||
$tagz = implode( ',', R::tag( $post ) );
|
||||
asrt( ( $tagz == "smart,clever" || $tagz == "clever,smart" ), TRUE );
|
||||
R::tag( $blog, array( "smart", "interesting" ) );
|
||||
asrt( implode( ',', R::tag( $blog ) ), "smart,interesting" );
|
||||
try {
|
||||
R::tag( $blog, array( "smart", "interesting", "lousy!" ) );
|
||||
pass();
|
||||
} catch ( RedException $e ) {
|
||||
fail();
|
||||
}
|
||||
asrt( implode( ',', R::tag( $blog ) ), "smart,interesting,lousy!" );
|
||||
asrt( implode( ",", R::tag( $blog ) ), "smart,interesting,lousy!" );
|
||||
R::untag( $blog, array( "smart", "interesting" ) );
|
||||
asrt( implode( ",", R::tag( $blog ) ), "lousy!" );
|
||||
asrt( R::hasTag( $blog, array( "lousy!" ) ), TRUE );
|
||||
asrt( R::hasTag( $blog, array( "lousy!", "smart" ) ), TRUE );
|
||||
asrt( R::hasTag( $blog, array( "lousy!", "smart" ), TRUE ), FALSE );
|
||||
R::tag( $blog, FALSE );
|
||||
asrt( count( R::tag( $blog ) ), 0 );
|
||||
R::tag( $blog, array( "funny", "comic" ) );
|
||||
asrt( count( R::tag( $blog ) ), 2 );
|
||||
R::addTags( $blog, array( "halloween" ) );
|
||||
asrt( count( R::tag( $blog ) ), 3 );
|
||||
asrt( R::hasTag( $blog, array( "funny", "commic", "halloween" ), TRUE ), FALSE );
|
||||
R::unTag( $blog, array( "funny" ) );
|
||||
R::addTags( $blog, "horror" );
|
||||
asrt( count( R::tag( $blog ) ), 3 );
|
||||
asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
|
||||
// No double tags
|
||||
R::addTags( $blog, "horror" );
|
||||
asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
|
||||
asrt( count( R::tag( $blog ) ), 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error handling options FUSE.
|
||||
*/
|
||||
public function testModelErrorHandling()
|
||||
{
|
||||
$test = R::dispense( 'feed' );
|
||||
$test->nonExistantMethod();
|
||||
pass();
|
||||
$old = R::setErrorHandlingFUSE( OODBBean::C_ERR_LOG );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], FALSE );
|
||||
asrt( $old[1], NULL);
|
||||
$test->nonExistantMethod(); //we cant really test this... :(
|
||||
pass();
|
||||
$old = R::setErrorHandlingFUSE( OODBBean::C_ERR_NOTICE );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_LOG );
|
||||
asrt( $old[1], NULL);
|
||||
set_error_handler(function($error, $str) {
|
||||
asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
|
||||
}, E_USER_NOTICE);
|
||||
$test->nonExistantMethod();
|
||||
restore_error_handler();
|
||||
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_WARN );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_NOTICE );
|
||||
asrt( $old[1], NULL);
|
||||
set_error_handler(function($error, $str) {
|
||||
asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
|
||||
}, E_USER_WARNING);
|
||||
$test->nonExistantMethod();
|
||||
restore_error_handler();
|
||||
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FATAL );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_WARN );
|
||||
asrt( $old[1], NULL);
|
||||
set_error_handler(function($error, $str) {
|
||||
asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
|
||||
}, E_USER_ERROR);
|
||||
$test->nonExistantMethod();
|
||||
restore_error_handler();
|
||||
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_EXCEPTION );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_FATAL );
|
||||
asrt( $old[1], NULL);
|
||||
try {
|
||||
$test->nonExistantMethod();
|
||||
fail();
|
||||
} catch (\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
global $test_bean;
|
||||
$test_bean = $test;
|
||||
global $has_executed_error_func_fuse;
|
||||
$has_executed_error_func_fuse = FALSE;
|
||||
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FUNC, function( $info ){
|
||||
global $has_executed_error_func_fuse;
|
||||
global $test_bean;
|
||||
$has_executed_error_func_fuse = TRUE;
|
||||
asrt( is_array( $info ), TRUE );
|
||||
asrt( $info['method'], 'nonExistantMethod' );
|
||||
asrt( json_encode( $info['bean']->export() ), json_encode( $test_bean->export() ) );
|
||||
asrt( $info['message'], 'FUSE: method does not exist in model: nonExistantMethod' );
|
||||
} );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_EXCEPTION );
|
||||
asrt( $old[1], NULL);
|
||||
$test->nonExistantMethod();
|
||||
asrt( $has_executed_error_func_fuse, TRUE );
|
||||
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_FUNC );
|
||||
asrt( is_callable( $old[1] ), TRUE );
|
||||
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
|
||||
asrt( is_array( $old ), TRUE );
|
||||
asrt( count( $old ), 2 );
|
||||
asrt( $old[0], OODBBean::C_ERR_IGNORE );
|
||||
asrt( $old[1], NULL);
|
||||
try {
|
||||
OODBBean::setErrorHandlingFUSE( 900 );
|
||||
fail();
|
||||
} catch (\Exception $e) {
|
||||
pass();
|
||||
asrt( $e->getMessage(), 'Invalid error mode selected' );
|
||||
}
|
||||
try {
|
||||
OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FUNC, 'hello' );
|
||||
fail();
|
||||
} catch (\Exception $e) {
|
||||
pass();
|
||||
asrt( $e->getMessage(), 'Invalid error handler' );
|
||||
}
|
||||
OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_EXCEPTION );
|
||||
//make sure ignore FUSE events
|
||||
$test = R::dispense('feed');
|
||||
R::store( $test );
|
||||
$test = $test->fresh();
|
||||
R::trash( $test );
|
||||
pass();
|
||||
OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
|
||||
}
|
||||
}
|
||||
175
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Hybrid.php
vendored
Normal file
175
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Hybrid.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Hybrid
|
||||
*
|
||||
* Test Hybrid mode where the database can be unfozen
|
||||
* in case of an exception during storing.
|
||||
*
|
||||
* @file RedUNIT/Base/Hybrid.php
|
||||
* @desc Tests hybrid mode
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Hybrid extends Base
|
||||
{
|
||||
/**
|
||||
* Tests hybrid mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testHybrid()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense('book');
|
||||
$book->pages = 123;
|
||||
$id = R::store( $book );
|
||||
R::freeze( TRUE );
|
||||
R::setAllowHybridMode( FALSE );
|
||||
$book->title = 'Tales of a misfit';
|
||||
try {
|
||||
R::store( $book, TRUE );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
try {
|
||||
R::store( $book, FALSE );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
$book = $book->fresh();
|
||||
asrt( is_null( $book->title ), TRUE );
|
||||
R::setAllowHybridMode( TRUE );
|
||||
$book->title = 'Tales of a misfit';
|
||||
try {
|
||||
R::store( $book );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
try {
|
||||
R::store( $book, FALSE );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
try {
|
||||
R::store( $book, TRUE );
|
||||
pass();
|
||||
} catch(\Exception $e) {
|
||||
fail();
|
||||
}
|
||||
$book = $book->fresh();
|
||||
asrt( $book->title, 'Tales of a misfit' );
|
||||
R::setAllowHybridMode( FALSE );
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can use Hybrid mode to alter columns.
|
||||
* This won't work for SQLite.
|
||||
*/
|
||||
public function testHybridDataType()
|
||||
{
|
||||
R::nuke();
|
||||
if ($this->currentlyActiveDriverID == 'mysql') {
|
||||
R::exec('SET @@SESSION.sql_mode=\'STRICT_TRANS_TABLES\';');
|
||||
}
|
||||
if ($this->currentlyActiveDriverID == 'sqlite') return;
|
||||
$book = R::dispense('book');
|
||||
$book->pages = 1;
|
||||
$id = R::store( $book, TRUE );
|
||||
R::freeze( TRUE );
|
||||
asrt( R::getRedBean()->isFrozen(), TRUE );
|
||||
R::setAllowHybridMode( FALSE );
|
||||
$book->pages = 'too many';
|
||||
try {
|
||||
R::store( $book, TRUE );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
asrt( R::getRedBean()->isFrozen(), TRUE );
|
||||
R::setAllowHybridMode( TRUE );
|
||||
asrt( R::getRedBean()->isFrozen(), TRUE );
|
||||
R::debug(1);
|
||||
try {
|
||||
R::store( $book, TRUE );
|
||||
pass();
|
||||
} catch(\Exception $e) {
|
||||
fail();
|
||||
}
|
||||
asrt( R::getRedBean()->isFrozen(), TRUE );
|
||||
$book = $book->fresh();
|
||||
echo $book;
|
||||
asrt( $book->pages, 'too many' );
|
||||
R::setAllowHybridMode( FALSE );
|
||||
R::freeze( FALSE );
|
||||
if ($this->currentlyActiveDriverID == 'mysql') {
|
||||
R::exec('SET @@SESSION.sql_mode=\'\';');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Other exceptions must fall through...
|
||||
*/
|
||||
public function testHybridNonSQLException()
|
||||
{
|
||||
R::nuke();
|
||||
$toy = R::dispense('brokentoy');
|
||||
R::freeze( TRUE );
|
||||
R::setAllowHybridMode( TRUE );
|
||||
try {
|
||||
R::store( $toy, TRUE );
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
R::setAllowHybridMode( FALSE );
|
||||
R::nuke();
|
||||
$toy = R::dispense('toy');
|
||||
R::freeze( TRUE );
|
||||
R::setAllowHybridMode( TRUE );
|
||||
try {
|
||||
R::store( $toy, TRUE );
|
||||
pass();
|
||||
} catch(\Exception $e) {
|
||||
fail();
|
||||
}
|
||||
R::setAllowHybridMode( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether Hybrid mode is only activated
|
||||
* for latest or 5.4 without novice and ensure
|
||||
* maintaining backward compatibility by not setting
|
||||
* Hybrid allowed for 5.3 and earlier.
|
||||
*/
|
||||
public function testVersions()
|
||||
{
|
||||
R::useFeatureSet('novice/latest');
|
||||
asrt( R::setAllowHybridMode( FALSE ), FALSE );
|
||||
R::useFeatureSet('latest');
|
||||
asrt( R::setAllowHybridMode( FALSE ), TRUE );
|
||||
R::useFeatureSet('novice/5.4');
|
||||
asrt( R::setAllowHybridMode( FALSE ), FALSE );
|
||||
R::useFeatureSet('5.4');
|
||||
asrt( R::setAllowHybridMode( FALSE ), TRUE );
|
||||
R::useFeatureSet('novice/5.3');
|
||||
asrt( R::setAllowHybridMode( FALSE ), FALSE );
|
||||
R::useFeatureSet('5.3');
|
||||
asrt( R::setAllowHybridMode( FALSE ), FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
136
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Indexes.php
vendored
Normal file
136
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Indexes.php
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Indexes
|
||||
*
|
||||
* Tests whether indexes are created properly and whether
|
||||
* index creation errors are supressed (they are not important
|
||||
* enough to cause exceptions during development phase).
|
||||
*
|
||||
* @file RedUNIT/Base/Indexes.php
|
||||
* @desc Tests whether indexes are applied properly.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Indexes extends Base {
|
||||
|
||||
/**
|
||||
* Tests whether a regular index is created properly.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIndexCreation()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$book->ownPageList[] = $page;
|
||||
R::store( $book );
|
||||
$indexes = getIndexes( 'page' );
|
||||
asrt( in_array( 'index_foreignkey_page_book', $indexes ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests indexes on parent beans.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIndexCreationParentBean()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$page = R::dispense( 'page' );
|
||||
$page->book = $book;
|
||||
R::store( $page );
|
||||
$indexes = getIndexes( 'page' );
|
||||
asrt( in_array( 'index_foreignkey_page_book', $indexes ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests indexes on link tables.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIndexCreationMany2Many()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$category = R::dispense( 'category' );
|
||||
$book->sharedCategoryList[] = $category;
|
||||
R::store( $book );
|
||||
$indexes = getIndexes( 'book_category' );
|
||||
asrt( in_array( 'index_foreignkey_book_category_book', $indexes ), TRUE );
|
||||
asrt( in_array( 'index_foreignkey_book_category_category', $indexes ), TRUE );
|
||||
R::nuke();
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$category = R::dispense( 'category' );
|
||||
$category->sharedBookList[] = $book;
|
||||
R::store( $category );
|
||||
$indexes = getIndexes( 'book_category' );
|
||||
asrt( in_array( 'index_foreignkey_book_category_book', $indexes ), TRUE );
|
||||
asrt( in_array( 'index_foreignkey_book_category_category', $indexes ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests indexes on aliases.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIndexCreationAlias()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$author = R::dispense( 'author' );
|
||||
$book->coAuthor = $author;
|
||||
R::store( $book );
|
||||
$indexes = getIndexes( 'book' );
|
||||
asrt( in_array( 'index_foreignkey_book_co_author', $indexes ), TRUE );
|
||||
R::nuke();
|
||||
$project = R::dispense( 'project' );
|
||||
$person = R::dispense( 'person' );
|
||||
$person->alias( 'teacher' )->ownProject[] = $project;
|
||||
$person2 = R::dispense( 'person' );
|
||||
$person2->alias( 'student' )->ownProject[] = $project;
|
||||
R::store( $person );
|
||||
$indexes = getIndexes( 'project' );
|
||||
asrt( in_array( 'index_foreignkey_project_teacher', $indexes ), TRUE );
|
||||
R::store( $person2 );
|
||||
$indexes = getIndexes( 'project' );
|
||||
asrt( in_array( 'index_foreignkey_project_student', $indexes ), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests index fails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIndexCreationFail()
|
||||
{
|
||||
R::nuke();
|
||||
$book = R::dispense( 'book' );
|
||||
$book->author_id = '999';
|
||||
R::store( $book );
|
||||
$indexes = getIndexes( 'book' );
|
||||
//should just work fine
|
||||
asrt( in_array( 'index_foreignkey_book_author', $indexes ), TRUE );
|
||||
//these should just pass, no indexes but no errors as well
|
||||
R::getWriter()->addIndex( 'book', 'bla', 'nonexist' );
|
||||
pass();
|
||||
R::getWriter()->addIndex( 'book', '@#$', 'nonexist' );
|
||||
pass();
|
||||
R::getWriter()->addIndex( 'nonexist', 'bla', 'nonexist' );
|
||||
pass();
|
||||
$indexesAfter = getIndexes( 'book' );
|
||||
asrt( count( $indexesAfter ), count( $indexes ) );
|
||||
}
|
||||
}
|
||||
68
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue259.php
vendored
Normal file
68
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue259.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\SimpleModel as SimpleModel;
|
||||
|
||||
/**
|
||||
* Issue259
|
||||
*
|
||||
* Tests whether this specific issue on github has been resolved.
|
||||
* Issue #259 - Stash Cache breaks model delegation in open().
|
||||
*
|
||||
* @file RedUNIT/Base/Issue259.php
|
||||
* @desc Issue #259 - Stash cache breaks model delegation in open().
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Issue259 extends Base
|
||||
{
|
||||
/**
|
||||
* Test to make sure stash cache works with recursively opening models
|
||||
* with FUSE.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssue259()
|
||||
{
|
||||
testpack( 'Testing Issue #259 - Stash Cache breaks model delegation in open().' );
|
||||
$mother = R::dispense( 'mother' );
|
||||
$mother->desc = 'I am mother';
|
||||
R::store( $mother );
|
||||
$child = R::dispense( 'child' );
|
||||
$child->mother = $mother;
|
||||
$child->desc = 'I am child';
|
||||
$id = R::store( $child );
|
||||
R::findOne( 'child', ' id = ?', array( $id ) );
|
||||
R::find( 'child', ' id = ? ', array( $id ) );
|
||||
R::load( 'child', $id );
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Mock Model.
|
||||
*/
|
||||
class Model_Mother extends SimpleModel
|
||||
{
|
||||
public function open()
|
||||
{
|
||||
$bean = $this->bean;
|
||||
asrt( $this->bean->desc, 'I am mother' );
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Mock Model.
|
||||
*/
|
||||
class Model_Child extends SimpleModel
|
||||
{
|
||||
public function open()
|
||||
{
|
||||
$this->bean->mother;
|
||||
asrt( $this->bean->desc, 'I am child' );
|
||||
}
|
||||
}
|
||||
55
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue303.php
vendored
Normal file
55
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue303.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\RedException as RedException;
|
||||
|
||||
/**
|
||||
* Issue303
|
||||
*
|
||||
* Tests whether this specific issue on github has been resolved.
|
||||
* Test whether we have two different exception messages for
|
||||
* properties and values.
|
||||
*
|
||||
* @file RedUNIT/Base/Issue303.php
|
||||
* @desc Issue #303 - Split bean property exception.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Issue303 extends Base
|
||||
{
|
||||
/**
|
||||
* Test whether we have two different exception messages for
|
||||
* properties and values.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssue303()
|
||||
{
|
||||
testpack( 'Testing Issue #303 - Test splitting bean exception property/value.' );
|
||||
try {
|
||||
R::store( R::dispense( 'invalidbean' )->setAttr( 'invalid.property', 'value' ) );
|
||||
fail();
|
||||
} catch (RedException $e ) {
|
||||
asrt( $e->getMessage(), 'Invalid Bean property: property invalid.property' );
|
||||
}
|
||||
try {
|
||||
R::store( R::dispense( 'invalidbean' )->setAttr( 'property', array() ) );
|
||||
fail();
|
||||
} catch (RedException $e ) {
|
||||
asrt( $e->getMessage(), 'Invalid Bean value: property property' );
|
||||
}
|
||||
try {
|
||||
R::store( R::dispense( 'invalidbean' )->setAttr( 'property', new \stdClass ) );
|
||||
fail();
|
||||
} catch (RedException $e ) {
|
||||
asrt( $e->getMessage(), 'Invalid Bean value: property property' );
|
||||
}
|
||||
}
|
||||
}
|
||||
82
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue408.php
vendored
Normal file
82
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue408.php
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Issue 408
|
||||
*
|
||||
* Tests whether this specific issue on github has been resolved.
|
||||
* Tests whether we can use export on beans having arrays in properties.
|
||||
*
|
||||
* @file RedUNIT/Mysql/Issue408.php
|
||||
* @desc Test whether we can export beans with arrays in properties
|
||||
* (deserialized/serialized on open/update).
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Issue408 extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* In the past it was not possible to export beans
|
||||
* like 'feed' (Model_Feed).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testExportIssue()
|
||||
{
|
||||
R::nuke();
|
||||
$feed = R::dispense( 'feed' );
|
||||
$feed->post = array(
|
||||
'first',
|
||||
'second'
|
||||
);
|
||||
R::store( $feed );
|
||||
$rows = R::getAll('SELECT * FROM feed');
|
||||
asrt( $rows[0]['post'], '["first","second"]' );
|
||||
$feed = $feed->fresh();
|
||||
asrt( is_array( $feed->post ), TRUE );
|
||||
asrt( $feed->post[0], 'first' );
|
||||
asrt( $feed->post[1], 'second' );
|
||||
R::store( $feed );
|
||||
$rows = R::getAll('SELECT * FROM feed');
|
||||
asrt( $rows[0]['post'], '["first","second"]' );
|
||||
$feed = R::load( 'feed', $feed->id );
|
||||
$feed->post[] = 'third';
|
||||
R::store( $feed );
|
||||
$rows = R::getAll('SELECT * FROM feed');
|
||||
asrt( $rows[0]['post'], '["first","second","third"]' );
|
||||
$feed = $feed->fresh();
|
||||
asrt( is_array( $feed->post ), TRUE );
|
||||
asrt( $feed->post[0], 'first' );
|
||||
asrt( $feed->post[1], 'second' );
|
||||
asrt( $feed->post[2], 'third' );
|
||||
//now the catch: can we use export?
|
||||
//PHP Fatal error: Call to a member function export() on a non-object
|
||||
$feeds = R::exportAll( R::find( 'feed' ) );
|
||||
asrt( is_array( $feeds ), TRUE );
|
||||
$feed = reset( $feeds );
|
||||
asrt( $feed['post'][0], 'first' );
|
||||
asrt( $feed['post'][1], 'second' );
|
||||
asrt( $feed['post'][2], 'third' );
|
||||
//can we also dup()?
|
||||
$feedOne = R::findOne( 'feed' );
|
||||
R::store( R::dup( $feedOne ) );
|
||||
asrt( R::count( 'feed' ), 2 );
|
||||
//can we delete?
|
||||
R::trash( $feedOne );
|
||||
asrt( R::count( 'feed' ), 1 );
|
||||
$feedTwo = R::findOne( 'feed' );
|
||||
$feed = $feedTwo->export();
|
||||
asrt( $feed['post'][0], 'first' );
|
||||
asrt( $feed['post'][1], 'second' );
|
||||
asrt( $feed['post'][2], 'third' );
|
||||
}
|
||||
}
|
||||
52
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue841.php
vendored
Normal file
52
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue841.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Issue 841
|
||||
*
|
||||
* After bindFunc() Shared List cannot remove items.
|
||||
*
|
||||
* @file RedUNIT/Base/Issue841.php
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Issue841 extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* After bindFunc() Shared List cannot remove items.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssue841()
|
||||
{
|
||||
R::nuke();
|
||||
R::bindFunc( 'read', 'record.point', 'abs' );
|
||||
R::bindFunc( 'write', 'record.point', 'abs' );
|
||||
for($i = 0;$i < 3;$i++){
|
||||
$tag = R::dispense('tag');
|
||||
$tag->name = 'TAG_'.$i;
|
||||
R::store($tag);
|
||||
}
|
||||
$record = R::dispense('record');
|
||||
$record->point = rand(-100,-1);
|
||||
$record->sharedTagList[] = R::load('tag',2);
|
||||
R::store($record);
|
||||
asrt(count($record->sharedTagList),1);
|
||||
$record = R::load('record',1);
|
||||
$record->sharedTagList = array();
|
||||
R::store($record);
|
||||
$record = R::load('record',1);
|
||||
asrt(count($record->sharedTagList),0);
|
||||
R::bindFunc( 'read', 'record.point', null );
|
||||
R::bindFunc( 'write', 'record.point', null );
|
||||
}
|
||||
}
|
||||
44
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue90.php
vendored
Normal file
44
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Issue90.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
|
||||
/**
|
||||
* Issue90
|
||||
*
|
||||
* Tests whether this specific issue on github has been resolved.
|
||||
* Checking 'own' relationship, makes it impossible to trash a bean.
|
||||
*
|
||||
* @file RedUNIT/Base/Issue90.php
|
||||
* @desc Issue #90 - cannot trash bean with ownproperty if checked in model.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Issue90 extends Base
|
||||
{
|
||||
/**
|
||||
* Test for issue90.
|
||||
* Checking 'own' relationship, makes it impossible to trash a bean.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testIssue90()
|
||||
{
|
||||
$s = R::dispense( 'box' );
|
||||
$s->name = 'a';
|
||||
$f = R::dispense( 'bottle' );
|
||||
$s->ownBottle[] = $f;
|
||||
R::store( $s );
|
||||
$s2 = R::dispense( 'box' );
|
||||
$s2->name = 'a';
|
||||
R::store( $s2 );
|
||||
R::trash( $s2 );
|
||||
pass();
|
||||
}
|
||||
}
|
||||
988
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Joins.php
vendored
Normal file
988
vendor/gabordemooij/redbean/testing/RedUNIT/Base/Joins.php
vendored
Normal file
@@ -0,0 +1,988 @@
|
||||
<?php
|
||||
|
||||
namespace RedUNIT\Base;
|
||||
|
||||
use RedUNIT\Base as Base;
|
||||
use RedBeanPHP\Facade as R;
|
||||
use RedBeanPHP\OODBBean as OODBBean;
|
||||
|
||||
/**
|
||||
* Joins
|
||||
*
|
||||
* Tests the @joined keyword, this keyword in an SQL snippet
|
||||
* allows you to join another table and use one or more of its columns
|
||||
* in the query snippet, for instance for sorting or filtering.
|
||||
*
|
||||
* @file RedUNIT/Base/Joins.php
|
||||
* @desc Tests joins in ownLists and trees.
|
||||
* @author Gabor de Mooij and the RedBeanPHP Community
|
||||
* @license New BSD/GPLv2
|
||||
*
|
||||
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
|
||||
* This source file is subject to the New BSD/GPLv2 License that is bundled
|
||||
* with this source code in the file license.txt.
|
||||
*/
|
||||
class Joins extends Base
|
||||
{
|
||||
|
||||
private function quickTestBeans( $beans, $property ) {
|
||||
$list = array();
|
||||
foreach( $beans as $bean ) {
|
||||
$list[] = $bean->{$property};
|
||||
}
|
||||
sort($list);
|
||||
return implode(',', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we can use [via] notation in parsed joins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testParsedJoinsWithVia()
|
||||
{
|
||||
$project1 = R::dispense('project');
|
||||
$project2 = R::dispense('project');
|
||||
$project1->title = 'project1';
|
||||
$project2->title = 'project2';
|
||||
$employee1 = R::dispense('employee');
|
||||
$employee2 = R::dispense('employee');
|
||||
$employee1->name = 'a';
|
||||
$employee2->name = 'b';
|
||||
$participant1 = R::dispense('participant');
|
||||
$participant2 = R::dispense('participant');
|
||||
$participant3 = R::dispense('participant');
|
||||
$participant1->employee = $employee1;
|
||||
$participant2->employee = $employee1;
|
||||
$participant3->employee = $employee2;
|
||||
$participant1->project = $project1;
|
||||
$participant2->project = $project2;
|
||||
$participant3->project = $project2;
|
||||
R::storeAll(array($participant1,$participant2,$participant3));
|
||||
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? ', array('a'));
|
||||
asrt(count($projects),2);
|
||||
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('a','project1'));
|
||||
asrt(count($projects),1);
|
||||
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('a','project2'));
|
||||
asrt(count($projects),1);
|
||||
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? ', array('b'));
|
||||
asrt(count($projects),1);
|
||||
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('b','project1'));
|
||||
asrt(count($projects),0);
|
||||
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('b','project2'));
|
||||
asrt(count($projects),1);
|
||||
/* shouldnt work if no [via:...] and no global aliases */
|
||||
$projects = R::find('project', ' @shared.participant.name LIKE ? ', array('a'));
|
||||
asrt(count($projects),0);
|
||||
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('a','project1'));
|
||||
asrt(count($projects),0);
|
||||
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('a','project2'));
|
||||
asrt(count($projects),0);
|
||||
$projects = R::find('project', ' @shared.participant.name LIKE ? ', array('b'));
|
||||
asrt(count($projects),0);
|
||||
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('b','project1'));
|
||||
asrt(count($projects),0);
|
||||
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('b','project2'));
|
||||
asrt(count($projects),0);
|
||||
R::aliases(array('work'=>'project'));
|
||||
$company1 = R::dispense('company');
|
||||
$company2 = R::dispense('company');
|
||||
$company1->work = $project1;
|
||||
$company2->work = $project2;
|
||||
R::storeAll( array( $company1, $company2 ) );
|
||||
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? ', array('a'));
|
||||
asrt(count($companies),2);
|
||||
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ? ', array('a','project1'));
|
||||
asrt(count($companies),1);
|
||||
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ? ', array('a','project2'));
|
||||
asrt(count($companies),1);
|
||||
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? ', array('b'));
|
||||
asrt(count($companies),1);
|
||||
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ?', array('b','project1'));
|
||||
asrt(count($companies),0);
|
||||
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ? ', array('b','project2'));
|
||||
asrt(count($companies),1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use joins with aliases?
|
||||
* Can we perform bean->alias->ownlist and bean->fetchas->other
|
||||
* like operation in SQL as well?
|
||||
*
|
||||
* In this test:
|
||||
* Albert: Almanac, Study Guide
|
||||
* Bob: Poetry Bundle, Study Guide (coauthor)
|
||||
*
|
||||
* Texts:
|
||||
* Example -> in Study Guide (as source)
|
||||
* Essay -> in Study Guide (as book)
|
||||
* Poem -> in Poetry Bundle (as book)
|
||||
* Poem -> also in Almanac (as magazine)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testParsedJoinsWithAliasing()
|
||||
{
|
||||
R::nuke();
|
||||
R::aliases(array());
|
||||
$albert = R::dispense('person');
|
||||
$bob = R::dispense('person');
|
||||
$guide = R::dispense('book');
|
||||
$almanac= R::dispense('book');
|
||||
$poetry = R::dispense('book');
|
||||
$albert->firstname = 'Albert';
|
||||
$bob->firstname = 'Bob';
|
||||
$guide->title = 'Study Guide';
|
||||
$almanac->title = 'Almanac';
|
||||
$poetry->title = 'Poems';
|
||||
$guide->author = $albert;
|
||||
$guide->coauthor = $bob;
|
||||
$almanac->author = $albert;
|
||||
$poetry->author = $bob;
|
||||
$poem = R::dispense('text');
|
||||
$essay = R::dispense('text');
|
||||
$example = R::dispense('text');
|
||||
$poem->content = 'poem';
|
||||
$essay->content = 'essay';
|
||||
$example->content = 'example';
|
||||
$poem->magazine = $almanac;
|
||||
$poem->book = $poetry;
|
||||
$essay->book = $guide;
|
||||
$example->source = $guide;
|
||||
$fiction = R::dispense('tag');
|
||||
$nonfiction = R::dispense('tag');
|
||||
$fiction->description = 'fiction';
|
||||
$nonfiction->description = 'non-fiction';
|
||||
$example->sharedTag[] = $nonfiction;
|
||||
$poem->sharedTag[] = $fiction;
|
||||
$essay->sharedTag = array( $nonfiction, $fiction );
|
||||
R::storeAll( array( $poem, $essay, $example ) );
|
||||
$books = R::find('book', ' @joined.author.firstname = ? ', array('Bob'));
|
||||
asrt(count($books), 0);
|
||||
$books = R::find('book', ' @joined.author.firstname = ? ', array('Albert'));
|
||||
asrt(count($books), 0);
|
||||
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Bob'));
|
||||
asrt(count($books), 1);
|
||||
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Albert'));
|
||||
asrt(count($books), 2);
|
||||
R::freeze( TRUE );
|
||||
try { $books = R::find('book', ' @joined.author.firstname = ? ', array('Bob')); fail(); } catch ( \Exception $e ) { pass(); }
|
||||
try { $books = R::find('book', ' @joined.author.firstname = ? ', array('Albert')); fail(); } catch ( \Exception $e ) {pass();}
|
||||
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Bob'));
|
||||
asrt(count($books), 1);
|
||||
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Albert'));
|
||||
asrt(count($books), 2);
|
||||
R::aliases(array('author' => 'person','coauthor' => 'person'));
|
||||
$books = R::find('book', ' @joined.author.firstname = ? ', array('Bob'));
|
||||
asrt(count($books), 1);
|
||||
$books = R::find('book', ' @joined.author.firstname = ? ', array('Albert'));
|
||||
asrt(count($books), 2);
|
||||
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Bob'));
|
||||
asrt(count($books), 1);
|
||||
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Albert'));
|
||||
asrt(count($books), 2);
|
||||
R::freeze( FALSE );
|
||||
R::aliases(array());
|
||||
//If we want to find all the people who authored books like X
|
||||
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%Study%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%Study%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
//yields Almanac and Poems
|
||||
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%m%' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//yields Almanac and Poems
|
||||
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%m%' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//yields nothing
|
||||
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%x%' ) );
|
||||
asrt( count($authors), 0 );
|
||||
//yields nothing
|
||||
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%x%' ) );
|
||||
asrt( count($authors), 0 );
|
||||
//If we want to find all the people who authored books starting with X
|
||||
$authors = R::find( 'person', ' @own.book[coauthor].title LIKE ? ', array( '%Study%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
R::freeze( TRUE );
|
||||
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%Study%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%Study%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
//yields Almanac and Poems
|
||||
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%m%' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//yields Almanac and Poems
|
||||
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%m%' ) );
|
||||
asrt( count($authors), 2 );
|
||||
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Albert,Bob');
|
||||
//yields nothing
|
||||
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%x%' ) );
|
||||
asrt( count($authors), 0 );
|
||||
//yields nothing
|
||||
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%x%' ) );
|
||||
asrt( count($authors), 0 );
|
||||
$authors = R::find( 'person', ' @own.book[coauthor].title LIKE ? ', array( '%Study%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Bob');
|
||||
R::freeze(FALSE);
|
||||
$books = R::find( 'book', ' @joined.person[author/coauthor].firstname = ?', array( 'Bob' ) );
|
||||
asrt( count($books), 2 );
|
||||
asrt( $this->quickTestBeans( $books, 'title' ), 'Poems,Study Guide');
|
||||
//If we want all books where the author or the coauthor is named 'Bob':
|
||||
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Bob' ) );
|
||||
asrt( count($books), 2 );
|
||||
asrt( $this->quickTestBeans( $books, 'title' ), 'Poems,Study Guide');
|
||||
//If we want all books where the author or the coauthor is named 'Albert':
|
||||
$books = R::find( 'book', ' @joined.person[as:author/].firstname = ?', array( 'Albert' ) );
|
||||
asrt( count($books), 2 );
|
||||
asrt( $this->quickTestBeans( $books, 'title' ), 'Almanac,Study Guide');
|
||||
$books = R::find( 'book', ' @joined.person[as:coauthor/author].firstname = ?', array( 'Albert' ) );
|
||||
asrt( count($books), 2 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Study%' ) );
|
||||
asrt( count($authors), 2 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Poem%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Bob');
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Alm%' ) );
|
||||
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Albert');
|
||||
asrt( count($authors), 1 );
|
||||
R::freeze(TRUE);
|
||||
$books = R::find( 'book', ' @joined.person[author/coauthor].firstname = ?', array( 'Bob' ) );
|
||||
asrt( count($books), 2 );
|
||||
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Bob' ) );
|
||||
asrt( count($books), 2 );
|
||||
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Albert' ) );
|
||||
asrt( count($books), 2 );
|
||||
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Albert' ) );
|
||||
asrt( count($books), 2 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Study%' ) );
|
||||
asrt( count($authors), 2 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Poem%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Alm%' ) );
|
||||
asrt( count($authors), 1 );
|
||||
R::freeze(FALSE);
|
||||
//2 people as author/coauthor have written a book (study guide) that contains the essay
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text.content = ?', array( 'essay' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//2 people as author/coauthor have written a book as source that contains the example
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source].content = ?', array( 'example' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//1 person as author/coauthor has written a book as magazine/source that contains the poem
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source/magazine].content = ?', array( 'poem' ) );
|
||||
asrt( count($authors), 1 );
|
||||
//If we include book, we get 2 authors because the poem is also in the poetry bundle (book)
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:book/source/magazine].content = ?', array( 'poem' ) );
|
||||
asrt( count($authors), 2 );
|
||||
R::freeze(TRUE);
|
||||
//2 people as author/coauthor have written a book (study guide) that contains the essay
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text.content = ?', array( 'essay' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//2 people as author/coauthor have written a book as source that contains the example
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source].content = ?', array( 'example' ) );
|
||||
asrt( count($authors), 2 );
|
||||
//1 person as author/coauthor has written a book as magazine/source that contains the poem
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source/magazine].content = ?', array( 'poem' ) );
|
||||
asrt( count($authors), 1 );
|
||||
//If we include book, we get 2 authors because the poem is also in the poetry bundle (book)
|
||||
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:book/source/magazine].content = ?', array( 'poem' ) );
|
||||
asrt( count($authors), 2 );
|
||||
R::freeze(FALSE);
|
||||
//Get all texts in books authored by Bob
|
||||
$texts = R::find('text', ' @joined.book.joined.person[as:author].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 1 );
|
||||
//Get all texts in books authored by Bob as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book.joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 2 );
|
||||
//Get all texts in books as magazines or sources authored by Bob as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book[as:magazine/source].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 1 );
|
||||
//Get all texts in books as magazines or sources or books authored by Bob as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 3 );
|
||||
//Get all texts in books as magazines or sources or books authored by Albert as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Albert'));
|
||||
asrt( count($texts), 3 );
|
||||
R::freeze(TRUE);
|
||||
//Get all texts in books authored by Bob
|
||||
$texts = R::find('text', ' @joined.book.joined.person[as:author].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 1 );
|
||||
//Get all texts in books authored by Bob as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book.joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 2 );
|
||||
//Get all texts in books as magazines or sources authored by Bob as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book[as:magazine/source].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 1 );
|
||||
//Get all texts in books as magazines or sources or books authored by Bob as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
|
||||
asrt( count($texts), 3 );
|
||||
//Get all texts in books as magazines or sources or books authored by Albert as author/coauthor
|
||||
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Albert'));
|
||||
asrt( count($texts), 3 );
|
||||
R::freeze(FALSE);
|
||||
//Get all texts in books as magazines or sources or books authored by Albert as author/coauthor that have been tagged
|
||||
//as non-fiction, i.e. the example text and the essay.
|
||||
$texts = R::find('text', '
|
||||
@joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ?
|
||||
AND
|
||||
@shared.tag.description = ?
|
||||
',array('Albert', 'non-fiction'));
|
||||
asrt( count($texts), 2 );
|
||||
//Get all texts in books or sources or books authored by Albert as author/coauthor that have been tagged
|
||||
//as non-fiction, i.e. nothing, only the essay
|
||||
$texts = R::find('text', '
|
||||
@joined.book[as:source/book].joined.person[as:author/coauthor].firstname = ?
|
||||
AND
|
||||
@shared.tag.description = ?
|
||||
',array('Albert', 'fiction'));
|
||||
asrt( count($texts), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-join keywords starting with @ should be
|
||||
* left untouched.
|
||||
*/
|
||||
public function testNonJoinsLeftUntouched()
|
||||
{
|
||||
$writer = R::getWriter();
|
||||
$types = array( 'book', 'cafe', 'bean' );
|
||||
$sqls = array(
|
||||
'@version',
|
||||
'@oined.satire',
|
||||
'@oined.satire.laugh',
|
||||
'@hared.lookalike.title',
|
||||
'@powned.by.a.hacker',
|
||||
'nothing here!',
|
||||
'shared.person.name',
|
||||
'owned.thing.name',
|
||||
'joined.thing.name',
|
||||
'shared.person.shared.tag.name',
|
||||
'owned.thing.shared.tag.name',
|
||||
'joined.thing.shared.tag.name',
|
||||
'shared.person.joined.tag.name',
|
||||
'owned.thing.joined.tag.name',
|
||||
'joined.thing.joined.tag.name',
|
||||
'shared.person.owned.tag.name',
|
||||
'owned.thing.owned.tag.name',
|
||||
'joined.thing.owned.tag.name'
|
||||
);
|
||||
$ctes = array( TRUE, FALSE );
|
||||
foreach($types as $type) {
|
||||
foreach($sqls as $sql) {
|
||||
foreach($ctes as $cte) {
|
||||
$same = $writer->parseJoin( $type, $sql, $cte );
|
||||
asrt( trim($same), trim($sql) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we join multiple tables with the same parent?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMultipleJoinsSameParent()
|
||||
{
|
||||
R::nuke();
|
||||
$TheHagueNorthEnd = R::dispense(array(
|
||||
'_type' => 'painting',
|
||||
'title' => 'Northend, The Hague',
|
||||
'artist' => array(
|
||||
'_type' => 'artist',
|
||||
'name' => 'Isaac Israels',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'Haagse School' )
|
||||
)
|
||||
),
|
||||
'ownDetail' => array(
|
||||
array(
|
||||
'_type' => 'detail',
|
||||
'description' => 'awnings',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'object' )
|
||||
)
|
||||
),
|
||||
array(
|
||||
'_type' => 'detail',
|
||||
'description' => 'sunlight',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'daytime' )
|
||||
)
|
||||
),
|
||||
)
|
||||
));
|
||||
$Nighthawks = R::dispense(array(
|
||||
'_type' => 'painting',
|
||||
'title' => 'Nighthawks',
|
||||
'artist' => array(
|
||||
'_type' => 'artist',
|
||||
'name' => 'Hopper',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'American Realism' )
|
||||
)
|
||||
),
|
||||
'ownDetail' => array(
|
||||
array(
|
||||
'_type' => 'detail',
|
||||
'description' => 'percolator',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'object' )
|
||||
)
|
||||
),
|
||||
array(
|
||||
'_type' => 'detail',
|
||||
'description' => 'cigarette',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'object' )
|
||||
)
|
||||
),
|
||||
array(
|
||||
'_type' => 'detail',
|
||||
'description' => 'night',
|
||||
'sharedCategory' => array(
|
||||
array( '_type' => 'category', 'label' => 'nocturnal' )
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
R::storeAll( array( $Nighthawks, $TheHagueNorthEnd ) );
|
||||
$paintings = R::find('painting', '
|
||||
@joined.artist.shared.category.label = ?
|
||||
OR
|
||||
@own.detail.shared.category.label= ?
|
||||
', array('American Realism', 'nocturnal'));
|
||||
asrt(count($paintings),1);
|
||||
$painting = reset($paintings);
|
||||
asrt($painting->title, 'Nighthawks');
|
||||
$paintings = R::find('painting', '
|
||||
@joined.artist.shared.category.label = ?
|
||||
OR
|
||||
@own.detail.shared.category.label= ?
|
||||
', array('Haagse School', 'daytime'));
|
||||
asrt(count($paintings),1);
|
||||
$painting = reset($paintings);
|
||||
asrt($painting->title, 'Northend, The Hague');
|
||||
$paintings = R::find('painting', '
|
||||
@own.detail.shared.category.label= ?
|
||||
', array('object'));
|
||||
asrt(count($paintings),2);
|
||||
$paintings = R::find('painting', '
|
||||
@own.detail.shared.category.label= ?
|
||||
AND @own.detail.description= ?
|
||||
', array('object', 'percolator'));
|
||||
asrt(count($paintings),1);
|
||||
$painting = reset($paintings);
|
||||
asrt($painting->title, 'Nighthawks');
|
||||
$paintings = R::find('painting', '
|
||||
@own.detail.shared.category.label= ?
|
||||
AND @own.detail.description= ?
|
||||
', array('object', 'ashtray'));
|
||||
asrt(count($paintings),0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complex tests for the parsed joins featured.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testComplexParsedJoins()
|
||||
{
|
||||
R::nuke();
|
||||
$other = R::dispense('book');
|
||||
R::store( $other );
|
||||
$book = R::dispense('book');
|
||||
$page = R::dispense('page');
|
||||
$paragraph = R::dispense('paragraph');
|
||||
$paragraph->title = 'hello';
|
||||
$book->title = 'book';
|
||||
$book->ownPage[] = $page;
|
||||
$page->ownParagraph[] = $paragraph;
|
||||
$figure = R::dispense('figure');
|
||||
$chart = R::dispense('chart');
|
||||
$chart->title = 'results';
|
||||
$page->ownFigure[] = $figure;
|
||||
$figure->ownChart[] = $chart;
|
||||
R::store($book);
|
||||
$books = R::find('book',' @own.page.own.paragraph.title = ? OR @own.page.own.figure.own.chart.title = ?', array('hello','results'));
|
||||
asrt(count($books),1);
|
||||
$book = reset($books);
|
||||
asrt($book->title, 'book');
|
||||
R::nuke();
|
||||
R::aliases(array( 'author' => 'person' ));
|
||||
$book = R::dispense('book');
|
||||
$author = R::dispense('person');
|
||||
$detail = R::dispense('detail');
|
||||
$shop = R::dispense('shop');
|
||||
$shop->name = 'Books4you';
|
||||
$shop2 = R::dispense('shop');
|
||||
$shop2->name = 'Readers Delight';
|
||||
$author->name = 'Albert';
|
||||
$detail->title = 'Book by Albert';
|
||||
$book->ownDetailList[] = $detail;
|
||||
$book->author = $author;
|
||||
$shop->sharedBookList[] = $book;
|
||||
$book2 = R::dispense('book');
|
||||
$author2 = R::dispense('person');
|
||||
$detail2 = R::dispense('detail');
|
||||
$author2->name = 'Bert';
|
||||
$detail2->title = 'Book by Bert';
|
||||
$book2->ownDetailList[] = $detail2;
|
||||
$book2->author = $author2;
|
||||
$shop->sharedBookList[] = $book2;
|
||||
$shop2->sharedBookList[] = $book2;
|
||||
R::store($shop);
|
||||
R::store($shop2);
|
||||
//joined+own
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('Albert', 'Book by Albert'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('%ert%', '%Book by%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('%ert%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('%ert%', 'Old Bookshop'));
|
||||
asrt(count($books),0);
|
||||
//joined+shared
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Books%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Read%'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', 'Old Bookshop'));
|
||||
asrt(count($books),0);
|
||||
//own+shared
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Read%'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', 'Old Bookshop'));
|
||||
asrt(count($books),0);
|
||||
//joined+own+shared
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', 'Books%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', 'Read%'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', 'Old'));
|
||||
asrt(count($books),0);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', '%'));
|
||||
asrt(count($books),2);
|
||||
//joined+joined
|
||||
$page = R::dispense('page');
|
||||
$page->text = 'Lorem Ipsum';
|
||||
$category = R::dispense('category');
|
||||
$category->name = 'biography';
|
||||
$publisher = R::dispense('publisher');
|
||||
$publisher->name = 'Good Books';
|
||||
$book->publisher = $publisher;
|
||||
$book->ownPageList[] = $page;
|
||||
$category->sharedBookList[] = $book;
|
||||
$page2 = R::dispense('page');
|
||||
$page2->text = 'Blah Blah';
|
||||
$category2 = R::dispense('category');
|
||||
$category2->name = 'fiction';
|
||||
$publisher2 = R::dispense('publisher');
|
||||
$publisher2->name = 'Gutenberg';
|
||||
$book2->publisher = $publisher2;
|
||||
$book2->ownPageList = array($page2);
|
||||
$category2->sharedBookList[] = $book2;
|
||||
R::store($category);
|
||||
R::store($category2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('%ert%', 'Good Books'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('%ert%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('Unknown', '%'));
|
||||
asrt(count($books),0);
|
||||
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('%', '%'));
|
||||
asrt(count($books),2);
|
||||
//shared+shared
|
||||
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Reader%', 'fiction'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Book%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Book%', 'biography'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Old Bookshop', '%'));
|
||||
asrt(count($books),0);
|
||||
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('%', 'horror'));
|
||||
asrt(count($books),0);
|
||||
//own+own
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Book%', 'Blah%'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Book%', 'Lorem%'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Book%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('%', '%'));
|
||||
asrt(count($books),2);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('%', 'Nah'));
|
||||
asrt(count($books),0);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Nah', '%'));
|
||||
asrt(count($books),0);
|
||||
//joined+joined+shared+shared+own+own
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
|
||||
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
|
||||
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Book%', 'Lorem%','Good%','Albert','Books4%','bio%'));
|
||||
asrt(count($books),1);
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
|
||||
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
|
||||
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('%', '%','%','%','%','%'));
|
||||
asrt(count($books),2);
|
||||
//in order clause
|
||||
$book = R::findOne('book', ' ORDER BY @shared.category.name ASC LIMIT 1');
|
||||
asrt($book->author->name, 'Albert');
|
||||
$book = R::findOne('book', ' ORDER BY @shared.category.name DESC LIMIT 1');
|
||||
asrt($book->author->name, 'Bert');
|
||||
$book = R::findOne('book', ' ORDER BY @own.detail.title ASC LIMIT 1');
|
||||
asrt($book->author->name, 'Albert');
|
||||
$book = R::findOne('book', ' ORDER BY @own.detail.title DESC LIMIT 1');
|
||||
asrt($book->author->name, 'Bert');
|
||||
//order+criteria
|
||||
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @shared.category.name ASC LIMIT 1', array('%'));
|
||||
asrt($book->author->name, 'Albert');
|
||||
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @shared.category.name DESC LIMIT 1', array('%'));
|
||||
asrt($book->author->name, 'Bert');
|
||||
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @own.detail.title ASC LIMIT 1', array('%'));
|
||||
asrt($book->author->name, 'Albert');
|
||||
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @own.detail.title DESC LIMIT 1', array('%'));
|
||||
asrt($book->author->name, 'Bert');
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
|
||||
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
|
||||
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?
|
||||
ORDER BY @own.detail.title ASC
|
||||
', array('%', '%','%','%','%','%'));
|
||||
asrt(count($books),2);
|
||||
$first = reset($books);
|
||||
$last = end($books);
|
||||
asrt($first->author->name, 'Albert');
|
||||
asrt($last->author->name, 'Bert');
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
|
||||
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
|
||||
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?
|
||||
ORDER BY
|
||||
@shared.shop.name DESC,
|
||||
@own.detail.title ASC
|
||||
', array('%', '%','%','%','%','%'));
|
||||
asrt(count($books),2);
|
||||
$first = reset($books);
|
||||
$last = end($books);
|
||||
asrt($first->author->name, 'Bert');
|
||||
asrt($last->author->name, 'Albert');
|
||||
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
|
||||
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
|
||||
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?
|
||||
ORDER BY
|
||||
@joined.publisher.name ASC,
|
||||
@shared.shop.name DESC,
|
||||
@own.detail.title ASC
|
||||
', array('%', '%','%','%','%','%'));
|
||||
asrt(count($books),2);
|
||||
$first = reset($books);
|
||||
$last = end($books);
|
||||
asrt($first->author->name, 'Albert');
|
||||
asrt($last->author->name, 'Bert');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests new parsed joins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testParsedJoins()
|
||||
{
|
||||
$person = R::dispense('person');
|
||||
$person->name = 'Albert';
|
||||
$book = R::dispense('book');
|
||||
$book->title = 'Book by Albert.';
|
||||
$book->author = $person;
|
||||
$person->movement = R::dispense(array('_type'=>'movement','name'=>'romanticism'));
|
||||
R::store( $book );
|
||||
$albert = $person;
|
||||
$person = R::dispense('person');
|
||||
$person->name = 'Bert';
|
||||
$book = R::dispense('book');
|
||||
$book->title = 'Book by Bert.';
|
||||
$book->author = $person;
|
||||
$bert = $person;
|
||||
$person->movement = R::dispense(array('_type'=>'movement','name'=>'gothic'));
|
||||
R::store( $book );
|
||||
R::aliases(array( 'author' => 'person' ));
|
||||
$books = R::find( 'book', ' @joined.author.name LIKE ? ', array('A%'));
|
||||
asrt(count($books), 1);
|
||||
$book = reset($books);
|
||||
asrt($book->title,'Book by Albert.');
|
||||
asrt($book->fetchAs('person')->author->name,'Albert');
|
||||
$people = R::find( 'person', ' @joined.movement.name = ? ', array('romanticism')); // This works (aliases not involved)
|
||||
asrt(count($people), 1);
|
||||
$people = R::find( 'person', ' @joined.movement.name = ? ', array('gothic')); // This works (aliases not involved)
|
||||
asrt(count($people), 1);
|
||||
$people = R::find( 'person', ' @joined.movement.name = ? ', array('popscience')); // This works (aliases not involved)
|
||||
asrt(count($people), 0);
|
||||
$movements = R::find( 'movement', ' @own.author.name LIKE ? ', array( 'A%' )); // This works
|
||||
asrt(count($movements), 1);
|
||||
$movement = reset($movements);
|
||||
asrt($movement->name, 'romanticism');
|
||||
R::freeze(TRUE);
|
||||
try{
|
||||
R::find( 'person', ' @own.book.title LIKE ? ', array( 'A%' )); // This doesn't work as RedBean cannot guess which column it should bind the person to in the book table.
|
||||
fail();
|
||||
} catch(\Exception $e) {
|
||||
pass();
|
||||
}
|
||||
R::freeze(FALSE);
|
||||
$group = R::dispense('group');
|
||||
$group->name = 'a';
|
||||
$group->sharedAuthorList = array($bert, $albert);
|
||||
R::store($group);
|
||||
$group = R::dispense('group');
|
||||
$group->name = 'b';
|
||||
$group->sharedAuthorList = array($bert);
|
||||
R::store($group);
|
||||
$groups = R::find( 'group', ' @shared.author.name = ? ', array( 'Bert' )); // This works
|
||||
asrt(count($groups),2);
|
||||
R::tag($albert, array('male','writer'));
|
||||
R::tag($bert, array('male'));
|
||||
$people = R::find( 'person', ' @shared.tag.title = ? ', array( 'writer' )); // This works (aliases not involved)
|
||||
asrt(count($people),1);
|
||||
R::tag($albert, array('male','writer'));
|
||||
R::tag($bert, array('male'));
|
||||
$people = R::find( 'person', ' @shared.tag.title = ? ', array( 'male' )); // This works (aliases not involved)
|
||||
asrt(count($people),2);
|
||||
$user1 = R::dispense('user');
|
||||
$user1->name = 'user1';
|
||||
$user2 = R::dispense('user');
|
||||
$user2->name = 'user2';
|
||||
$status = R::dispense('status');
|
||||
$status->whitelist = TRUE;
|
||||
$user1->status = $status;
|
||||
$status2 = R::dispense('status');
|
||||
$status2->whitelist = FALSE;
|
||||
$user2->status = $status2;
|
||||
R::storeAll(array($user1,$user2));
|
||||
$whitelisted = R::find( 'user', ' @joined.status.whitelist = ? ', array( 1 ) );
|
||||
asrt(count($whitelisted), 1);
|
||||
$user = reset($whitelisted);
|
||||
asrt($user->name, 'user1');
|
||||
$whitelisted = R::find( 'user', ' @joined.status.whitelist = ? ', array( 0 ) );
|
||||
R::debug(0);
|
||||
asrt(count($whitelisted), 1);
|
||||
$user = reset($whitelisted);
|
||||
asrt($user->name, 'user2');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests joins with ownCount().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testJoinsInCount()
|
||||
{
|
||||
R::nuke();
|
||||
$author = R::dispense( 'author' );
|
||||
$book = R::dispense( 'book' );
|
||||
$info = R::dispense( 'info' );
|
||||
$info->title = 'x';
|
||||
$author->xownBookList[] = $book;
|
||||
$book->info = $info;
|
||||
$book = R::dispense( 'book' );
|
||||
$info = R::dispense( 'info' );
|
||||
$info->title = 'y';
|
||||
$author->xownBookList[] = $book;
|
||||
$book->info = $info;
|
||||
R::store( $author );
|
||||
$author = $author->fresh();
|
||||
$books = $author->withCondition(' @joined.info.title != ? ', array('x'))->countOwn('book');
|
||||
asrt($books, 1);
|
||||
$books = $author->withCondition(' @joined.info.title != ? ', array('y'))->countOwn('book');
|
||||
asrt($books, 1);
|
||||
$books = $author->withCondition(' @joined.info.title IN (?,?) ', array('x','y'))->countOwn('book');
|
||||
asrt($books, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Joins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testJoins()
|
||||
{
|
||||
R::nuke();
|
||||
list($a1, $a2, $a3) = R::dispense('area', 3);
|
||||
list($p1, $p2) = R::dispense('person', 2);
|
||||
list($v1, $v2, $v3, $v4) = R::dispense('visit', 4);
|
||||
$a1->name = 'Belgium';
|
||||
$a2->name = 'Arabia';
|
||||
$a3->name = 'France';
|
||||
$v1->person = $p1;
|
||||
$v2->person = $p1;
|
||||
$v3->person = $p2;
|
||||
$v4->person = $p2;
|
||||
$v1->area = $a3;
|
||||
$v2->area = $a2;
|
||||
$v3->area = $a2;
|
||||
$v4->area = $a1;
|
||||
$v1->label = 'v1 to France';
|
||||
$v2->label = 'v2 to Arabia';
|
||||
$v3->label = 'v3 to Arabia';
|
||||
$v4->label = 'v4 to Belgium';
|
||||
R::storeAll( array($v1,$v2,$v3,$v4) );
|
||||
$visits = $p1->ownVisit;
|
||||
asrt( is_array( $visits ), TRUE );
|
||||
asrt( count( $visits ), 2 );
|
||||
$names = array();
|
||||
foreach( $visits as $visit ) {
|
||||
asrt( isset( $visit->label ), TRUE );
|
||||
asrt( isset( $visit->name ), FALSE );
|
||||
asrt( isset( $visit->visit_id ), FALSE );
|
||||
$names[] = $visit->label;
|
||||
}
|
||||
$labelList = implode( ',', $names );
|
||||
asrt( $labelList, 'v1 to France,v2 to Arabia' );
|
||||
$visits = $p1
|
||||
->with('ORDER BY @joined.area.name ASC')->ownVisit;
|
||||
asrt( is_array( $visits ), TRUE );
|
||||
asrt( count( $visits ), 2 );
|
||||
$names = array();
|
||||
foreach( $visits as $visit ) {
|
||||
asrt( isset( $visit->label ), TRUE );
|
||||
asrt( isset( $visit->name ), FALSE );
|
||||
asrt( isset( $visit->visit_id ), FALSE );
|
||||
$names[] = $visit->label;
|
||||
}
|
||||
$labelList = implode( ',', $names );
|
||||
asrt( $labelList, 'v2 to Arabia,v1 to France' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for the next test.
|
||||
*
|
||||
* @param array $books the books we are going to check
|
||||
* @param string $numberList the numbers that are expected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function checkBookNumbers( $books, $numberList )
|
||||
{
|
||||
$numbers = explode( ',', $numberList );
|
||||
asrt( is_array( $books ), TRUE );
|
||||
asrt( count( $books ), count( $numbers ) );
|
||||
$bookNumbers = '';
|
||||
$bookNumberArray = array();
|
||||
foreach( $books as $book ) {
|
||||
asrt( isset( $book->num ), TRUE );
|
||||
asrt( isset( $book->title), FALSE );
|
||||
$bookNumberArray[] = $book->num;
|
||||
}
|
||||
$bookNumbers = implode( ',', $bookNumberArray);
|
||||
asrt( $bookNumbers, $numberList );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the more complicated scenarios for
|
||||
* with-joins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function testComplexCombinationsJoins()
|
||||
{
|
||||
$author = R::dispense( 'author' );
|
||||
$books = R::dispense( 'book', 4 );
|
||||
$books[0]->num = 0;
|
||||
$books[1]->num = 1;
|
||||
$books[2]->num = 2;
|
||||
$books[3]->num = 3;
|
||||
$books[0]->info = R::dispense('info')->setAttr('title', 'Learning PHP');
|
||||
$books[1]->info = R::dispense('info')->setAttr('title', 'Learning PHP and JavaScript');
|
||||
$books[2]->info = R::dispense('info')->setAttr('title', 'Learning Cobol');
|
||||
$books[3]->info = R::dispense('info')->setAttr('title','Gardening for Beginners');
|
||||
$books[0]->category = R::dispense('category')->setAttr('title', 'computers');
|
||||
$books[1]->category = R::dispense('category')->setAttr('title', 'computers');
|
||||
$books[2]->category = R::dispense('category')->setAttr('title', 'computers');
|
||||
$books[3]->category = R::dispense('category')->setAttr('title','gardening');
|
||||
$author->ownBookList = $books;
|
||||
R::store($author);
|
||||
//Base test...
|
||||
$books = $author->ownBookList;
|
||||
$this->checkBookNumbers( $books, '0,1,2,3' );
|
||||
//Just a basic Join...
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? ORDER BY book.num ASC ', array( '%PHP%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '0,1' );
|
||||
//Mix Join and criteria
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? AND num > 0 ORDER BY book.num ASC ', array( '%PHP%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '1' );
|
||||
//Basic join
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? ORDER BY book.num ASC', array( '%ing%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '0,1,2,3' );
|
||||
//Two joins
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.category.title = ? ORDER BY book.num ASC', array( '%ing%', 'computers' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '0,1,2' );
|
||||
//Join the same type twice... and order
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.category.title = ? ORDER BY @joined.info.title ASC ', array( '%ing%', 'computers' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '2,0,1' );
|
||||
//Join the same type twice
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.info.title LIKE ? ORDER BY book.num ASC', array( '%ing%', '%Learn%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '0,1,2' );
|
||||
//Join the same type 3 times and order
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.info.title LIKE ? ORDER BY @joined.info.title DESC', array( '%ing%', '%Learn%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '1,0,2' );
|
||||
//Join the same type 3 times and order and limit
|
||||
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.info.title LIKE ? ORDER BY @joined.info.title DESC LIMIT 1', array( '%ing%', '%Learn%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '1' );
|
||||
//Other combinations I can think of...
|
||||
$books = $author->withCondition(' @joined.category.title LIKE ? ORDER BY @joined.info.title DESC', array( '%ing%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '3' );
|
||||
$books = $author->withCondition(' @joined.category.title LIKE ? AND num < 4 ORDER BY @joined.info.title DESC', array( '%ing%' ) )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '3' );
|
||||
//multiple ordering
|
||||
$books = $author->with(' ORDER BY @joined.category.title ASC, @joined.info.title ASC' )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '2,0,1,3' );
|
||||
$books = $author->with(' ORDER BY @joined.category.title DESC, @joined.info.title ASC' )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '3,2,0,1' );
|
||||
$books = $author->with(' ORDER BY @joined.category.title DESC, @joined.info.title ASC LIMIT 2' )->ownBookList;
|
||||
$this->checkBookNumbers( $books, '3,2' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the more complicated scenarios for
|
||||
* with-joins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testComplexInFrozenMode()
|
||||
{
|
||||
R::freeze( FALSE );
|
||||
$this->testComplexCombinationsJoins();
|
||||
R::freeze( TRUE );
|
||||
$this->testComplexCombinationsJoins();
|
||||
R::freeze( FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests R::setNarrowFieldMode() and
|
||||
* OODBBean::ignoreJoinFeature().
|
||||
*/
|
||||
public function testSystemWideSettingsForJoins()
|
||||
{
|
||||
R::nuke();
|
||||
$author = R::dispense( 'author' );
|
||||
$book = R::dispense( 'book' );
|
||||
$info = R::dispense( 'info' );
|
||||
$info->title = 'x';
|
||||
$author->xownBookList[] = $book;
|
||||
$book->info = $info;
|
||||
R::store( $author );
|
||||
$author = $author->fresh();
|
||||
$books = $author->withCondition(' @joined.info.title != ? ', array('y1') )->xownBookList;
|
||||
$firstBook = reset( $books );
|
||||
asrt( isset( $firstBook->title ), FALSE );
|
||||
R::setNarrowFieldMode( FALSE );
|
||||
$author = $author->fresh();
|
||||
$books = $author->withCondition(' @joined.info.title != ? ', array('y2') )->xownBookList;
|
||||
$firstBook = reset( $books );
|
||||
asrt( isset( $firstBook->title ), TRUE );
|
||||
R::setNarrowFieldMode( TRUE );
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user