/**
* Fetch fields that haven't yet been loaded. Lazy-loaded fields
* and lazy-loaded relationships are handled this way. Once a
* field is retrieved, it is cached in the $_fields array so it
* doesn't need to be fetched again.
*
* @param string $field The name of the field to access.
*
* @return mixed The value of $field or null.
*/
public function __get($field)
{
// Honor any explicit getters.
$fieldMethod = 'get' . Horde_String::ucfirst($field);
// If an Rdo_Base subclass has a __call() method, is_callable
// returns true on every method name, so use method_exists
// instead.
if (method_exists($this, $fieldMethod)) {
return call_user_func(array($this, $fieldMethod));
}
if (isset($this->_fields[$field])) {
return $this->_fields[$field];
}
$mapper = $this->getMapper();
// Look for lazy fields first, then relationships.
if (in_array($field, $mapper->lazyFields)) {
// @TODO Support composite primary keys
$query = new Horde_Rdo_Query($mapper);
$query->setFields($field)->addTest($mapper->primaryKey, '=', $this->{$mapper->primaryKey});
list($sql, $params) = $query->getQuery();
$this->_fields[$field] = $mapper->adapter->selectValue($sql, $params);
return $this->_fields[$field];
} elseif (isset($mapper->lazyRelationships[$field])) {
$rel = $mapper->lazyRelationships[$field];
} else {
return null;
}
// Try to find the Mapper class for the object the
// relationship is with, and fail if we can't.
if (isset($rel['mapper'])) {
if ($mapper->factory) {
$m = $mapper->factory->create($rel['mapper']);
} else {
// @TODO - should be getting this instance from somewhere
// else external, and not passing the adapter along
// automatically.
$m = new $rel['mapper']($mapper->adapter);
}
} else {
$m = $mapper->tableToMapper($field);
if (is_null($m)) {
return null;
}
}
// Based on the kind of relationship, fetch the appropriate
// objects and fill the cache.
switch ($rel['type']) {
case Horde_Rdo::ONE_TO_ONE:
case Horde_Rdo::MANY_TO_ONE:
if (isset($rel['query'])) {
$query = $this->_fillPlaceholders($rel['query']);
$this->_fields[$field] = $m->findOne($query);
} elseif (!empty($this->{$rel['foreignKey']})) {
$this->_fields[$field] = $m->findOne($this->{$rel['foreignKey']});
if (empty($this->_fields[$field])) {
throw new Horde_Rdo_Exception('The referenced object with key ' . $this->{$rel['foreignKey']} . ' does not exist. Your data is inconsistent');
}
} else {
$this->_fields[$field] = null;
}
break;
case Horde_Rdo::ONE_TO_MANY:
$this->_fields[$field] = $m->find(array($rel['foreignKey'] => $this->{$rel['foreignKey']}));
break;
case Horde_Rdo::MANY_TO_MANY:
$key = $mapper->primaryKey;
$query = new Horde_Rdo_Query();
$on = isset($rel['on']) ? $rel['on'] : $m->primaryKey;
$query->addRelationship($field, array('mapper' => $mapper, 'table' => $rel['through'], 'type' => Horde_Rdo::MANY_TO_MANY, 'query' => array("{$m->table}.{$on}" => new Horde_Rdo_Query_Literal($rel['through'] . '.' . $on), $key => $this->{$key})));
$this->_fields[$field] = $m->find($query);
break;
}
return $this->_fields[$field];
}