2002-02-18 07:52:33 +01:00
< ? php
2006-04-20 19:12:30 +02:00
/**
* eGroupWare generalized SQL Storage Object
*
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package etemplate
* @ link http :// www . egroupware . org
* @ author Ralf Becker < RalfBecker @ outdoor - training . de >
2009-01-17 04:36:58 +01:00
* @ copyright 2002 - 9 by RalfBecker @ outdoor - training . de
2006-04-20 19:12:30 +02:00
* @ version $Id $
*/
2002-02-18 07:52:33 +01:00
2005-02-13 14:03:36 +01:00
/**
* generalized SQL Storage Object
*
* the class can be used in following ways :
* 1 ) by calling the constructor with an app and table - name or
* 2 ) by setting the following documented class - vars in a class derifed from this one
* Of cause can you derife the class and call the constructor with params .
*
* @ package etemplate
2005-11-10 06:15:06 +01:00
* @ subpackage api
2005-02-13 14:03:36 +01:00
* @ author RalfBecker - AT - outdoor - training . de
2005-04-08 20:30:32 +02:00
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
2009-08-19 14:08:52 +02:00
* @ todo modify search () to return an interator instead of an array
2005-02-13 14:03:36 +01:00
*/
2002-02-18 07:52:33 +01:00
class so_sql
{
2008-04-26 09:49:01 +02:00
/**
* need to be set in the derived class to the db - table - name
*
2006-08-29 06:19:38 +02:00
* @ var string
2005-02-13 14:03:36 +01:00
*/
var $table_name ;
/**
2006-08-29 06:19:38 +02:00
* db - col - name of autoincrement id or ''
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var string
2005-02-13 14:03:36 +01:00
*/
var $autoinc_id = '' ;
/**
2006-08-29 06:19:38 +02:00
* all cols in data which are not ( direct ) in the db , for data_merge
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var array
2005-02-13 14:03:36 +01:00
*/
var $non_db_cols = array ();
/**
2006-08-29 06:19:38 +02:00
* 4 turns on the so_sql debug - messages , default 0
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var int
2005-02-13 14:03:36 +01:00
*/
var $debug = 0 ;
/**
2006-08-29 06:19:38 +02:00
* string to be written to db if a col - value is '' , eg . " '' " or 'NULL' ( default )
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var string
2005-02-13 14:03:36 +01:00
*/
var $empty_on_write = 'NULL' ;
2005-02-27 22:31:10 +01:00
/**
2006-08-29 06:19:38 +02:00
* total number of entries of last search with start != false
2008-04-26 09:49:01 +02:00
*
2009-10-08 18:18:28 +02:00
* @ var int | boolean
2005-02-27 22:31:10 +01:00
*/
var $total = false ;
/**
2008-03-13 12:08:54 +01:00
* protected instance or reference ( depeding on $no_clone param of constructor ) of the db - object
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var egw_db
2005-02-27 22:31:10 +01:00
*/
2008-03-13 12:08:54 +01:00
protected $db ;
2005-02-27 22:31:10 +01:00
/**
2006-08-29 06:19:38 +02:00
* unique keys / index , set by derived class or via so_sql ( $app , $table )
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var array
2005-02-27 22:31:10 +01:00
*/
2002-02-18 07:52:33 +01:00
var $db_uni_cols = array ();
2005-02-27 22:31:10 +01:00
/**
2006-08-29 06:19:38 +02:00
* db - col - name / internal - name pairs , set by derived calls or via so_sql ( $app , $table )
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var array
2005-02-27 22:31:10 +01:00
*/
var $db_key_cols = array ();
/**
2008-04-26 09:49:01 +02:00
* db - col - name / internal - name pairs , set by derived calls or via so_sql ( $app , $table )
*
2006-08-29 06:19:38 +02:00
* @ var array
2005-02-27 22:31:10 +01:00
*/
var $db_data_cols = array ();
/**
* @ var array $db_cols all columns = $db_key_cols + $db_data_cols , set in the constructor
*/
var $db_cols = array ();
2006-04-19 10:03:28 +02:00
/**
2006-08-29 06:19:38 +02:00
* eGW table definition
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var array
2006-04-19 10:03:28 +02:00
*/
var $table_def = array ();
2008-03-13 12:08:54 +01:00
/**
* Appname to use in all queries , set via constructor
*
* @ var string
*/
var $app ;
2005-02-27 22:31:10 +01:00
/**
2006-08-29 06:19:38 +02:00
* holds the content of all columns
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var array
2005-02-27 22:31:10 +01:00
*/
var $data = array ();
/**
2007-12-04 02:13:53 +01:00
* Timestaps that need to be adjusted to user - time on reading or saving
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ var array
2005-02-27 22:31:10 +01:00
*/
2007-12-04 02:13:53 +01:00
var $timestamps = array ();
2009-10-08 18:18:28 +02:00
/**
* Type of timestamps returned by this class ( read and search methods ), default null means leave them unchanged
*
* Possible values :
* - 'ts' | 'integer' convert every timestamp to an integer unix timestamp
* - 'string' convert every timestamp to a 'Y-m-d H:i:s' string
* - 'object' convert every timestamp to a egw_time object
*
* @ var string
*/
public $timestamp_type ;
2007-12-04 02:13:53 +01:00
/**
2008-04-26 09:49:01 +02:00
* Offset in secconds between user and server - time , it need to be add to a server - time to get the user - time
2007-12-04 02:13:53 +01:00
* or substracted from a user - time to get the server - time
2008-04-26 09:49:01 +02:00
*
2007-12-04 02:13:53 +01:00
* @ var int
2009-10-08 18:18:28 +02:00
* @ deprecated use egw_time methods instead , as the offset between user and server time is only valid for current time
2007-12-04 02:13:53 +01:00
*/
var $tz_offset_s ;
/**
2009-10-08 18:18:28 +02:00
* Current time in user timezone
2008-04-26 09:49:01 +02:00
*
2009-10-08 18:18:28 +02:00
* @ var int | string | DateTime format depends on $this -> timestamp_type
2007-12-04 02:13:53 +01:00
*/
var $now ;
2009-08-19 14:08:52 +02:00
/**
* Which columns should be searched , if a non - empty string is passed to criteria parameter of search ()
*
* If not set ( by extending class ), all data columns will be searched .
*
* @ var array
*/
var $columns_to_search ;
2002-02-18 07:52:33 +01:00
2009-11-24 11:59:19 +01:00
/**
* Should search return an iterator ( true ) or an array ( false = default )
*
* @ var boolean
*/
public $search_return_iterator = false ;
2005-02-13 14:03:36 +01:00
/**
* constructor of the class
*
* NEED to be called from the constructor of the derived class ! ! !
*
* @ param string $app should be set if table - defs to be read from < app >/ setup / tables_current . inc . php
* @ param string $table should be set if table - defs to be read from < app >/ setup / tables_current . inc . php
2009-05-03 20:36:38 +02:00
* @ param egw_db $db database object , if not the one in $GLOBALS [ 'egw' ] -> db should be used , eg . for an other database
2006-04-19 10:03:28 +02:00
* @ param string $colum_prefix = '' column prefix to automatic remove from the column - name , if the column name starts with it
2008-04-26 09:49:01 +02:00
* @ param boolean $no_clone = false can we avoid to clone the db - object , default no
2008-03-13 12:08:54 +01:00
* new code using appnames and foreach ( select ( ... , $app ) can set it to avoid an extra instance of the db object
2010-04-04 10:59:37 +02:00
* @ param string $timestamp_type = null default null = leave them as is , 'ts' | 'integer' use integer unix timestamps ,
2010-02-17 01:55:43 +01:00
* 'object' use egw_time objects or 'string' use DB timestamp ( Y - m - d H : i : s ) string
2008-04-26 09:49:01 +02:00
*
2006-08-29 06:19:38 +02:00
* @ return so_sql
2005-02-13 14:03:36 +01:00
*/
2009-10-08 18:18:28 +02:00
function __construct ( $app = '' , $table = '' , $db = null , $column_prefix = '' , $no_clone = false , $timestamp_type = null )
2002-02-18 07:52:33 +01:00
{
2008-03-13 12:08:54 +01:00
if ( $no_clone )
{
$this -> db = is_object ( $db ) ? $db : $GLOBALS [ 'egw' ] -> db ;
}
else
{
$this -> db = is_object ( $db ) ? clone ( $db ) : clone ( $GLOBALS [ 'egw' ] -> db );
}
2002-02-18 07:52:33 +01:00
$this -> db_cols = $this -> db_key_cols + $this -> db_data_cols ;
2005-02-27 22:31:10 +01:00
if ( $app )
2003-03-27 18:21:58 +01:00
{
2008-03-13 12:08:54 +01:00
$this -> app = $app ;
2008-04-26 09:49:01 +02:00
2008-03-13 12:08:54 +01:00
if ( ! $no_clone ) $this -> db -> set_app ( $app );
2005-02-27 22:31:10 +01:00
2006-04-19 10:03:28 +02:00
if ( $table ) $this -> setup_table ( $app , $table , $column_prefix );
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
$this -> init ();
2005-10-16 13:45:59 +02:00
if (( int ) $this -> debug >= 4 )
2002-02-18 07:52:33 +01:00
{
echo " <p>so_sql(' $app ',' $table ')</p> \n " ;
_debug_array ( $this );
}
2010-02-17 01:55:43 +01:00
$this -> set_times ( $timestamp_type );
}
2010-04-04 10:59:37 +02:00
2010-02-17 01:55:43 +01:00
/**
* Set class vars timestamp_type , now and tz_offset_s
2010-04-04 10:59:37 +02:00
*
2010-02-17 01:55:43 +01:00
* @ param string | boolean $timestamp_type = false default false do NOT set time_stamptype ,
* null = leave them as is , 'ts' | 'integer' use integer unix timestamps , 'object' use egw_time objects ,
* 'string' use DB timestamp ( Y - m - d H : i : s ) string
*/
public function set_times ( $timestamp_type = false )
{
if ( $timestamp_type !== false ) $this -> timestamp_type = $timestamp_type ;
// set current time
switch ( $this -> timestamp_type )
2009-10-08 18:18:28 +02:00
{
case 'object' :
$this -> now = new egw_time ( 'now' );
break ;
case 'string' :
$this -> now = egw_time :: to ( 'now' , egw_time :: DATABASE );
break ;
default :
$this -> now = egw_time :: to ( 'now' , 'ts' );
}
$this -> tz_offset_s = egw_time :: tz_offset_s ();
2002-02-18 07:52:33 +01:00
}
2008-05-26 10:27:24 +02:00
/**
* php4 constructor
*
* @ deprecated use __construct
*/
2008-05-26 10:32:27 +02:00
function so_sql ( $app = '' , $table = '' , $db = null , $column_prefix = '' , $no_clone = false )
2008-05-26 10:27:24 +02:00
{
2008-05-26 10:32:27 +02:00
self :: __construct ( $app , $table , $db , $column_prefix , $no_clone );
2008-05-26 10:27:24 +02:00
}
2005-02-13 14:03:36 +01:00
/**
2005-02-27 22:31:10 +01:00
* sets up the class for an app and table ( by using the table - definition of $app / setup / tables_current . inc . php
2005-02-13 14:03:36 +01:00
*
2006-04-19 10:03:28 +02:00
* If you need a more complex conversation then just removing the column_prefix , you have to do so in a derifed class ! ! !
*
* @ param string $app app - name $table belongs too
* @ param string $table table - name
* @ param string $colum_prefix = '' column prefix to automatic remove from the column - name , if the column name starts with it
2005-02-13 14:03:36 +01:00
*/
2006-04-19 10:03:28 +02:00
function setup_table ( $app , $table , $colum_prefix = '' )
2002-02-18 07:52:33 +01:00
{
$this -> table_name = $table ;
2006-04-19 10:03:28 +02:00
$this -> table_def = $this -> db -> get_table_definitions ( $app , $table );
if ( ! $this -> table_def || ! is_array ( $this -> table_def [ 'fd' ]))
2005-03-06 22:39:46 +01:00
{
echo " <p>so_sql::setup_table(' $app ',' $table '): No table definitions found !!!<br> \n " . function_backtrace () . " </p> \n " ;
}
2002-02-18 07:52:33 +01:00
$this -> db_key_cols = $this -> db_data_cols = $this -> db_cols = array ();
$this -> autoinc_id = '' ;
2006-04-19 10:03:28 +02:00
$len_prefix = strlen ( $colum_prefix );
foreach ( $this -> table_def [ 'fd' ] as $col => $def )
2002-02-18 07:52:33 +01:00
{
2006-04-19 10:03:28 +02:00
$name = $col ;
if ( $len_prefix && substr ( $name , 0 , $len_prefix ) == $colum_prefix )
2003-03-27 18:21:58 +01:00
{
2006-04-19 10:03:28 +02:00
$name = substr ( $col , $len_prefix );
}
if ( in_array ( $col , $this -> table_def [ 'pk' ]))
{
$this -> db_key_cols [ $col ] = $name ;
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
else
2003-03-27 18:21:58 +01:00
{
2006-04-19 10:03:28 +02:00
$this -> db_data_cols [ $col ] = $name ;
2003-03-27 18:21:58 +01:00
}
2006-04-19 10:03:28 +02:00
$this -> db_cols [ $col ] = $name ;
2002-02-18 07:52:33 +01:00
if ( $def [ 'type' ] == 'auto' )
2003-03-27 18:21:58 +01:00
{
2006-04-19 10:03:28 +02:00
$this -> autoinc_id = $col ;
2003-03-27 18:21:58 +01:00
}
2008-05-28 14:17:06 +02:00
foreach ( $this -> table_def [ 'uc' ] as $k => $uni_index )
2003-03-27 18:21:58 +01:00
{
2008-05-28 14:17:06 +02:00
if ( is_array ( $uni_index ) && in_array ( $name , $uni_index ))
{
$this -> db_uni_cols [ $k ][ $col ] = $name ;
}
elseif ( $name === $uni_index )
{
$this -> db_uni_cols [ $col ] = $name ;
}
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
}
}
2009-08-10 15:48:11 +02:00
/**
* Add all timestamp fields to $this -> timestamps to get automatically converted to usertime
*
*/
function convert_all_timestamps ()
{
$check_already_included = ! empty ( $this -> timestamps );
foreach ( $this -> table_def [ 'fd' ] as $name => $data )
{
if ( $data [ 'type' ] == 'timestamp' && ( ! $check_already_included || ! in_array ( $name , $this -> timestamps )))
{
$this -> timestamps [] = $name ;
}
}
}
2005-02-13 14:03:36 +01:00
/**
* merges in new values from the given new data - array
*
* @ param $new array in form col => new_value with values to set
*/
2003-03-27 18:21:58 +01:00
function data_merge ( $new )
2002-02-18 07:52:33 +01:00
{
2005-10-16 13:45:59 +02:00
if (( int ) $this -> debug >= 4 ) echo " <p>so_sql::data_merge( " . print_r ( $new , true ) . " )</p> \n " ;
2005-04-08 20:30:32 +02:00
2002-02-18 07:52:33 +01:00
if ( ! is_array ( $new ) || ! count ( $new ))
2003-03-27 18:21:58 +01:00
{
2002-02-18 07:52:33 +01:00
return ;
2003-03-27 18:21:58 +01:00
}
foreach ( $this -> db_cols as $db_col => $col )
{
2007-07-05 06:46:49 +02:00
if ( array_key_exists ( $col , $new ))
2003-03-27 18:21:58 +01:00
{
2002-02-18 07:52:33 +01:00
$this -> data [ $col ] = $new [ $col ];
2003-03-27 18:21:58 +01:00
}
}
foreach ( $this -> non_db_cols as $db_col => $col )
{
2007-07-05 06:46:49 +02:00
if ( array_key_exists ( $col , $new ))
2003-03-27 18:21:58 +01:00
{
2002-02-18 07:52:33 +01:00
$this -> data [ $col ] = $new [ $col ];
2003-03-27 18:21:58 +01:00
}
}
2010-02-17 01:55:43 +01:00
if ( isset ( $new [ self :: USER_TIMEZONE_READ ]))
{
$this -> data [ self :: USER_TIMEZONE_READ ] = $new [ self :: USER_TIMEZONE_READ ];
}
2005-10-16 13:45:59 +02:00
if (( int ) $this -> debug >= 4 ) _debug_array ( $this -> data );
2002-02-18 07:52:33 +01:00
}
2005-02-13 14:03:36 +01:00
/**
* changes the data from the db - format to your work - format
*
2007-12-04 02:13:53 +01:00
* It gets called everytime when data is read from the db .
2009-08-17 14:28:30 +02:00
* This default implementation only converts the timestamps mentioned in $this -> timestamps from server to user time .
* You can reimplement it in a derived class like this :
*
* function db2data ( $data = null )
* {
* if (( $intern = ! is_array ( $data )))
* {
* $data =& $this -> data ;
* }
* // do your own modifications here
*
* return parent :: db2data ( $intern ? null : $data ); // important to use null, if $intern!
* }
2005-02-13 14:03:36 +01:00
*
2009-05-03 20:36:38 +02:00
* @ param array $data = null if given works on that array and returns result , else works on internal data - array
2009-08-17 14:28:30 +02:00
* @ return array
2005-02-13 14:03:36 +01:00
*/
function db2data ( $data = null )
2002-02-18 07:52:33 +01:00
{
2005-02-13 14:03:36 +01:00
if ( ! is_array ( $data ))
2003-03-27 18:21:58 +01:00
{
2005-02-13 14:03:36 +01:00
$data = & $this -> data ;
2003-03-27 18:21:58 +01:00
}
2009-10-08 18:18:28 +02:00
if ( $this -> timestamps )
2007-12-04 02:13:53 +01:00
{
foreach ( $this -> timestamps as $name )
{
2009-08-10 15:48:11 +02:00
if ( isset ( $data [ $name ]) && $data [ $name ])
{
2009-10-08 18:18:28 +02:00
$data [ $name ] = egw_time :: server2user ( $data [ $name ], $this -> timestamp_type );
2009-08-10 15:48:11 +02:00
}
2007-12-04 02:13:53 +01:00
}
}
2002-02-18 07:52:33 +01:00
return $data ;
}
2005-02-13 14:03:36 +01:00
/**
* changes the data from your work - format to the db - format
*
2007-12-04 02:13:53 +01:00
* It gets called everytime when data gets writen into db or on keys for db - searches .
* This default implementation only converts the timestamps mentioned in $this -> timestampfs from user to server time .
2009-08-17 14:28:30 +02:00
* You can reimplement it in a derived class like this :
*
* function data2db ( $data = null )
* {
* if (( $intern = ! is_array ( $data )))
* {
* $data =& $this -> data ;
* }
* // do your own modifications here
*
* return parent :: data2db ( $intern ? null : $data ); // important to use null, if $intern!
* }
2005-02-13 14:03:36 +01:00
*
2009-05-03 20:36:38 +02:00
* @ param array $data = null if given works on that array and returns result , else works on internal data - array
2009-08-17 14:28:30 +02:00
* @ return array
2005-02-13 14:03:36 +01:00
*/
function data2db ( $data = null )
2002-02-18 07:52:33 +01:00
{
2009-08-17 14:28:30 +02:00
if ( ! is_array ( $data ))
2003-03-27 18:21:58 +01:00
{
2005-02-13 14:03:36 +01:00
$data = & $this -> data ;
2003-03-27 18:21:58 +01:00
}
2009-10-08 18:18:28 +02:00
if ( $this -> timestamps )
2007-12-04 02:13:53 +01:00
{
foreach ( $this -> timestamps as $name )
{
2009-08-10 15:48:11 +02:00
if ( isset ( $data [ $name ]) && $data [ $name ])
{
2009-10-08 18:18:28 +02:00
$data [ $name ] = egw_time :: user2server ( $data [ $name ], $this -> timestamp_type );
2009-08-10 15:48:11 +02:00
}
2007-12-04 02:13:53 +01:00
}
}
2002-02-18 07:52:33 +01:00
return $data ;
}
2005-02-13 14:03:36 +01:00
/**
* initializes data with the content of key
*
2009-05-03 20:36:38 +02:00
* @ param array $keys = array () array with keys in form internalName => value
2007-01-08 16:57:58 +01:00
* @ return array internal data after init
2005-02-13 14:03:36 +01:00
*/
2003-03-27 18:21:58 +01:00
function init ( $keys = array ())
2002-02-18 07:52:33 +01:00
{
$this -> data = array ();
$this -> db2data ();
$this -> data_merge ( $keys );
2008-04-26 09:49:01 +02:00
2007-01-08 16:57:58 +01:00
return $this -> data ;
2002-02-18 07:52:33 +01:00
}
2010-04-04 10:59:37 +02:00
2010-02-17 01:55:43 +01:00
/**
* Name of automatically set user timezone field from read
*/
const USER_TIMEZONE_READ = 'user_timezone_read' ;
2002-02-18 07:52:33 +01:00
2005-02-13 14:03:36 +01:00
/**
* reads row matched by key and puts all cols in the data array
*
* @ param array $keys array with keys in form internalName => value , may be a scalar value if only one key
2009-05-03 20:36:38 +02:00
* @ param string | array $extra_cols = '' string or array of strings to be added to the SELECT , eg . " count(*) as num "
* @ param string $join = '' sql to do a join , added as is after the table - name , eg . " , table2 WHERE x=y " or
* @ return array | boolean data if row could be retrived else False
2007-01-08 16:57:58 +01:00
*/
2005-04-10 23:15:33 +02:00
function read ( $keys , $extra_cols = '' , $join = '' )
2002-02-18 07:52:33 +01:00
{
2003-11-03 17:19:48 +01:00
if ( ! is_array ( $keys ))
{
2006-04-19 10:03:28 +02:00
$pk = array_values ( $this -> db_key_cols );
2003-11-03 17:19:48 +01:00
if ( $pk ) $keys = array ( $pk [ 0 ] => $keys );
2008-04-26 09:49:01 +02:00
}
2002-02-18 07:52:33 +01:00
$this -> init ( $keys );
$this -> data2db ();
2005-04-24 19:11:34 +02:00
2005-02-27 22:31:10 +01:00
$query = false ;
2003-03-27 18:21:58 +01:00
foreach ( $this -> db_key_cols as $db_col => $col )
2002-02-18 07:52:33 +01:00
{
if ( $this -> data [ $col ] != '' )
2003-03-27 18:21:58 +01:00
{
2005-02-27 22:31:10 +01:00
$query [ $db_col ] = $this -> data [ $col ];
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
}
if ( ! $query ) // no primary key in keys, lets try the data_cols for a unique key
2003-03-27 18:21:58 +01:00
{
2005-04-24 19:11:34 +02:00
foreach ( $this -> db_uni_cols as $db_col => $col )
2002-02-18 07:52:33 +01:00
{
2008-05-28 14:17:06 +02:00
if ( ! is_array ( $col ) && $this -> data [ $col ] != '' )
2003-03-27 18:21:58 +01:00
{
2005-02-27 22:31:10 +01:00
$query [ $db_col ] = $this -> data [ $col ];
2003-03-27 18:21:58 +01:00
}
2008-05-28 14:17:06 +02:00
elseif ( is_array ( $col ))
{
$q = array ();
foreach ( $col as $db_c => $c )
{
if ( $this -> data [ $col ] == '' )
{
$q = null ;
break ;
}
$q [ $db_c ] = $this -> data [ $c ];
}
if ( $q ) $query += $q ;
}
2002-02-18 07:52:33 +01:00
}
2003-03-27 18:21:58 +01:00
}
2006-07-08 02:38:06 +02:00
if ( ! $query ) // no unique key in keys, lets try everything else
{
foreach ( $this -> db_data_cols as $db_col => $col )
{
if ( $this -> data [ $col ] != '' )
{
$query [ $db_col ] = $this -> data [ $col ];
}
}
}
2002-02-18 07:52:33 +01:00
if ( ! $query ) // keys has no cols
{
$this -> db2data ();
return False ;
}
2007-02-14 08:07:58 +01:00
if ( $join ) // Prefix the columns with the table-name, as they might exist in the join
{
foreach ( $query as $col => $val )
{
2007-04-29 16:25:19 +02:00
if ( is_int ( $col ) || strpos ( $join , $col ) === false ) continue ;
2007-02-14 08:07:58 +01:00
$query [] = $this -> db -> expression ( $this -> table_name , $this -> table_name . '.' , array ( $col => $val ));
unset ( $query [ $col ]);
}
}
2008-03-13 12:08:54 +01:00
foreach ( $this -> db -> select ( $this -> table_name , '*' . ( $extra_cols ? ',' . ( is_array ( $extra_cols ) ? implode ( ',' , $extra_cols ) : $extra_cols ) : '' ),
$query , __LINE__ , __FILE__ , False , '' , $this -> app , 0 , $join ) as $row )
2002-02-18 07:52:33 +01:00
{
2008-03-13 12:08:54 +01:00
$cols = $this -> db_cols ;
if ( $extra_cols ) // extra columns to report
2003-03-27 18:21:58 +01:00
{
2008-03-13 12:08:54 +01:00
foreach ( is_array ( $extra_cols ) ? $extra_cols : array ( $extra_cols ) as $col )
{
if ( FALSE !== stripos ( $col , ' as ' )) $col = preg_replace ( '/^.* as *([a-z0-9_]+) *$/i' , '\\1' , $col );
$cols [ $col ] = $col ;
}
}
foreach ( $cols as $db_col => $col )
{
$this -> data [ $col ] = $row [ $db_col ];
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
$this -> db2data ();
2008-04-26 09:49:01 +02:00
2010-02-17 01:55:43 +01:00
// store user timezone used for reading
$this -> data [ self :: USER_TIMEZONE_READ ] = egw_time :: $user_timezone -> getName ();
2008-03-13 12:08:54 +01:00
if (( int ) $this -> debug >= 4 )
2005-04-10 23:15:33 +02:00
{
2008-03-13 12:08:54 +01:00
echo " data = \n " ; _debug_array ( $this -> data );
2005-04-10 23:15:33 +02:00
}
2008-03-13 12:08:54 +01:00
return $this -> data ;
2005-04-10 23:15:33 +02:00
}
2008-03-13 12:08:54 +01:00
if ( $this -> autoinc_id )
2005-04-10 23:15:33 +02:00
{
2008-03-13 12:08:54 +01:00
unset ( $this -> data [ $this -> db_key_cols [ $this -> autoinc_id ]]);
2003-03-27 18:21:58 +01:00
}
2008-03-13 12:08:54 +01:00
if (( int ) $this -> debug >= 4 ) echo " nothing found !!!</p> \n " ;
2002-02-18 07:52:33 +01:00
$this -> db2data ();
2010-04-04 10:59:37 +02:00
2008-03-13 12:08:54 +01:00
return False ;
2002-02-18 07:52:33 +01:00
}
2005-02-13 14:03:36 +01:00
/**
* saves the content of data to the db
*
2009-05-03 20:36:38 +02:00
* @ param array $keys = null if given $keys are copied to data before saveing => allows a save as
2008-05-17 08:44:17 +02:00
* @ param string | array $extra_where = null extra where clause , eg . to check an etag , returns true if no affected rows !
* @ return int | boolean 0 on success , or errno != 0 on error , or true if $extra_where is given and no rows affected
2005-02-13 14:03:36 +01:00
*/
2008-04-26 09:49:01 +02:00
function save ( $keys = null , $extra_where = null )
2002-02-18 07:52:33 +01:00
{
2005-02-13 14:03:36 +01:00
if ( is_array ( $keys ) && count ( $keys )) $this -> data_merge ( $keys );
2010-04-04 10:59:37 +02:00
2010-02-17 01:55:43 +01:00
// check if data contains user timezone during read AND user changed timezone since then
// --> load old timezone for the rest of this request
// this only a grude hack, better handle this situation in app code:
// history logging eg. depends on old data read before calling save, which is then in new timezone!
// anyway it's better fixing it here then not fixing it at all ;-)
if ( isset ( $this -> data [ self :: USER_TIMEZONE_READ ]) && $this -> data [ self :: USER_TIMEZONE_READ ] != egw_time :: $user_timezone -> getName ())
{
//echo "<p>".__METHOD__."() User change TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.egw_time::$user_timezone->getName()." --> fixing</p>\n";
error_log ( __METHOD__ . " () User changed TZ since read! tz-read= " . $this -> data [ self :: USER_TIMEZONE_READ ] . ' != current-tz=' . egw_time :: $user_timezone -> getName () . " --> fixing</p> " );
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'tz' ] = $this -> data [ self :: USER_TIMEZONE_READ ];
egw_time :: setUserPrefs ( $this -> data [ self :: USER_TIMEZONE_READ ]);
$this -> set_times ();
}
2002-02-18 07:52:33 +01:00
$this -> data2db ();
2005-10-16 13:45:59 +02:00
if (( int ) $this -> debug >= 4 ) { echo " so_sql::save( " . print_r ( $keys , true ) . " ) autoinc_id=' $this->autoinc_id ', data= " ; _debug_array ( $this -> data ); }
2005-04-08 20:30:32 +02:00
2006-04-19 10:03:28 +02:00
if ( $this -> autoinc_id && ! $this -> data [ $this -> db_key_cols [ $this -> autoinc_id ]]) // insert with auto id
2002-02-18 07:52:33 +01:00
{
2003-03-27 18:21:58 +01:00
foreach ( $this -> db_cols as $db_col => $col )
2002-02-18 07:52:33 +01:00
{
2003-03-27 18:21:58 +01:00
if ( ! $this -> autoinc_id || $db_col != $this -> autoinc_id ) // not write auto-inc-id
{
2006-08-29 06:19:38 +02:00
if ( ! array_key_exists ( $col , $this -> data ) && // handling of unset columns in $this->data
2006-04-19 10:03:28 +02:00
( isset ( $this -> table_def [ 'fd' ][ $db_col ][ 'default' ]) || // we have a default value
! isset ( $this -> table_def [ 'fd' ][ $db_col ][ 'nullable' ]) || $this -> table_def [ 'fd' ][ $db_col ][ 'nullable' ])) // column is nullable
{
continue ; // no need to write that (unset) column
}
2005-04-08 20:30:32 +02:00
$data [ $db_col ] = ( string ) $this -> data [ $col ] === '' && $this -> empty_on_write == 'NULL' ? null : $this -> data [ $col ];
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
}
2008-03-13 12:08:54 +01:00
$this -> db -> insert ( $this -> table_name , $data , false , __LINE__ , __FILE__ , $this -> app );
2002-02-18 07:52:33 +01:00
if ( $this -> autoinc_id )
2003-03-27 18:21:58 +01:00
{
2002-02-18 14:51:04 +01:00
$this -> data [ $this -> db_key_cols [ $this -> autoinc_id ]] = $this -> db -> get_last_insert_id ( $this -> table_name , $this -> autoinc_id );
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
}
2006-04-19 10:03:28 +02:00
else // insert in table without auto id or update of existing row, dont write colums unset in $this->data
2002-02-18 07:52:33 +01:00
{
2003-03-27 18:21:58 +01:00
foreach ( $this -> db_data_cols as $db_col => $col )
{
2006-08-29 06:19:38 +02:00
// we need to update columns set to null: after a $this->data[$col]=null:
// - array_key_exits($col,$this->data) === true
// - isset($this->data[$col]) === false
if ( ! array_key_exists ( $col , $this -> data ) && // handling of unset columns in $this->data
2006-04-19 10:03:28 +02:00
( $this -> autoinc_id || // update of table with auto id or
isset ( $this -> table_def [ 'fd' ][ $db_col ][ 'default' ]) || // we have a default value or
! isset ( $this -> table_def [ 'fd' ][ $db_col ][ 'nullable' ]) || $this -> table_def [ 'fd' ][ $db_col ][ 'nullable' ])) // column is nullable
{
continue ; // no need to write that (unset) column
}
2009-10-08 18:18:28 +02:00
$data [ $db_col ] = ! is_object ( $this -> data [ $col ]) && ( string ) $this -> data [ $col ] === '' && $this -> empty_on_write == 'NULL' ? null : $this -> data [ $col ];
2003-03-27 18:21:58 +01:00
}
2007-09-06 13:56:37 +02:00
// allow to add direct sql updates, eg. "etag=etag+1" with int keys
if ( is_array ( $keys ) && isset ( $keys [ 0 ]))
{
for ( $n = 0 ; isset ( $keys [ $n ]); ++ $n )
{
$data [] = $keys [ $n ];
}
}
2008-04-26 09:49:01 +02:00
$keys = $extra_where ;
2003-03-27 18:21:58 +01:00
foreach ( $this -> db_key_cols as $db_col => $col )
{
2005-02-27 22:31:10 +01:00
$keys [ $db_col ] = $this -> data [ $col ];
2003-03-27 18:21:58 +01:00
}
2005-05-21 20:17:21 +02:00
if ( ! $data && ! $this -> autoinc_id ) // happens if all columns are in the primary key
{
$data = $keys ;
$keys = False ;
}
2008-05-17 08:44:17 +02:00
if ( $this -> autoinc_id )
{
$this -> db -> update ( $this -> table_name , $data , $keys , __LINE__ , __FILE__ , $this -> app );
if (( $nothing_affected = ! $this -> db -> Errno && ! $this -> db -> affected_rows ()) && $extra_where )
{
return true ; // extra_where not met, eg. etag wrong
}
}
2008-04-27 22:05:04 +02:00
// always try an insert if we have no autoinc_id, as we dont know if the data exists
2008-05-17 08:44:17 +02:00
if ( ! $this -> autoinc_id || $nothing_affected )
2005-04-08 20:30:32 +02:00
{
2008-03-13 12:08:54 +01:00
$this -> db -> insert ( $this -> table_name , $data , $keys , __LINE__ , __FILE__ , $this -> app );
2005-04-08 20:30:32 +02:00
}
2003-03-27 18:21:58 +01:00
}
2002-02-18 07:52:33 +01:00
$this -> db2data ();
2008-05-17 08:44:17 +02:00
return $this -> db -> Errno ;
2002-02-18 07:52:33 +01:00
}
2007-07-05 06:46:49 +02:00
/**
* Update only the given fields , if the primary key is not given , it will be taken from $this -> data
2008-04-26 09:49:01 +02:00
*
2007-07-05 06:46:49 +02:00
* @ param array $fields
* @ param boolean $merge = true if true $fields will be merged with $this -> data ( after update ! ), otherwise $this -> data will be just $fields
2008-07-02 17:25:54 +02:00
* @ return int | boolean 0 on success , or errno != 0 on error , or true if $extra_where is given and no rows affected
2007-07-05 06:46:49 +02:00
*/
function update ( $fields , $merge = true )
{
2008-07-02 17:25:54 +02:00
if ( $merge ) $this -> data_merge ( $fields );
$fields = $this -> data2db ( $fields );
2007-07-05 06:46:49 +02:00
2008-07-02 17:25:54 +02:00
// extract the keys from $fields or - if not set there - from $this->data
$keys = array ();
foreach ( $this -> db_key_cols as $col => $name )
{
$keys [ $col ] = isset ( $fields [ $name ]) ? $fields [ $name ] : $this -> data [ $name ];
unset ( $fields [ $name ]);
}
// extract the data from $fields
$data = array ();
foreach ( $this -> db_data_cols as $col => $name )
2007-07-05 06:46:49 +02:00
{
2008-07-02 17:25:54 +02:00
if ( array_key_exists ( $name , $fields ))
2007-07-05 06:46:49 +02:00
{
2008-07-02 17:25:54 +02:00
$data [ $col ] = $fields [ $name ];
unset ( $fields [ $name ]);
2007-07-05 06:46:49 +02:00
}
}
2008-07-02 17:25:54 +02:00
// add direct sql like 'etag=etag+1' (it has integer keys)
foreach ( $fields as $key => $value )
{
if ( is_int ( $key ))
{
$data [] = $value ;
}
}
if ( ! $data )
{
return 0 ; // nothing to update
}
if ( ! $this -> db -> update ( $this -> table_name , $data , $keys , __LINE__ , __FILE__ , $this -> app ))
2007-07-05 06:46:49 +02:00
{
2008-07-02 17:25:54 +02:00
return $this -> db -> Errno ;
2007-07-05 06:46:49 +02:00
}
2008-07-02 17:25:54 +02:00
return 0 ;
2007-07-05 06:46:49 +02:00
}
2008-04-26 09:49:01 +02:00
2005-02-13 14:03:36 +01:00
/**
* deletes row representing keys in internal data or the supplied $keys if != null
*
2009-09-16 12:20:34 +02:00
* @ param array | int $keys = null if given array with col => value pairs to characterise the rows to delete , or integer autoinc id
2009-01-17 04:36:58 +01:00
* @ param boolean $only_return_query = false return $query of delete call to db object , but not run it ( used by so_sql_cf ! )
2009-09-16 12:20:34 +02:00
* @ return int | array affected rows , should be 1 if ok , 0 if an error or array with id ' s if $only_return_ids
2005-04-15 00:09:40 +02:00
*/
2009-01-17 04:36:58 +01:00
function delete ( $keys = null , $only_return_query = false )
2002-02-18 07:52:33 +01:00
{
2008-06-04 07:26:52 +02:00
if ( $this -> autoinc_id && $keys && ! is_array ( $keys ))
{
$keys = array ( $this -> autoinc_id => $keys );
}
2002-02-18 07:52:33 +01:00
if ( ! is_array ( $keys ) || ! count ( $keys )) // use internal data
{
$data = $this -> data ;
$keys = $this -> db_key_cols ;
}
else // data and keys are supplied in $keys
{
$data = $keys ; $keys = array ();
2003-03-27 18:21:58 +01:00
foreach ( $this -> db_cols as $db_col => $col )
{
2002-02-18 07:52:33 +01:00
if ( isset ( $data [ $col ]))
2003-03-27 18:21:58 +01:00
{
2002-02-18 07:52:33 +01:00
$keys [ $db_col ] = $col ;
2003-03-27 18:21:58 +01:00
}
}
2002-02-18 07:52:33 +01:00
}
$data = $this -> data2db ( $data );
2003-03-27 18:21:58 +01:00
foreach ( $keys as $db_col => $col )
{
2005-02-27 22:31:10 +01:00
$query [ $db_col ] = $data [ $col ];
2003-03-27 18:21:58 +01:00
}
2009-01-17 04:36:58 +01:00
if ( $only_return_query ) return $query ;
2009-03-11 13:14:15 +01:00
2008-03-13 12:08:54 +01:00
$this -> db -> delete ( $this -> table_name , $query , __LINE__ , __FILE__ , $this -> app );
2002-02-18 07:52:33 +01:00
return $this -> db -> affected_rows ();
}
2005-02-13 14:03:36 +01:00
/**
* searches db for rows matching searchcriteria
*
* '*' and '?' are replaced with sql - wildcards '%' and '_'
*
2006-04-05 17:22:50 +02:00
* For a union - query you call search for each query with $start == 'UNION' and one more with only $order_by and $start set to run the union - query .
*
2009-08-19 14:08:52 +02:00
* @ param array | string $criteria array of key and data cols , OR string with search pattern ( incl . * or ? as wildcards )
2009-05-03 20:36:38 +02:00
* @ param boolean | string | array $only_keys = true True returns only keys , False returns all cols . or
2006-04-24 22:48:18 +02:00
* comma seperated list or array of columns to return
2005-04-24 19:11:34 +02:00
* @ param string $order_by = '' fieldnames + { ASC | DESC } separated by colons ',' , can also contain a GROUP BY ( if it contains ORDER BY )
2009-05-03 20:36:38 +02:00
* @ param string | array $extra_cols = '' string or array of strings to be added to the SELECT , eg . " count(*) as num "
2005-04-24 19:11:34 +02:00
* @ param string $wildcard = '' appended befor and after each criteria
* @ param boolean $empty = false False = empty criteria are ignored in query , True = empty have to be empty in row
* @ param string $op = 'AND' defaults to 'AND' , can be set to 'OR' too , then criteria 's are OR' ed together
2006-04-05 17:22:50 +02:00
* @ param mixed $start = false if != false , return only maxmatch rows begining with start , or array ( $start , $num ), or 'UNION' for a part of a union query
2005-04-24 19:11:34 +02:00
* @ param array $filter = null if set ( != null ) col - data pairs , to be and - ed ( ! ) into the query without wildcards
2006-04-24 22:48:18 +02:00
* @ param string $join = '' sql to do a join , added as is after the table - name , eg . " JOIN table2 ON x=y " or
* " LEFT JOIN table2 ON (x=y AND z=o) " , Note : there ' s no quoting done on $join , you are responsible for it !!!
2005-05-19 15:15:19 +02:00
* @ param boolean $need_full_no_count = false If true an unlimited query is run to determine the total number of rows , default false
2009-08-19 14:08:52 +02:00
* @ todo return an interator instead of an array
2009-05-03 20:36:38 +02:00
* @ return array | NULL array of matching rows ( the row is an array of the cols ) or NULL
2005-02-13 14:03:36 +01:00
*/
2005-05-19 15:15:19 +02:00
function & search ( $criteria , $only_keys = True , $order_by = '' , $extra_cols = '' , $wildcard = '' , $empty = False , $op = 'AND' , $start = false , $filter = null , $join = '' , $need_full_no_count = false )
2002-02-18 07:52:33 +01:00
{
2006-04-24 22:48:18 +02:00
if (( int ) $this -> debug >= 4 ) echo " <p>so_sql::search( " . print_r ( $criteria , true ) . " ,' $only_keys ',' $order_by ', " . print_r ( $extra_cols , true ) . " ,' $wildcard ',' $empty ',' $op ',' $start ', " . print_r ( $filter , true ) . " ,' $join ')</p> \n " ;
2005-04-27 22:47:34 +02:00
2009-08-19 14:08:52 +02:00
// if extending class or instanciator set columns to search, convert string criteria to array
if ( $criteria && ! is_array ( $criteria ))
{
2010-03-31 23:29:11 +02:00
$search = $this -> search2criteria ( $criteria , $wildcard , $op );
$criteria = array ( $search );
2009-08-19 14:08:52 +02:00
}
2003-04-16 16:32:15 +02:00
if ( ! is_array ( $criteria ))
{
2005-02-27 22:31:10 +01:00
$query = $criteria ;
2003-04-16 16:32:15 +02:00
}
else
{
$criteria = $this -> data2db ( $criteria );
2005-05-19 15:15:19 +02:00
foreach ( $criteria as $col => $val )
{
if ( is_int ( $col ))
{
$query [] = $val ;
}
elseif ( $empty || $val != '' )
2003-04-16 16:32:15 +02:00
{
2005-05-19 15:15:19 +02:00
if ( ! ( $db_col = array_search ( $col , $this -> db_cols )))
{
$db_col = $col ;
}
2008-05-26 10:27:24 +02:00
if ( $wildcard || $criteria [ $col ][ 0 ] == '!' ||
2006-12-07 20:05:40 +01:00
is_string ( $criteria [ $col ]) && ( strpos ( $criteria [ $col ], '*' ) !== false || strpos ( $criteria [ $col ], '?' ) !== false ))
2005-04-08 20:30:32 +02:00
{
2006-12-17 09:44:05 +01:00
$cmp_op = ' ' . $this -> db -> capabilities [ 'case_insensitive_like' ] . ' ' ;
2008-06-17 09:28:35 +02:00
$negate = false ;
2008-05-26 10:27:24 +02:00
if ( $criteria [ $col ][ 0 ] == '!' )
2005-11-01 11:13:28 +01:00
{
2006-12-17 09:44:05 +01:00
$cmp_op = ' NOT' . $cmp_op ;
2008-06-19 18:07:57 +02:00
$criteria [ $col ] = substr ( $criteria [ $col ], 1 );
2008-06-17 09:28:35 +02:00
$negate = true ;
2005-11-01 11:13:28 +01:00
}
2008-06-26 18:08:47 +02:00
foreach ( explode ( ' ' , $criteria [ $col ]) as $crit )
{
2009-08-19 14:08:52 +02:00
$query [] = ( $negate ? ' (' . $db_col . ' IS NULL OR ' : '' ) . $db_col . $cmp_op .
$this -> db -> quote ( $wildcard . str_replace ( array ( '%' , '_' , '*' , '?' ), array ( '\\%' , '\\_' , '%' , '_' ), $crit ) . $wildcard ) .
( $negate ? ') ' : '' );
2008-06-26 18:08:47 +02:00
}
2005-04-08 20:30:32 +02:00
}
2009-08-19 14:08:52 +02:00
elseif ( strpos ( $db_col , '.' ) !== false ) // we have a table-name specified
2005-05-19 15:15:19 +02:00
{
list ( $table , $only_col ) = explode ( '.' , $db_col );
2008-04-26 09:49:01 +02:00
2006-04-23 16:42:41 +02:00
$table_def = $this -> db -> get_table_definitions ( true , $table );
2008-03-13 12:08:54 +01:00
if ( is_array ( $val ) && count ( $val ) > 1 )
{
array_walk ( $val , array ( $this -> db , 'quote' ), $table_def [ 'fd' ][ $only_col ][ 'type' ]);
$query [] = $sql = $db_col . ' IN (' . implode ( ',' , $val ) . ')' ;
}
else
{
$query [] = $db_col . '=' . $this -> db -> quote ( is_array ( $val ) ? array_shift ( $val ) : $val , $table_def [ 'fd' ][ $only_col ][ 'type' ]);
}
2005-05-19 15:15:19 +02:00
}
2005-04-08 20:30:32 +02:00
else
{
$query [ $db_col ] = $criteria [ $col ];
}
2003-04-16 16:32:15 +02:00
}
2003-03-27 18:21:58 +01:00
}
2005-04-08 20:30:32 +02:00
if ( is_array ( $query ) && $op != 'AND' ) $query = $this -> db -> column_data_implode ( ' ' . $op . ' ' , $query );
2002-02-18 07:52:33 +01:00
}
2005-02-27 22:31:10 +01:00
if ( is_array ( $filter ))
{
$db_filter = array ();
2005-03-05 15:54:14 +01:00
$data2db_filter = $this -> data2db ( $filter );
if ( ! is_array ( $data2db_filter )) {
echo function_backtrace () . " <br/> \n " ;
echo " filter= " ; _debug_array ( $filter );
echo " data2db(filter)= " ; _debug_array ( $data2db_filter );
2008-04-26 09:49:01 +02:00
}
2005-03-29 19:06:50 +02:00
foreach ( $data2db_filter as $col => $val )
2005-02-27 22:31:10 +01:00
{
2005-04-19 15:54:55 +02:00
if ( $val !== '' )
{
2005-04-22 15:49:36 +02:00
// check if a db-internal name conversation necessary
2008-04-26 09:49:01 +02:00
if ( ! is_int ( $col ) && ( $c = array_search ( $col , $this -> db_cols )))
2005-04-22 15:49:36 +02:00
{
$col = $c ;
}
2006-06-17 20:30:38 +02:00
if ( is_int ( $col ))
{
$db_filter [] = $val ;
}
elseif ( $val === " !'' " )
2006-04-24 22:48:18 +02:00
{
$db_filter [] = $col . " != '' " ;
}
else
{
$db_filter [ $col ] = $val ;
}
2005-04-19 15:54:55 +02:00
}
2005-02-27 22:31:10 +01:00
}
2008-04-26 09:49:01 +02:00
if ( $query )
2005-04-08 20:30:32 +02:00
{
if ( $op != 'AND' )
{
$db_filter [] = '(' . $this -> db -> column_data_implode ( ' ' . $op . ' ' , $query ) . ')' ;
}
2008-04-26 09:49:01 +02:00
else
2005-04-08 20:30:32 +02:00
{
$db_filter = array_merge ( $db_filter , $query );
}
}
2005-02-27 22:31:10 +01:00
$query = $db_filter ;
}
2006-03-27 00:09:02 +02:00
if (( int ) $this -> debug >= 4 )
{
echo " <p>so_sql::search(,only_keys= $only_keys ,order_by=' $order_by ',wildcard=' $wildcard ',empty= $empty , $op ,start=' $start ', " . print_r ( $filter , true ) . " ) query= " . print_r ( $query , true ) . " , total=' $this->total '</p> \n " ;
echo " <br>criteria = " ; _debug_array ( $criteria );
}
2006-04-24 22:48:18 +02:00
if ( $only_keys === true )
{
$colums = implode ( ',' , array_keys ( $this -> db_key_cols ));
}
elseif ( is_array ( $only_keys ))
{
$colums = array ();
foreach ( $only_keys as $key => $col )
{
$colums [] = ( $db_col = array_search ( $col , $this -> db_cols )) ? $db_col : $col ;
}
$colums = implode ( ',' , $colums );
}
elseif ( ! $only_keys )
{
$colums = '*' ;
}
else
{
$colums = $only_keys ;
}
2006-05-17 05:02:47 +02:00
if ( $extra_cols ) $colums .= ( $colums ? ',' : '' ) . ( is_array ( $extra_cols ) ? implode ( ',' , $extra_cols ) : $extra_cols );
2006-03-27 00:09:02 +02:00
2010-04-04 10:59:37 +02:00
// add table-name to otherwise ambiguous id over which we join (incl. "AS id" to return it with the right name)
if ( $join && $this -> autoinc_id && strpos ( $colums , $this -> autoinc_id ) !== false )
{
$colums = preg_replace ( '/([ ,]+)' . preg_quote ( $this -> autoinc_id ) . '([ ,]+)/' , '\\1' . $this -> table_name . '.' . $this -> autoinc_id . ' AS ' . $this -> autoinc_id . '\\2' , $colums );
}
2005-04-24 19:11:34 +02:00
$num_rows = 0 ; // as spec. in max_matches in the user-prefs
if ( is_array ( $start )) list ( $start , $num_rows ) = $start ;
2008-04-26 09:49:01 +02:00
2006-12-07 13:46:46 +01:00
if ( $order_by && stripos ( $order_by , 'ORDER BY' ) === false && stripos ( $order_by , 'GROUP BY' ) === false )
2006-03-27 00:09:02 +02:00
{
$order_by = 'ORDER BY ' . $order_by ;
}
static $union = array ();
static $union_cols = array ();
if ( $start === 'UNION' || $union )
{
if ( $start === 'UNION' )
2005-05-19 15:15:19 +02:00
{
2006-04-05 17:22:50 +02:00
$union [] = array (
'table' => $this -> table_name ,
'cols' => $colums ,
'where' => $query ,
'append' => $order_by ,
'join' => $join ,
);
2006-03-27 00:09:02 +02:00
if ( ! $union_cols ) // union used the colum-names of the first query
{
$union_cols = $this -> _get_columns ( $only_keys , $extra_cols );
}
return true ; // waiting for further calls, before running the union-query
2005-05-19 15:15:19 +02:00
}
2006-03-27 00:09:02 +02:00
// running the union query now
2008-10-07 10:17:09 +02:00
if ( $start !== false ) // need to get the total too, saved in $this->total
2005-04-27 22:47:34 +02:00
{
2006-03-27 00:09:02 +02:00
if ( $this -> db -> Type == 'mysql' && $this -> db -> ServerInfo [ 'version' ] >= 4.0 )
{
$union [ 0 ][ 'cols' ] = ( $mysql_calc_rows = 'SQL_CALC_FOUND_ROWS ' ) . $union [ 0 ][ 'cols' ];
}
else // cant do a count, have to run the query without limit
{
2008-03-13 12:08:54 +01:00
$this -> total = $this -> db -> union ( $union , __LINE__ , __FILE__ ) -> NumRows ();
2006-03-27 00:09:02 +02:00
}
2005-04-27 22:47:34 +02:00
}
2008-03-13 12:08:54 +01:00
$rs = $this -> db -> union ( $union , __LINE__ , __FILE__ , $order_by , $start , $num_rows );
2006-03-27 00:09:02 +02:00
$cols = $union_cols ;
$union = $union_cols = array ();
}
else // no UNION
{
2008-10-07 10:17:09 +02:00
if ( $start !== false ) // need to get the total too, saved in $this->total
2005-04-27 22:47:34 +02:00
{
2006-03-27 00:09:02 +02:00
if ( $this -> db -> Type == 'mysql' && $this -> db -> ServerInfo [ 'version' ] >= 4.0 )
{
$mysql_calc_rows = 'SQL_CALC_FOUND_ROWS ' ;
}
2006-12-07 13:46:46 +01:00
elseif ( ! $need_full_no_count && ( ! $join || stripos ( $join , 'LEFT JOIN' ) !== false ))
2006-03-27 00:09:02 +02:00
{
2009-06-08 18:21:14 +02:00
$this -> total = $this -> db -> select ( $this -> table_name , 'COUNT(*)' , $query , __LINE__ , __FILE__ , false , '' , $this -> app , 0 , $join ) -> fetchColumn ();
2006-03-27 00:09:02 +02:00
}
else // cant do a count, have to run the query without limit
{
2008-03-13 12:08:54 +01:00
$this -> total = $this -> db -> select ( $this -> table_name , $colums , $query , __LINE__ , __FILE__ , false , $order_by , false , 0 , $join ) -> NumRows ();
2006-03-27 00:09:02 +02:00
}
2005-04-27 22:47:34 +02:00
}
2008-03-13 12:08:54 +01:00
$rs = $this -> db -> select ( $this -> table_name , $mysql_calc_rows . $colums , $query , __LINE__ , __FILE__ ,
$start , $order_by , $this -> app , $num_rows , $join );
2002-02-18 07:52:33 +01:00
2006-03-27 00:09:02 +02:00
$cols = $this -> _get_columns ( $only_keys , $extra_cols );
}
2006-04-24 22:48:18 +02:00
if (( int ) $this -> debug >= 4 ) echo " <p>sql=' { $this -> db -> Query_ID -> sql } '</p> \n " ;
2005-05-19 15:15:19 +02:00
if ( $mysql_calc_rows )
{
2009-06-08 18:21:14 +02:00
$this -> total = $this -> db -> query ( 'SELECT FOUND_ROWS()' ) -> fetchColumn ();
2005-05-19 15:15:19 +02:00
}
2009-08-19 14:08:52 +02:00
// ToDo: Implement that as an iterator, as $rs is also an interator and we could return one instead of an array
2009-11-24 11:59:19 +01:00
if ( $this -> search_return_iterator )
{
return new so_sql_db2data_iterator ( $this , $rs );
}
2006-03-27 00:09:02 +02:00
$arr = array ();
2008-03-13 12:08:54 +01:00
if ( $rs ) foreach ( $rs as $row )
2002-02-18 07:52:33 +01:00
{
2006-03-27 00:09:02 +02:00
$data = array ();
foreach ( $cols as $db_col => $col )
{
$data [ $col ] = $row [ $db_col ];
}
$arr [] = $this -> db2data ( $data );
2008-03-13 12:08:54 +01:00
$n ++ ;
2002-02-18 07:52:33 +01:00
}
2009-05-03 20:36:38 +02:00
return $n ? $arr : null ;
2006-03-27 00:09:02 +02:00
}
2009-08-19 14:08:52 +02:00
/**
* Return criteria array for a given search pattern
*
* @ param string $pattern search pattern incl . * or ? as wildcard , if no wildcards used we append and prepend one !
* @ param string & $wildcard = '' on return wildcard char to use , if pattern does not already contain wildcards !
* @ param string & $op = 'AND' on return boolean operation to use , if pattern does not start with ! we use OR else AND
* @ param string $extra_col = null extra column to search
2010-04-01 21:59:50 +02:00
* @ param array $search_cols = array () List of columns to search . If not provided , all columns in $this -> db_cols will be considered
2009-08-19 14:08:52 +02:00
* @ return array or column => value pairs
*/
2010-03-31 23:29:11 +02:00
public function search2criteria ( $pattern , & $wildcard = '' , & $op = 'AND' , $extra_col = null , $search_cols = array ())
2009-08-19 14:08:52 +02:00
{
2010-03-31 23:29:11 +02:00
// This function can get called multiple times. Make sure it doesn't re-process.
2009-08-19 14:08:52 +02:00
if ( empty ( $pattern ) || is_array ( $pattern )) return $pattern ;
2010-04-04 10:59:37 +02:00
if ( strpos ( $pattern , 'CONCAT' ) !== false )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
return $pattern ;
}
2009-08-19 14:08:52 +02:00
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
$pattern = trim ( $pattern );
2010-03-31 23:29:11 +02:00
$criteria = array ();
$filter = array ();
$columns = '' ;
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
2010-04-04 10:59:37 +02:00
/*
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
* Special handling for numeric columns . They are only considered if the pattern is numeric .
* If the pattern is numeric , an equality search is used instead .
*/
$numeric_types = array ( 'auto' , 'int' , 'float' , 'double' );
$numeric_columns = array ();
2010-04-04 10:59:37 +02:00
if ( ! $search_cols )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-04-01 21:59:50 +02:00
$search_cols = $this -> get_default_search_columns ();
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
}
2010-04-04 10:59:37 +02:00
if ( ! $search_cols )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
return array ();
2009-08-19 14:08:52 +02:00
}
2010-03-31 23:29:11 +02:00
// Concat all fields to be searched together, so the conditions operate across the whole record
foreach ( $search_cols as $col )
2009-08-19 14:08:52 +02:00
{
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
if ( $this -> table_def [ 'fd' ][ $col ] && in_array ( $this -> table_def [ 'fd' ][ $col ][ 'type' ], $numeric_types ))
{
$numeric_columns [] = $col ;
continue ;
}
2010-03-31 23:29:11 +02:00
$columns .= " CAST(COALESCE( $col ,'') AS char), " ;
2009-08-19 14:08:52 +02:00
}
2010-04-04 10:59:37 +02:00
if ( strlen ( $columns ) > 0 )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$columns = 'CONCAT(' . substr ( $columns , 0 , - 1 ) . ')' ;
}
// Break the search string into tokens
$break = ' ' ;
$token = strtok ( $pattern , $break );
2010-04-04 10:59:37 +02:00
while ( $token )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-04-04 10:59:37 +02:00
if ( $token == strtoupper ( lang ( 'AND' )))
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$token = '+' . strtok ( $break );
2010-04-04 10:59:37 +02:00
}
elseif ( $token == strtoupper ( lang ( 'OR' )))
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
continue ;
2010-04-04 10:59:37 +02:00
}
elseif ( $token == strtoupper ( lang ( 'NOT' )))
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$token = '-' . strtok ( $break );
}
2010-04-01 22:48:16 +02:00
if ( $token [ 0 ] == '"' )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$token = substr ( $token , 1 , strlen ( $token ));
2010-04-01 22:48:16 +02:00
if ( substr ( $token , - 1 ) != '"' )
{
$token .= ' ' . strtok ( '"' );
}
else
{
$token = substr ( $token , 0 , - 1 );
}
2010-03-31 23:29:11 +02:00
}
// prepend and append extra wildcard %, if pattern does NOT already contain wildcards
if ( strpos ( $token , '*' ) === false && strpos ( $token , '?' ) === false )
{
$wildcard = '%' ; // if pattern contains no wildcards, add them before AND after the pattern
}
else
{
$wildcard = '' ; // no extra wildcard, if pattern already contains some
}
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
2010-04-04 10:59:37 +02:00
switch ( $token [ 0 ])
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
case '+' :
$op = 'AND' ;
$token = substr ( $token , 1 , strlen ( $token ));
break ;
2010-04-04 10:59:37 +02:00
case '-' :
case '!' :
2010-03-31 23:29:11 +02:00
$op = 'NOT' ;
$token = substr ( $token , 1 , strlen ( $token ));
break ;
default :
$op = 'OR' ;
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
break ;
2010-03-31 23:29:11 +02:00
}
2010-04-04 10:59:37 +02:00
$token_filter = " $columns LIKE " .
2010-03-31 23:29:11 +02:00
$GLOBALS [ 'egw' ] -> db -> quote ( $wildcard . str_replace ( array ( '%' , '_' , '*' , '?' ), array ( '\\%' , '\\_' , '%' , '_' ), $token ) . $wildcard );
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
// Compare numeric token as equality for numeric columns
if ( is_numeric ( str_replace ( array ( '%' , '_' , '*' , '?' ), '' , $token )))
{
$numeric_filter = array ();
foreach ( $numeric_columns as $col )
{
2010-04-01 21:59:50 +02:00
if ( $wildcard == '' )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
// Token has a wildcard from user, use LIKE
$numeric_filter [] = " ( $col IS NOT NULL AND CAST( $col AS CHAR) LIKE " .
$GLOBALS [ 'egw' ] -> db -> quote ( str_replace ( array ( '%' , '_' , '*' , '?' ), array ( '\\%' , '\\_' , '%' , '_' ), $token )) . ')' ;
}
2010-04-04 10:59:37 +02:00
else
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
$numeric_filter [] = " ( $col IS NOT NULL AND $col = $token ) " ;
}
}
2010-04-01 21:59:50 +02:00
if ( count ( $numeric_filter ) > 0 )
{
$token_filter = '(' . $token_filter . ' OR ' . implode ( ' OR ' , $numeric_filter ) . ')' ;
}
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
}
$criteria [ $op ][] = $token_filter ;
2010-03-31 23:29:11 +02:00
$token = strtok ( $break );
}
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
2010-04-04 10:59:37 +02:00
if ( $criteria [ 'NOT' ])
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$filter [] = 'NOT (' . implode ( ' OR ' , $criteria [ 'NOT' ]) . ') ' ;
}
2010-04-04 10:59:37 +02:00
if ( $criteria [ 'AND' ])
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$filter [] = implode ( ' AND ' , $criteria [ 'AND' ]) . ' ' ;
}
2010-04-04 10:59:37 +02:00
if ( $criteria [ 'OR' ])
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-03-31 23:29:11 +02:00
$filter [] = '(' . implode ( ' OR ' , $criteria [ 'OR' ]) . ') ' ;
2009-08-19 14:08:52 +02:00
}
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
if ( count ( $filter ))
{
$result = '(' . implode ( ' AND ' , $filter ) . ')' ;
}
2010-04-04 10:59:37 +02:00
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
// OR extra column on the end so a null or blank won't block a hit in the main columns
2010-04-04 10:59:37 +02:00
if ( $extra_col )
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
{
2010-04-01 22:19:28 +02:00
$result .= ( strlen ( $result ) ? ' OR ' : ' ' ) . " $extra_col = " . $GLOBALS [ 'egw' ] -> db -> quote ( $pattern );
More sensible handling of numeric columns:
- If app doesn't specify columns to search, all columns except numerics with '_id', 'modified', 'modifier', 'status', 'cat_id', 'owner' will be considered
- Numeric columns will be searched only if the search string contains a numeric token. 'maintenance 50' is ok to search for, but for numeric columns only 50 will be searched.
- Numeric columns are searched using equality, unless user puts in a wildcard. Searching for 50 will only match 50, not 150 or 1950/04/10, but *50 will match 150.
- Text columns are searched using LIKE, including numeric search terms, as before.
2010-04-01 19:03:51 +02:00
}
$op = 'OR' ;
return array ( '(' . $result . ')' );
2009-08-19 14:08:52 +02:00
}
2010-04-01 21:59:50 +02:00
/**
* Get a default list of columns to search
* This is to be used as a fallback , for when the extending class does not define
* $this -> columns_to_search . All the columns are considered , and any with $skip_columns_with in
* their name are discarded because these columns are expected to be foreign keys or other numeric
* values with no meaning to the user .
*
* @ return array of column names
*/
2010-04-04 10:59:37 +02:00
protected function get_default_search_columns ()
2010-04-01 21:59:50 +02:00
{
$skip_columns_with = array ( '_id' , 'modified' , 'modifier' , 'status' , 'cat_id' , 'owner' );
$search_cols = is_null ( $this -> columns_to_search ) ? $this -> db_cols : $this -> columns_to_search ;
2010-04-01 22:07:01 +02:00
$numeric_types = array ( 'auto' , 'int' , 'float' , 'double' );
2010-04-04 10:59:37 +02:00
2010-04-01 21:59:50 +02:00
// Skip some numeric columns that don't make sense to search if we have to default to all columns
if ( is_null ( $this -> columns_to_search ))
{
2010-04-01 22:48:16 +02:00
foreach ( $search_cols as $key => & $col )
2010-04-01 21:59:50 +02:00
{
2010-04-01 22:48:16 +02:00
// If the name as given isn't a real column name, and adding the prefix doesn't help, skip it
if ( ! $this -> table_def [ 'fd' ][ $col ] && ! ( $col = $this -> prefix . array_search ( $col , $search_cols ))) {
// Can't search this column
unset ( $search_cols [ $key ]);
continue ;
}
2010-04-04 10:59:37 +02:00
if ( in_array ( $this -> table_def [ 'fd' ][ $col ][ 'type' ], $numeric_types ))
2010-04-01 21:59:50 +02:00
{
2010-04-04 10:59:37 +02:00
foreach ( $skip_columns_with as $bad )
2010-04-01 21:59:50 +02:00
{
2010-04-04 10:59:37 +02:00
if ( strpos ( $col , $bad ) !== false )
2010-04-01 21:59:50 +02:00
{
unset ( $search_cols [ $key ]);
continue 2 ;
}
}
}
2010-04-01 22:48:16 +02:00
// Prefix with table name to avoid ambiguity
$col = $this -> table_name . '.' . $col ;
2010-04-01 21:59:50 +02:00
}
}
return $search_cols ;
}
2006-03-27 00:09:02 +02:00
/**
* extract the requested columns from $only_keys and $extra_cols param of a search
*
2008-04-26 09:49:01 +02:00
* @ internal
2009-05-03 20:36:38 +02:00
* @ param boolean | string $only_keys = true True returns only keys , False returns all cols . comma seperated list of keys to return
* @ param string | array $extra_cols = '' string or array of strings to be added to the SELECT , eg . " count(*) as num "
2006-03-27 00:09:02 +02:00
* @ return array with columns as db - name => internal - name pairs
2008-04-26 09:49:01 +02:00
*/
2006-03-27 00:09:02 +02:00
function _get_columns ( $only_keys , $extra_cols )
{
2006-04-24 22:48:18 +02:00
//echo "_get_columns() only_keys="; _debug_array($only_keys); echo "extra_cols="; _debug_array($extra_cols);
2005-04-15 00:09:40 +02:00
if ( $only_keys === true ) // only primary key
2005-04-08 20:30:32 +02:00
{
$cols = $this -> db_key_cols ;
}
2005-04-24 19:11:34 +02:00
else
2005-04-08 20:30:32 +02:00
{
2005-04-24 19:11:34 +02:00
$cols = array ();
2006-04-24 22:48:18 +02:00
foreach ( is_array ( $only_keys ) ? $only_keys : explode ( ',' , str_replace ( array ( 'DISTINCT ' , 'distinct ' ), '' , $only_keys )) as $col )
2005-04-08 20:30:32 +02:00
{
2005-04-24 19:11:34 +02:00
if ( ! $col || $col == '*' || $col == $this -> table_name . '.*' ) // all columns
{
$cols = array_merge ( $cols , $this -> db_cols );
}
else // only the specified columns
{
2006-12-07 13:46:46 +01:00
if ( stripos ( $col , 'as' ) !== false ) $col = preg_replace ( '/^.*as +([a-z0-9_]+) *$/i' , '\\1' , $col );
2006-04-24 22:48:18 +02:00
if (( $db_col = array_search ( $col , $this -> db_cols )) !== false )
{
$cols [ $db_col ] = $col ;
}
else
{
$cols [ $col ] = isset ( $this -> db_cols [ $col ]) ? $this -> db_cols [ $col ] : $col ;
}
2005-04-24 19:11:34 +02:00
}
2005-04-08 20:30:32 +02:00
}
}
if ( $extra_cols ) // extra columns to report
2005-03-05 15:54:14 +01:00
{
2005-04-20 19:27:48 +02:00
foreach ( is_array ( $extra_cols ) ? $extra_cols : explode ( ',' , $extra_cols ) as $col )
2005-03-05 15:54:14 +01:00
{
2006-12-07 13:46:46 +01:00
if ( stripos ( $col , 'as ' ) !== false ) $col = preg_replace ( '/^.*as +([a-z0-9_]+) *$/i' , '\\1' , $col );
2006-04-24 22:48:18 +02:00
if (( $db_col = array_search ( $col , $this -> db_cols )) !== false )
{
$cols [ $db_col ] = $col ;
}
else
{
$cols [ $col ] = isset ( $this -> db_cols [ $col ]) ? $this -> db_cols [ $col ] : $col ;
}
2005-03-05 15:54:14 +01:00
}
}
2006-03-27 00:09:02 +02:00
return $cols ;
2002-02-18 07:52:33 +01:00
}
2005-02-27 22:31:10 +01:00
/**
* query rows for the nextmatch widget
*
* @ param array $query with keys 'start' , 'search' , 'order' , 'sort' , 'col_filter'
* For other keys like 'filter' , 'cat_id' you have to reimplement this method in a derived class .
* @ param array & $rows returned rows / competitions
* @ param array & $readonlys eg . to disable buttons based on acl , not use here , maybe in a derived class
2008-04-26 09:49:01 +02:00
* @ param string $join = '' sql to do a join , added as is after the table - name , eg . " , table2 WHERE x=y " or
2005-04-27 22:47:34 +02:00
* " LEFT JOIN table2 ON (x=y) " , Note : there ' s no quoting done on $join !
2005-05-19 15:15:19 +02:00
* @ param boolean $need_full_no_count = false If true an unlimited query is run to determine the total number of rows , default false
2008-03-13 12:08:54 +01:00
* @ param mixed $only_keys = false , see search
2009-05-03 20:36:38 +02:00
* @ param string | array $extra_cols = array ()
2005-05-19 15:15:19 +02:00
* @ return int total number of rows
2005-02-27 22:31:10 +01:00
*/
2009-03-11 13:14:15 +01:00
function get_rows ( $query , & $rows , & $readonlys , $join = '' , $need_full_no_count = false , $only_keys = false , $extra_cols = array ())
2005-02-27 22:31:10 +01:00
{
2005-10-16 13:45:59 +02:00
if (( int ) $this -> debug >= 4 )
2005-02-27 22:31:10 +01:00
{
echo " <p>so_sql::get_rows( " . print_r ( $query , true ) . " ,,)</p> \n " ;
}
$criteria = array ();
2009-08-19 14:08:52 +02:00
$op = 'AND' ;
2005-02-27 22:31:10 +01:00
if ( $query [ 'search' ])
{
2010-04-01 21:59:50 +02:00
$criteria = $query [ 'search' ];
2005-02-27 22:31:10 +01:00
}
2009-03-11 13:14:15 +01:00
$rows = $this -> search ( $criteria , $only_keys , $query [ 'order' ] ? $query [ 'order' ] . ' ' . $query [ 'sort' ] : '' , $extra_cols ,
2009-08-19 14:08:52 +02:00
$wildcard , false , $op , $query [ 'num_rows' ] ? array (( int ) $query [ 'start' ], $query [ 'num_rows' ]) : ( int ) $query [ 'start' ],
2007-10-07 15:29:24 +02:00
$query [ 'col_filter' ], $join , $need_full_no_count );
2008-04-26 09:49:01 +02:00
2007-11-27 22:24:50 +01:00
if ( ! $rows ) $rows = array (); // otherwise false returned from search would be returned as array(false)
2005-02-27 22:31:10 +01:00
return $this -> total ;
}
2008-04-26 09:49:01 +02:00
2005-02-13 14:03:36 +01:00
/**
2008-05-28 14:17:06 +02:00
* Check if values for unique keys and the primary keys are unique are unique
2005-02-13 14:03:36 +01:00
*
2009-05-03 20:36:38 +02:00
* @ param array $data = null data - set to check , defaults to $this -> data
2005-02-13 14:03:36 +01:00
* @ return int 0 : all keys are unique , 1 : first key not unique , 2 : ...
*/
function not_unique ( $data = null )
2002-02-18 07:52:33 +01:00
{
if ( ! is_array ( $data ))
2003-03-27 18:21:58 +01:00
{
2002-02-18 07:52:33 +01:00
$data = $this -> data ;
2003-03-27 18:21:58 +01:00
}
$n = 1 ;
2008-05-30 10:20:06 +02:00
$uni_keys = $this -> db_uni_cols ;
// add the primary key, only if it's NOT an auto id
if ( ! $this -> autoinc_id )
{
$uni_keys [] = $this -> db_key_cols ;
}
foreach ( $uni_keys as $db_col => $col )
2002-02-18 07:52:33 +01:00
{
2008-05-28 14:17:06 +02:00
if ( is_array ( $col ))
{
$query = array ();
foreach ( $col as $db_c => $c )
{
$query [ $db_c ] = $data [ $c ];
}
}
else
{
$query = array ( $db_col => $data [ $col ]);
}
2008-09-22 18:00:45 +02:00
foreach ( $this -> db -> select ( $this -> table_name , $this -> db_key_cols , $query , __LINE__ , __FILE__ , false , '' , $this -> app ) as $other )
2002-02-18 07:52:33 +01:00
{
2003-03-27 18:21:58 +01:00
foreach ( $this -> db_key_cols as $db_key_col => $key_col )
2002-02-18 07:52:33 +01:00
{
2005-04-08 20:30:32 +02:00
if ( $data [ $key_col ] != $other [ $key_col ])
2003-03-27 18:21:58 +01:00
{
2008-06-04 07:26:52 +02:00
if (( int ) $this -> debug >= 4 )
2002-02-18 07:52:33 +01:00
{
2008-05-30 10:20:06 +02:00
echo " <p>not_unique in " . array2string ( $col ) . " as for ' $key_col ': ' ${ data[$key_col] } ' != ' ${ other[$key_col] } '</p> \n " ;
2002-02-18 07:52:33 +01:00
}
return $n ; // different entry => $n not unique
}
}
}
2003-03-27 18:21:58 +01:00
++ $n ;
2002-02-18 07:52:33 +01:00
}
return 0 ;
}
2008-04-26 09:49:01 +02:00
2005-05-10 16:44:34 +02:00
/**
2005-05-21 20:17:21 +02:00
* Query DB for a list / array with one colum as key and an other one ( s ) as value , eg . id => title pairs
2005-05-10 16:44:34 +02:00
*
* We do some caching as these kind of function is usualy called multiple times , eg . for option - lists .
*
2005-05-21 20:17:21 +02:00
* @ param string $value_col array of column - names for the values of the array , can also be an expression aliased with AS ,
* if more then one column given , an array with keys identical to the given ones is returned and not just the value of the column
* @ param string $key_col = '' column - name for the keys , default '' = same as ( first ) $value_col : returns a distinct list
2005-05-10 16:44:34 +02:00
* @ param array $filter = array () to filter the entries
2005-05-21 20:17:21 +02:00
* @ param string $order = '' order , default '' = same as ( first ) $value_col
* @ return array with key_col => value_col pairs or array if more then one value_col given ( keys as in value_col )
2005-05-10 16:44:34 +02:00
*/
2005-05-19 15:15:19 +02:00
function query_list ( $value_col , $key_col = '' , $filter = array (), $order = '' )
2005-05-10 16:44:34 +02:00
{
static $cache = array ();
2008-04-26 09:49:01 +02:00
2005-05-21 20:17:21 +02:00
$cache_key = serialize ( $value_col ) . '-' . $key_col . '-' . serialize ( $filter ) . '-' . $order ;
2008-04-26 09:49:01 +02:00
2005-05-10 16:44:34 +02:00
if ( isset ( $cache [ $cache_key ]))
{
return $cache [ $cache_key ];
}
2005-05-21 20:17:21 +02:00
if ( ! is_array ( $value_col )) $value_col = array ( $value_col );
2008-04-26 09:49:01 +02:00
2005-05-21 20:17:21 +02:00
$cols = array ();
2006-04-23 16:42:41 +02:00
foreach ( $value_col as $key => $col )
2005-05-19 15:15:19 +02:00
{
2005-05-21 20:17:21 +02:00
$cols [ $key ] = preg_match ( '/AS ([a-z_0-9]+)$/i' , $col , $matches ) ? $matches [ 1 ] : $col ;
2008-04-26 09:49:01 +02:00
}
2005-05-21 20:17:21 +02:00
if ( ! $order ) $order = current ( $cols );
2005-05-19 15:15:19 +02:00
2005-05-21 20:17:21 +02:00
if (( $search =& $this -> search ( array (),( $key_col ? $key_col . ',' : 'DISTINCT ' ) . implode ( ',' , $value_col ), $order , '' , '' , false , 'AND' , false , $filter )))
2005-05-10 16:44:34 +02:00
{
2005-05-19 15:15:19 +02:00
if ( preg_match ( '/AS ([a-z_0-9]+)$/i' , $key_col , $matches ))
2005-05-10 16:44:34 +02:00
{
2005-05-19 15:15:19 +02:00
$key_col = $matches [ 1 ];
2005-05-10 16:44:34 +02:00
}
2005-05-21 20:17:21 +02:00
elseif ( ! $key_col )
{
$key_col = current ( $cols );
}
2005-05-10 16:44:34 +02:00
foreach ( $search as $row )
{
2005-05-21 20:17:21 +02:00
if ( count ( $cols ) > 1 )
{
$data = array ();
foreach ( $cols as $key => $col )
{
$data [ $key ] = $row [ $col ];
}
}
else
{
$data = $row [ current ( $cols )];
}
$ret [ $row [ $key_col ]] = $data ;
2005-05-10 16:44:34 +02:00
}
}
return $cache [ $cache_key ] =& $ret ;
}
2009-05-13 11:58:21 +02:00
/**
* Get comments for all columns or a specific one
*
* @ param $column = null name of column or null for all ( default )
* @ return array | string array with internal - name => comment pairs , or string with comment , if $column given
*/
public function get_comments ( $column = null )
{
static $comments ;
if ( is_null ( $comments ))
{
foreach ( $this -> db_cols as $db_col => $col )
{
$comments [ $col ] = $this -> table_def [ 'fd' ][ $db_col ][ 'comment' ];
}
}
return is_null ( $column ) ? $comments : $comments [ $column ];
}
2005-02-13 14:03:36 +01:00
}
2009-11-24 11:59:19 +01:00
/**
* Iterator applying a so_sql ' s db2data method on each element retrived
*
*/
class so_sql_db2data_iterator implements Iterator
{
/**
* Reference of so_sql class to use it ' s db2data method
*
* @ var so_sql
*/
private $so_sql ;
/**
* Instance of ADOdb record set to iterate
*
* @ var Iterator
*/
private $rs ;
/**
* Total count of entries
*
* @ var int
*/
public $total ;
/**
* Constructor
*
* @ param so_sql $so_sql
* @ param Traversable $rs
*/
public function __construct ( so_sql $so_sql , Traversable $rs = null )
{
$this -> so_sql = $so_sql ;
$this -> total = $so_sql -> total ;
if ( is_a ( $rs , 'IteratorAggregate' ))
{
$this -> rs = $rs -> getIterator ();
}
else
{
$this -> rs = $rs ;
}
}
/**
* Return the current element
*
* @ return array
*/
public function current ()
{
if ( is_a ( $this -> rs , 'iterator' ))
{
$data = $this -> rs -> current ();
return $this -> so_sql -> data2db ( $data );
}
return null ;
}
/**
* Return the key of the current element
*
* @ return int
*/
public function key ()
{
if ( is_a ( $this -> rs , 'iterator' ))
{
return $this -> rs -> key ();
}
return 0 ;
}
/**
* Move forward to next element ( called after each foreach loop )
*/
public function next ()
{
if ( is_a ( $this -> rs , 'iterator' ))
{
return $this -> rs -> next ();
}
}
/**
* Rewind the Iterator to the first element ( called at beginning of foreach loop )
*/
public function rewind ()
{
if ( is_a ( $this -> rs , 'iterator' ))
{
return $this -> rs -> rewind ();
}
}
/**
* Checks if current position is valid
*
* @ return boolean
*/
public function valid ()
{
if ( is_a ( $this -> rs , 'iterator' ))
{
return $this -> rs -> valid ();
}
return false ;
}
2010-03-31 23:29:11 +02:00
}