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