Laravel - 在插入

时间:2015-05-22 02:42:50

标签: php mysql eloquent laravel-5

我填充了一种形式,其中生成的每个文本字段都基于数据库结果。我只需使用id命名每个文本字段。现在填写表单时,我使用控制器来保存它。但是在插入数据库之前,我循环Request::input()来检查每个项目是否存在这样的条目。我只是想知道是否有有效方式来检查循环中的每个项目以将其插入到db中。这是我的代码

public function store(Request $request, $id, $inid)
{       
    $startOfDay = Carbon::now()->startOfDay();
    $endOfDay = Carbon::now()->endOfDay();

    $instruments = InstrumentReading::whereBetween('created_at', [$startOfDay, $endOfDay])
                                    ->where('iv_inid', '=', $inid)
                                    ->get();
    foreach ($request->input() as $k => $v) {
        $read = new InstrumentReading;
        $read->iv_inid = $inid;
        $read->iv_ipid = $k;
        $read->iv_usid = Auth::user()->id;
        $read->iv_reading = $v;
        $read->save();
    }
    if ($instruments->count() > 0) {            
        //to filter the iv_ipid...
        foreach($instruments as $instrument)
        {
            $instrument->iv_status = "VOID";
            $instrument->save();
        }
    }

}

3 个答案:

答案 0 :(得分:4)

用高效的方法来说,你能做的就是简单地检查/获取 ONLY 数据库中所有可行的行,如果行已经插入则检查循环。还只提取iv_ipid列,因为我们不需要表中的所有列来进行检查。选择我们需要的列会更快。您可以直接在Fluent上使用EloquentQuery Builder)从数据库中提取数据,因为它可以大大提高此类简单查询的效果。

public function store(Request $request, $id, $inid)
{
    // Search only records with submitted iv_ipid, iv_inid and created today
    $alreadyInserted = DB::table('instrument_readings')
                   ->whereBetween('created_at', [
                       Carbon::now()->startOfDay(), 
                       Carbon::now()->endOfDay()
                   ])
                   // Get only records with submitted iv_ipid
                   ->whereIn('iv_ipid', array_keys($request->input()))
                   // Get records with given iv_inid only
                   ->where('iv_inid', $inid)
                   // For our check we need only one column, 
                   // no need to select all of them, it will be fast
                   ->select('iv_ipid')
                   // Get the records from DB
                   ->lists('iv_ipid');

    foreach ($request->input() as $k => $v) {
        // Very simple check if iv_ipid is not in the array
        // it does not exists in the database
        if (!in_array($k, $alreadyInserted)) {
            $read = new InstrumentReading;
            $read->iv_inid = $inid;
            $read->iv_ipid = $k;
            $read->iv_usid = Auth::user()->id;
            $read->iv_reading = $v;
            $read->save();
        } else {
            //todo
        }
}

这是迄今为止建议的最有效方式,因为您只会立即获取您感兴趣的记录,而不是今天的所有记录。此外,您只获取一列,我们需要进行检查。 Eloquent ususlally在性能方面给出了很多过热,因此在建议的代码中我直接使用Fluent,这将提高代码执行速度约20%。

原始代码中的错误是每次循环时都在进行数据库调用。当您需要这么简单的任务作为检查时,从不将数据库调用,查询等放入循环中。这太过分了。而是在循环之前选择所有需要的数据,然后进行检查。

现在,这是为了您只需要将新记录保存到数据库。如果你想操纵循环中的每个记录,让我们说你需要循环遍历每个提交的条目,获取模型或创建它(如果它不存在)然后用这个模型做其他事情,最多有效的方式将是这一个:

public function store(Request $request, $id, $inid)
{
    foreach ($request->input() as $k => $v) {
       // Here you search for match with given attributes
       // If object in DB with this attributes exists
       // It will be returned, otherwise new one will be constructed
       // But yet not saved in DB
       $model = InstrumentReading::firstOrNew([
           'iv_inid' => $inid,
           'iv_ipid' => $k,
           'iv_usid' => Auth::user()->id
       ]);

       // Check if it is existing DB row or a new instance
       if (!$model->exists()) {
           // If it is a new one set $v and save
           $model->iv_reading = $v;
           $model->save();
       }

    // Do something with the model here
    .....   
}

这样,Laravel将检查带有传递参数的模型是否已经存在于数据库中,如果存在,它将为您返回。如果它不存在,它将创建它的新实例,因此您可以设置$v并保存到db。因此,您可以使用此模型执行任何其他操作,并确保在此之后它存在于数据库中。

答案 1 :(得分:1)

每次需要检查值时,您的代码都会向数据库发送请求。相反,搜索当天的所有值,然后检查值。这种方法只会向数据库发送一次请求。

$startOfDay = Carbon::now()->startOfDay();
$endOfDay = Carbon::now()->endOfDay();

// Search only this day
$instruments = InstrumentReading::whereBetween('created_at', [$startOfDay, $endOfDay])->get();

foreach($instruments as $instrument)
{
    // Check the value
}

答案 2 :(得分:1)

第一种方法(效率优先)

考虑使用简单的SQL INSERT IGNORE查询并使用Fluent,即:

  • 创建一个包含以下内容的复合唯一键:

    1. iv_inid
    2. iv_ipid
    3. created_time,最多一小时的粒度,这很重要,因为created_at可能具有比预期目的更大的粒度,并且可能会使事情变慢。
  • 使用DB,即:

  

DB ::查询(       " INSIGN IGNORE INTO $ yourTable VALUES(...)"   );

优点:
- 速度极快,所有必要的检查都在数据库服务器上完成

缺点:
- 您无法知道哪些值触发了重复值/唯一键冲突,因为相关错误被视为警告。

第二种方法(先方便)

使用firstOrFail,即:

$startOfDay = Carbon::now()->startOfDay();
$endOfDay = Carbon::now()->endOfDay();

// ... for

try {
    InstrumentReading::where('iv_inid', $inid)
        ->where('iv_ipid', $k)
        ->whereBetween('created_at', [$startOfDay, $endOfDay])
        ->firstOrFail();

    continue;
} catch (ModelNotFoundException $e) {
    $instrumentReading = InstrumentReading::create([
        // your values
    ]);
}

// ... endfor

优点:
- 易于实施

缺点:
- 比简单查询慢一点