是否有一种快速方法可以修改由Laravel's Fluent生成的SQL查询,而不是INSERT IGNORE
而不是INSERT
?
我正在尝试插入一个包含50个元素的数组。手动写出整个查询会使代码膨胀并使其更容易受到人为错误的影响。
答案 0 :(得分:11)
在你的模特中试试这个魔法:
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在多用户环境中可能发生的约束违规的好方法。
答案 1 :(得分:6)
我无法按照拉斯蒂斯拉夫的回答建议修补补丁。
这对我有用:
覆盖自定义Query Grammar类中的compileInsert
方法,该类扩展了框架的MySqlGrammar类。
通过从数据库连接实例调用setQueryGrammar
方法,使用此自定义语法类的实例。
所以,类代码是这样的:
<?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);
}
}
答案 2 :(得分:2)
这还处理多个同时插入(而不是一次记录)。
警告:Eric's comment below可能是正确的。这段代码适用于我以前的项目,但是在再次使用此代码之前,我将仔细研究它并添加测试用例并调整功能,直到它始终按预期工作为止。就像将TODO行向下移到
if
括号外一样简单。
将其放在模型的类中或模型扩展的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']]);
答案 3 :(得分:1)
对于这项工作,您需要创建一个新的语法,其中包含正确的字符串:
grammar.php (1)
语法是DB
或本例Database
存储连接的公共属性。这不是很直接,但从属性的可见性来看,您应该能够将特殊语法注入数据库层。
我还建议你把这个问题带到项目中,他们可能会更好地了解如何使这些案例更加灵活。
(1)这是前者,截至答案参考日期。如果你今天看到这个,你需要采用你使用的Laravel版本,例如Grammar.php for 4.0,这些类已进入laravel/framework
。
答案 4 :(得分:1)
不确定是否对任何人有帮助但最近我已经采用了hakre对Laravel 5的方法:
您必须更改以下3个文件才能使“插入忽略”工作:
在Builder.php(vendor / laravel / framework / src / illuminate / database / query / Builder.php)中,您必须克隆函数insert,将名称更改为insertIgnore并更改语法调用功能:$sql = $this->grammar->compileInsertIgnore($this, $values);)
在Grammar.php(vendor / laravel / framework / src / illuminate / database / query / grammars / Grammar.php)中,您必须克隆compileInsert函数并将其重命名为compileInsertIgnore,在此处将return更改为: return "insert ignore into $table ($columns) values $parameters";
在Connection.php(vendor / laravel / framework / src / illuminate / database / Connection.php)中,您必须简单地克隆函数insert并将其重命名为insertIgnore
现在你应该完成,连接能够识别函数insertIgnore,构建器能够指出它正确的语法,语法包含语句中的'ignore'。请问这不适用于MySQL,对其他数据库来说可能不太顺利。
答案 5 :(得分: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时间戳。
答案 6 :(得分:1)
如果现在有人读这本书:不需要任何黑客或Query Builder扩展。查询构建器本身提供了一个insertOrIgnore
方法,即可完成此操作。
只需使用
DB::table('tablename')->insertOrIgnore([
['column_name' => 'row1', 'column2_name' => 'row1'],
['column_name' => 'row2', 'column2_name' => 'row2']
]);
答案 7 :(得分:0)
我最后发现了这个https://github.com/yadakhov/insert-on-duplicate-key帮助了我很多
用户:: insertIgnore($用户); 这是我正在使用的方法,为它提供行数组及其返回的有效行
通过composer安装它:composer require yadakhov / insert-on-duplicate-key
答案 8 :(得分:0)
避免编写代码的选项是: https://github.com/guidocella/eloquent-insert-on-duplicate-key
我刚刚对其进行了测试-它一次可以处理5000个插入片段,有时可以重复...
有了它,您将获得以下功能:
WhilePrintingRecords;
Shared NumberVar qty;
答案 9 :(得分:-15)
$your_array = array('column' => 'value', 'second_column' => 'value');
DB::table('your_table')->insert($your_array);
请记住,我不知道您的数据来自哪里,但您应该始终对其进行消毒。如果你有多个记录,只需循环迭代。
就INSERT IGNORE
而言,在fluent库中找到INSERT
方法,创建一个名为insert_ignore的新方法,与插入完全相同,只需使用IGNORE
进行修改。 / p>