浏览代码

Add the ability to add automatic fields.

When selecting specific fields (such as computed fields) it is often
really helpful to also select all the fields in the model. Instead of
having to type out all the columns in the schema, the autoFields()
method can be used to tell an ORM\Query to also include the automatic
fields. This new method also works in contain() builders.

Refs #4360
mark_story 11 年之前
父节点
当前提交
44fee92558
共有 3 个文件被更改,包括 100 次插入3 次删除
  1. 6 1
      src/ORM/Association.php
  2. 26 1
      src/ORM/Query.php
  3. 68 1
      tests/TestCase/ORM/QueryTest.php

+ 6 - 1
src/ORM/Association.php

@@ -596,13 +596,18 @@ abstract class Association {
 	protected function _appendFields($query, $surrogate, $options) {
 		$options['fields'] = $surrogate->clause('select') ?: $options['fields'];
 		$target = $this->_targetTable;
-		if (empty($options['fields'])) {
+		$autoFields = $surrogate->autoFields();
+		if (empty($options['fields']) && !$autoFields) {
 			$f = isset($options['fields']) ? $options['fields'] : null;
 			if ($options['includeFields'] && ($f === null || $f !== false)) {
 				$options['fields'] = $target->schema()->columns();
 			}
 		}
 
+		if ($autoFields === true) {
+			$options['fields'] = array_merge((array)$options['fields'], $target->schema()->columns());
+		}
+
 		if (!empty($options['fields'])) {
 			$query->select($query->aliasFields($options['fields'], $target->alias()));
 		}

+ 26 - 1
src/ORM/Query.php

@@ -67,6 +67,14 @@ class Query extends DatabaseQuery implements JsonSerializable {
 	protected $_hasFields;
 
 /**
+ * Tracks whether or not the original query should include
+ * fields from the top level model.
+ *
+ * @var bool
+ */
+	protected $_autoFields;
+
+/**
  * Boolean for tracking whether or not buffered results
  * are enabled.
  *
@@ -625,7 +633,7 @@ class Query extends DatabaseQuery implements JsonSerializable {
 		$select = $this->clause('select');
 		$this->_hasFields = true;
 
-		if (!count($select)) {
+		if (!count($select) || $this->_autoFields === true) {
 			$this->_hasFields = false;
 			$this->select($this->repository()->schema()->columns());
 			$select = $this->clause('select');
@@ -756,4 +764,21 @@ class Query extends DatabaseQuery implements JsonSerializable {
 		return $this->all();
 	}
 
+/**
+ * Get/Set whether or not the ORM should automatically append fields.
+ *
+ * By default calling select() will disable auto-fields. You can re-enable
+ * auto-fields with this method.
+ *
+ * @param bool $value The value to set or null to read the current value.
+ * @return bool|$this Either the current value or the query object.
+ */
+	public function autoFields($value = null) {
+		if ($value === null) {
+			return $this->_autoFields;
+		}
+		$this->_autoFields = (bool)$value;
+		return $this;
+	}
+
 }

+ 68 - 1
tests/TestCase/ORM/QueryTest.php

@@ -27,7 +27,6 @@ use Cake\TestSuite\TestCase;
 
 /**
  * Tests Query class
- *
  */
 class QueryTest extends TestCase {
 
@@ -2006,4 +2005,72 @@ class QueryTest extends TestCase {
 		);
 	}
 
+/**
+ * Test that addFields() works in the basic case.
+ *
+ * @return void
+ */
+	public function testAutoFields() {
+		$table = TableRegistry::get('Articles');
+		$result = $table->find('all')
+			->select(['myField' => 'id + 20'])
+			->autoFields(true)
+			->hydrate(false)
+			->first();
+
+		$this->assertArrayHasKey('myField', $result);
+		$this->assertArrayHasKey('id', $result);
+		$this->assertArrayHasKey('title', $result);
+	}
+
+/**
+ * Test autoFields with auto fields.
+ *
+ * @return void
+ */
+	public function testAutoFieldsWithAssociations() {
+		$table = TableRegistry::get('Articles');
+		$table->belongsTo('Authors');
+
+		$result = $table->find()
+			->select(['myField' => '(SELECT RAND())'])
+			->autoFields(true)
+			->hydrate(false)
+			->contain('Authors')
+			->first();
+
+		$this->assertArrayHasKey('myField', $result);
+		$this->assertArrayHasKey('title', $result);
+		$this->assertArrayHasKey('author', $result);
+		$this->assertNotNull($result['author']);
+		$this->assertArrayHasKey('name', $result['author']);
+	}
+
+/**
+ * Test autoFields in contain query builder
+ *
+ * @return void
+ */
+	public function testAutoFieldsWithContainQueryBuilder() {
+		$table = TableRegistry::get('Articles');
+		$table->belongsTo('Authors');
+
+		$result = $table->find()
+			->select(['myField' => '(SELECT RAND())'])
+			->autoFields(true)
+			->hydrate(false)
+			->contain(['Authors' => function($q) {
+				return $q->select(['compute' => '2 + 20'])
+					->autoFields(true);
+			}])
+			->first();
+
+		$this->assertArrayHasKey('myField', $result);
+		$this->assertArrayHasKey('title', $result);
+		$this->assertArrayHasKey('author', $result);
+		$this->assertNotNull($result['author']);
+		$this->assertArrayHasKey('name', $result['author']);
+		$this->assertArrayHasKey('compute', $result);
+	}
+
 }