Skip to content

Instantly share code, notes, and snippets.

@bretto36
Last active June 22, 2025 02:51
Show Gist options
  • Save bretto36/b9fd23d902d6b52715c0e9c8e6c6c38d to your computer and use it in GitHub Desktop.
Save bretto36/b9fd23d902d6b52715c0e9c8e6c6c38d to your computer and use it in GitHub Desktop.

Revisions

  1. bretto36 revised this gist Nov 2, 2023. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion QueryBuilder.php
    Original file line number Diff line number Diff line change
    @@ -46,7 +46,8 @@ public function upsertWithoutUpdatedAt(array $values, $uniqueBy, $update = null)
    $parts = [];
    foreach ($update as $key => $value) {
    if (is_numeric($key)) {
    $parts[] = $this->grammar->wrap($value) . ' <> VALUES(' . $this->grammar->wrap($value) . ')';
    // Use <=> to ensure that it compares NULL values too
    $parts[] = 'NOT(' . $this->grammar->wrap($value) . ' <=> VALUES(' . $this->grammar->wrap($value) . '))';
    }
    }

  2. bretto36 revised this gist Nov 8, 2022. No changes.
  3. bretto36 created this gist Nov 8, 2022.
    37 changes: 37 additions & 0 deletions EloquentQueryBuilder.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    <?php

    namespace App\Database;

    use Illuminate\Database\Eloquent\Builder;

    class EloquentQueryBuilder extends Builder
    {
    /**
    * Insert new records or update the existing ones.
    *
    * @param array $values
    * @param array|string $uniqueBy
    * @param array|null $update
    * @return int
    */
    public function upsertWithoutUpdatedAt(array $values, $uniqueBy, $update = null)
    {
    if (empty($values)) {
    return 0;
    }

    if (!is_array(reset($values))) {
    $values = [$values];
    }

    if (is_null($update)) {
    $update = array_keys(reset($values));
    }

    return $this->toBase()->upsertWithoutUpdatedAt(
    $this->addTimestampsToUpsertValues($values),
    $uniqueBy,
    $this->addUpdatedAtToUpsertColumns($update)
    );
    }
    }
    26 changes: 26 additions & 0 deletions Invoice.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    <?php

    namespace App\Models;

    use App\Database\EloquentQueryBuilder;
    use App\Database\QueryBuilder;
    use Illuminate\Database\Eloquent\Model;

    class Invoice extends Model
    {
    protected function newBaseQueryBuilder()
    {
    $connection = $this->getConnection();

    return new QueryBuilder(
    $connection,
    $connection->getQueryGrammar(),
    $connection->getPostProcessor()
    );
    }

    public function newEloquentBuilder($query)
    {
    return new EloquentQueryBuilder($query);
    }
    }
    73 changes: 73 additions & 0 deletions QueryBuilder.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,73 @@
    <?php

    namespace App\Database;

    use Illuminate\Support\Arr;
    use Illuminate\Support\Facades\DB;

    class QueryBuilder extends \Illuminate\Database\Query\Builder
    {
    /**
    * Insert new records or update the existing ones without modifying the updated at column everytime
    *
    * @param array $values
    * @param array|string $uniqueBy
    * @param array|null $update
    * @return int
    */
    public function upsertWithoutUpdatedAt(array $values, $uniqueBy, $update = null)
    {
    if (empty($values)) {
    return 0;
    } elseif ($update === []) {
    return (int)$this->insert($values);
    }

    if (!is_array(reset($values))) {
    $values = [$values];
    } else {
    foreach ($values as $key => $value) {
    ksort($value);

    $values[$key] = $value;
    }
    }

    if (is_null($update)) {
    $update = array_keys(reset($values));
    }

    if ($updatedAtKey = array_search('updated_at', $update)) {
    $updatedAtValue = $values[array_key_first($values)]['updated_at'];

    // Replace the item and then move it to the front
    unset($update[$updatedAtKey]);

    $parts = [];
    foreach ($update as $key => $value) {
    if (is_numeric($key)) {
    $parts[] = $this->grammar->wrap($value) . ' <> VALUES(' . $this->grammar->wrap($value) . ')';
    }
    }

    $newUpdatedAtValue = DB::raw('CASE WHEN ' . implode(' OR ', $parts) . ' THEN "' . $updatedAtValue . '" ELSE ' . $this->grammar->wrap('updated_at') . ' END');

    // This MUST be at the front otherwise the query performs the updates before it compares the value
    $update = array_merge(['updated_at' => $newUpdatedAtValue], $update);
    }

    $this->applyBeforeQueryCallbacks();

    $bindings = $this->cleanBindings(array_merge(
    Arr::flatten($values, 1),
    collect($update)->reject(function ($value, $key) {
    return is_int($key);
    })->all()
    ));

    return $this->connection->affectingStatement(
    $this->grammar->compileUpsert($this, $values, (array)$uniqueBy, $update),
    $bindings
    );
    }
    }
    8 changes: 8 additions & 0 deletions usage
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    <?php

    $rows = [
    ['unique_field' => 123, 'name' => 'Test1'],
    ['unique_field' => 124, 'name' => 'Test2'],
    ];

    Invoice::upsertWithoutUpdatedAt($rows, ['unique_field'])