public function _connect() { // avoid those annoying PEAR::DB strict standards warnings it causes $old = error_reporting(); error_reporting(error_reporting() & ~E_STRICT); $res = parent::_connect(); // reset error_reporting($old); return $res; }
/** * joinAdd - adds another dataobject to this, building a joined query. * * example (requires links.ini to be set up correctly) * // get all the images for product 24 * $i = new DataObject_Image(); * $pi = new DataObjects_Product_image(); * $pi->product_id = 24; // set the product id to 24 * $i->joinAdd($pi); // add the product_image connectoin * $i->find(); * while ($i->fetch()) { * // do stuff * } * // an example with 2 joins * // get all the images linked with products or productgroups * $i = new DataObject_Image(); * $pi = new DataObject_Product_image(); * $pgi = new DataObject_Productgroup_image(); * $i->joinAdd($pi); * $i->joinAdd($pgi); * $i->find(); * while ($i->fetch()) { * // do stuff * } * * * @param optional $obj object |array the joining object (no value resets the join) * If you use an array here it should be in the format: * array('local_column','remotetable:remote_column'); * if remotetable does not have a definition, you should * use @ to hide the include error message.. * * * @param optional $joinType string | array * 'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates * just select ... from a,b,c with no join and * links are added as where items. * * If second Argument is array, it is assumed to be an associative * array with arguments matching below = eg. * 'joinType' => 'INNER', * 'joinAs' => '...' * 'joinCol' => .... * 'useWhereAsOn' => false, * * @param optional $joinAs string if you want to select the table as anther name * useful when you want to select multiple columsn * from a secondary table. * @param optional $joinCol string The column on This objects table to match (needed * if this table links to the child object in * multiple places eg. * user->friend (is a link to another user) * user->mother (is a link to another user..) * * optional 'useWhereAsOn' bool default false; * convert the where argments from the object being added * into ON arguments. * * * @return none * @access public * @author Stijn de Reede <*****@*****.**> */ function joinAdd($obj = false, $joinType = 'INNER', $joinAs = false, $joinCol = false) { global $_DB_DATAOBJECT; if ($obj === false) { $this->_join = ''; return; } //echo '<PRE>'; print_r(func_get_args()); $useWhereAsOn = false; // support for 2nd argument as an array of options if (is_array($joinType)) { // new options can now go in here... (dont forget to document them) $useWhereAsOn = !empty($joinType['useWhereAsOn']); $joinCol = isset($joinType['joinCol']) ? $joinType['joinCol'] : $joinCol; $joinAs = isset($joinType['joinAs']) ? $joinType['joinAs'] : $joinAs; $joinType = isset($joinType['joinType']) ? $joinType['joinType'] : 'INNER'; } // support for array as first argument // this assumes that you dont have a links.ini for the specified table. // and it doesnt exist as am extended dataobject!! - experimental. $ofield = false; // object field $tfield = false; // this field $toTable = false; if (is_array($obj)) { $tfield = $obj[0]; list($toTable, $ofield) = explode(':', $obj[1]); $obj = DB_DataObject::factory($toTable); if (!$obj || is_a($obj, 'PEAR_Error')) { $obj = new DB_DataObject(); $obj->__table = $toTable; } $obj->_connect(); // set the table items to nothing.. - eg. do not try and match // things in the child table...??? $items = array(); } if (!is_object($obj) || !is_a($obj, 'DB_DataObject')) { return $this->raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA, PEAR_ERROR_DIE); } /* make sure $this->_database is set. */ $this->_connect(); $DB =& $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; /// CHANGED 26 JUN 2009 - we prefer links from our local table over the remote one. /* otherwise see if there are any links from this table to the obj. */ //print_r($this->links()); if ($ofield === false && ($links = $this->links())) { foreach ($links as $k => $v) { /* link contains {this column} = {linked table}:{linked column} */ $ar = explode(':', $v); // Feature Request #4266 - Allow joins with multiple keys if (strpos($k, ',') !== false) { $k = explode(',', $k); } if (strpos($ar[1], ',') !== false) { $ar[1] = explode(',', $ar[1]); } if ($ar[0] == $obj->__table) { if ($joinCol !== false) { if ($k == $joinCol) { $tfield = $k; $ofield = $ar[1]; break; } else { continue; } } else { $tfield = $k; $ofield = $ar[1]; break; } } } } /* look up the links for obj table */ //print_r($obj->links()); if (!$ofield && ($olinks = $obj->links())) { foreach ($olinks as $k => $v) { /* link contains {this column} = {linked table}:{linked column} */ $ar = explode(':', $v); // Feature Request #4266 - Allow joins with multiple keys $links_key_array = strpos($k, ','); if ($links_key_array !== false) { $k = explode(',', $k); } $ar_array = strpos($ar[1], ','); if ($ar_array !== false) { $ar[1] = explode(',', $ar[1]); } if ($ar[0] == $this->__table) { // you have explictly specified the column // and the col is listed here.. // not sure if 1:1 table could cause probs here.. if ($joinCol !== false) { $this->raiseError("joinAdd: You cannot target a join column in the " . "'link from' table ({$obj->__table}). " . "Either remove the fourth argument to joinAdd() " . "({$joinCol}), or alter your links.ini file.", DB_DATAOBJECT_ERROR_NODATA); return false; } $ofield = $k; $tfield = $ar[1]; break; } } } // finally if these two table have column names that match do a join by default on them if ($ofield === false && $joinCol) { $ofield = $joinCol; $tfield = $joinCol; } /* did I find a conneciton between them? */ if ($ofield === false) { $this->raiseError("joinAdd: {$obj->__table} has no link with {$this->__table}", DB_DATAOBJECT_ERROR_NODATA); return false; } $joinType = strtoupper($joinType); // we default to joining as the same name (this is remvoed later..) if ($joinAs === false) { $joinAs = $obj->__table; } $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); $options = $_DB_DATAOBJECT['CONFIG']; // not sure how portable adding database prefixes is.. $objTable = $quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table; $dbPrefix = ''; if (strlen($obj->_database) && in_array($DB->dsn['phptype'], array('mysql', 'mysqli'))) { $dbPrefix = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->_database) : $obj->_database) . '.'; } // if they are the same, then dont add a prefix... if ($obj->_database == $this->_database) { $dbPrefix = ''; } // as far as we know only mysql supports database prefixes.. // prefixing the database name is now the default behaviour, // as it enables joining mutiple columns from multiple databases... // prefix database (quoted if neccessary..) $objTable = $dbPrefix . $objTable; $cond = ''; // if obj only a dataobject - eg. no extended class has been defined.. // it obvioulsy cant work out what child elements might exist... // until we get on the fly querying of tables.. // note: we have already checked that it is_a(db_dataobject earlier) if (strtolower(get_class($obj)) != 'db_dataobject') { // now add where conditions for anything that is set in the object $items = $obj->table(); // will return an array if no items.. // only fail if we where expecting it to work (eg. not joined on a array) if (!$items) { $this->raiseError("joinAdd: No table definition for {$obj->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG); return false; } $ignore_null = !isset($options['disable_null_strings']) || !is_string($options['disable_null_strings']) || strtolower($options['disable_null_strings']) !== 'full'; foreach ($items as $k => $v) { if (!isset($obj->{$k}) && $ignore_null) { continue; } $kSql = $quoteIdentifiers ? $DB->quoteIdentifier($k) : $k; if (DB_DataObject::_is_null($obj, $k)) { $obj->whereAdd("{$joinAs}.{$kSql} IS NULL"); continue; } if ($v & DB_DATAOBJECT_STR) { $obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) ($v & DB_DATAOBJECT_BOOL ? $obj->{$k} === 'f' ? 0 : (int) (bool) $obj->{$k} : $obj->{$k}))); continue; } if (is_numeric($obj->{$k})) { $obj->whereAdd("{$joinAs}.{$kSql} = {$obj->{$k}}"); continue; } if (is_a($obj->{$k}, 'DB_DataObject_Cast')) { $value = $obj->{$k}->toString($v, $DB); if (PEAR::isError($value)) { $this->raiseError($value->getMessage(), DB_DATAOBJECT_ERROR_INVALIDARG); return false; } $obj->whereAdd("{$joinAs}.{$kSql} = {$value}"); continue; } /* this is probably an error condition! */ $obj->whereAdd("{$joinAs}.{$kSql} = 0"); } if ($this->_query === false) { $this->raiseError("joinAdd can not be run from a object that has had a query run on it,\n clone the object or create a new one and use setFrom()", DB_DATAOBJECT_ERROR_INVALIDARGS); return false; } } // and finally merge the whereAdd from the child.. if ($obj->_query['condition']) { $cond = preg_replace('/^\\sWHERE/i', '', $obj->_query['condition']); if (!$useWhereAsOn) { $this->whereAdd($cond); } } // nested (join of joined objects..) $appendJoin = ''; if ($obj->_join) { // postgres allows nested queries, with ()'s // not sure what the results are with other databases.. // may be unpredictable.. if (in_array($DB->dsn["phptype"], array('pgsql'))) { $objTable = "({$objTable} {$obj->_join})"; } else { $appendJoin = $obj->_join; } } // fix for #2216 // add the joinee object's conditions to the ON clause instead of the WHERE clause if ($useWhereAsOn && strlen($cond)) { $appendJoin = ' AND ' . $cond . ' ' . $appendJoin; } $table = $this->__table; if ($quoteIdentifiers) { $joinAs = $DB->quoteIdentifier($joinAs); $table = $DB->quoteIdentifier($table); $ofield = is_array($ofield) ? array_map(array($DB, 'quoteIdentifier'), $ofield) : $DB->quoteIdentifier($ofield); $tfield = is_array($tfield) ? array_map(array($DB, 'quoteIdentifier'), $tfield) : $DB->quoteIdentifier($tfield); } // add database prefix if they are different databases $fullJoinAs = ''; $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table) != $joinAs; if ($addJoinAs) { // join table a AS b - is only supported by a few databases and is probably not needed // , however since it makes the whole Statement alot clearer we are leaving it in // for those databases. $fullJoinAs = in_array($DB->dsn["phptype"], array('mysql', 'mysqli', 'pgsql')) ? "AS {$joinAs}" : $joinAs; } else { // if $joinAs = $dbPrefix . $joinAs; } switch ($joinType) { case 'INNER': case 'LEFT': case 'RIGHT': // others??? .. cross, left outer, right outer, natural..? // Feature Request #4266 - Allow joins with multiple keys $jadd = "\n {$joinType} JOIN {$objTable} {$fullJoinAs}"; //$this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}"; if (is_array($ofield)) { $key_count = count($ofield); for ($i = 0; $i < $key_count; $i++) { if ($i == 0) { $jadd .= " ON ({$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]}) "; } else { $jadd .= " AND {$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]} "; } } $jadd .= ' ' . $appendJoin . ' '; } else { $jadd .= " ON ({$joinAs}.{$ofield}={$table}.{$tfield}) {$appendJoin} "; } // jadd avaliable for debugging join build. //echo $jadd ."\n"; $this->_join .= $jadd; break; case '': // this is just a standard multitable select.. $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}"; $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}"); } return true; }
/** * joinAdd - adds another dataobject to this, building a joined query. * * example (requires links.ini to be set up correctly) * // get all the images for product 24 * $i = new DataObject_Image(); * $pi = new DataObjects_Product_image(); * $pi->product_id = 24; // set the product id to 24 * $i->joinAdd($pi); // add the product_image connectoin * $i->find(); * while ($i->fetch()) { * // do stuff * } * // an example with 2 joins * // get all the images linked with products or productgroups * $i = new DataObject_Image(); * $pi = new DataObject_Product_image(); * $pgi = new DataObject_Productgroup_image(); * $i->joinAdd($pi); * $i->joinAdd($pgi); * $i->find(); * while ($i->fetch()) { * // do stuff * } * * * @param optional $obj object |array the joining object (no value resets the join) * If you use an array here it should be in the format: * array('local_column','remotetable:remote_column'); * if remotetable does not have a definition, you should * use @ to hide the include error message.. * * * @param optional $joinType string 'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates * just select ... from a,b,c with no join and * links are added as where items. * * @param optional $joinAs string if you want to select the table as anther name * useful when you want to select multiple columsn * from a secondary table. * @param optional $joinCol string The column on This objects table to match (needed * if this table links to the child object in * multiple places eg. * user->friend (is a link to another user) * user->mother (is a link to another user..) * * @return none * @access public * @author Stijn de Reede <*****@*****.**> */ function joinAdd($obj = false, $joinType = 'INNER', $joinAs = false, $joinCol = false) { global $_DB_DATAOBJECT; if ($obj === false) { $this->_join = ''; return; } // support for array as first argument // this assumes that you dont have a links.ini for the specified table. // and it doesnt exist as am extended dataobject!! - experimental. $ofield = false; // object field $tfield = false; // this field $toTable = false; if (is_array($obj)) { $tfield = $obj[0]; list($toTable, $ofield) = explode(':', $obj[1]); $obj = DB_DataObject::factory($toTable); if (!$obj || is_a($obj, 'PEAR_Error')) { $obj = new DB_DataObject(); $obj->__table = $toTable; } $obj->_connect(); // set the table items to nothing.. - eg. do not try and match // things in the child table...??? $items = array(); } if (!is_object($obj)) { $this->raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA, PEAR_ERROR_DIE); } /* make sure $this->_database is set. */ $this->_connect(); $DB =& $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; /* look up the links for obj table */ //print_r($obj->links()); if (!$ofield && ($olinks = $obj->links())) { foreach ($olinks as $k => $v) { /* link contains {this column} = {linked table}:{linked column} */ $ar = explode(':', $v); if ($ar[0] == $this->__table) { // you have explictly specified the column // and the col is listed here.. // not sure if 1:1 table could cause probs here.. if ($joinCol !== false) { $this->raiseError("joinAdd: You cannot target a join column in the " . "'link from' table ({$obj->__table}). " . "Either remove the fourth argument to joinAdd() " . "({$joinCol}), or alter your links.ini file.", DB_DATAOBJECT_ERROR_NODATA); return false; } $ofield = $k; $tfield = $ar[1]; break; } } } /* otherwise see if there are any links from this table to the obj. */ //print_r($this->links()); if ($ofield === false && ($links = $this->links())) { foreach ($links as $k => $v) { /* link contains {this column} = {linked table}:{linked column} */ $ar = explode(':', $v); if ($ar[0] == $obj->__table) { if ($joinCol !== false) { if ($k == $joinCol) { $tfield = $k; $ofield = $ar[1]; break; } else { continue; } } else { $tfield = $k; $ofield = $ar[1]; break; } } } } /* did I find a conneciton between them? */ if ($ofield === false) { $this->raiseError("joinAdd: {$obj->__table} has no link with {$this->__table}", DB_DATAOBJECT_ERROR_NODATA); return false; } $joinType = strtoupper($joinType); // we default to joining as the same name (this is remvoed later..) if ($joinAs === false) { $joinAs = $obj->__table; } $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); // not sure how portable adding database prefixes is.. $objTable = $quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table; // as far as we know only mysql supports database prefixes.. if (in_array($DB->dsn['phptype'], array('mysql', 'mysqli')) && $obj->_database != $this->_database && strlen($obj->_database)) { // prefix database (quoted if neccessary..) $objTable = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->_database) : $obj->_database) . '.' . $objTable; } // nested (join of joined objects..) $appendJoin = ''; if ($obj->_join) { // postgres allows nested queries, with ()'s // not sure what the results are with other databases.. // may be unpredictable.. if (in_array($DB->dsn["phptype"], array('pgsql'))) { $objTable = "({$objTable} {$obj->_join})"; } else { $appendJoin = $obj->_join; } } $table = $this->__table; if ($quoteIdentifiers) { $joinAs = $DB->quoteIdentifier($joinAs); $table = $DB->quoteIdentifier($table); $ofield = $DB->quoteIdentifier($ofield); $tfield = $DB->quoteIdentifier($tfield); } // add database prefix if they are different databases $fullJoinAs = ''; $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table) != $joinAs; if ($addJoinAs) { $fullJoinAs = "AS {$joinAs}"; } else { // if if (in_array($DB->dsn['phptype'], array('mysql', 'mysqli')) && $obj->_database != $this->_database && strlen($this->_database)) { $joinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->_database) : $obj->_database) . '.' . $joinAs; } } switch ($joinType) { case 'INNER': case 'LEFT': case 'RIGHT': // others??? .. cross, left outer, right outer, natural..? $this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}" . " ON {$joinAs}.{$ofield}={$table}.{$tfield} {$appendJoin} "; break; case '': // this is just a standard multitable select.. $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}"; $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}"); } // if obj only a dataobject - eg. no extended class has been defined.. // it obvioulsy cant work out what child elements might exist... // untill we get on the fly querying of tables.. if (strtolower(get_class($obj)) == 'db_dataobject') { return true; } /* now add where conditions for anything that is set in the object */ $items = $obj->table(); // will return an array if no items.. // only fail if we where expecting it to work (eg. not joined on a array) if (!$items) { $this->raiseError("joinAdd: No table definition for {$obj->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG); return false; } foreach ($items as $k => $v) { if (!isset($obj->{$k})) { continue; } $kSql = $quoteIdentifiers ? $DB->quoteIdentifier($k) : $k; if ($v & DB_DATAOBJECT_STR) { $this->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) ($v & DB_DATAOBJECT_BOOL ? $obj->{$k} == 'f' ? 0 : (int) (bool) $obj->{$k} : $obj->{$k}))); continue; } if (is_numeric($obj->{$k})) { $this->whereAdd("{$joinAs}.{$kSql} = {$obj->{$k}}"); continue; } /* this is probably an error condition! */ $this->whereAdd("{$joinAs}.{$kSql} = 0"); } if (!isset($this->_query)) { $this->raiseError("joinAdd can not be run from a object that has had a query run on it,\n clone the object or create a new one and use setFrom()", DB_DATAOBJECT_ERROR_INVALIDARGS); return false; } // and finally merge the whereAdd from the child.. if (!$obj->_query['condition']) { return true; } $cond = preg_replace('/^\\sWHERE/i', '', $obj->_query['condition']); $this->whereAdd("({$cond})"); return true; }
/** * Added storing reference to DataBase connection * * @todo Add sharing connections in connection Pool * @see DB_DataObject::_connect() * * @return PEAR_Error | true */ function _connect() { if ($this->_database_dsn_md5 && !empty($GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]) && $this->_database) { return true; } if (empty($_DB_DATAOBJECT['CONFIG'])) { $this->_loadConfig(); } $dbh =& OA_DB::singleton(); if (PEAR::isError($dbh)) { return $dbh; } $this->_database_dsn_md5 = md5(OA_DB::getDsn()); $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5] =& $dbh; $GLOBALS['_DB_DATAOBJECT']['CONFIG']['quote_identifiers'] = $dbh->options['quote_identifier']; $this->_database = $dbh->getDatabase(); // store the reference in ADMIN_DB_LINK - backward compatibility $GLOBALS['_MAX']['ADMIN_DB_LINK'] =& $dbh->connection; return parent::_connect(); }
/** * classic factory method for loading a table class * usage: $do = DB_DataObject::factory('person') * WARNING - this may emit a include error if the file does not exist.. * use @ to silence it (if you are sure it is acceptable) * eg. $do = @DB_DataObject::factory('person') * * table name will eventually be databasename/table * - and allow modular dataobjects to be written.. * (this also helps proxy creation) * * * @param string $table tablename (use blank to create a new instance of the same class.) * @access private * @return DataObject|PEAR_Error */ function factory($table = '') { global $_DB_DATAOBJECT; if (empty($_DB_DATAOBJECT['CONFIG'])) { DB_DataObject::_loadConfig(); } if ($table === '') { if (is_a($this, 'DB_DataObject') && strlen($this->__table)) { $table = $this->__table; } else { return DB_DataObject::raiseError("factory did not recieve a table name", DB_DATAOBJECT_ERROR_INVALIDARGS); } } $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ? $_DB_DATAOBJECT['CONFIG']['class_prefix'] : ''; $class = $p . preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)); $class = class_exists($class) ? $class : DB_DataObject::_autoloadClass($class); // proxy = full|light if (!$class && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) { $proxyMethod = 'getProxy' . $_DB_DATAOBJECT['CONFIG']['proxy']; require_once 'DB/DataObject/Generator.php'; $d = new DB_DataObject(); $d->__table = $table; $d->_connect(); $x = new DB_DataObject_Generator(); return $x->{$proxyMethod}($d->_database, $table); } if (!$class) { return DB_DataObject::raiseError("factory could not find class {$class} from {$table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG); } return new $class(); }