/** * Get related objects from the database * * Supports the following signatures: * * - getRelated( object|array $source , string $relationName ) * - getRelated( object|array $source , string $relationName , $query ) * - getRelated( object|array $source , string $relationName , Meta $meta ) * - getRelated( object|array $source , string $relationName , $query, Meta $meta ) * * @return object[] */ public function getRelated($source, $relationName, $query = null, $meta = null) { if (!$source) { return; } $sourceIsArray = false; $test = $source; if (is_array($test) || $test instanceof \Traversable) { $sourceIsArray = true; $test = $test[0]; } if (!$meta) { if ($query instanceof Meta) { list($meta, $query) = [$query, null]; } else { $meta = $this->mapper->getMeta(get_class($test)); } } else { $meta = $meta instanceof Meta ? $meta : $this->mapper->getMeta($meta); } if (!isset($meta->relations[$relationName])) { throw new Exception("Unknown relation {$relationName} on {$meta->class}"); } $relation = $meta->relations[$relationName]; $relator = $this->getRelator($relation); if ($query) { // need to support both Query\Criteria and Query\Select // this is a cheeky hack - the API doesn't declare support for // Select in Relators because it carries promises of things like // 'fields' and whatnot that we'll never be able to satisfy. // That whole hierarchy needs to be cleaned // up into a bunch of traits so we can have RelatorCriteria or something. $query = $query instanceof Query\Criteria ? $query : Query\Select::fromParamArgs([$query]); $stack = $query->stack; } else { $stack = []; } $stack[$meta->class] = true; if ($sourceIsArray) { return $relator->getRelatedForList($meta, $source, $relation, $query ?: null, $stack); } else { return $relator->getRelated($meta, $source, $relation, $query ?: null, $stack); } }
protected function buildQuery($index, $relatedMeta, $viaMeta, $sourceToViaOn, $viaToDestOn, $criteria) { $viaFields = $viaMeta->fields; $relatedFields = $relatedMeta->fields; $query = new Query\Select(); list($query->where, $query->params) = $this->buildRelatedClause($index, 't2'); if ($criteria instanceof Query\Select) { $query->page = $criteria->page; $query->limit = $criteria->limit; $query->args = $criteria->args; $query->offset = $criteria->offset; $query->order = $criteria->order; $query->forUpdate = $criteria->forUpdate; } $queryFields = $query->buildFields($relatedMeta, 't1'); $sourcePkFields = array(); foreach ($sourceToViaOn as $l => $r) { $field = $viaFields[$r]; $sourcePkFields[] = $field['name']; } $joinOn = array(); foreach ($viaToDestOn as $l => $r) { $joinOn[] = 't2.`' . $viaFields[$l]['name'] . '` = t1.`' . $relatedFields[$r]['name'] . '`'; } $joinOn = implode(' AND ', $joinOn); list($where, $params, $props) = $query->buildClause(null); if ($criteria) { list($cWhere, $cParams, $cProps) = $criteria->buildClause($relatedMeta); if ($cWhere) { $params = array_merge($cParams, $params); $props = array_merge($props, $cProps); $where .= ' AND (' . $cWhere . ')'; } } $order = $query->buildOrder($relatedMeta, 't1'); list($limit, $offset) = $query->getLimitOffset(); $vt = ($viaMeta->schema ? "`{$viaMeta->schema}`." : null) . "`{$viaMeta->table}`"; $rt = ($relatedMeta->schema ? "`{$relatedMeta->schema}`." : null) . "`{$relatedMeta->table}`"; $sql = "\n SELECT \n {$queryFields}, t2." . '`' . implode('`, t2.`', $sourcePkFields) . '`' . "\n FROM\n {$vt} t2\n INNER JOIN\n {$rt} t1\n ON ({$joinOn})\n WHERE {$where} " . ($order ? "ORDER BY {$order} " : '') . ' ' . ($limit ? "LIMIT " . (int) $limit . " " : '') . ' ' . ($offset ? "OFFSET " . (int) $offset . " " : '') . ' ' . ($query->forUpdate ? 'FOR UPDATE' : ''); return array($sql, $params, $sourcePkFields, $props); }
/** * @covers Amiss\Sql\Query\Select::buildOrder */ public function testBuildOrderFromArrayWithIncompleteMeta() { $criteria = new Query\Select(); $criteria->order = array('foo', 'bar'); $meta = new \Amiss\Meta('stdClass', array('table' => 'std_class', 'fields' => array('foo' => array('name' => 'foo_field')))); $fields = $criteria->buildOrder($meta); $this->assertEquals('`foo_field`, `bar`', $fields); }