INSERT IGNORE using Laravel's Fluent

前端 未结 10 1275
执笔经年
执笔经年 2020-12-11 02:12

Is there a quick way to modify a SQL query generated by Laravel\'s Fluent to have an INSERT IGNORE instead of the usual INSERT?

I\'m trying

相关标签:
10条回答
  • 2020-12-11 02:55

    Answer for Laravel 5.8.33+

    If anyone reads this nowadays: there's no need for any hacks or Query Builder extensions. The query builder natively provides an insertOrIgnore method that does just that.

    Just use

    DB::table('tablename')->insertOrIgnore([
        ['column_name' => 'row1', 'column2_name' => 'row1'],
        ['column_name' => 'row2', 'column2_name' => 'row2']
    ]);
    

    See the documentation or the API docs for details.

    0 讨论(0)
  • 2020-12-11 02:59

    Add the follow method insertIgnore to your Model

    <?php
    
    namespace App;
    
    use Illuminate\Auth\Authenticatable;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Auth\Passwords\CanResetPassword;
    use Illuminate\Foundation\Auth\Access\Authorizable;
    use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
    use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
    use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
    
    class User extends Model implements AuthenticatableContract,
                                        AuthorizableContract,
                                        CanResetPasswordContract
    {
        use Authenticatable, Authorizable, CanResetPassword;
    
        /**
         * The database table used by the model.
         *
         * @var string
         */
        protected $table = 'users';
    
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        protected $fillable = ['name', 'email', 'password'];
    
        /**
         * The attributes excluded from the model's JSON form.
         *
         * @var array
         */
        protected $hidden = ['password', 'remember_token'];
    
    
        public static function insertIgnore(array $attributes = [])
        {
            $model = new static($attributes);
    
            if ($model->usesTimestamps()) {
                $model->updateTimestamps();
            }
    
            $attributes = $model->getAttributes();
    
            $query = $model->newBaseQueryBuilder();
            $processor = $query->getProcessor();
            $grammar = $query->getGrammar();
    
            $table = $grammar->wrapTable($model->getTable());
            $keyName = $model->getKeyName();
            $columns = $grammar->columnize(array_keys($attributes));
            $values = $grammar->parameterize($attributes);
    
            $sql = "insert ignore into {$table} ({$columns}) values ({$values})";
    
            $id = $processor->processInsertGetId($query, $sql, array_values($attributes));
    
            $model->setAttribute($keyName, $id);
    
            return $model;
        }
    }
    

    You can use:

    App\User::insertIgnore([
        'name' => 'Marco Pedraza',
        'email' => 'mpdrza@gmail.com'
    ]);
    

    The next query it will be executed:

    insert ignore into `users` (`name`, `email`, `updated_at`, `created_at`) values (?, ?, ?, ?)
    

    This method automatically add/remove the Eloquent timestamps if you have enabled or disabled.

    0 讨论(0)
  • 2020-12-11 02:59

    I lastly found this one https://github.com/yadakhov/insert-on-duplicate-key which helped me alot

    User::insertIgnore($users); this is the method I am using, giving array of rows to it and its returning effected rows

    install it through composer: composer require yadakhov/insert-on-duplicate-key

    0 讨论(0)
  • 2020-12-11 03:07

    I couldn't monkey patch as suggested in Rastislav's answer.

    This is what worked for me:

    1. Override compileInsert method in a custom Query Grammar class, which extends the framework's MySqlGrammar class.

    2. Use an instance of this custom grammar class by calling the setQueryGrammar method from the DB connection instance.

    So, the class code is like this:

    <?php
    
    namespace My\Namespace;
    
    use Illuminate\Database\Query\Builder;
    use Illuminate\Database\Query\Grammars\MySqlGrammar;
    
    /**
     * Changes "INSERT" to "INSERT IGNORE"
     */
    class CustomMySqlGrammar extends MySqlGrammar
    {
        /**
         * Compile an insert statement into SQL.
         *
         * @param  \Illuminate\Database\Query\Builder  $query
         * @param  array  $values
         * @return string
         */
        public function compileInsert(Builder $query, array $values)
        {
            // Essentially we will force every insert to be treated as a batch insert which
            // simply makes creating the SQL easier for us since we can utilize the same
            // basic routine regardless of an amount of records given to us to insert.
            $table = $this->wrapTable($query->from);
    
            if (! is_array(reset($values))) {
                $values = [$values];
            }
    
            $columns = $this->columnize(array_keys(reset($values)));
    
            // We need to build a list of parameter place-holders of values that are bound
            // to the query. Each insert should have the exact same amount of parameter
            // bindings so we will loop through the record and parameterize them all.
            $parameters = collect($values)->map(function ($record) {
                return '('.$this->parameterize($record).')';
            })->implode(', ');
    
            return "insert ignore into $table ($columns) values $parameters";
        }
    }
    

    I copied the compileInsert method from the framework's class and then, inside the method, I have only changed insert to insert ignore. Everything else has been kept the same.

    Then, in the specific spot of code, in the application (a scheduled task), where I needed "insert ignore", I have simply done as follows:

    <?php
    
    use DB;
    use My\Namespace\CustomMySqlGrammar;
    
    class SomeClass
    {
        public function someMethod()
        {
            // Changes "INSERT" to "INSERT IGNORE"
            DB::connection()->setQueryGrammar(new CustomMySqlGrammar());
    
            // et cetera... for example:
            ModelClass::insert($data);
        }
    }
    
    0 讨论(0)
提交回复
热议问题