From 611c9ee7c194d4ec21f865ad74a4c4a4b52e81d0 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Fri, 3 Jul 2020 01:21:36 -0500 Subject: [PATCH 1/4] Added Query::subquery() which disables field aliasing --- src/ORM/Query.php | 39 ++++++++++++++++++++++++++++---- tests/TestCase/ORM/QueryTest.php | 35 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/ORM/Query.php b/src/ORM/Query.php index 978611b7ce0..605c968bd09 100644 --- a/src/ORM/Query.php +++ b/src/ORM/Query.php @@ -130,6 +130,13 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface */ protected $_hydrate = true; + /** + * Whether aliases are generated for fields. + * + * @var bool + */ + protected $aliasingEnabled = true; + /** * A callable function that can be used to calculate the total amount of * records this query will match when not using `limit` @@ -225,7 +232,11 @@ public function select($fields = [], bool $overwrite = false) } if ($fields instanceof Table) { - $fields = $this->aliasFields($fields->getSchema()->columns(), $fields->getAlias()); + if ($this->aliasingEnabled) { + $fields = $this->aliasFields($fields->getSchema()->columns(), $fields->getAlias()); + } else { + $fields = $fields->getSchema()->columns(); + } } return parent::select($fields, $overwrite); @@ -255,9 +266,11 @@ public function selectAllExcept($table, array $excludedFields, bool $overwrite = } $fields = array_diff($table->getSchema()->columns(), $excludedFields); - $aliasedFields = $this->aliasFields($fields); + if ($this->aliasingEnabled) { + $fields = $this->aliasFields($fields); + } - return $this->select($aliasedFields, $overwrite); + return $this->select($fields, $overwrite); } /** @@ -1163,8 +1176,10 @@ protected function _addDefaultFields(): void $select = $this->clause('select'); } - $aliased = $this->aliasFields($select, $repository->getAlias()); - $this->select($aliased, true); + if ($this->aliasingEnabled) { + $select = $this->aliasFields($select, $repository->getAlias()); + } + $this->select($select, true); } /** @@ -1285,6 +1300,20 @@ public function insert(array $columns, array $types = []) return parent::insert($columns, $types); } + /** + * Returns a new Query that has automatic field aliasing disabled. + * + * @param \Cake\ORM\Table $table The table this query is starting on + * @return self + */ + public function subquery(Table $table): self + { + $query = new self($this->getConnection(), $table); + $query->aliasingEnabled = false; + + return $query; + } + /** * {@inheritDoc} * diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index 8b900319beb..5311f9a580e 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -4001,4 +4001,39 @@ public function testWith(): void $this->assertEquals($expected, $query->toArray()); } + + /** + * Tests subquery() copies connection by default. + * + * @return void + */ + public function testSubqueryConnection() + { + $subquery = (new Query($this->connection, $this->table))->subquery($this->table); + $this->assertEquals($this->connection, $subquery->getConnection()); + } + + /** + * Tests subquery() disables aliasing. + * + * @return void + */ + public function testSubqueryAliasing() + { + $articles = $this->getTableLocator()->get('Articles'); + $subquery = (new Query($this->connection, $this->table))->subquery($articles); + + $subquery->select('Articles.field1'); + $this->assertRegExpSql( + 'SELECT . FROM ', + $subquery->sql(), + !$this->connection->getDriver()->isAutoQuotingEnabled() + ); + + $subquery->select($articles, true); + $this->assertEqualsSql('SELECT id, author_id, title, body, published FROM articles Articles', $subquery->sql()); + + $subquery->selectAllExcept($articles, ['author_id'], true); + $this->assertEqualsSql('SELECT id, title, body, published FROM articles Articles', $subquery->sql()); + } } From 0387ecfe93dc5ecba7872074f8f1a10b850fe8a7 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Fri, 3 Jul 2020 07:49:19 -0500 Subject: [PATCH 2/4] Changed subquery() to return static --- phpstan-baseline.neon | 5 ++++- src/ORM/Query.php | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8bf62dc78af..ce94433e4b6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -559,4 +559,7 @@ parameters: message: "#^Access to an undefined property Cake\\\\Mailer\\\\Renderer\\:\\:\\$response\\.$#" count: 1 path: src/Mailer/Renderer.php - + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/ORM\Query.php diff --git a/src/ORM/Query.php b/src/ORM/Query.php index 605c968bd09..e3b339ab0d8 100644 --- a/src/ORM/Query.php +++ b/src/ORM/Query.php @@ -1304,11 +1304,11 @@ public function insert(array $columns, array $types = []) * Returns a new Query that has automatic field aliasing disabled. * * @param \Cake\ORM\Table $table The table this query is starting on - * @return self + * @return static */ - public function subquery(Table $table): self + public function subquery(Table $table) { - $query = new self($this->getConnection(), $table); + $query = new static($this->getConnection(), $table); $query->aliasingEnabled = false; return $query; From 1e8a9968b116b2ba2420ee0fb05cf6a1524befc8 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Fri, 17 Jul 2020 00:16:37 -0500 Subject: [PATCH 3/4] Changed subquery() to static function and use connection from Table --- src/ORM/Query.php | 4 ++-- tests/TestCase/ORM/QueryTest.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ORM/Query.php b/src/ORM/Query.php index e3b339ab0d8..21415d19537 100644 --- a/src/ORM/Query.php +++ b/src/ORM/Query.php @@ -1306,9 +1306,9 @@ public function insert(array $columns, array $types = []) * @param \Cake\ORM\Table $table The table this query is starting on * @return static */ - public function subquery(Table $table) + public static function subquery(Table $table) { - $query = new static($this->getConnection(), $table); + $query = new static($table->getConnection(), $table); $query->aliasingEnabled = false; return $query; diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index 5311f9a580e..f3a9c491394 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -4009,8 +4009,8 @@ public function testWith(): void */ public function testSubqueryConnection() { - $subquery = (new Query($this->connection, $this->table))->subquery($this->table); - $this->assertEquals($this->connection, $subquery->getConnection()); + $subquery = Query::subquery($this->table); + $this->assertEquals($this->table->getConnection(), $subquery->getConnection()); } /** @@ -4021,7 +4021,7 @@ public function testSubqueryConnection() public function testSubqueryAliasing() { $articles = $this->getTableLocator()->get('Articles'); - $subquery = (new Query($this->connection, $this->table))->subquery($articles); + $subquery = Query::subquery($articles); $subquery->select('Articles.field1'); $this->assertRegExpSql( From a881dcec87589198e0c351814ceb7409e0d16dd8 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Fri, 17 Jul 2020 00:25:12 -0500 Subject: [PATCH 4/4] Add test for using subquery() for WHERE IN (SELECT ...) --- tests/TestCase/ORM/QueryTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index f3a9c491394..cc53b68cb06 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -4036,4 +4036,19 @@ public function testSubqueryAliasing() $subquery->selectAllExcept($articles, ['author_id'], true); $this->assertEqualsSql('SELECT id, title, body, published FROM articles Articles', $subquery->sql()); } + + public function testSubquerySelect() + { + $subquery = Query::subquery($this->getTableLocator()->get('Authors')) + ->select(['Authors.id']) + ->where(['Authors.name' => 'mariano']); + + $query = $this->getTableLocator()->get('Articles')->find() + ->where(['Articles.author_id IN' => $subquery]) + ->order(['Articles.id' => 'ASC']); + + $results = $query->all()->toList(); + $this->assertCount(2, $results); + $this->assertEquals([1, 3], array_column($results, 'id')); + } }