From 74587500c575b3fd278252eaa5aa4b04ec625b38 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 14 Jan 2025 18:12:50 +0000 Subject: [PATCH 1/5] Update CHANGELOG --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb7dda18233..61e779b0f1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.38.0...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.38.1...11.x) + +## [v11.38.1](https://github.com/laravel/framework/compare/v11.38.0...v11.38.1) - 2025-01-14 + +* Fix breaking change - Revert "[11.x] Replace string class names with ::class constants" by [@SanderMuller](https://github.com/SanderMuller) in https://github.com/laravel/framework/pull/54185 +* Add failing test for #54185 by [@SanderMuller](https://github.com/SanderMuller) in https://github.com/laravel/framework/pull/54186 ## [v11.38.0](https://github.com/laravel/framework/compare/v11.37.0...v11.38.0) - 2025-01-14 From 04955c4cdeed63aea31099b2d5a39697caf2c2dc Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Tue, 14 Jan 2025 21:04:15 +0100 Subject: [PATCH 2/5] Simplify codebase by using qualifyColumn helper method (#54187) --- src/Illuminate/Database/Eloquent/Builder.php | 6 +----- .../Database/Eloquent/Concerns/HasRelationships.php | 12 ++++-------- src/Illuminate/Database/Eloquent/Model.php | 2 +- .../Database/Eloquent/Relations/BelongsToMany.php | 2 +- .../Eloquent/Relations/Concerns/CanBeOneOfMany.php | 2 +- .../Eloquent/Relations/HasOneOrManyThrough.php | 2 +- .../Database/Eloquent/Relations/MorphTo.php | 2 +- 7 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index cfe3cdc71fa..9a4642d3fea 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -1729,12 +1729,8 @@ protected function createSelectWithConstraint($name) { return [explode(':', $name)[0], static function ($query) use ($name) { $query->select(array_map(static function ($column) use ($query) { - if (str_contains($column, '.')) { - return $column; - } - return $query instanceof BelongsToMany - ? $query->getRelated()->getTable().'.'.$column + ? $query->getRelated()->qualifyColumn($column) : $column; }, explode(',', explode(':', $name)[1]))); }]; diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php index c62ce076b92..b991fd08227 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php @@ -110,7 +110,7 @@ public function hasOne($related, $foreignKey = null, $localKey = null) $localKey = $localKey ?: $this->getKeyName(); - return $this->newHasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey); + return $this->newHasOne($instance->newQuery(), $this, $instance->qualifyColumn($foreignKey), $localKey); } /** @@ -198,11 +198,9 @@ public function morphOne($related, $name, $type = null, $id = null, $localKey = [$type, $id] = $this->getMorphs($name, $type, $id); - $table = $instance->getTable(); - $localKey = $localKey ?: $this->getKeyName(); - return $this->newMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey); + return $this->newMorphOne($instance->newQuery(), $this, $instance->qualifyColumn($type), $instance->qualifyColumn($id), $localKey); } /** @@ -431,7 +429,7 @@ public function hasMany($related, $foreignKey = null, $localKey = null) $localKey = $localKey ?: $this->getKeyName(); return $this->newHasMany( - $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey + $instance->newQuery(), $this, $instance->qualifyColumn($foreignKey), $localKey ); } @@ -527,11 +525,9 @@ public function morphMany($related, $name, $type = null, $id = null, $localKey = // get the table and create the relationship instances for the developers. [$type, $id] = $this->getMorphs($name, $type, $id); - $table = $instance->getTable(); - $localKey = $localKey ?: $this->getKeyName(); - return $this->newMorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey); + return $this->newMorphMany($instance->newQuery(), $this, $instance->qualifyColumn($type), $instance->qualifyColumn($id), $localKey); } /** diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 7504f04fb8a..5d19f7f17f8 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -2134,7 +2134,7 @@ protected function resolveChildRouteBindingQuery($childType, $value, $field) if ($relationship instanceof HasManyThrough || $relationship instanceof BelongsToMany) { - $field = $relationship->getRelated()->getTable().'.'.$field; + $field = $relationship->getRelated()->qualifyColumn($field); } return $relationship instanceof Model diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 6a4e5dae4d6..500dcc20842 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -892,7 +892,7 @@ public function get($columns = ['*']) protected function shouldSelect(array $columns = ['*']) { if ($columns == ['*']) { - $columns = [$this->related->getTable().'.*']; + $columns = [$this->related->qualifyColumn('*')]; } return array_merge($columns, $this->aliasedPivotColumns()); diff --git a/src/Illuminate/Database/Eloquent/Relations/Concerns/CanBeOneOfMany.php b/src/Illuminate/Database/Eloquent/Relations/Concerns/CanBeOneOfMany.php index a5cdb240ed7..800999f86c7 100644 --- a/src/Illuminate/Database/Eloquent/Relations/Concerns/CanBeOneOfMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/Concerns/CanBeOneOfMany.php @@ -297,7 +297,7 @@ public function qualifySubSelectColumn($column) */ protected function qualifyRelatedColumn($column) { - return str_contains($column, '.') ? $column : $this->query->getModel()->getTable().'.'.$column; + return $this->query->getModel()->qualifyColumn($column); } /** diff --git a/src/Illuminate/Database/Eloquent/Relations/HasOneOrManyThrough.php b/src/Illuminate/Database/Eloquent/Relations/HasOneOrManyThrough.php index 5ae9204fd14..c048deac369 100644 --- a/src/Illuminate/Database/Eloquent/Relations/HasOneOrManyThrough.php +++ b/src/Illuminate/Database/Eloquent/Relations/HasOneOrManyThrough.php @@ -501,7 +501,7 @@ public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = protected function shouldSelect(array $columns = ['*']) { if ($columns == ['*']) { - $columns = [$this->related->getTable().'.*']; + $columns = [$this->related->qualifyColumn('*')]; } return array_merge($columns, [$this->getQualifiedFirstKeyName().' as laravel_through_key']); diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php index 3ae4a7e2283..e743e93ea37 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php @@ -162,7 +162,7 @@ protected function getResultsByType($type) $whereIn = $this->whereInMethod($instance, $ownerKey); return $query->{$whereIn}( - $instance->getTable().'.'.$ownerKey, $this->gatherKeysByType($type, $instance->getKeyType()) + $instance->qualifyColumn($ownerKey), $this->gatherKeysByType($type, $instance->getKeyType()) )->get(); } From d53756ea223d16f09c2dafc723cf11a909c590ad Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Wed, 15 Jan 2025 08:03:53 +0800 Subject: [PATCH 3/5] Revert "Add support for missing Postgres connection options (#54101)" (#54195) This reverts commit 36a4a5335c1fd697188cb8116542ab8498da9cbc. --- .../Database/Connectors/PostgresConnector.php | 61 +------------------ 1 file changed, 2 insertions(+), 59 deletions(-) diff --git a/src/Illuminate/Database/Connectors/PostgresConnector.php b/src/Illuminate/Database/Connectors/PostgresConnector.php index ef700e2a72e..ef806dd9ab2 100755 --- a/src/Illuminate/Database/Connectors/PostgresConnector.php +++ b/src/Illuminate/Database/Connectors/PostgresConnector.php @@ -150,9 +150,7 @@ protected function getDsn(array $config) $dsn .= ";application_name='".str_replace("'", "\'", $application_name)."'"; } - $dsn = $this->addSslOptions($dsn, $config); - - return $this->addPostgresOptions($dsn, $config); + return $this->addSslOptions($dsn, $config); } /** @@ -164,62 +162,7 @@ protected function getDsn(array $config) */ protected function addSslOptions($dsn, array $config) { - foreach ([ - 'sslmode', - 'sslcert', - 'sslkey', - 'sslrootcert', - 'requiressl', - 'sslnegotiation', - 'sslcompression', - 'sslpassword', - 'sslcertmode', - 'sslcrl', - 'sslcrldir', - 'sslsni', - ] as $option) { - if (isset($config[$option])) { - $dsn .= ";{$option}={$config[$option]}"; - } - } - - return $dsn; - } - - /** - * Add Postgres specific options to the DSN. - * - * @param string $dsn - * @param array $config - * @return string - */ - protected function addPostgresOptions($dsn, array $config) - { - foreach ([ - 'channel_binding', - 'connect_timeout', - 'fallback_application_name', - 'gssdelegation', - 'gssencmode', - 'gsslib', - 'hostaddr', - 'keepalives', - 'keepalives_count', - 'keepalives_idle', - 'keepalives_interval', - 'krbsrvname', - 'load_balance_hosts', - 'options', - 'passfile', - 'replication', - 'require_auth', - 'requirepeer', - 'service', - 'ssl_max_protocol_version', - 'ssl_min_protocol_version', - 'target_session_attrs', - 'tcp_user_timeout', - ] as $option) { + foreach (['sslmode', 'sslcert', 'sslkey', 'sslrootcert'] as $option) { if (isset($config[$option])) { $dsn .= ";{$option}={$config[$option]}"; } From 908522439172658c8076798ca2ae59ef5d68c625 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Wed, 15 Jan 2025 08:04:19 +0800 Subject: [PATCH 4/5] Revert "[11.x] Support DB aggregate by group (new methods) (#53679)" (#54196) This reverts commit 4b9aeae639eb0ec77e49adfbebdfdb329e04ccda. --- src/Illuminate/Database/Query/Builder.php | 70 ------------------ .../Database/Query/Grammars/Grammar.php | 14 +--- tests/Database/DatabaseQueryBuilderTest.php | 71 ------------------- 3 files changed, 2 insertions(+), 153 deletions(-) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index f2f171b4bc9..f166b28bbbf 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -3561,17 +3561,6 @@ public function count($columns = '*') return (int) $this->aggregate(__FUNCTION__, Arr::wrap($columns)); } - /** - * Retrieve the "count" of the distinct results of a given column for each group. - * - * @param \Illuminate\Contracts\Database\Query\Expression|string $columns - * @return \Illuminate\Support\Collection - */ - public function countByGroup($columns = '*') - { - return $this->aggregateByGroup('count', Arr::wrap($columns)); - } - /** * Retrieve the minimum value of a given column. * @@ -3583,17 +3572,6 @@ public function min($column) return $this->aggregate(__FUNCTION__, [$column]); } - /** - * Retrieve the minimum value of a given column by group. - * - * @param \Illuminate\Contracts\Database\Query\Expression|string $column - * @return \Illuminate\Support\Collection - */ - public function minByGroup($column) - { - return $this->aggregateByGroup('min', [$column]); - } - /** * Retrieve the maximum value of a given column. * @@ -3605,17 +3583,6 @@ public function max($column) return $this->aggregate(__FUNCTION__, [$column]); } - /** - * Retrieve the maximum value of a given column by group. - * - * @param \Illuminate\Contracts\Database\Query\Expression|string $column - * @return \Illuminate\Support\Collection - */ - public function maxByGroup($column) - { - return $this->aggregateByGroup('max', [$column]); - } - /** * Retrieve the sum of the values of a given column. * @@ -3629,17 +3596,6 @@ public function sum($column) return $result ?: 0; } - /** - * Retrieve the sum of the values of a given column by group. - * - * @param \Illuminate\Contracts\Database\Query\Expression|string $column - * @return \Illuminate\Support\Collection - */ - public function sumByGroup($column) - { - return $this->aggregateByGroup('sum', [$column]); - } - /** * Retrieve the average of the values of a given column. * @@ -3651,17 +3607,6 @@ public function avg($column) return $this->aggregate(__FUNCTION__, [$column]); } - /** - * Retrieve the average of the values of a given column by group. - * - * @param \Illuminate\Contracts\Database\Query\Expression|string $column - * @return \Illuminate\Support\Collection - */ - public function avgByGroup($column) - { - return $this->aggregateByGroup('avg', [$column]); - } - /** * Alias for the "avg" method. * @@ -3692,21 +3637,6 @@ public function aggregate($function, $columns = ['*']) } } - /** - * Execute an aggregate function for each group. - * - * @param string $function - * @param array $columns - * @return \Illuminate\Support\Collection - */ - public function aggregateByGroup(string $function, array $columns = ['*']) - { - return $this->cloneWithout($this->unions || $this->havings ? [] : ['columns']) - ->cloneWithoutBindings($this->unions || $this->havings ? [] : ['select']) - ->setAggregate($function, $columns) - ->get($columns); - } - /** * Execute a numeric aggregate function on the database. * diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index 584f7855fef..c64aa49a783 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -139,15 +139,7 @@ protected function compileAggregate(Builder $query, $aggregate) $column = 'distinct '.$column; } - $sql = 'select '; - - $sql .= $aggregate['function'].'('.$column.') as aggregate'; - - if ($query->groups) { - $sql .= ', '.$this->columnize($query->groups); - } - - return $sql; + return 'select '.$aggregate['function'].'('.$column.') as aggregate'; } /** @@ -1139,12 +1131,10 @@ protected function wrapUnion($sql) protected function compileUnionAggregate(Builder $query) { $sql = $this->compileAggregate($query, $query->aggregate); - $groups = $query->groups ? ' '.$this->compileGroups($query, $query->groups) : ''; $query->aggregate = null; - $query->groups = null; - return $sql.' from ('.$this->compileSelect($query).') as '.$this->wrapTable('temp_table').$groups; + return $sql.' from ('.$this->compileSelect($query).') as '.$this->wrapTable('temp_table'); } /** diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 5f07e97ae10..096b82450d2 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -25,7 +25,6 @@ use Illuminate\Pagination\Cursor; use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; use Illuminate\Tests\Database\Fixtures\Enums\Bar; use InvalidArgumentException; use Mockery as m; @@ -1805,38 +1804,6 @@ public function testGroupBys() $this->assertEquals(['whereRawBinding', 'groupByRawBinding', 'havingRawBinding'], $builder->getBindings()); } - public function testAggregateByGroup() - { - $builder = $this->getBuilder(); - - $queryResults = [['aggregate' => 2, 'role' => 'admin', 'city' => 'NY'], ['aggregate' => 5, 'role' => 'user', 'city' => 'LA']]; - $builder->getConnection() - ->shouldReceive('select')->once() - ->with('select count(*) as aggregate, "role", "city" from "users" group by "role", "city"', [], true) - ->andReturn($queryResults); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $builder->from('users')->groupBy('role', 'city'); - $builder->aggregate = ['function' => 'count', 'columns' => ['*']]; - $results = $builder->get(); - $this->assertEquals($queryResults, $results->toArray()); - } - - public function testUnionAndAggregateByGroup() - { - $builder = $this->getBuilder(); - - $queryResults = [['aggregate' => 2, 'role' => 'admin'], ['aggregate' => 5, 'role' => 'user']]; - $builder->getConnection() - ->shouldReceive('select')->once() - ->with('select count(*) as aggregate, "role" from ((select * from "users") union (select * from "members")) as "temp_table" group by "role"', [], true) - ->andReturn($queryResults); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $results = $builder->from('users') - ->union($this->getBuilder()->select('*')->from('members')) - ->groupBy('role')->aggregateByGroup('count'); - $this->assertEquals($queryResults, $results->toArray()); - } - public function testOrderBys() { $builder = $this->getBuilder(); @@ -3497,44 +3464,6 @@ public function testAggregateFunctions() $this->assertEquals(1, $results); } - public function testAggregateFunctionsWithGroupBy() - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count(*) as aggregate, "role" from "users" group by "role"', [], true)->andReturn([['role' => 'admin', 'aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $results = $builder->from('users')->groupBy('role')->countByGroup(); - $this->assertInstanceOf(Collection::class, $results); - $this->assertEquals([['role' => 'admin', 'aggregate' => 1]], $results->toArray()); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select max("id") as aggregate, "role" from "users" group by "role"', [], true)->andReturn([['role' => 'admin', 'aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $results = $builder->from('users')->groupBy('role')->maxByGroup('id'); - $this->assertInstanceOf(Collection::class, $results); - $this->assertEquals([['role' => 'admin', 'aggregate' => 1]], $results->toArray()); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select min("id") as aggregate, "role" from "users" group by "role"', [], true)->andReturn([['role' => 'admin', 'aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $results = $builder->from('users')->groupBy('role')->minByGroup('id'); - $this->assertInstanceOf(Collection::class, $results); - $this->assertEquals([['role' => 'admin', 'aggregate' => 1]], $results->toArray()); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select sum("id") as aggregate, "role" from "users" group by "role"', [], true)->andReturn([['role' => 'admin', 'aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $results = $builder->from('users')->groupBy('role')->sumByGroup('id'); - $this->assertInstanceOf(Collection::class, $results); - $this->assertEquals([['role' => 'admin', 'aggregate' => 1]], $results->toArray()); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select avg("id") as aggregate, "role" from "users" group by "role"', [], true)->andReturn([['role' => 'admin', 'aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(fn ($builder, $results) => $results); - $results = $builder->from('users')->groupBy('role')->avgByGroup('id'); - $this->assertInstanceOf(Collection::class, $results); - $this->assertEquals([['role' => 'admin', 'aggregate' => 1]], $results->toArray()); - } - public function testSqlServerExists() { $builder = $this->getSqlServerBuilder(); From 9d290aa90fcad44048bedca5219d2b872e98772a Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 15 Jan 2025 00:06:46 +0000 Subject: [PATCH 5/5] Update version to v11.38.2 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 80c209bebb8..c3c78a0c514 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.38.1'; + const VERSION = '11.38.2'; /** * The base path for the Laravel installation.