Links.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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 Configuration (basically properties of this object)
  74. */
  75. public 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. public 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. ;
  129. if (!is_a($obj, 'DB_DataObject')) {
  130. $this->do->raiseError(
  131. "getLink:Could not find class for row $field, table $tn",
  132. DB_DATAOBJECT_ERROR_INVALIDCONFIG
  133. );
  134. return false;
  135. }
  136. // -1 or 0 -- no referenced record..
  137. $ret = false;
  138. if ($link) {
  139. if ($obj->get($link, $this->do->$field)) {
  140. $ret = $obj;
  141. }
  142. // this really only happens when no link config is set (old BC stuff)
  143. } elseif ($obj->get($this->do->$field)) {
  144. $ret= $obj;
  145. }
  146. if ($this->cached) {
  147. $cache[$tn.':'. $link .':'. $this->do->$field] = $ret;
  148. }
  149. return $ret;
  150. }
  151. /**
  152. * get link information for a field or field specification
  153. *
  154. * alll link (and join methods accept the 'link' info ) in various ways
  155. * string : 'field' = which field to get (uses ???.links.ini to work out what)
  156. * array(2) : 'field', 'table:remote_col' << just like the links.ini def.
  157. * array(3) : 'field', $dataobject, 'remote_col' (handy for joinAdd to do nested joins.)
  158. *
  159. * @param string|array $field or link spec to use.
  160. * @return (false|array) array of dataobject and linked field or false.
  161. *
  162. *
  163. */
  164. public function linkInfo($field)
  165. {
  166. if (is_array($field)) {
  167. if (count($field) == 3) {
  168. // array with 3 args:
  169. // local_col , dataobject, remote_col
  170. return array(
  171. $field[1],
  172. $field[2],
  173. $field[0]
  174. );
  175. }
  176. list($table, $link) = explode(':', $field[1]);
  177. return array(
  178. $this->do->factory($table),
  179. $link,
  180. $field[0]
  181. );
  182. }
  183. // work out the link.. (classic way)
  184. $links = $this->do->links();
  185. if (empty($links) || !is_array($links)) {
  186. return false;
  187. }
  188. if (!isset($links[$field])) {
  189. return false;
  190. }
  191. list($table, $link) = explode(':', $links[$field]);
  192. //??? needed???
  193. if ($p = strpos($field, ".")) {
  194. $field = substr($field, 0, $p);
  195. }
  196. return array(
  197. $this->do->factory($table),
  198. $link,
  199. $field
  200. );
  201. }
  202. /**
  203. * a generic geter/setter provider..
  204. *
  205. * provides a generic getter setter for the referenced object
  206. * eg.
  207. * $link->link('company_id') returns getLink for the object
  208. * if nothing is linked (it will return an empty dataObject)
  209. * $link->link('company_id', array(1)) - just sets the
  210. *
  211. * also array as the field speck supports
  212. * $link->link(array('company_id', 'company:id'))
  213. *
  214. *
  215. * @param string|array $field the field to fetch or link spec.
  216. * @params array $args the arguments sent to the getter setter
  217. * @return mixed true of false on set, the object on getter.
  218. *
  219. */
  220. public function link($field, $args = array())
  221. {
  222. $info = $this->linkInfo($field);
  223. if (!$info) {
  224. $this->do->raiseError(
  225. "getLink:Could not find link for row $field",
  226. DB_DATAOBJECT_ERROR_INVALIDCONFIG
  227. );
  228. return false;
  229. }
  230. $field = $info[2];
  231. if (empty($args)) { // either an empty array or really empty....
  232. if (!isset($this->do->$field)) {
  233. return $info[0]; // empty dataobject.
  234. }
  235. $ret = $this->getLink($field);
  236. // nothing linked -- return new object..
  237. return ($ret === 0) ? $info[0] : $ret;
  238. }
  239. $assign = is_array($args) ? $args[0] : $args;
  240. // otherwise it's a set call..
  241. if (!is_a($assign, 'DB_DataObject')) {
  242. if (is_numeric($assign) && is_integer($assign * 1)) {
  243. if ($assign > 0) {
  244. if (!$info) {
  245. return false;
  246. }
  247. // check that record exists..
  248. if (!$info[0]->get($info[1], $assign)) {
  249. return false;
  250. }
  251. }
  252. $this->do->$field = $assign ;
  253. return true;
  254. }
  255. return false;
  256. }
  257. // otherwise we are assigning it ...
  258. $this->do->$field = $assign->{$info[1]};
  259. return true;
  260. }
  261. /**
  262. * load related objects
  263. *
  264. * Generally not recommended to use this.
  265. * The generator should support creating getter_setter methods which are better suited.
  266. *
  267. * Relies on <dbname>.links.ini
  268. *
  269. * Sets properties on the calling dataobject you can change what
  270. * object vars the links are stored in by changeing the format parameter
  271. *
  272. *
  273. * @param string format (default _%s) where %s is the table name.
  274. * @author Tim White <tim@cyface.com>
  275. * @access public
  276. * @return boolean , true on success
  277. */
  278. public function applyLinks($format = '_%s')
  279. {
  280. // get table will load the options.
  281. if ($this->do->_link_loaded) {
  282. return true;
  283. }
  284. $this->do->_link_loaded = false;
  285. $cols = $this->do->table();
  286. $links = $this->do->links();
  287. $loaded = array();
  288. if ($links) {
  289. foreach ($links as $key => $match) {
  290. list($table, $link) = explode(':', $match);
  291. $k = sprintf($format, str_replace('.', '_', $key));
  292. // makes sure that '.' is the end of the key;
  293. if ($p = strpos($key, '.')) {
  294. $key = substr($key, 0, $p);
  295. }
  296. $this->do->$k = $this->getLink($key, $table, $link);
  297. if (is_object($this->do->$k)) {
  298. $loaded[] = $k;
  299. }
  300. }
  301. $this->do->_link_loaded = $loaded;
  302. return true;
  303. }
  304. // this is the autonaming stuff..
  305. // it sends the column name down to getLink and lets that sort it out..
  306. // if there is a links file then it is not used!
  307. // IT IS DEPRECATED!!!! - DO NOT USE
  308. if (!is_null($links)) {
  309. return false;
  310. }
  311. foreach (array_keys($cols) as $key) {
  312. if (!($p = strpos($key, '_'))) {
  313. continue;
  314. }
  315. // does the table exist.
  316. $k =sprintf($format, $key);
  317. $this->do->$k = $this->getLink($key);
  318. if (is_object($this->do->$k)) {
  319. $loaded[] = $k;
  320. }
  321. }
  322. $this->do->_link_loaded = $loaded;
  323. return true;
  324. }
  325. /**
  326. * getLinkArray
  327. * Fetch an array of related objects. This should be used in conjunction with a
  328. * <dbname>.links.ini file configuration (see the introduction on linking for details on this).
  329. *
  330. * You may also use this with all parameters to specify, the column and related table.
  331. *
  332. * @access public
  333. * @param string $field- either column or column.xxxxx
  334. * @param string $table (optional) name of table to look up value in
  335. * @param string $fkey (optional) fetchall key see DB_DataObject::fetchAll()
  336. * @param string $fval (optional)fetchall val DB_DataObject::fetchAll()
  337. * @param string $fval (optional) fetchall method DB_DataObject::fetchAll()
  338. * @return array - array of results (empty array on failure)
  339. *
  340. * Example - Getting the related objects
  341. *
  342. * $person = new DataObjects_Person;
  343. * $person->get(12);
  344. * $children = $person->getLinkArray('children');
  345. *
  346. * echo 'There are ', count($children), ' descendant(s):<br />';
  347. * foreach ($children as $child) {
  348. * echo $child->name, '<br />';
  349. * }
  350. *
  351. */
  352. public function getLinkArray($field, $table = null, $fkey = false, $fval = false, $fmethod = false)
  353. {
  354. $ret = array();
  355. if (!$table) {
  356. $links = $this->do->links();
  357. if (is_array($links)) {
  358. if (!isset($links[$field])) {
  359. // failed..
  360. return $ret;
  361. }
  362. list($table, $link) = explode(':', $links[$field]);
  363. return $this->getLinkArray($field, $table);
  364. }
  365. if (!($p = strpos($field, '_'))) {
  366. return $ret;
  367. }
  368. return $this->getLinkArray($field, substr($field, 0, $p));
  369. }
  370. $c = $this->do->factory($table);
  371. if (!is_object($c) || !is_a($c, 'DB_DataObject')) {
  372. $this->do->raiseError(
  373. "getLinkArray:Could not find class for row $field, table $table",
  374. DB_DATAOBJECT_ERROR_INVALIDCONFIG
  375. );
  376. return $ret;
  377. }
  378. // if the user defined method list exists - use it...
  379. if (method_exists($c, 'listFind')) {
  380. $c->listFind($this->id);
  381. while ($c->fetch()) {
  382. $ret[] = clone($c);
  383. }
  384. return $ret;
  385. }
  386. return $c->fetchAll($fkey, $fval, $fmethod);
  387. }
  388. }