Last active
March 18, 2020 09:32
-
-
Save tomcastleman/7285a59ee242f929e0cbe6974bc4b799 to your computer and use it in GitHub Desktop.
Laravel Query Builder Upsert Macro for MySQL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Database; | |
use Illuminate\Database\Query\Builder; | |
use Illuminate\Support\Collection; | |
class UpsertBuilder | |
{ | |
/** @var Builder */ | |
protected $builder; | |
public function __construct(Builder $builder) | |
{ | |
$this->builder = $builder; | |
} | |
/** | |
* @param Collection $values | |
* | |
* @return Collection | |
*/ | |
protected function getWrappedColumns(Collection $values): Collection | |
{ | |
return collect($values->first())->keys()->map([$this->builder->grammar, 'wrap']); | |
} | |
/** | |
* @param Collection $values | |
* | |
* @return Collection | |
*/ | |
protected function getQuotedValues(Collection $values): Collection | |
{ | |
return $values->map(function ($row) { | |
return collect($row)->map(function ($value) { | |
if (is_null($value)) { | |
return 'NULL'; | |
} | |
return $this->builder->connection->getPdo()->quote($value); | |
}); | |
}); | |
} | |
/** | |
* @param Collection $values | |
* | |
* @return string | |
*/ | |
protected function generateInsertValues(Collection $values): string | |
{ | |
return $values->map(function (Collection $row) { | |
return '(' . $row->implode(',') . ')'; | |
})->implode(','); | |
} | |
/** | |
* @param Collection $columns | |
* | |
* @return string | |
*/ | |
protected function generateUpdateValues(Collection $columns): string | |
{ | |
return $columns->map(function ($column) { | |
return sprintf( | |
'%s=%s', | |
$column, | |
"VALUES({$column})" | |
); | |
})->implode(','); | |
} | |
/** | |
* @param array $values | |
* | |
* @return string | |
*/ | |
public function getQuery(array $values): string | |
{ | |
$values = collect($values); | |
$wrappedColumns = $this->getWrappedColumns($values); | |
return sprintf( | |
'INSERT INTO %s (%s) VALUES %s ON DUPLICATE KEY UPDATE %s', | |
$this->builder->grammar->wrapTable($this->builder->from), | |
$wrappedColumns->implode(','), | |
$this->generateInsertValues($this->getQuotedValues($values)), | |
$this->generateUpdateValues($wrappedColumns) | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Providers; | |
use App\Database\UpsertBuilder; | |
use Illuminate\Database\Query\Builder; | |
use Illuminate\Support\ServiceProvider; | |
class UpsertServiceProvider extends ServiceProvider | |
{ | |
public function boot() | |
{ | |
// | |
} | |
public function register() | |
{ | |
Builder::macro('upsert', function (array $values) { | |
/** @var Builder $this */ | |
$builder = new UpsertBuilder($this); | |
return $this->connection->insert($builder->getQuery($values)); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@mpdx there is no
updateOrCreate
method for Laravel Query Builder, it exists in Eloquent and also that method is not as atomic asON DUPLICATE KEY UPDATE