diff --git a/src/Query/Grammars/Traits/CompilesPostgresExpressions.php b/src/Query/Grammars/Traits/CompilesPostgresExpressions.php index 158c678..d4b095c 100644 --- a/src/Query/Grammars/Traits/CompilesPostgresExpressions.php +++ b/src/Query/Grammars/Traits/CompilesPostgresExpressions.php @@ -3,6 +3,7 @@ namespace Staudenmeir\LaravelCte\Query\Grammars\Traits; use Illuminate\Database\Query\Builder; +use Illuminate\Support\Str; trait CompilesPostgresExpressions { @@ -44,6 +45,38 @@ public function getBindingsForUpdate(Builder $query, array $bindings, array $val return $this->prepareBindingsForUpdate($bindings, $values); } + /** + * Compile an update from statement into SQL. + * + * @param \Illuminate\Database\Query\Builder $query + * @param array $values + * @return string + */ + public function compileUpdateFrom(Builder $query, $values) + { + $compiled = parent::compileUpdateFrom($query, $values); + + return (string) Str::of($compiled) + ->prepend($this->compileExpressions($query, $query->expressions), ' ') + ->trim(); + } + + /** + * Prepare the bindings for an update statement. + * + * @param array $bindings + * @param array $values + * @return array + */ + public function prepareBindingsForUpdateFrom(array $bindings, array $values) + { + $values = array_merge($bindings['expressions'], $values); + + unset($bindings['expressions']); + + return parent::prepareBindingsForUpdateFrom($bindings, $values); + } + /** * Compile a delete statement into SQL. * diff --git a/src/Query/Traits/BuildsExpressionQueries.php b/src/Query/Traits/BuildsExpressionQueries.php index 36080ba..c0872fe 100644 --- a/src/Query/Traits/BuildsExpressionQueries.php +++ b/src/Query/Traits/BuildsExpressionQueries.php @@ -204,7 +204,7 @@ public function insertUsing(array $columns, $query) /** * Update records in the database. * - * @param array $values + * @param array $values * @return int */ public function update(array $values) @@ -217,4 +217,21 @@ public function update(array $values) $this->grammar->getBindingsForUpdate($this, $this->bindings, $values) )); } + + /** + * Update records in a PostgreSQL database using the update from syntax. + * + * @param array $values + * @return int + */ + public function updateFrom(array $values) + { + $this->applyBeforeQueryCallbacks(); + + $sql = $this->grammar->compileUpdateFrom($this, $values); + + return $this->connection->update($sql, $this->cleanBindings( + $this->grammar->prepareBindingsForUpdateFrom($this->bindings, $values) + )); + } } diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 50e2d8e..992241f 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -389,6 +389,22 @@ public function testUpdateWithLimit() $this->assertEquals([1, 0], DB::table('posts')->orderBy('id')->pluck('views')->all()); } + public function testUpdateFrom() + { + if ($this->database !== 'pgsql') { + $this->markTestSkipped(); + } + + DB::table('posts') + ->withExpression('u', DB::table('users')->where('id', '>', 1)) + ->join('u', 'u.id', '=', 'posts.user_id') + ->updateFrom([ + 'views' => DB::raw('"u"."followers"'), + ]); + + $this->assertEquals([0, 20], DB::table('posts')->orderBy('id')->pluck('views')->all()); + } + public function testDelete() { if ($this->database === 'mariadb') { diff --git a/tests/TestCase.php b/tests/TestCase.php index af17851..2231aab 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -24,6 +24,7 @@ protected function setUp(): void Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->unsignedInteger('parent_id')->nullable(); + $table->unsignedBigInteger('followers'); $table->timestamps(); }); @@ -36,9 +37,9 @@ protected function setUp(): void Model::unguard(); - User::create(['parent_id' => null]); - User::create(['parent_id' => 1]); - User::create(['parent_id' => 2]); + User::create(['parent_id' => null, 'followers' => 10]); + User::create(['parent_id' => 1, 'followers' => 20]); + User::create(['parent_id' => 2, 'followers' => 30]); Post::create(['user_id' => 1]); Post::create(['user_id' => 2]);