12

LaravelのFluentによって生成されたSQLクエリをINSERT IGNORE通常の代わりに変更する簡単な方法はありますINSERTか?

50個の要素を持つ配列を挿入しようとしています。クエリ全体を手動で書き出すと、コードが肥大化し、人為的エラーの影響を受けやすくなります。

4

10 に答える 10

15

モデルでこの魔法を試してください:

public static function insertIgnore($array){
    $a = new static();
    if($a->timestamps){
        $now = \Carbon\Carbon::now();
        $array['created_at'] = $now;
        $array['updated_at'] = $now;
    }
    DB::insert('INSERT IGNORE INTO '.$a->table.' ('.implode(',',array_keys($array)).
        ') values (?'.str_repeat(',?',count($array) - 1).')',array_values($array));
}

このように使用します:

Shop::insertIgnore(array('name' => 'myshop'));

これは、「name」プロパティが一意のキーである場合に、マルチユーザー環境でfirstOrCreateで発生する可能性のある制約違反を防ぐための優れた方法です。

于 2014-08-24T13:43:34.423 に答える
5

Rastislavの回答で示唆されているように、モンキーパッチを適用できませんでした。

これは私のために働いたものです:

  1. compileInsertフレームワークのMySqlGrammarクラスを拡張するカスタムQueryGrammarクラスのオーバーライドメソッド。

  2. setQueryGrammarDB接続インスタンスからメソッドを呼び出して、このカスタム文法クラスのインスタンスを使用します。

したがって、クラスコードは次のようになります。

<?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";
    }
}

フレームワークのクラスからメソッドをコピーしたcompileInsert後、メソッド内でに変更insertしただけinsert ignoreです。他のすべては同じに保たれています。

次に、コードの特定の場所で、「挿入無視」が必要なアプリケーション(スケジュールされたタスク)で、次のように実行しました。

<?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);
    }
}
于 2017-07-17T18:58:50.773 に答える
4

2018年にLaravelEloquentの回答を更新

これは、(一度に1つのレコードではなく)複数の同時挿入も処理します。

警告:以下のエリックのコメントはおそらく正しいです。このコードは私の過去のプロジェクトでは機能しましたが、このコードを再度使用する前に、コードを詳しく調べてテストケースを追加し、常に意図したとおりに機能するまで関数を調整しました。ifTODO行を中括弧の外側に移動するのと同じくらい簡単かもしれません。

これをモデルのクラスまたはモデルが拡張するBaseModelクラスに配置します。

/**
 * @see https://stackoverflow.com/a/25472319/470749
 * 
 * @param array $arrayOfArrays
 * @return bool
 */
public static function insertIgnore($arrayOfArrays) {
    $static = new static();
    $table = with(new static)->getTable(); //https://github.com/laravel/framework/issues/1436#issuecomment-28985630
    $questionMarks = '';
    $values = [];
    foreach ($arrayOfArrays as $k => $array) {
        if ($static->timestamps) {
            $now = \Carbon\Carbon::now();
            $arrayOfArrays[$k]['created_at'] = $now;
            $arrayOfArrays[$k]['updated_at'] = $now;
            if ($k > 0) {
                $questionMarks .= ',';
            }
            $questionMarks .= '(?' . str_repeat(',?', count($array) - 1) . ')';
            $values = array_merge($values, array_values($array));//TODO
        }
    }
    $query = 'INSERT IGNORE INTO ' . $table . ' (' . implode(',', array_keys($array)) . ') VALUES ' . $questionMarks;
    return DB::insert($query, $values);
}

このように使用します:

Shop::insertIgnore([['name' => 'myShop'], ['name' => 'otherShop']]);

于 2018-07-19T20:14:25.257 に答える
3

Laravel5.8.33+の回答

最近誰かがこれを読んだら:ハックやQueryBuilder拡張機能は必要ありません。クエリビルダーは、insertOrIgnoreまさにそれを行うメソッドをネイティブに提供します。

使用するだけ

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

詳細については、ドキュメントまたはAPIドキュメントを参照してください。

于 2020-08-19T15:57:28.217 に答える
1

この仕事では、正しい文字列を含む新しい文法を作成する必要があります。

grammar.php (1)

DB文法は、またはこの場合はDatabase保存された接続のパブリックプロパティです。これは実際には簡単ではありませんが、プロパティの可視性から、データベースレイヤーに特別な文法を挿入できるはずです。

また、プロジェクトで問題を提起することをお勧めします。彼らはおそらく、このような場合にそれをより柔軟にする方法についてより良いアイデアを持っているでしょう。


(1)これは、回答参照の日付までは前者でした。これが今日見られる場合は、使用するLaravelバージョン(4.0のGrammar.phpなど)を採用する必要があります。これらのクラスはに移動しましlaravel/frameworkた。

于 2012-09-27T13:31:49.383 に答える
1

誰かに役立つかどうかはわかりませんが、最近、hakreのアプローチをLaravel5に適応させました。

Insert Ignoreを機能させるには、次の3つのファイルを変更する必要があります。

  1. Builder.php(vendor / laravel / framework / src / illuminate / database / query / Builder.php)では、名前をinsertIgnoreに変更し、文法呼び出し関数を次のように変更して、関数insertをクローン化する必要があります。$sql = $this->grammar->compileInsertIgnore($this, $values);)

  2. Grammar.php(vendor / laravel / framework / src / illuminate / database / query / grammars / Grammar.php)で、compileInsert関数のクローンを作成し、名前をcompileInsertIgnoreに変更する必要があります。ここで、returnを次のように変更します。return "insert ignore into $table ($columns) values $parameters";

  3. Connection.php(vendor / laravel / framework / src / illuminate / database / Connection.php)では、関数insertのクローンを作成し、名前をinsertIgnoreに変更するだけです。

これで完了です。接続は関数insertIgnoreを認識でき、ビルダーはそれを正しい文法に向けることができ、文法にはステートメントに「ignore」が含まれています。これはMySQLでうまく機能することに注意してください。他のデータベースでは、これほどスムーズではない可能性があります。

于 2016-08-16T16:09:13.997 に答える
1

次のメソッドinsertIgnoreをモデルに追加します

<?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;
    }
}

次を使用できます。

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

次のクエリが実行されます。

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

このメソッドは、有効または無効にした場合、Eloquentタイムスタンプを自動的に追加/削除します。

于 2016-11-02T02:53:16.753 に答える
0

私は最後にこれを見つけましたhttps://github.com/yadakhov/insert-on-duplicate-keyこれは私を大いに助けました

User :: insertIgnore($ users); これは私が使用しているメソッドであり、行の配列とその返される影響を受けた行を提供します

作曲家を通してそれをインストールしてください:作曲家はyadakhov/insert-on-duplicate-keyを必要とします

于 2018-05-10T05:05:13.360 に答える
0

コードの記述を回避するオプションは次のとおりです: https ://github.com/guidocella/eloquent-insert-on-duplicate-key

私は今それをテストしました-それは時々重複して私の5000のインサートで動作します...

これを使用すると、次の機能を利用できます。

User::insertOnDuplicateKey($data);
User::insertIgnore($data);
于 2019-07-15T09:45:52.903 に答える
-18
$your_array = array('column' => 'value', 'second_column' => 'value');

DB::table('your_table')->insert($your_array);

データがどこから来ているのかわかりませんが、常にサニタイズする必要があります。複数のレコードがある場合は、ループで繰り返します。

までは、流暢なライブラリでメソッドをINSERT IGNORE見つけ、INSERTinsert_ignoreという新しいメソッドを挿入とまったく同じ方法で作成し、。で変更しIGNOREます。

于 2012-09-27T13:26:40.647 に答える