Fix `data\source\Database::read()` hack. #752

Merged
merged 2 commits into from Dec 19, 2012
View
@@ -98,7 +98,7 @@ class Query extends \lithium\core\Object {
protected $_alias = array();
/**
- * Map the generated aliases to their corresponding relation path
+ * Map beetween generated aliases and corresponding relation paths
*
* @see lithium\data\model\Query::alias()
*
@@ -107,7 +107,16 @@ class Query extends \lithium\core\Object {
protected $_paths = array();
/**
- * Map the generated aliases to their corresponding model
+ * Map beetween relation paths and their corresponding fieldname paths
+ *
+ * @see lithium\data\model\Query::alias()
+ *
+ * @var array
+ */
+ protected $_relationNames = array();
+
+ /**
+ * Map beetween generated aliases and corresponding models.
*
* @see lithium\data\model\Query::alias()
*
@@ -720,23 +729,40 @@ public function alias($alias = true, $relpath = null) {
}
$this->_paths[$alias] = $relpath;
+ $fieldname = array();
foreach ($paths as $path) {
if (!$relation = $model::relations($path)) {
$model = null;
break;
}
+ $fieldname[] = $relation->fieldName();
$model = $relation->to();
}
$this->_models[$alias] = $model;
+ $this->_relationNames[$relpath] = join('.', $fieldname);
return $alias;
}
/**
+ * Return the relation paths mapped to their corredponding fieldname paths.
+ *
+ * @param object $source Instance of the data source (`lithium\data\Source`) to use for
+ * conversion.
+ * @return array Map between relation paths and their corresponding fieldname paths.
+ */
+ public function relationNames(Source $source = null) {
+ if ($source) {
+ $this->applyStrategy($source);
+ }
+ return $this->_relationNames;
+ }
+
+ /**
* Return the generated aliases mapped to their relation path
*
* @param object $source Instance of the data source (`lithium\data\Source`) to use for
* conversion.
- * @return array Map between alias and their corresponding dotted relation
+ * @return array Map between aliases and their corresponding dotted relation paths.
*/
public function paths(Source $source = null) {
if ($source) {
@@ -750,7 +776,7 @@ public function paths(Source $source = null) {
*
* @param object $source Instance of the data source (`lithium\data\Source`) to use for
* conversion.
- * @return array Map between alias and their corresponding model
+ * @return array Map between aliases and their corresponding fully-namespaced model names.
*/
public function models(Source $source = null) {
if ($source) {
@@ -465,18 +465,30 @@ public function read($query, array $options = array()) {
return $result;
case 'array':
$columns = $args['schema'] ?: $self->schema($query, $result);
- $records = array();
- if (is_array(reset($columns))) {
- $columns = reset($columns);
+
+ if (!isset($columns['']) || !is_array($columns[''])) {
+ $columns = array('' => $columns);
}
- while ($data = $result->next()) {
- // @hack: Fix this to support relationships
- if (count($columns) != count($data) && is_array(current($columns))) {
- $columns = current($columns);
+
+ $relationNames = is_object($query) ? $query->relationNames($self) : array();
+ $i = 0;
+ $records = array();
+ foreach ($result as $data) {
+ $offset = 0;
+ $records[$i] = array();
+ foreach ($columns as $path => $cols) {
+ $len = count($cols);
+ $values = array_combine($cols, array_slice($data, $offset, $len));
+ if ($path) {
+ $records[$i][$relationNames[$path]] = $values;
+ } else {
+ $records[$i] += $values;
+ }
+ $offset += $len;
}
- $records[] = array_combine($columns, $data);
+ $i++;
}
- return $records;
+ return Set::expand($records);
case 'item':
return $self->item($query->model(), array(), compact('query', 'result') + array(
'class' => 'set'
@@ -633,6 +633,27 @@ public function testModels() {
$this->assertEqual($expected, $query->models($this->db));
}
+ public function testRelationNames() {
+ $model = 'lithium\tests\mocks\data\model\MockQueryPost';
+ $query = new Query(compact('model'));
+ $query->alias(null, 'MockQueryComment');
+
+ $expected = array(
+ 'MockQueryComment' => 'mock_query_comments'
+ );
+ $this->assertEqual($expected, $query->relationNames($this->db));
+
+ $query->alias('Post');
+ $query->alias('Comment', 'MockQueryComment');
+ $query->alias('Post2', 'MockQueryComment.MockQueryPost');
+
+ $expected = array(
+ 'MockQueryComment' => 'mock_query_comments',
+ 'MockQueryComment.MockQueryPost' => 'mock_query_comments.mock_query_post'
+ );
+ $this->assertEqual($expected, $query->relationNames($this->db));
+ }
+
public function testExportWithJoinedStrategy() {
$query = new Query(array(
'alias' => 'MyAlias',
@@ -16,6 +16,7 @@
use lithium\tests\mocks\data\model\MockDatabaseComment;
use lithium\tests\mocks\data\model\MockDatabaseTagging;
use lithium\tests\mocks\data\model\MockDatabasePostRevision;
+use lithium\tests\mocks\data\model\mock_database\MockResult;
class DatabaseTest extends \lithium\test\Unit {
@@ -42,6 +43,7 @@ public function setUp() {
public function tearDown() {
$this->db->logs = array();
+ $this->db->return = array();
}
public function testDefaultConfig() {
@@ -1498,6 +1500,84 @@ public function testCustomField() {
$this->assertEqual($expected, $this->db->sql);
$this->assertEqual($map, $query->map());
}
+
+ public function testReturnArrayOnReadWithString() {
+ $data = new MockResult(array('records' => array(
+ array ('id', 'int(11)', 'NO', 'PRI', null, 'auto_increment'),
+ array ('name', 'varchar(256)', 'YES', '', null, '')
+ )));
+ $this->db->return = array(
+ 'schema' => array('field', 'type', 'null', 'key', 'default', 'extra'),
+ '_execute' => $data
+ );
+ $result = $this->db->read('DESCRIBE {table};', array('return' => 'array'));
+ $expected = array(
+ array(
+ 'field' => 'id',
+ 'type' => 'int(11)',
+ 'null' => 'NO',
+ 'key' => 'PRI',
+ 'default' => null,
+ 'extra' => 'auto_increment',
+ ),
+ array(
+ 'field' => 'name',
+ 'type' => 'varchar(256)',
+ 'null' => 'YES',
+ 'key' => '',
+ 'default' => null,
+ 'extra' => '',
+ )
+ );
+ $this->assertEqual($expected, $result);
+ }
+
+ public function testReturnArrayOnReadWithQuery() {
+ $data = new MockResult(array('records' => array(array(
+ '1',
+ '2',
+ 'Post title',
+ '2012-12-17 17:04:00',
+ '3',
+ '1',
+ '2',
+ 'Very good post',
+ '2012-12-17 17:05:00',
+ '1',
+ '2',
+ 'Post title',
+ '2012-12-17 17:04:00',
+ ))));
+ $this->db->return = array(
+ '_execute' => $data
+ );
+ $query = new Query(array(
+ 'type' => 'read',
+ 'model' => $this->_model,
+ 'with' => array('MockDatabaseComment.MockDatabasePost')
+ ));
+ $result = $this->db->read($query, array('return' => 'array'));
+ $expected = array(array(
+ 'id' => '1',
+ 'author_id' => '2',
+ 'title' => 'Post title',
+ 'created' => '2012-12-17 17:04:00',
+ 'mock_database_comments' => array(
+ 'id' => '3',
+ 'post_id' => '1',
+ 'author_id' => '2',
+ 'body' => 'Very good post',
+ 'created' => '2012-12-17 17:05:00',
+ 'mock_database_post' => array(
+ 'id' => '1',
+ 'author_id' => '2',
+ 'title' => 'Post title',
+ 'created' => '2012-12-17 17:04:00',
+ )
+ )
+ ));
+ $this->assertEqual($expected, $result);
+ }
}
?>
@@ -41,6 +41,8 @@ class MockDatabase extends \lithium\data\source\Database {
public $log = false;
+ public $return = array();
+
protected $_quotes = array('{', '}');
public function __construct(array $config = array()) {
@@ -94,9 +96,19 @@ protected function _execute($sql) {
if ($this->log) {
$this->logs[] = $sql;
}
+ if (isset($this->return['_execute'])) {
+ return $this->return['_execute'];
+ }
return new MockResult();
}
+ public function schema($query, $resource = null, $context = null) {
+ if (isset($this->return['schema'])) {
+ return $this->return['schema'];
+ }
+ return parent::schema($query, $resource = null, $context = null);
+ }
+
protected function _insertId($query) {
$query = $query->export($this);
ksort($query);