* @copyright 1997-2006 The PHP Group * @license http://www.php.net/license/3_01.txt PHP License 3.01 * @version : FIXME * @link http://pear.php.net/package/DB_DataObject */ /** * * Example of how this could be used.. * * The lind method are now in here. * * Currenly only supports existing methods, and new 'link()' method * */ /** * Links class * * @package DB_DataObject */ class DB_DataObject_Links { /** * @property {DB_DataObject} do DataObject to apply this to. */ public $do = false; /** * @property {Array|String} load What to load, 'all' or an array of properties. (default all) */ public $load = 'all'; /** * @property {String|Boolean} scanf use part of column name as resulting * property name. (default false) */ public $scanf = false; /** * @property {String|Boolean} printf use column name as sprintf for resulting property name.. * (default %s_link if apply is true, otherwise it is %s) */ public $printf = false; /** * @property {Boolean} cached cache the result, so future queries will use cache rather * than running the expensive sql query. */ public $cached = false; /** * @property {Boolean} apply apply the result to this object, (default true) */ public $apply = true; //------------------------- RETURN ------------------------------------ /** * @property {Array} links key value associative array of links. */ public $links; /** * Constructor * -- good ole style.. * @param {DB_DataObject} do DataObject to apply to. * @param {Array} cfg Configuration (basically properties of this object) */ public function DB_DataObject_Links($do, $cfg= array()) { // check if do is set!!!? $this->do = $do; foreach ($cfg as $k=>$v) { $this->$k = $v; } } /** * return name from related object * * The relies on a .links.ini file, unless you specify the arguments. * * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName') * * * @param string $field|array either row or row.xxxxx or links spec. * @param string|DB_DataObject $table (optional) name of table to look up value in * @param string $link (optional) name of column in other table to match * @author Tim White * @access public * @return mixed object on success false on failure or '0' when not linked */ public function getLink($field, $table= false, $link='') { static $cache = array(); // GUESS THE LINKED TABLE.. (if found - recursevly call self) if ($table == false) { $info = $this->linkInfo($field); if ($info) { return $this->getLink($field, $info[0], $link === false ? $info[1] : $link); } // no links defined.. - use borked BC method... // use the old _ method - this shouldnt happen if called via getLinks() if (!($p = strpos($field, '_'))) { return false; } $table = substr($field, 0, $p); return $this->getLink($field, $table); } $tn = is_string($table) ? $table : $table->tableName(); if (!isset($this->do->$field)) { $this->do->raiseError("getLink: row not set $field", DB_DATAOBJECT_ERROR_NODATA); return false; } // check to see if we know anything about this table.. if (empty($this->do->$field) || $this->do->$field < 0) { return 0; // no record. } if ($this->cached && isset($cache[$tn.':'. $link .':'. $this->do->$field])) { return $cache[$tn.':'. $link .':'. $this->do->$field]; } $obj = is_string($table) ? $this->do->factory($tn) : $table; ; if (!is_a($obj, 'DB_DataObject')) { $this->do->raiseError( "getLink:Could not find class for row $field, table $tn", DB_DATAOBJECT_ERROR_INVALIDCONFIG ); return false; } // -1 or 0 -- no referenced record.. $ret = false; if ($link) { if ($obj->get($link, $this->do->$field)) { $ret = $obj; } // this really only happens when no link config is set (old BC stuff) } elseif ($obj->get($this->do->$field)) { $ret= $obj; } if ($this->cached) { $cache[$tn.':'. $link .':'. $this->do->$field] = $ret; } return $ret; } /** * get link information for a field or field specification * * alll link (and join methods accept the 'link' info ) in various ways * string : 'field' = which field to get (uses ???.links.ini to work out what) * array(2) : 'field', 'table:remote_col' << just like the links.ini def. * array(3) : 'field', $dataobject, 'remote_col' (handy for joinAdd to do nested joins.) * * @param string|array $field or link spec to use. * @return (false|array) array of dataobject and linked field or false. * * */ public function linkInfo($field) { if (is_array($field)) { if (count($field) == 3) { // array with 3 args: // local_col , dataobject, remote_col return array( $field[1], $field[2], $field[0] ); } list($table, $link) = explode(':', $field[1]); return array( $this->do->factory($table), $link, $field[0] ); } // work out the link.. (classic way) $links = $this->do->links(); if (empty($links) || !is_array($links)) { return false; } if (!isset($links[$field])) { return false; } list($table, $link) = explode(':', $links[$field]); //??? needed??? if ($p = strpos($field, ".")) { $field = substr($field, 0, $p); } return array( $this->do->factory($table), $link, $field ); } /** * a generic geter/setter provider.. * * provides a generic getter setter for the referenced object * eg. * $link->link('company_id') returns getLink for the object * if nothing is linked (it will return an empty dataObject) * $link->link('company_id', array(1)) - just sets the * * also array as the field speck supports * $link->link(array('company_id', 'company:id')) * * * @param string|array $field the field to fetch or link spec. * @params array $args the arguments sent to the getter setter * @return mixed true of false on set, the object on getter. * */ public function link($field, $args = array()) { $info = $this->linkInfo($field); if (!$info) { $this->do->raiseError( "getLink:Could not find link for row $field", DB_DATAOBJECT_ERROR_INVALIDCONFIG ); return false; } $field = $info[2]; if (empty($args)) { // either an empty array or really empty.... if (!isset($this->do->$field)) { return $info[0]; // empty dataobject. } $ret = $this->getLink($field); // nothing linked -- return new object.. return ($ret === 0) ? $info[0] : $ret; } $assign = is_array($args) ? $args[0] : $args; // otherwise it's a set call.. if (!is_a($assign, 'DB_DataObject')) { if (is_numeric($assign) && is_integer($assign * 1)) { if ($assign > 0) { if (!$info) { return false; } // check that record exists.. if (!$info[0]->get($info[1], $assign)) { return false; } } $this->do->$field = $assign ; return true; } return false; } // otherwise we are assigning it ... $this->do->$field = $assign->{$info[1]}; return true; } /** * load related objects * * Generally not recommended to use this. * The generator should support creating getter_setter methods which are better suited. * * Relies on .links.ini * * Sets properties on the calling dataobject you can change what * object vars the links are stored in by changeing the format parameter * * * @param string format (default _%s) where %s is the table name. * @author Tim White * @access public * @return boolean , true on success */ public function applyLinks($format = '_%s') { // get table will load the options. if ($this->do->_link_loaded) { return true; } $this->do->_link_loaded = false; $cols = $this->do->table(); $links = $this->do->links(); $loaded = array(); if ($links) { foreach ($links as $key => $match) { list($table, $link) = explode(':', $match); $k = sprintf($format, str_replace('.', '_', $key)); // makes sure that '.' is the end of the key; if ($p = strpos($key, '.')) { $key = substr($key, 0, $p); } $this->do->$k = $this->getLink($key, $table, $link); if (is_object($this->do->$k)) { $loaded[] = $k; } } $this->do->_link_loaded = $loaded; return true; } // this is the autonaming stuff.. // it sends the column name down to getLink and lets that sort it out.. // if there is a links file then it is not used! // IT IS DEPRECATED!!!! - DO NOT USE if (!is_null($links)) { return false; } foreach (array_keys($cols) as $key) { if (!($p = strpos($key, '_'))) { continue; } // does the table exist. $k =sprintf($format, $key); $this->do->$k = $this->getLink($key); if (is_object($this->do->$k)) { $loaded[] = $k; } } $this->do->_link_loaded = $loaded; return true; } /** * getLinkArray * Fetch an array of related objects. This should be used in conjunction with a * .links.ini file configuration (see the introduction on linking for details on this). * * You may also use this with all parameters to specify, the column and related table. * * @access public * @param string $field- either column or column.xxxxx * @param string $table (optional) name of table to look up value in * @param string $fkey (optional) fetchall key see DB_DataObject::fetchAll() * @param string $fval (optional)fetchall val DB_DataObject::fetchAll() * @param string $fval (optional) fetchall method DB_DataObject::fetchAll() * @return array - array of results (empty array on failure) * * Example - Getting the related objects * * $person = new DataObjects_Person; * $person->get(12); * $children = $person->getLinkArray('children'); * * echo 'There are ', count($children), ' descendant(s):
'; * foreach ($children as $child) { * echo $child->name, '
'; * } * */ public function getLinkArray($field, $table = null, $fkey = false, $fval = false, $fmethod = false) { $ret = array(); if (!$table) { $links = $this->do->links(); if (is_array($links)) { if (!isset($links[$field])) { // failed.. return $ret; } list($table, $link) = explode(':', $links[$field]); return $this->getLinkArray($field, $table); } if (!($p = strpos($field, '_'))) { return $ret; } return $this->getLinkArray($field, substr($field, 0, $p)); } $c = $this->do->factory($table); if (!is_object($c) || !is_a($c, 'DB_DataObject')) { $this->do->raiseError( "getLinkArray:Could not find class for row $field, table $table", DB_DATAOBJECT_ERROR_INVALIDCONFIG ); return $ret; } // if the user defined method list exists - use it... if (method_exists($c, 'listFind')) { $c->listFind($this->id); while ($c->fetch()) { $ret[] = clone($c); } return $ret; } return $c->fetchAll($fkey, $fval, $fmethod); } }