/** * {@inheritDoc} */ public function open(array $data, Query\ReadSelection $selection) { $additional = $selection->getAdditionalFields(); if (empty($additional)) { return RecordBuilder::open($this, $data, []); } $virtual = array(); $subrecords = array(); foreach ($data as $field => $value) { if (isset($additional[$field])) { if (isset($additional[$field]['record'])) { $record = $additional[$field]['record']; if (!isset($subrecords[$record])) { $subrecords[$record] = ['model' => $additional[$field]['model'], 'null' => true, 'data' => []]; } $subrecords[$record]['data'][$additional[$field]['recordField']] = $value; if (isset($value)) { $subrecords[$record]['null'] = false; } } else { $virtual[$field] = $value; } unset($data[$field]); } } foreach ($subrecords as $field => $record) { if ($record['null']) { $virtual[$field] = null; } else { $virtual[$field] = RecordBuilder::open($record['model'], $record['data']); } } return RecordBuilder::open($this, $data, $virtual); }
/** * {@inheritdoc} */ public function readSelection(ReadSelection $selection) { // TODO: distinct and additionalFields $data = $this->getData(); if (count($selection->getJoins()) > 0) { throw new \Exception('unsupported operation'); } $predicate = $selection->getPredicate(); if (isset($predicate)) { $data = new PredicateArray($data, $predicate); } $grouping = $selection->getGrouping(); if (count($grouping)) { $data = self::sortAll($data, array_map(function ($column) { return [$column, false]; }, $grouping)); $previous = null; $data = array_filter($data, function ($record) use($grouping, &$previous) { $group = array_intersect_key($record, array_flip($grouping)); if (isset($previous) and $group == $previous) { return false; } $previous = $group; return true; }); $predicate = $selection->getGroupPredicate(); if (isset($predicate)) { $data = new PredicateArray($data, $predicate); } } $data = self::sortAll($data, $selection->getOrdering()); $limit = $selection->getLimit(); $offset = $selection->getOffset(); if (isset($limit)) { $data = array_slice($data, $offset, $limit); } $projection = $selection->getProjection(); if (count($projection)) { $projected = array(); foreach ($data as $key => $record) { $recordData = array(); $i = 0; foreach ($projection as $field) { if (isset($field['alias'])) { $alias = $field['alias']; } else { $alias = 'expr#' . $i++; } $recordData[$alias] = $field['expression']->__invoke($record); } $projected[] = $recordData; } $data = $projected; } if ($data instanceof \Iterator) { return $data; } return new \ArrayIterator($data); }
/** * {@inheritdoc} */ public function read(ReadSelection $selection) { $data = $this->getData(); if (count($selection->getJoins()) > 0) { throw new \Exception('unsupported operation'); } $predicate = $selection->getPredicate(); $data = new PredicateArray($data, $predicate); $grouping = $selection->getGrouping(); if (count($grouping)) { $data = self::sortAll($data, $grouping); $predicate = $selection->getGroupPredicate(); if (isset($predicate)) { $data = new PredicateArray($data, $predicate); } } $data = self::sortAll($data, $selection->getOrdering()); $limit = $selection->getLimit(); $offset = $selection->getOffset(); if (isset($limit)) { $data = array_slice($data, $offset, $limit); } // TODO: implement projection return $data; }
/** * Convert a read selection to an SQL query. * * @param ReadSelectionBuilder $selection * Read selection. * @param string|null $projection * Projection override. * @return string SQL query. */ private function convertReadSelection(ReadSelection $selection, $projection = null) { $sqlString = 'SELECT '; $alias = $selection->getAlias(); if ($selection->isDistinct()) { $sqlString .= 'DISTINCT '; } if (isset($projection)) { $sqlString .= $projection; } elseif (count($selection->getProjection())) { $fields = $selection->getProjection(); array_walk($fields, array($this, 'getColumnList')); $sqlString .= implode(', ', $fields); } else { if (isset($alias)) { $sqlString .= $alias . '.*'; } else { $sqlString .= $this->owner->quoteModel($this->name) . '.*'; } $additional = $selection->getAdditionalFields(); if (count($additional)) { array_walk($additional, array($this, 'getColumnList')); $sqlString .= ', ' . implode(', ', $additional); } } $sqlString .= ' FROM ' . $this->owner->quoteModel($this->name); if (isset($alias)) { $sqlString .= ' AS ' . $alias; } // if (!empty($selection->sources)) { // foreach ($selection->sources as $source) { // if (is_string($source['source'])) { // $table = $source['source']; // } elseif ($source['source'] instanceof SqlTable) { // $table = $source['source']->name; // } else { // continue; // } // $sqlString .= ', ' . $this->owner->quoteModel($table); // if (isset($source['alias'])) { // $sqlString .= ' AS ' . $source['alias']; // } // } // } $joins = $selection->getJoins(); if (count($joins)) { foreach ($joins as $join) { $joinSource = $join['source']->joinWith($this); if (!isset($joinSource)) { throw new InvalidTableException('Unable to join SqlTable with data source of type "' . get_class($join['source']) . '"'); } if ($joinSource->owner !== $this->owner) { throw new InvalidTableException('Unable to join SqlTable with table of different database'); } $table = $joinSource->name; $sqlString .= ' ' . $join['type'] . ' JOIN ' . $this->owner->quoteModel($table); if (isset($join['alias'])) { $sqlString .= ' AS ' . $join['alias']; } if (isset($join['condition'])) { $sqlString .= ' ON ' . $join['condition']->toString($this->owner); } } } if ($selection->getPredicate() !== null) { $sqlString .= ' WHERE ' . $selection->getPredicate()->toString($this->owner); } $grouping = $selection->getGrouping(); if (count($grouping)) { $columns = array(); foreach ($grouping as $column) { $columns[] = $this->escapeQuery($column); } $sqlString .= ' GROUP BY ' . implode(', ', $columns); $predicate = $selection->getGroupPredicate(); if (isset($predicate)) { $sqlString .= ' HAVING ' . $predicate->toString($this->owner); } } $ordering = $selection->getOrdering(); if (count($ordering)) { $columns = array(); foreach ($ordering as $orderBy) { $columns[] = $this->escapeQuery($orderBy[0]) . ($orderBy[1] ? ' DESC' : ' ASC'); } $sqlString .= ' ORDER BY ' . implode(', ', $columns); } $limit = $selection->getLimit(); if (isset($limit)) { $sqlString .= ' ' . $this->owner->sqlLimitOffset($limit, $selection->getOffset()); } return $sqlString; }