181 lines
5.4 KiB
PHP
181 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Database\Query\Grammars;
|
|
|
|
use Illuminate\Database\Query\Builder;
|
|
use Illuminate\Database\Query\Grammars\PostgresGrammar as BasePostgresGrammar;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class PostgresGrammar extends BasePostgresGrammar
|
|
{
|
|
|
|
public function getTableOrAlias(Builder $query)
|
|
{
|
|
return last(preg_split('/\s+as\s+/i', $query->from));
|
|
}
|
|
|
|
public function getTableName(Builder $query)
|
|
{
|
|
return current(preg_split('/\s+as\s+/i', $query->from));
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
*
|
|
* Ex:
|
|
* update transactions as t
|
|
* set
|
|
* date = c.date,
|
|
* value = c.value
|
|
* from ( values
|
|
* (1, '2023-01-05 00:00:00'::timestamp, 23.44),
|
|
* (2, '2023-01-03 00:00:00'::timestamp, 23.44)
|
|
* ) as c (id, date, value)
|
|
* where t.id = c.id";
|
|
* @throws \Exception
|
|
*/
|
|
|
|
public function compileMassUpdateWithFrom(Builder $query, array $items, string $uniqueBy = 'id')
|
|
{
|
|
if (count($items) === 0) {
|
|
throw new \Exception('values is empty');
|
|
}
|
|
|
|
$table = $this->wrapTable($query->from);
|
|
$tableName = $this->getTableName($query);
|
|
$tableOrAlias = $this->getTableOrAlias($query);
|
|
$tableAuxName = 'mu';
|
|
|
|
$properties = array_keys($items[0]);
|
|
|
|
$columnTypes = $this->getColumnTypes($tableName, $properties);
|
|
|
|
$columnsSql = [
|
|
// $this->wrap('type') . ' = ("mu"."id"::int + "t"."type" + 2)::int',
|
|
// $this->wrap('status') . ' = (case "mu"."id"::int when 1 then \'paid\' end)::text',
|
|
];
|
|
foreach ($properties as $property) {
|
|
if ($property !== $uniqueBy) {
|
|
$columnsSql[] = $this->wrap($property) . ' = ' . $this->wrap("$tableAuxName.$property") . '::' . $columnTypes[$property];
|
|
}
|
|
}
|
|
$columns = implode(', ', $columnsSql);
|
|
|
|
$parameters = [];
|
|
foreach ($items as $item) {
|
|
if (!isset($item[$uniqueBy])) {
|
|
throw new \Exception('column \'' . $uniqueBy . '\' not found');
|
|
}
|
|
|
|
$parametersItem = [];
|
|
foreach ($properties as $property) {
|
|
$parametersItem[] = $this->parameter($item[$property]);
|
|
}
|
|
|
|
$parameters[] = '(' . implode(', ', $parametersItem) . ')';
|
|
}
|
|
$parameters = implode(', ', $parameters);
|
|
|
|
$parametersColumns = implode(', ', $properties);
|
|
|
|
$from = " from (values $parameters) as $tableAuxName ($parametersColumns)";
|
|
|
|
$query->where("$tableOrAlias.$uniqueBy", '=', $query->raw($this->wrap("$tableAuxName.$uniqueBy") . '::' . $columnTypes[$uniqueBy]));
|
|
|
|
$where = $this->compileWheres($query);
|
|
|
|
return "update {$table} set {$columns}{$from} {$where}";
|
|
}
|
|
|
|
public function prepareBindingsForMassUpdateWithFrom(array $bindings, array $items)
|
|
{
|
|
$properties = array_keys($items[0]);
|
|
|
|
$values = [];
|
|
foreach ($items as $item) {
|
|
foreach ($properties as $property) {
|
|
$values[] = $item[$property];
|
|
}
|
|
}
|
|
|
|
$values = collect($values)
|
|
->map(function ($value, $column) {
|
|
return is_array($value) || ($this->isJsonSelector($column) && !$this->isExpression($value))
|
|
? json_encode($value)
|
|
: $value;
|
|
})
|
|
->all();
|
|
|
|
$cleanBindings = Arr::except($bindings, 'select');
|
|
|
|
return array_values(
|
|
array_merge($values, Arr::flatten($cleanBindings))
|
|
);
|
|
}
|
|
|
|
private function getColumnTypes(string $tableName, array $columns): array
|
|
{
|
|
$key = vsprintf('column;types;%s;%s', [
|
|
$tableName,
|
|
implode(';', $columns)
|
|
]);
|
|
|
|
return Cache::store('array')->rememberForever($key, function () use ($tableName, $columns) {
|
|
$schemaColumns = DB::table('information_schema.columns')
|
|
->select([
|
|
'column_name',
|
|
'udt_name',
|
|
])
|
|
->where('table_name', '=', $tableName)
|
|
->whereIn('column_name', $columns)
|
|
->get();
|
|
|
|
$types = [];
|
|
foreach ($schemaColumns as $schemaColumn) {
|
|
$types[$schemaColumn->column_name] = $schemaColumn->udt_name;
|
|
}
|
|
|
|
return $types;
|
|
});
|
|
}
|
|
|
|
public function compileJoinFrom(Builder $query, array $items, $tableAlias)
|
|
{
|
|
if (count($items) === 0) {
|
|
throw new \Exception('items is empty');
|
|
}
|
|
|
|
$values = [];
|
|
$properties = array_keys($items[0]);
|
|
foreach ($items as $item) {
|
|
$valuesItem = [];
|
|
foreach ($properties as $property) {
|
|
$valuesItem[] = $this->parameter($item[$property]);
|
|
}
|
|
|
|
$values[] = '(' . implode(', ', $valuesItem) . ')';
|
|
}
|
|
|
|
$values = implode(', ', $values);
|
|
$properties = implode(', ', $properties);
|
|
|
|
return "(values $values) as $tableAlias ($properties)";
|
|
}
|
|
|
|
public function prepareBindingsJoinFrom(array $items)
|
|
{
|
|
$values = [];
|
|
|
|
$properties = array_keys($items[0]);
|
|
foreach ($items as $item) {
|
|
foreach ($properties as $property) {
|
|
$values[] = $item[$property];
|
|
}
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
}
|