/** * Get mapper value * * @param string * @return string */ protected function value($name, $default = null) { return $this->mapper && $this->mapper->exists($name) ? $this->mapper->get($name) : $this->get($name, $default); }
/** * Retrieve contents of key * @return mixed * @param string $key * @param bool $raw */ function &get($key, $raw = false) { // handle virtual fields if (isset($this->vFields[$key])) { $out = is_callable($this->vFields[$key]) ? call_user_func($this->vFields[$key], $this) : $this->vFields[$key]; return $out; } $fields = $this->fieldConf; $id = $this->primary; if ($key == '_id' && $this->dbsType == 'sql') { $key = $id; } if ($this->whitelist && !in_array($key, $this->whitelist)) { $out = null; return $out; } if ($raw) { $out = $this->exists($key) ? $this->mapper->{$key} : NULL; return $out; } if (!empty($fields) && isset($fields[$key]) && is_array($fields[$key]) && !array_key_exists($key, $this->fieldsCache)) { // load relations if (isset($fields[$key]['belongs-to-one'])) { // one-to-X, bidirectional, direct way if (!$this->exists($key) || is_null($this->mapper->{$key})) { $this->fieldsCache[$key] = null; } else { // get config for this field $relConf = $fields[$key]['belongs-to-one']; // fetch related model $rel = $this->getRelFromConf($relConf, $key); // am i part of a result collection? if ($cx = $this->getCollection()) { // does the collection has cached results for this key? if (!$cx->hasRelSet($key)) { // build the cache, find all values of current key $relKeys = array_unique($cx->getAll($key, true)); // find related models $crit = array($relConf[1] . ' IN ?', $relKeys); $relSet = $rel->find($this->mergeWithRelFilter($key, $crit), $this->getRelFilterOption($key), $this->_ttl); // cache relSet, sorted by ID $cx->setRelSet($key, $relSet ? $relSet->getBy($relConf[1]) : NULL); } // get a subset of the preloaded set $result = $cx->getSubset($key, (string) $this->get($key, true)); $this->fieldsCache[$key] = $result ? $result[0] : NULL; } else { $crit = array($relConf[1] . ' = ?', $this->get($key, true)); $crit = $this->mergeWithRelFilter($key, $crit); $this->fieldsCache[$key] = $rel->findone($crit, $this->getRelFilterOption($key), $this->_ttl); } } } elseif (($type = isset($fields[$key]['has-one'])) || isset($fields[$key]['has-many'])) { $type = $type ? 'has-one' : 'has-many'; $fromConf = $fields[$key][$type]; if (!is_array($fromConf)) { trigger_error(sprintf(self::E_REL_CONF_INC, $key)); } $rel = $this->getRelInstance($fromConf[0], null, $key, true); $relFieldConf = $rel->getFieldConfiguration(); $relType = key($relFieldConf[$fromConf[1]]); // one-to-*, bidirectional, inverse way if ($relType == 'belongs-to-one') { $toConf = $relFieldConf[$fromConf[1]]['belongs-to-one']; if (!is_array($toConf)) { $toConf = array($toConf, $id); } if ($toConf[1] != $id && (!$this->exists($toConf[1]) || is_null($this->mapper->get($toConf[1])))) { $this->fieldsCache[$key] = null; } elseif ($cx = $this->getCollection()) { // part of a result set if (!$cx->hasRelSet($key)) { // emit eager loading $relKeys = $cx->getAll($toConf[1], true); $crit = array($fromConf[1] . ' IN ?', $relKeys); $relSet = $rel->find($this->mergeWithRelFilter($key, $crit), $this->getRelFilterOption($key), $this->_ttl); $cx->setRelSet($key, $relSet ? $relSet->getBy($fromConf[1], true) : NULL); } $result = $cx->getSubset($key, array($this->get($toConf[1]))); $this->fieldsCache[$key] = $result ? $type == 'has-one' ? $result[0][0] : CortexCollection::factory($result[0]) : NULL; } else { $crit = array($fromConf[1] . ' = ?', $this->get($toConf[1], true)); $crit = $this->mergeWithRelFilter($key, $crit); $opt = $this->getRelFilterOption($key); $this->fieldsCache[$key] = $type == 'has-one' ? $rel->findone($crit, $opt, $this->_ttl) : $rel->find($crit, $opt, $this->_ttl) ?: NULL; } } elseif ($relType == 'has-many') { $toConf = $relFieldConf[$fromConf[1]]['has-many']; $mmTable = $this->mmTable($fromConf, $key, $toConf); // create mm table mapper if (!$this->get($id, true)) { $this->fieldsCache[$key] = null; return $this->fieldsCache[$key]; } $id = $toConf['relPK']; $rel = $this->getRelInstance(null, array('db' => $this->db, 'table' => $mmTable)); if ($cx = $this->getCollection()) { if (!$cx->hasRelSet($key)) { // get IDs of all results $relKeys = $cx->getAll($id, true); // get all pivot IDs $mmRes = $rel->find(array($fromConf['relField'] . ' IN ?', $relKeys), null, $this->_ttl); if (!$mmRes) { $cx->setRelSet($key, NULL); } else { $pivotRel = array(); $pivotKeys = array(); foreach ($mmRes as $model) { $val = $model->get($key, true); $pivotRel[(string) $model->get($fromConf['relField'])][] = $val; $pivotKeys[] = $val; } // cache pivot keys $cx->setRelSet($key . '_pivot', $pivotRel); // preload all rels $pivotKeys = array_unique($pivotKeys); $fRel = $this->getRelInstance($fromConf[0], null, $key, true); $crit = array($toConf['relPK'] . ' IN ?', $pivotKeys); $relSet = $fRel->find($this->mergeWithRelFilter($key, $crit), $this->getRelFilterOption($key), $this->_ttl); $cx->setRelSet($key, $relSet ? $relSet->getBy($id) : NULL); unset($fRel); } } // fetch subset from preloaded rels using cached pivot keys $fkeys = $cx->getSubset($key . '_pivot', array($this->get($id))); $this->fieldsCache[$key] = $fkeys ? CortexCollection::factory($cx->getSubset($key, $fkeys[0])) : NULL; } else { // find foreign keys $results = $rel->find(array($fromConf['relField'] . ' = ?', $this->get($fromConf['relPK'], true)), null, $this->_ttl); if (!$results) { $this->fieldsCache[$key] = NULL; } else { $fkeys = $results->getAll($key, true); // create foreign table mapper unset($rel); $rel = $this->getRelInstance($fromConf[0], null, $key, true); // load foreign models $filter = array($toConf['relPK'] . ' IN ?', $fkeys); $filter = $this->mergeWithRelFilter($key, $filter); $this->fieldsCache[$key] = $rel->find($filter, $this->getRelFilterOption($key), $this->_ttl); } } } } elseif (isset($fields[$key]['belongs-to-many'])) { // many-to-many, unidirectional $fields[$key]['type'] = self::DT_JSON; $result = !$this->exists($key) ? null : $this->mapper->get($key); if ($this->dbsType == 'sql') { $result = json_decode($result, true); } if (!is_array($result)) { $this->fieldsCache[$key] = $result; } else { // create foreign table mapper $relConf = $fields[$key]['belongs-to-many']; $rel = $this->getRelFromConf($relConf, $key); $fkeys = array(); foreach ($result as $el) { $fkeys[] = is_int($el) || ctype_digit($el) ? (int) $el : (string) $el; } // if part of a result set if ($cx = $this->getCollection()) { if (!$cx->hasRelSet($key)) { // find all keys $relKeys = $cx->getAll($key, true); if ($this->dbsType == 'sql') { foreach ($relKeys as &$val) { $val = substr($val, 1, -1); unset($val); } $relKeys = json_decode('[' . implode(',', $relKeys) . ']'); } else { $relKeys = call_user_func_array('array_merge', $relKeys); } // get related models $crit = array($relConf[1] . ' IN ?', array_unique($relKeys)); $relSet = $rel->find($this->mergeWithRelFilter($key, $crit), $this->getRelFilterOption($key), $this->_ttl); // cache relSet, sorted by ID $cx->setRelSet($key, $relSet ? $relSet->getBy($relConf[1]) : NULL); } // get a subset of the preloaded set $this->fieldsCache[$key] = CortexCollection::factory($cx->getSubset($key, $fkeys)); } else { // load foreign models $filter = array($relConf[1] . ' IN ?', $fkeys); $filter = $this->mergeWithRelFilter($key, $filter); $this->fieldsCache[$key] = $rel->find($filter, $this->getRelFilterOption($key), $this->_ttl); } } } elseif (isset($fields[$key]['type'])) { if ($this->dbsType == 'sql') { if ($fields[$key]['type'] == self::DT_SERIALIZED) { $this->fieldsCache[$key] = unserialize($this->mapper->{$key}); } elseif ($fields[$key]['type'] == self::DT_JSON) { $this->fieldsCache[$key] = json_decode($this->mapper->{$key}, true); } } if ($this->exists($key) && preg_match('/BOOL/i', $fields[$key]['type'])) { $this->fieldsCache[$key] = (bool) $this->mapper->{$key}; } } } // fetch cached value, if existing $val = array_key_exists($key, $this->fieldsCache) ? $this->fieldsCache[$key] : ($this->exists($key) ? $this->mapper->{$key} : null); if ($this->dbsType == 'mongo' && $val instanceof \MongoId) { // conversion to string makes further processing in template, etc. much easier $val = (string) $val; } // custom getter $out = $this->emit('get_' . $key, $val); return $out; }
/** * Get current field value * @return mixed */ protected function getValue() { return $this->cursor ? $this->map && $this->map->exists($this->cursor) ? $this->map->get($this->cursor) : (isset($this->lookup[$this->cursor]) ? $this->lookup[$this->cursor] : null) : null; }