【问题标题】:Laravel create or update without two queriesLaravel 无需两次查询即可创建或更新
【发布时间】:2014-06-12 12:22:28
【问题描述】:

我正在尝试使用一种形式来创建和更新。两个动作都通过这个方法保存:

public function store() {
    $data = Input::all();
    $data['company_id'] = Auth::user()->company_id;
    $validator = Validator::make($data, Feature::$rules);

    if($validator->fails()) {
        return Redirect::back()->withErrors($validator)->withInput();
    }

    Feature::firstOrNew(['id' => Input::get('id')])->update($data);

    return Redirect::route('features.index');
}

如何改写这一行:

Feature::firstOrNew(['id' => Input::get('id')])->update($data);

所以它不会先从数据库中获取对象?没有必要这样做;我没有用它做任何事情。如果设置了Input::get('id'),它应该发出一个INSERT,如果没有设置,它应该发出一个UPDATE

【问题讨论】:

  • 深思熟虑:我认为这个“难”与许多缺乏“upsert”概念的数据库直接相关。数据库之间的不一致足以让大多数 ORM 跳过该功能。
  • @fideloper 不必使用ON DUPLICATE KEY UPDATE 样式查询;我们可以在 PHP 端进行分支。虽然在数据库端做会更可靠,但万一有人在你开始编辑和点击保存之间删除了记录。
  • 当然,这并不是说这听起来也不可行!

标签: php laravel laravel-4


【解决方案1】:

如果你遵循 laravel 的资源路由方法,你应该使用一个名为 update 的单独方法来更新你的模型,这样分离可以由框架完成。有了这个,仍然可以重复使用您的表单。

如果你真的想避免使用新方法来更新模型,你可以重写如下:

public function store() {
    $model = Input::has('id')
        ? ModelClass::findOrFail(Input::get('id'))
        : new ModelClass;

    $inputData = Input::all();
    $validator = Validator::make($inputData, ModelClass::$rules);

    if ($validator->fails()) {
        return Redirect::back()
            ->withErrors($validator)
            ->withInput();
    }

    $model->fill($inputData);
    $model->save();

    return Redirect::route('modelclass.index');
}

【讨论】:

  • 这仍然会通过findOrFail 发出第二个查询,但我想出错总比什么都不做更新要好。
  • 为了避免第二个 sql 查询,我认为您必须使用带有 INSERT INTO ... ON DUPLICATE KEY UPDATE 语句的原始查询,但这样您就可以绕过 Eloquent ORM。
  • "ON DUPLICATE KEY UPDATE" 是 MySQL 特有的,任何人都想知道。这意味着使用它会将您与代码中的特定数据库类型联系起来。对许多人来说,这可能无关紧要,但在使用前值得考虑。
【解决方案2】:

你可以试试这个:

$data = Input::except('_token');
$newOrUpdate = Input::has('id') ? true : false;
$isSaved = with(new Feature)->newInstance($data, $newOrUpdate)->save();

如果$data 包含id/primary key,它将被更新,否则将执行插入。换句话说,为了更新,您需要将$data/attributes中的id/primary key与其他属性一起传递,并且newInstance方法中的第二个参数必须是true

如果您将false/default 传递给newInstance 方法,那么它将执行插入,但$data 不能包含id/primary 键。你明白了,这三行代码应该可以工作。 $isSaved 将是 Boolean 值,true/false

你可以用一行写代码:

with(new Feature)->newInstance($data, array_key_exists('id', $data))->save();

如果$data 包含id/primary key,那么它将被更新,否则将执行插入。

【讨论】:

  • 我唯一不喜欢的是它两次实例化 Feature 对象。 newInstance 应该是静态的,但不是。也许我会创建一个辅助方法。
【解决方案3】:

我曾经遇到过这个问题,并在 Laravel 上创建了一个接受的 pull request,你可以使用它。试试下面的代码。您基本上需要的方法是findOrNew

public function store($id=0)
{
    $user = User::findOrNew($id);
    $data = Input::all();

    if (!$user->validate($data)) {
        return Redirect::back()->withInput()->withErrors($user->errors);
    }

    $user->fill($data);
    $user->save();
    return Redirect::to('/')->with('message','Record successfully saved!');
}

我的模型使用自我验证,但您可以像现在这样创建自己的验证。

【讨论】:

  • 您忽略了问题的整个前提。 findOrNew 是一个查询,save 是另一个。不需要有两个。我确实喜欢将$id 作为参数传入。实际上,findOrNew 也可能对合并我的创建/编辑方法有用..hrm.
  • 是的,您可以使用 $id 作为参考,将您的创建/编辑方法合并到 findOrNew 的单个帖子中。如果$id0null 或模型不存在,则为INSERT,否则为UPDATE。这使您的表单和代码可重用。
  • 你没有关注。 findOrNew 发出 SELECT。我是说我们可以跳过SELECT。那里没有我需要的数据,一切都来自表单。
【解决方案4】:

如果您的模型中的所有字段都无人看管,我认为您可以这样做:

$feature = new Feature($data);
$feature->exists = Input::has('id');
$feature->save();

如果你有一些受保护的字段,那么你可以先解除它:

$feature = new Feature();
$feature->unguard();
$feature->fill($data);
$feature->exists = Input::has('id');
$feature->reguard();
$feature->save();

如果您不对模型执行任何其他操作,则实际上不需要调用 reguard()

【讨论】:

    【解决方案5】:

    这是我用的:

    Model::updateOrCreate(
       ['primary_key' => 8],
       ['field' => 'value', 'another_field' => 'another value']
    );
    

    第二个参数是要保存的模型的数据数组。

    【讨论】:

    • 它仍然进行 2 次查询:php if (! is_null($instance = $this->where($attributes)->first())) { return $instance; } return $this->model->newInstance($attributes);
    【解决方案6】:

    根据主键id

    查找或新建
    $data = Input::all();
    $user = User::findOrNew($id);  // if exist then update else insert
    $user->name= $data['user_name'];
    $user->save();
    

    First 或 New 基于非主键单字段

    $user = User::firstOrNew(['field' => $data['value'] ]);
    $user->name= $data['user_name'];
    $user->save();
    

    First or New 基于非主键multiple filed

    $user = User::firstOrNew([
                         'field1'=>$data['value1'],
                         'field2'=>$data['value2']
                         ]);
    $user->name= $data['user_name'];
    $user->save();
    

    【讨论】:

    • findOrNew + save 是两个查询。 firstOrNew 不做更新。
    猜你喜欢
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-15
    • 1970-01-01
    • 2014-01-12
    • 1970-01-01
    相关资源
    最近更新 更多