1<?php 2/** 3 * Link tool for DB_DataObject 4 * 5 * PHP versions 5 6 * 7 * LICENSE: This source file is subject to version 3.01 of the PHP license 8 * that is available through the world-wide-web at the following URI: 9 * http://www.php.net/license/3_01.txt. If you did not receive a copy of 10 * the PHP License and are unable to obtain it through the web, please 11 * send a note to license@php.net so we can mail you a copy immediately. 12 * 13 * @category Database 14 * @package DB_DataObject 15 * @author Alan Knowles <alan@akbkhome.com> 16 * @copyright 1997-2006 The PHP Group 17 * @license http://www.php.net/license/3_01.txt PHP License 3.01 18 * @version : FIXME 19 * @link http://pear.php.net/package/DB_DataObject 20 */ 21 22 23/** 24 * 25 * Example of how this could be used.. 26 * 27 * The lind method are now in here. 28 * 29 * Currenly only supports existing methods, and new 'link()' method 30 * 31 */ 32 33 34/** 35 * Links class 36 * 37 * @package DB_DataObject 38 */ 39class DB_DataObject_Links 40{ 41 /** 42 * @property {DB_DataObject} do DataObject to apply this to. 43 */ 44 var $do = false; 45 46 47 /** 48 * @property {Array|String} load What to load, 'all' or an array of properties. (default all) 49 */ 50 var $load = 'all'; 51 /** 52 * @property {String|Boolean} scanf use part of column name as resulting 53 * property name. (default false) 54 */ 55 var $scanf = false; 56 /** 57 * @property {String|Boolean} printf use column name as sprintf for resulting property name.. 58 * (default %s_link if apply is true, otherwise it is %s) 59 */ 60 var $printf = false; 61 /** 62 * @property {Boolean} cached cache the result, so future queries will use cache rather 63 * than running the expensive sql query. 64 */ 65 var $cached = false; 66 /** 67 * @property {Boolean} apply apply the result to this object, (default true) 68 */ 69 var $apply = true; 70 71 72 //------------------------- RETURN ------------------------------------ 73 /** 74 * @property {Array} links key value associative array of links. 75 */ 76 var $links; 77 78 79 /** 80 * Constructor 81 * -- good ole style.. 82 * @param {DB_DataObject} do DataObject to apply to. 83 * @param {Array} cfg Configuration (basically properties of this object) 84 */ 85 86 function DB_DataObject_Links($do,$cfg= array()) 87 { 88 // check if do is set!!!? 89 $this->do = $do; 90 91 foreach($cfg as $k=>$v) { 92 $this->$k = $v; 93 } 94 95 96 } 97 98 /** 99 * return name from related object 100 * 101 * The relies on a <dbname>.links.ini file, unless you specify the arguments. 102 * 103 * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName') 104 * 105 * 106 * @param string $field|array either row or row.xxxxx or links spec. 107 * @param string|DB_DataObject $table (optional) name of table to look up value in 108 * @param string $link (optional) name of column in other table to match 109 * @author Tim White <tim@cyface.com> 110 * @access public 111 * @return mixed object on success false on failure or '0' when not linked 112 */ 113 function getLink($field, $table= false, $link='') 114 { 115 116 static $cache = array(); 117 118 // GUESS THE LINKED TABLE.. (if found - recursevly call self) 119 120 if ($table == false) { 121 122 123 $info = $this->linkInfo($field); 124 125 if ($info) { 126 return $this->getLink($field, $info[0], $link === false ? $info[1] : $link ); 127 } 128 129 // no links defined.. - use borked BC method... 130 // use the old _ method - this shouldnt happen if called via getLinks() 131 if (!($p = strpos($field, '_'))) { 132 return false; 133 } 134 $table = substr($field, 0, $p); 135 return $this->getLink($field, $table); 136 137 138 139 } 140 141 $tn = is_string($table) ? $table : $table->tableName(); 142 143 144 145 if (!isset($this->do->$field)) { 146 $this->do->raiseError("getLink: row not set $field", DB_DATAOBJECT_ERROR_NODATA); 147 return false; 148 } 149 150 // check to see if we know anything about this table.. 151 152 153 if (empty($this->do->$field) || $this->do->$field < 0) { 154 return 0; // no record. 155 } 156 157 if ($this->cached && isset($cache[$tn.':'. $link .':'. $this->do->$field])) { 158 return $cache[$tn.':'. $link .':'. $this->do->$field]; 159 } 160 161 $obj = is_string($table) ? $this->do->factory($tn) : $table;; 162 163 if (!is_a($obj,'DB_DataObject')) { 164 $this->do->raiseError( 165 "getLink:Could not find class for row $field, table $tn", 166 DB_DATAOBJECT_ERROR_INVALIDCONFIG); 167 return false; 168 } 169 // -1 or 0 -- no referenced record.. 170 171 $ret = false; 172 if ($link) { 173 174 if ($obj->get($link, $this->do->$field)) { 175 $ret = $obj; 176 } 177 178 179 // this really only happens when no link config is set (old BC stuff) 180 } else if ($obj->get($this->do->$field)) { 181 $ret= $obj; 182 183 } 184 if ($this->cached) { 185 $cache[$tn.':'. $link .':'. $this->do->$field] = $ret; 186 } 187 return $ret; 188 189 } 190 /** 191 * get link information for a field or field specification 192 * 193 * alll link (and join methods accept the 'link' info ) in various ways 194 * string : 'field' = which field to get (uses ???.links.ini to work out what) 195 * array(2) : 'field', 'table:remote_col' << just like the links.ini def. 196 * array(3) : 'field', $dataobject, 'remote_col' (handy for joinAdd to do nested joins.) 197 * 198 * @param string|array $field or link spec to use. 199 * @return (false|array) array of dataobject and linked field or false. 200 * 201 * 202 */ 203 204 function linkInfo($field) 205 { 206 207 if (is_array($field)) { 208 if (count($field) == 3) { 209 // array with 3 args: 210 // local_col , dataobject, remote_col 211 return array( 212 $field[1], 213 $field[2], 214 $field[0] 215 ); 216 217 } 218 list($table,$link) = explode(':', $field[1]); 219 220 return array( 221 $this->do->factory($table), 222 $link, 223 $field[0] 224 ); 225 226 } 227 // work out the link.. (classic way) 228 229 $links = $this->do->links(); 230 231 if (empty($links) || !is_array($links)) { 232 233 return false; 234 } 235 236 237 if (!isset($links[$field])) { 238 239 return false; 240 } 241 list($table,$link) = explode(':', $links[$field]); 242 243 244 //??? needed??? 245 if ($p = strpos($field,".")) { 246 $field = substr($field,0,$p); 247 } 248 249 return array( 250 $this->do->factory($table), 251 $link, 252 $field 253 ); 254 255 256 257 258 } 259 260 261 262 /** 263 * a generic geter/setter provider.. 264 * 265 * provides a generic getter setter for the referenced object 266 * eg. 267 * $link->link('company_id') returns getLink for the object 268 * if nothing is linked (it will return an empty dataObject) 269 * $link->link('company_id', array(1)) - just sets the 270 * 271 * also array as the field speck supports 272 * $link->link(array('company_id', 'company:id')) 273 * 274 * 275 * @param string|array $field the field to fetch or link spec. 276 * @params array $args the arguments sent to the getter setter 277 * @return mixed true of false on set, the object on getter. 278 * 279 */ 280 function link($field, $args = array()) 281 { 282 $info = $this->linkInfo($field); 283 284 if (!$info) { 285 $this->do->raiseError( 286 "getLink:Could not find link for row $field", 287 DB_DATAOBJECT_ERROR_INVALIDCONFIG); 288 return false; 289 } 290 $field = $info[2]; 291 292 293 if (empty($args)) { // either an empty array or really empty.... 294 295 if (!isset($this->do->$field)) { 296 return $info[0]; // empty dataobject. 297 } 298 299 $ret = $this->getLink($field); 300 // nothing linked -- return new object.. 301 return ($ret === 0) ? $info[0] : $ret; 302 303 } 304 $assign = is_array($args) ? $args[0] : $args; 305 306 // otherwise it's a set call.. 307 if (!is_a($assign , 'DB_DataObject')) { 308 309 if (is_numeric($assign) && is_integer($assign * 1)) { 310 if ($assign > 0) { 311 312 if (!$info) { 313 return false; 314 } 315 // check that record exists.. 316 if (!$info[0]->get($info[1], $assign )) { 317 return false; 318 } 319 320 } 321 322 $this->do->$field = $assign ; 323 return true; 324 } 325 326 return false; 327 } 328 329 // otherwise we are assigning it ... 330 331 $this->do->$field = $assign->{$info[1]}; 332 return true; 333 334 335 } 336 /** 337 * load related objects 338 * 339 * Generally not recommended to use this. 340 * The generator should support creating getter_setter methods which are better suited. 341 * 342 * Relies on <dbname>.links.ini 343 * 344 * Sets properties on the calling dataobject you can change what 345 * object vars the links are stored in by changeing the format parameter 346 * 347 * 348 * @param string format (default _%s) where %s is the table name. 349 * @author Tim White <tim@cyface.com> 350 * @access public 351 * @return boolean , true on success 352 */ 353 354 function applyLinks($format = '_%s') 355 { 356 357 // get table will load the options. 358 if ($this->do->_link_loaded) { 359 return true; 360 } 361 362 $this->do->_link_loaded = false; 363 $cols = $this->do->table(); 364 $links = $this->do->links(); 365 366 $loaded = array(); 367 368 if ($links) { 369 foreach($links as $key => $match) { 370 list($table,$link) = explode(':', $match); 371 $k = sprintf($format, str_replace('.', '_', $key)); 372 // makes sure that '.' is the end of the key; 373 if ($p = strpos($key,'.')) { 374 $key = substr($key, 0, $p); 375 } 376 377 $this->do->$k = $this->getLink($key, $table, $link); 378 379 if (is_object($this->do->$k)) { 380 $loaded[] = $k; 381 } 382 } 383 $this->do->_link_loaded = $loaded; 384 return true; 385 } 386 // this is the autonaming stuff.. 387 // it sends the column name down to getLink and lets that sort it out.. 388 // if there is a links file then it is not used! 389 // IT IS DEPRECATED!!!! - DO NOT USE 390 if (!is_null($links)) { 391 return false; 392 } 393 394 395 foreach (array_keys($cols) as $key) { 396 if (!($p = strpos($key, '_'))) { 397 continue; 398 } 399 // does the table exist. 400 $k =sprintf($format, $key); 401 $this->do->$k = $this->getLink($key); 402 if (is_object($this->do->$k)) { 403 $loaded[] = $k; 404 } 405 } 406 $this->do->_link_loaded = $loaded; 407 return true; 408 } 409 410 /** 411 * getLinkArray 412 * Fetch an array of related objects. This should be used in conjunction with a 413 * <dbname>.links.ini file configuration (see the introduction on linking for details on this). 414 * 415 * You may also use this with all parameters to specify, the column and related table. 416 * 417 * @access public 418 * @param string $field- either column or column.xxxxx 419 * @param string $table (optional) name of table to look up value in 420 * @param string $fkey (optional) fetchall key see DB_DataObject::fetchAll() 421 * @param string $fval (optional)fetchall val DB_DataObject::fetchAll() 422 * @param string $fval (optional) fetchall method DB_DataObject::fetchAll() 423 * @return array - array of results (empty array on failure) 424 * 425 * Example - Getting the related objects 426 * 427 * $person = new DataObjects_Person; 428 * $person->get(12); 429 * $children = $person->getLinkArray('children'); 430 * 431 * echo 'There are ', count($children), ' descendant(s):<br />'; 432 * foreach ($children as $child) { 433 * echo $child->name, '<br />'; 434 * } 435 * 436 */ 437 function getLinkArray($field, $table = null, $fkey = false, $fval = false, $fmethod = false) 438 { 439 440 $ret = array(); 441 if (!$table) { 442 443 444 $links = $this->do->links(); 445 446 if (is_array($links)) { 447 if (!isset($links[$field])) { 448 // failed.. 449 return $ret; 450 } 451 list($table,$link) = explode(':',$links[$field]); 452 return $this->getLinkArray($field,$table); 453 } 454 if (!($p = strpos($field,'_'))) { 455 return $ret; 456 } 457 return $this->getLinkArray($field,substr($field,0,$p)); 458 459 460 } 461 462 $c = $this->do->factory($table); 463 464 if (!is_object($c) || !is_a($c,'DB_DataObject')) { 465 $this->do->raiseError( 466 "getLinkArray:Could not find class for row $field, table $table", 467 DB_DATAOBJECT_ERROR_INVALIDCONFIG 468 ); 469 return $ret; 470 } 471 472 // if the user defined method list exists - use it... 473 if (method_exists($c, 'listFind')) { 474 $c->listFind($this->id); 475 while ($c->fetch()) { 476 $ret[] = clone($c); 477 } 478 return $ret; 479 } 480 return $c->fetchAll($fkey, $fval, $fmethod); 481 482 483 } 484 485}