/** * @param string $direction * @param object $object * @param integer $pageLimit * @return array */ protected function makePagingLink($direction, $object, $pageLimit) { $return = array('offset-dir' => $direction, 'limit' => $pageLimit, $this->offsetKey => $this->storage->serializeOffset($object, $this->sort)); if (isset($this->options['sortby'])) { $return['sortby'] = $this->options['sortby']; } return $return; }
public function merge(array $query, array $result) { $query = ObjectManager::splitFromRow($query, $this->queryKeys); if ($query === null) { // the queryKeys are either unset or null, and not indexable // TODO: what should happen here? return; } $this->result[$query] = $result; }
public function canAnswer(array $keys, array $options) { if (!parent::canAnswer($keys, $options)) { return false; } if (isset($options['sort'], $options['order'])) { return ObjectManager::makeArray($options['sort']) === $this->options['sort'] && strtoupper($options['order']) === $this->options['order']; } return true; }
public function fromStorageRow(array $row, $object = null) { $pk = ObjectManager::splitFromRow($row, $this->primaryKey); if ($pk === null) { throw new \InvalidArgumentException('Storage row has no pk'); } elseif (!isset($this->loaded[$pk])) { // unserialize the object return $this->loaded[$pk] = call_user_func($this->fromStorageRow, $row, $object); } elseif ($object === null) { // provide previously loaded object return $this->loaded[$pk]; } elseif ($object !== $this->loaded[$pk]) { // loaded object of this id is not same object $class = get_class($object); $id = json_encode($pk); throw new \InvalidArgumentException("Duplicate '{$class}' objects for id {$id}"); } else { // object was provided, load $row into $object // we already know $this->loaded[$pk] === $object return call_user_func($this->fromStorageRow, $row, $object); } }
/** * Calculates the DB updates to be performed to update data from $old to * $new. * * @param array $old * @param array $new * @return array * @throws DataModelException */ public function calcUpdates(array $old, array $new) { $changeSet = ObjectManager::calcUpdatesWithoutValidation($old, $new); foreach ($this->obsoleteUpdateColumns as $val) { // Need to use array_key_exists to check null value if (array_key_exists($val, $changeSet)) { unset($changeSet[$val]); } } if (is_array($this->allowedUpdateColumns)) { $extra = array_diff(array_keys($changeSet), $this->allowedUpdateColumns); if ($extra) { throw new DataModelException('Update not allowed on: ' . implode(', ', $extra), 'process-data'); } } return $changeSet; }
/** * {@inheritDoc} */ public function onAfterRemove($object, array $old, array $metadata) { $indexed = ObjectManager::splitFromRow($old, $this->indexed); if (!$indexed) { throw new DataModelException('Unindexable row: ' . FormatJson::encode($old), 'process-data'); } $this->removeFromIndex($indexed, $old); }
/** * Fetch a node and all its descendants. Children are returned in the * same order they were inserted. * * @param UUID|UUID[] $roots * @return array Multi-dimensional tree. The top level is a map from the uuid of a node * to attributes about that node. The top level contains not just the parents, but all nodes * within this tree. Within each node there is a 'children' key that contains a map from * the child uuid's to references back to the top level of this identity map. As such this * result can be read either as a list or a tree. * @throws DataModelException When invalid data is received from self::fetchSubtreeNodeList */ public function fetchSubtreeIdentityMap($roots) { $roots = ObjectManager::makeArray($roots); if (!$roots) { return array(); } $nodes = $this->fetchSubtreeNodeList($roots); if (!$nodes) { throw new DataModelException('subtree node list should have at least returned root: ' . $root, 'process-data'); } elseif (count($nodes) === 1) { $parentMap = $this->fetchParentMap(reset($nodes)); } else { $parentMap = $this->fetchParentMap(call_user_func_array('array_merge', $nodes)); } $identityMap = array(); foreach ($parentMap as $child => $parent) { if (!array_key_exists($child, $identityMap)) { $identityMap[$child] = array('children' => array()); } // Root nodes have no parent if ($parent !== null) { $identityMap[$parent->getAlphadecimal()]['children'][$child] =& $identityMap[$child]; } } foreach (array_keys($identityMap) as $parent) { ksort($identityMap[$parent]['children']); } return $identityMap; }
/** * Takes an array of rows going to/from the database/cache. Converts uuid and * things that look like uuids into the requested format. * * @param array $array * @param string $format * @return string[]|UUIDBlob[] Typically an array of strings. If required by the database when * $format === 'binary' uuid values will be represented as Blob objects. */ public static function convertUUIDs($array, $format = 'binary') { $array = ObjectManager::makeArray($array); foreach ($array as $key => $value) { if ($value instanceof UUIDBlob) { // database encoded binary value if ($format === 'alphadecimal') { $array[$key] = UUID::create($value->fetch())->getAlphadecimal(); } } elseif ($value instanceof UUID) { if ($format === 'binary') { $array[$key] = $value->getBinary(); } elseif ($format === 'alphadecimal') { $array[$key] = $value->getAlphadecimal(); } } elseif (is_string($value) && substr($key, -3) === '_id') { // things that look like uuids $len = strlen($value); if ($format === 'alphadecimal' && $len === self::BIN_LEN) { $array[$key] = UUID::create($value)->getAlphadecimal(); } elseif ($format === 'binary' && ($len >= self::MIN_ALNUM_LEN && $len <= self::ALNUM_LEN || $len === self::HEX_LEN)) { // Note that if a value is a binary string, but needs to be encoded // for the database, that is unhandled here. A patch is under // consideration to allow binary data to always be wrapped in a Blob // to clear up this inconsistency. $array[$key] = UUID::create($value)->getBinary(); } } } return $array; }
public function findMulti(array $queries, array $options = array()) { $keys = array_keys(reset($queries)); $pks = $this->getPrimaryKeyColumns(); if (count($keys) !== count($pks) || array_diff($keys, $pks)) { return $this->fallbackFindMulti($queries, $options); } $conds = array(); $dbr = $this->dbFactory->getDB(DB_SLAVE); foreach ($queries as $query) { $conds[] = $dbr->makeList($this->preprocessSqlArray($query), LIST_AND); } unset($query); $conds = $dbr->makeList($conds, LIST_OR); $result = array(); // options can be ignored for primary key search $res = $this->find(array(new RawSql($conds))); if (!$res) { return $result; } // create temp array with pk value (usually uuid) as key and full db row // as value $temp = new MultiDimArray(); foreach ($res as $val) { $val = UUID::convertUUIDs($val, 'alphadecimal'); $temp[ObjectManager::splitFromRow($val, $this->primaryKey)] = $val; } // build return value by mapping the database rows to the matching array // index in $queries foreach ($queries as $i => $val) { $val = UUID::convertUUIDs($val, 'alphadecimal'); $pk = ObjectManager::splitFromRow($val, $this->primaryKey); $result[$i][] = isset($temp[$pk]) ? $temp[$pk] : null; } return $result; }
/** * Gets the required updates. Any changes to External Store will be reflected in * the returned array. * * @param array $old Associative array mapping prior columns to old values * @param array $new Associative array mapping updated columns to new values * * @return array Validated change set as associative array, mapping columns to * change to their new values */ public function calcUpdates(array $old, array $new) { // First, see if there are any changes to content at all. // If not, processExternalStore will know not to insert a useless row for // unchanged content (if updating content is allowed). $unvalidatedChangeset = ObjectManager::calcUpdatesWithoutValidation($old, $new); // We check here so if it's not allowed, we don't insert a wasted External // Store entry, then throw an exception in the parent calcUpdates. if ($this->isUpdatingExistingRevisionContentAllowed()) { $unvalidatedChangeset = $this->processExternalStore($unvalidatedChangeset); } // The parent calcUpdates does the validation that we're not changing a non-allowed // field, regardless of whether explicitly passed in, or done by processExternalStore. $validatedChangeset = parent::calcUpdates(array(), $unvalidatedChangeset); return $validatedChangeset; }
/** * Returns a boolean true/false if the getMulti()-operation for the given * attributes has already been resolves and doesn't need to query any * outside cache/database. * Determining if a find() has not yet been resolved may be useful so that * additional data may be loaded at once. * * @param array $objectIds Ids to getMulti() * @return bool */ public function gotMulti(array $objectIds) { if (!$objectIds) { return true; } $primaryKey = $this->storage->getPrimaryKeyColumns(); $queries = array(); foreach ($objectIds as $id) { $query = array_combine($primaryKey, ObjectManager::makeArray($id)); $query = UUID::convertUUIDs($query, 'alphadecimal'); if (!$this->mapper->get($query)) { $queries[] = $query; } } if ($queries && $this->mapper instanceof Mapper\CachingObjectMapper) { return false; } return $this->foundMulti($queries); }