Links.php 14 KB

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