4

I have a table like this

Schema::create('user', function(Blueprint $table)
        {
            $table->increments('id');
            $table->datetime('DateTimeCode');
            $table->integer('user_id');
            $table->integer('Foo');
            $table->softDeletes();
            $table->timestamps();

            $table->unique(array('user_id', 'DateTimeCode'));
        });

When I try to

User::firstOrCreate($SomeData);

Or

$new_user = new User($SomeData);
$new_user->save();

I get

Integrity constraint violation: 1062 Duplicate entry

Which I don't get because I think my unique values are well defined and save() and firstOrCreate() should only be inserting a new record if it does not already exist.

Wistar
  • 3,770
  • 4
  • 45
  • 70
  • Can you also paste the `$someData` variable, maybe error is there – briankip Sep 18 '14 at 08:06
  • I am facing the same issue @Wistar. Did you find any solution? – VijayRana Jun 05 '16 at 09:18
  • I believe I did something like `//Checks if user exists $user = User::where('a', $a)->where('b', $b)->first(); if(!$user){ $new_user = new User($SomeData); $new_user->save(); }else{ //User already exists }` – Wistar Jun 05 '16 at 17:23
  • [mpyw/laravel-retry-on-duplicate-key: Automatically retry non-atomic upsert operation when unique key constraints are violated.](https://github.com/mpyw/laravel-retry-on-duplicate-key) – mpyw Nov 27 '21 at 06:53

2 Answers2

2

I had the same problem too... i solved it with a trait with a litte different approach then from here: https://gist.github.com/troatie/def0fba42fcfb70f873b7f033fbe255f

I explain in short why this approach. I do not like regular database locks for events that rarely happen, we have an exception on our busy server once every two days due to the firstOrCreate problematic. To make a lock and delete a lock on every create for such a rare incident... well... This approach takes care when it happens and because i am paranoid i take more than double care and thats it.

<?php

namespace App\Traits;

use Exception;

/**
 * Trait OrCreateTrait
 */
trait OrCreateTrait
{
    /**
     * @param array $attributes
     * @param array $values
     *
     * @return mixed
     */
    public static function updateOrCreate(array $attributes, array $values = [])
    {
        return static::tryManyTimes(function () use ($attributes, $values) {
            return (new static)->newQuery()->updateOrCreate($attributes, $values);
        });
    }

    /**
     * @param array $attributes
     * @param array $values
     *
     * @return mixed
     */
    public static function firstOrCreate(array $attributes, array $values = [])
    {
        return static::tryManyTimes(function () use ($attributes, $values) {
            return (new static)->newQuery()->firstOrCreate($attributes, $values);
        });
    }

    /**
     * @param callable $callback
     *
     * @return mixed
     */
    private static function tryManyTimes(callable $callback)
    {
        try {
            $output = $callback();
        } catch (Exception $e) {
            try {
                $output = $callback();
            } catch (Exception $e) {
                usleep(10000);
                try {
                    $output = $callback();
                } catch (Exception $e) {
                    usleep(100000);
                    try {
                        $output = $callback();
                    } catch (Exception $e) {
                        usleep(250000);
                        try {
                            $output = $callback();
                        } catch (Exception $e) {
                            $output = null;
                        }
                    }
                }
            }
        }
        return $output;
    }
}
ojsoft
  • 21
  • 2
1
$table->increments('id');
$table->integer('user_id');

And

$table->unique(array('user_id', 'DateTimeCode'));

Your user_id is unique, but not incrementing. Every time you try to create a user the same user_id will be taken - which is not possible.

nehalist
  • 1,434
  • 18
  • 45
  • 1
    I called it user_id but it could be any field that I don't want to increment but that I still want to be unique in combination with DateTimeCode. Plus, Laravel methods I stated in my question shouldn't be creating a new record if it already exists. – Wistar Jul 02 '14 at 22:16
  • This doesn't make any sense. When the `user_id` is the same as a record in the database it should simply do the `first` part and not the `create` part. – Derk Jan Speelman Jan 18 '18 at 09:18