Links.php 14 KB

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