Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

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

Closed
wants to merge 1 commit into from

2 participants

@jails
Collaborator

With this PR array('return' => 'array') option on queries with relations will return something like the following:

array(
    'field1' => 'value1',
    'field2' => 'value2',
    'field3' => 'value3',
    /* ... other fields ... */
    'Dotted.Relation.Path' => array(
             'field1' => 'value1',
             'field2' => 'value2',
             'field3' => 'value3',
             /* ... other fields ... */
 );

When the 'joined' strategy is used, all primary keys of relations are automagically added to the query to allow a correct hydratation of nested entities. However with the array('return' => 'array') you will get the following:

array(
    'field1' => 'value1',
    'field2' => 'value2',
    'field3' => 'value3',
    /* ... other fields ... */
    'Relation' => array( 'id' => #),
    'Relation.Sub' => array( 'id' => #),
    'Relation.Sub.Sub'  => array( 'id' => #),
    'Relation.Sub.Sub.Sub' => array( 'id' => #)
 );

If you are only interested by the root level fields, you will need something like the following for removing the "pollution":

    $result = $db->query(/*the query*/, array('return' => 'array'));
    $filtered = array_filter($result, function($v) {return !is_array($v);})

To avoid this 'tricky step", you can get only the first level using the array('return' => 'root') option.

    $result = $db->query(/*the query*/, array('return' => 'root'));
@jails jails closed this
@jails jails reopened this
@jails jails closed this
@nateabele
Owner

@jails Maybe the same, but with Set::expand() applied?

@jails
Collaborator

The first thing that confuses me is difference beetween array('return' => 'array') / ->to('array').
1)- Is array('return' => 'array') should return the same thing as ->to('array') ?
2)- Or 'array' means more something "raw" (i.e. with no extra processing, only fieldname matching is done) ?

The other thing that confuses me is the use case when you only need "root" datas. Sometimes you're using "joins" for selecting a correct record but due to the "automagically added key" by the strategy, you'll need to filter the result on your own.

Solution 1) the primary keys won't be added anymore, so the entity hydratation will be stoped as soon as no primary key are present.
Solution 3) Having a kind of array('root' => true/false) option for both array('return' => 'array') / array('return' => 'item')
Solution 3) Let people filtering datas with things like : $filtered = array_filter($result, function($v) {return !is_array($v);})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 17, 2012
  1. @jails
This page is out of date. Refresh to see the latest.
View
28 data/source/Database.php
@@ -463,18 +463,30 @@ public function read($query, array $options = array()) {
switch ($return) {
case 'resource':
return $result;
+ case 'root':
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);
+ }
+
+ if ($return == 'root') {
+ $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);
+
+ $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));
+ $records[$i] += $path ? array($path => $values) : $values;
+ $offset += $len;
}
- $records[] = array_combine($columns, $data);
+ $i++;
}
return $records;
case 'item':
View
79 tests/cases/data/source/DatabaseTest.php
@@ -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() {
@@ -1466,6 +1468,83 @@ public function testExportedFieldsWithJoinedStrategyAndRecursiveRelation () {
$expected = '{ParentOfParent}.{name}, {ParentOfParent}.{id}, {Gallery}.{id}, {Parent}.{id}';
$this->assertEqual($expected, $result['fields']);
}
+
+ 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'
+ ))));
+ $this->db->return = array(
+ '_execute' => $data
+ );
+ $query = new Query(array(
+ 'type' => 'read',
+ 'model' => $this->_model,
+ 'with' => array('MockDatabaseComment')
+ ));
+ $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',
+ 'MockDatabaseComment' => array(
+ 'id' => '3',
+ 'post_id' => '1',
+ 'author_id' => '2',
+ 'body' => 'Very good post',
+ 'created' => '2012-12-17 17:05:00'
+ )
+ ));
+ $this->assertEqual($expected, $result);
+
+ $result = $this->db->read($query, array('return' => 'root'));
+ $expected = array(array(
+ 'id' => '1',
+ 'author_id' => '2',
+ 'title' => 'Post title',
+ 'created' => '2012-12-17 17:04:00'
+ ));
+ $this->assertEqual($expected, $result);
+ }
}
?>
View
12 tests/mocks/data/model/MockDatabase.php
@@ -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);
Something went wrong with that request. Please try again.