Skip to content

Commit

Permalink
RecordSet: Added management for relations (particularly hasMany), fix…
Browse files Browse the repository at this point in the history
…ed fetching of fields so

they properly reflect the fields that were pulled rather than the tables actual schema
Query: limit(false) now returns the object instance for chaining, groups can be removed using
group(false)
Database:  moved relationship management into the filter, finished hasMany relationship (for
fetching), added a post processing hack for queries with a hasmany relationship
Entity: Added depth support for recursively calling data so that numeric arrays (such as those
generated by hasMany) will have their contents parsed to arrays as well
Source: Added depth management so that you item can be called on a top level and still have full
coverage of the related model data
  • Loading branch information
unknown authored and nateabele committed Jun 11, 2011
1 parent 69429ef commit 2c09acf
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 86 deletions.
8 changes: 8 additions & 0 deletions data/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,14 @@ public function data($name = null) {
foreach($return as $key => $val){
if(is_callable(array($val, 'data'))) {
$return[$key] = $val->data();
}else if(is_array($val) && is_numeric(key($val))){
$r = array();
foreach($val as $k => $v){
if(is_callable(array($v, 'data'))) {
$r[$k] = $v->data();
}
}
$return[$key] = $r;
}
}
return $return;
Expand Down
13 changes: 13 additions & 0 deletions data/Source.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,19 @@ public function item($model, array $data = array(), array $options = array()) {
$class = isset($this->_classes[$type]) ? $this->_classes[$type] : $type;
unset($options['class']);

foreach($data as $key => $val) {
switch(true) {
case is_array($val) && !is_numeric(key($val)):
$data[$key] = $this->item($model, $val, $options);
break;
case is_array($val) && is_numeric(key($val)):
foreach($val as $k => $v) {
$data[$key][$k] = $this->item($model, $v, $options);
}
break;
}
}

return new $class(compact('model', 'data') + $options);
}
}
Expand Down
38 changes: 29 additions & 9 deletions data/collection/RecordSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,10 @@ protected function _populate($data = null, $key = null) {
return $this->close();
}
$key = null;
$index = false;
$offset = 0;
$recordMap = is_object($data) ? array($model => $data) : array();
$primaryModel = $this->_model;

if (!$recordMap) {
foreach ($this->_columns as $model => $fields) {
Expand All @@ -279,11 +281,31 @@ protected function _populate($data = null, $key = null) {
$record = array_combine($fields, array_slice($data, $offset, $fieldCount));

if ($model == $this->_model) {
$key = $model::key($record);
$recordMap[$this->_model] = $record;
$key = reset($model::key($record));
if (($index = array_search($key, $this->_index)) !== false){
$recordMap[$this->_model] = $this->_data[$index];
if(is_object($recordMap[$this->_model])) {
$recordMap[$this->_model] = $recordMap[$this->_model]->data();
}
}else {
$recordMap[$this->_model] = $record;
}
}else {
$record = $conn->item($model, $record, array('exists' => true));
$recordMap[$this->_model][$model::meta('name')] = $record;
$relation = $primaryModel::relations($model::meta('name') ?: $model);
$useArr = false;
switch($relation->type){
case 'hasMany':
$useArr = true;
break;
}

if($useArr === false){
$modelName = $model::meta('name');
$recordMap[$this->_model][$model::meta('name')] = $record;
}else{
$recordKey = reset($model::key($record));
$recordMap[$this->_model][$model::meta('name')][$recordKey] = $record;
}
}

$offset += $fieldCount;
Expand Down Expand Up @@ -321,15 +343,13 @@ protected function _columnMap() {
if(!$joins = $this->_query->join()) {
return $model::connection()->schema($this->_query, $this->_result, $this);
}
$model = $this->_query->model();

$model = $this->_model;
$map = $model::connection()->schema($this->_query, $this->_result, $this);

foreach($joins as $join) {
$model = $join->model();
$map += $model::connection()->schema($join, $this->_result, $this);
$map += $model::connection()->schema($this->_query, $join->model(), $join);
}

return $map;
}
}
Expand Down
5 changes: 5 additions & 0 deletions data/model/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public function limit($limit = null) {
return $this;
}else if($limit === false){
$this->_config['limit'] = null;
return $this;
}
return $this->_config['limit'];
}
Expand Down Expand Up @@ -313,6 +314,10 @@ public function group($group = null) {
$this->_config['group'] = $group;
return $this;
}
if($group === false){
$this->_config['group'] = null;
return $this;
}
return $this->_config['group'];
}

Expand Down
164 changes: 87 additions & 77 deletions data/source/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,88 +251,84 @@ public function read($query, array $options = array()) {
$defaults = array('return' => is_string($query) ? 'array' : 'item', 'schema' => array());
$options += $defaults;

$model = is_object($query) ? $query->model() : null;
$schema = !is_null($model) ? $model::schema() : array();

$addHostSubquery = false;
$conditionAssignments = array('root' => array());
$relations = is_array($options['relations']) ? $options['relations'] : array();
foreach($relations as $relation) {
foreach($relation->constraint as $key => $val){
if(strpos('.', $key) === false) {
continue;
}
list($model, $field) = explode('.', $val);
$conditions[] = $this->_processConditions($key,$val,$schema);
}
$relation->constraint = implode(' AND ', $conditions);

switch($relation->type) {
case 'hasOne':
case 'belongsTo':
$query->join($relation->to, new Query(array(
'model' => $relation->to,
'constraint' => $relation->constraint
)));
break;
case 'hasMany':
$query->join($relation->to, new Query(array(
'model' => $relation->to,
'constraint' => $relation->constraint
)));

if($query->limit()) {
$addHostSubquery = true;
}

$model = $relation->to;
$name = $model::meta('name');
$key = $model::key();
$query->conditions(array(
array($this->name($name . '.' . $key) => new Query(array(
'type' => 'read',
'fields' => array($key),
'model' => $model,
'conditions' => $relation->constraint
)))
));
break;
}
}


if($addHostSubquery){
$model = $query->model();
$name = $model::meta('name');
$key = $model::key();
$conditions = array();

$query->conditions(array(
array($this->name($name . '.' . $key) => new Query(array(
'type' => 'read',
'fields' => array($key),
'model' => $model,
'limit' => $query->limit()
)))
));

$query->limit(false);
}


var_dump($query);

return $this->_filter(__METHOD__, compact('query', 'options'), function($self, $params) {
$query = $params['query'];
$args = $params['options'];
$return = $args['return'];
unset($args['return']);

$model = is_object($query) ? $query->model() : null;
$schema = !is_null($model) ? $model::schema() : array();

$hasMany = array();

$addHostSubquery = false;
$relations = is_array($args['relations']) ? $args['relations'] : array();
foreach($relations as $relation) {
$conditions = array();
$constraint = is_array($relation->constraint) ? $relation->constraint : array();
foreach($constraint as $key => $val){
$conditions[] = $self->_processConditions($key,$val,$schema);
}
$constraint = implode(' AND ', $conditions);
switch($relation->type) {
case 'hasOne':
case 'belongsTo':
$query->join($relation->to, new Query(array(
'model' => $relation->to,
'constraint' => $constraint,
'type' => 'LEFT'
)));
break;
case 'hasMany':
$query->join($relation->to, new Query(array(
'model' => $relation->to,
'constraint' => $constraint,
'type' => 'LEFT'
)));

$toModel = $relation->to;
$hasMany[] = $toModel::meta('name');

if($query->limit()) {
$addHostSubquery = true;
}
break;
}
}

if($addHostSubquery){
$model = $query->model();
$name = $model::meta('name');
$key = $model::key();

$idOptions = array(
'relations' => false,
'group' => 'GROUP BY ' . $name . '.' . $key,
'fields' => array($name . '.' . $key),
'joins' => $query->joins()
) + $args;

$query->fields($idOptions['fields'], true)
->group($idOptions['group']);

$ids = $self->read($query, $idOptions);
$ids = $ids->data();
$ids = array_map(function($index) use ($key){
return $index[$key];
}, $ids);

$query->fields($args['fields'] ?: false, true)
->group($args['group'] ?: false)
->limit(false)
->conditions(array($name . '.' . $key => $ids));
}

if (is_string($query)) {
$sql = String::insert($query, $self->value($args));
} else {
$sql = $self->renderCommand($query);
}var_dump($sql);
}
$result = $self->invokeMethod('_execute', array($sql));

switch ($return) {
Expand All @@ -351,9 +347,18 @@ public function read($query, array $options = array()) {
}
return $records;
case 'item':
return $self->item($query->model(), array(), compact('query', 'result') + array(
'class' => 'set'
));
$return = $self->item($query->model(), array(), compact('query', 'result') +
array(
'class' => 'set',
));

//@todo - this is a hack, should have a more formal way to setup the data
if(count($hasMany)) {
count($return);
}


return $return;
}
});
}
Expand Down Expand Up @@ -479,7 +484,7 @@ public function renderCommand($type, $data = null, $context = null) {
* @return void
*/
public function schema($query, $resource = null, $context = null) {
$model = $query->model();
$model = is_scalar($resource) ? $resource : $query->model();
$fields = $query->fields();
$result = array();

Expand All @@ -493,6 +498,7 @@ public function schema($query, $resource = null, $context = null) {
$namespace = preg_replace('/\w+$/', '', $model);
$relations = $model ? $model::relations() : array();
$schema = $model::schema();
$modelName = $model::meta('name');

foreach ($fields as $scope => $field) {
switch (true) {
Expand All @@ -502,6 +508,10 @@ public function schema($query, $resource = null, $context = null) {
case (is_numeric($scope) && isset($schema[$field])):
$result[$model][] = $field;
break;
case is_numeric($scope) && preg_match('/^' . $modelName . '\./', $field):
list($modelName, $field) = explode('.', $field);
$result[$model][] = $field;
break;
case (is_numeric($scope) && isset($relations[$field])):
$scope = $field;
case (in_array($scope, $relations, true) && $field == '*'):
Expand Down Expand Up @@ -561,7 +571,7 @@ public function conditions($conditions, $context, array $options = array()) {
return ($options['prepend'] && !empty($result)) ? "WHERE {$result}" : $result;
}

protected function _processConditions($key, $value, $schema, $glue = 'AND'){
public function _processConditions($key, $value, $schema, $glue = 'AND'){
$constraintTypes = &$this->_constraintTypes;

switch (true) {
Expand Down

0 comments on commit 2c09acf

Please sign in to comment.