/** * 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]; }