RE: [PEAR] Re: How to do multiple joins to the same table using joinAdd() in DB_DataObject

From: Date: Thu, 07 Aug 2003 20:44:41 +0000
Subject: RE: [PEAR] Re: How to do multiple joins to the same table using joinAdd() in DB_DataObject
References: 1  Groups: php.pear.general 
Request: Send a blank email to pear-general+get-7090@lists.php.net to get a copy of this message
Alan, Looks great and works fine for me. Good call switching the parameter order. Not only does it preserve backward compatibility, but it just makes more sense too: I don't think you'd ever try and succeed in targeting a column without first aliasing tables, although you might wish to alias tables without targeting a column. Andy > -----Original Message----- > From: Alan Knowles [mailto:alan@akbkhome.com] > Sent: Wednesday, August 06, 2003 10:24 PM > To: Andy Crain > Cc: pear-general@lists.php.net > Subject: Re: [PEAR] Re: How to do multiple joins to the same table using > joinAdd() in DB_DataObject > > > I've flipped the joinAs/JoinCol arguments - It looks like that would > retain Backwards Compatibility.. Let me know if it works > > �£Â4ñ†$¾“P+ > •http://cvs.php.net/co.php/pear/DB_DataObject/DataObject.php?r=1.116 > > Regards > Alan > > Andy Crain wrote: > > Alan, > > Here's a quick hack to joinAdd that resolves the problem for me. It's > > pretty clumsy, and I'm sure there must be a better way, but it works for > > me. New code is marked. The only way I could figure to do this was to > > add an additional optional parameter, $joinCol, which is the column > > targeted for the join. Also, targeting this way only works on fields in > > the "join to" side of links.ini entries, i.e. the right side, as in > > table:field. In other words, this will work: > > > > $sourcebook->joinAdd($owned_by,'INNER','emp_id','owner'); > > > > $sourcebook->joinAdd($edited_by,'INNER','editby_emp_id','editor'); > > [src_main] > > emp_id = users:emp_id > > editby_emp_id = users:emp_id > > > > while this will not (since $links[users] will have only one element for > > emp_id): > > > > $sourcebook->joinAdd($owned_by,'INNER','emp_id','owner'); > > > > $sourcebook->joinAdd($edited_by,'INNER','editby_emp_id','editor'); > > [users] > > emp_id = src_main:editby_emp_id > > emp_id = src_main:emp_id > > > > > > > > /** > > * 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 the joining object (no > > value resets the join) > > * @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 $joinCol string if you want to > > specifically target a field to join on > > * > > useful generally only if you are joining more than once > > * > > to the same table and same column using aliases > > * @param optional $joinAs string if you want to select > > the table as anther name > > * usefull when you want to > > select multiple columsn > > * from a secondary table. > > * @return none > > * @access public > > * @author Stijn de Reede <sjr@gmx.co.uk> > > */ > > > > function joinAdd($obj = > > false,$joinType='INNER',$joinCol=false,$joinAs=false) > > { > > global $_DB_DATAOBJECT; > > if ($obj === false) { > > $this->_join = ''; > > return; > > } > > if (!is_object($obj)) { > > DB_DataObject::raiseError("joinAdd: called without an > > object", DB_DATAOBJECT_ERROR_NODATA,PEAR_ERROR_DIE); > > } > > > > $this->_connect(); /* make sure $this->_database is set. */ > > > > $this->_get_table(); /* make sure the links are loaded */ > > > > $__DB = > > &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; > > > > > > $links = array(); > > if (isset($_DB_DATAOBJECT['LINKS'][$this->_database])) { > > $links = &$_DB_DATAOBJECT['LINKS'][$this->_database]; > > } > > > > $ofield = false; // object field > > $tfield = false; // this field > > > > /* look up the links for obj table */ > > > > if (isset($links[$obj->__table])) { > > foreach ($links[$obj->__table] as $k => $v) { > > /* link contains {this column} = {linked > > table}:{linked column} */ > > $ar = explode(':', $v); > > if ($ar[0] == $this->__table) { > > //START NEW CODE: > > if ($joinCol !== false) { > > > > DB_DataObject::raiseError("joinAdd: You cannot target a join column in > > the 'link from' table ({$obj->__table}). Either remove the third > > argument to joinAdd() ($joinCol), or alter your links.ini file.", > > DB_DATAOBJECT_ERROR_NODATA); > > return false; > > } > > //END NEW CODE > > $ofield = $k; > > $tfield = $ar[1]; > > break; > > } > > } > > } > > > > /* otherwise see if there are any links from this table > > to the obj. */ > > > > if (($ofield === false) &&isset($links[$this->__table])) > > { > > foreach ($links[$this->__table] as $k => $v) { > > /* link contains {this column} = {linked > > table}:{linked column} */ > > $ar = explode(':', $v); > > if ($ar[0] == $obj->__table) { > > /*OLD CODE > > $tfield = $k; > > $ofield = $ar[1]; > > break; > > */ > > //START NEW CODE > > if ($joinCol !== false) { > > if ($k == $joinCol) { > > $tfield = $k; > > $ofield = > > $ar[1]; > > break; > > } else { > > continue; > > } > > } else { > > $tfield = $k; > > $ofield = $ar[1]; > > break; > > } > > //END NEW CODE > > } > > } > > } > > > > /* did I find a conneciton between them? */ > > > > if ($ofield === false) { > > DB_DataObject::raiseError("joinAdd: > > {$obj->__table} has no link with {$this->__table}", > > DB_DATAOBJECT_ERROR_NODATA); > > return false; > > } > > $joinType = strtoupper($joinType); > > if ($joinAs === false) { > > $joinAs = $obj->__table; > > } > > > > $objTable = $obj->__table; > > if ($obj->_join) { > > $objTable = "($objTable {$obj->_join})"; > > } > > $fullJoinAs = ''; > > if ($obj->__table != $joinAs) { > > $fullJoinAs = "AS {$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}={$this->__table}.{$tfield} "; > > break; > > case '': // this is just a standard multitable select.. > > $this->_join .= "\n , {$objTable} {$fullJoinAs} "; > > > > $this->whereAdd("{$joinAs}.{$ofield}={$this->__table}.{$tfield}"); > > } > > > > /* now add where conditions for anything that is set in the > > object */ > > > > $items = $obj->_get_table(); > > if (!$items) { > > DB_DataObject::raiseError("joinAdd: No table definition for > > {$obj->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG); > > return false; > > } > > > > foreach($items as $k => $v) { > > if (!isset($obj->$k)) { > > continue; > > } > > if ($v & DB_DATAOBJECT_STR) { > > $this->whereAdd("{$joinAs}.{$k} = " . > > $__DB->quote($obj->$k)); > > continue; > > } > > if (is_numeric($obj->$k)) { > > $this->whereAdd("{$joinAs}.{$k} = {$obj->$k}"); > > continue; > > } > > /* this is probably an error condition! */ > > $this->whereAdd("{$joinAs}.{$k} = 0"); > > } > > > > // and finally merge the whereAdd from the child.. > > if (!$obj->_condition) { > > return; > > } > > $cond = preg_replace('/^\sWHERE/i','',$obj->_condition); > > $this->whereAdd("($cond)"); > > } > > > > > >>-----Original Message----- > >>From: Alan Knowles [mailto:alan@akbkhome.com] > >>Sent: Wednesday, August 06, 2003 1:39 AM > >>To: pear-general@lists.php.net; Andy Crain > >>Cc: pear-general@lists.php.net > >>Subject: [PEAR] Re: How to do multiple joins to the same table using > >>joinAdd() in DB_DataObject > >> > >> > >>joinAdd is basically very problematic.. > >>- JOINS syntax for databases is not very standard. > >> > >> > >>however what you have may work... have a play with the joinAdd code, > > > > and > > > >>see if you can fix it.. - I'll patch it up here. > >> > >> > >>In general though, I'm very tempted to do emulated joins in the > > > > future, > > > >>using IN (a,b,c,d,e) and multiple queries, as it will probably be more > >>portable, and alot simpler to understand.. (and from previous > > > > experience > > > >>may even be faster..) - basically alot of the time JOIN's tend to end > > > > up > > > >>with very unpredictable results. > >> > >> > >>Regards > >>Alan > >> > >> > >> > >>Andy Crain wrote: > >> > >>>Is it possible, using joinAdd() and [database].links.ini, to > > > > construct a > > > >>>query in which one table is joined twice to another table on the > > > > same > > > >>>field, using aliases? Specifically, I'm trying to achieve something > > > > like > > > >>>this: > >>> > >>>SELECT > >>> > > > > main_table.ownedby_emp_id,main_table.editedby_emp_id,owner_table.emp_nam > > > >>>e,editor_table.emp_name > >>>FROM main_table > >>>INNER JOIN users AS owner_table ON > >>>owner_table.emp_id=main_table.ownedby_emp_id > >>>INNER JOIN users AS editor_table ON > >>>editor_table.emp_id=main_table.editedby_emp_id > >>> > >>>By using the following code... > >>> > >>>//set up main_table object, then > >>>require_once('DB/DataObject/Classes/Users.php'); > >>>$owned_by = new DataObjects_Users; > >>>$edited_by = new DataObjects_Users; > >>>$this->joinAdd($owned_by,'INNER','owner_table'); > >>>$this->joinAdd($edited_by,'INNER','editor_table'); > >>>$this->selectAs(); > >>>$this->selectAs($owned_by,'owned_by_%s','owner_table'); > >>>$this->selectAs($edited_by,'edited_by_%s','editor_table'); > >>>$this->find(); > >>>//etc. > >>> > >>>...with my links.ini file containing the following: > >>>[main_table] > >>>ownedby_emp_id = users:emp_id > >>>editedby_emp_id = users:emp_id > >>> > >>>This results, however, in something like this (note the field names > > > > in > > > >>>the joins; both joins join on the same field in the main table, > > > > rather > > > >>>than the two different fields specified in links.ini): > >>> > >>>SELECT > >>> > > > > main_table.ownedby_emp_id,main_table.editedby_emp_id,owner_table.emp_nam > > > >>>e,editor_table.emp_name > >>>FROM main_table > >>>INNER JOIN users AS owner_table ON > >>>owner_table.emp_id=main_table.ownedby_emp_id > >>>INNER JOIN users AS editor_table ON > >>>editor_table.emp_id=main_table.ownedby_emp_id > >>> > >>>Looking at DataObject::joinAdd(), it appears it will return only the > >>>first link in links.ini in which the two objects' tablenames match, > > > > so > > > >>>it never sees the second link relationship listed above. Is there > > > > some > > > >>>way to construct a query like the one above without using getLink(), > >>>getLinks() or query()--I'd rather not use getLink() or getLinks() so > > > > as > > > >>>to accomplish in one query what these would do in two, and I'd > > > > rather > > > >>>not use query() for portability reasons. > >>>Thanks, > >>>Andy > >>> > >>> > >> > >> > >>-- > >>PEAR General Mailing List (http://pear.php.net/) > >>To unsubscribe, visit: http://www.php.net/unsub.php > > > > > > > > > > > > > -- > Can you help out? > Need Consulting Services or Know of a Job? > http://www.akbkhome.com

« previous php.pear.general (#7090) next »