Only update field if form value exists

2020-07-02 13:01发布

问题:

I'm using Form Model Binding as such and updating my DB using the fill() and save() methods.

{{ Form::model($account) }}
  {{ Form::text('name', null, array('class'=>'class')) }}
  {{ Form::text('email', null, array('class'=>'class')) }}
  {{ Form::password('password', array('class'=>'class')) }}
  {{ Form::password('password_confirmation', array('class'=>'class')) }}
{{ Form::close() }}

Which fires my editAccount controller method:

$rules = array(
  'name' => array('required'),
  'email' => array('required'),
  'password' => array('confirmed')
);

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

if ($validator->fails())
{
 // Redirect
}

// Save to DB
$account->fill(Input::all());
$account->save();

Which works fine, but if no password was supplied (because the user doesn't want to update/modify it) then the password field is set to null in the db. So, I only want the password field to update if a new password value is supplied via the form.

I know I can do the following:

// Set the fields manually
$account->name = Input::get('name');
$account->email = Input::get('email');

// Only update the password field if a value is supplied
if (Input::get('password')) {
    $account->password = Input::get('password');
}
$account->save();

However I'm wondering if there is a more cleaner way to handle this? Like an UpdateOnlyIfValueExists() method within Laravel/Eloquent.

回答1:

Using Input::only('foo', 'bar') will grab only the values needed to complete the request - instead of using Input::all().

However, if 'foo' or 'bar' doesn't exist within the input, the key will exist with the value of null:

$input = Input::only('foo', 'bar');
var_dump($input);

// Outputs
array (size=2)
  'foo' => null
  'bar' => null

To filter in a clean way, any values with a null value:

$input = array_filter($input, 'strlen');

In your example, this would replace: $account->fill(Input::all());



回答2:

Create Base model and override update function like

/**
 * @param array $attributes
 * @return mixed
 */
public function update(Array $attributes = array()){
    foreach($attributes as $key => $value){
        if(!is_null($value)) $this->{$key} = $value;
    }
    return $this->save();
}

After use:

$model = Model::find($id);
$model->update(Input::only('param1', 'param2', 'param3'));


回答3:

Check this, you can validate if password is present in input, and exclude it from mass assignment. You can use Input::except and Input::only for this purpose

public function update ($id) {
    $user = User::findOrFail ($id);
    if (Input::get ('password') == '') {
        $user->update (Input::except ('password'));
    }
    else {
        $user->update (Input::all ());
    }

    //return something
}


回答4:

$data = $request->password ? $request->all():$request->except('password');
$user->update($data);

This will only update the password if it's not null



回答5:

I would stick with your latter example. Another option would be to use a mutator which checks the value there, and doesn't update if the value is empty. But in my opinion, Eloquent should not be responsible for doing that.

I'd also avoid using ALL input with fill(). Choose only what you want.



回答6:

This is a pretty shitty and common issue with Laravel (and other frameworks). My solution resembles some of the previous...

I always have the form data Input::all() stored in a variable at the beginning of the update/store methods. Since you usually need it at least twice (validate and create/update) it seems like a good practice. Then with that and before doing anything else I check in update() for the presence of the password, something like this:

$aFormData = Input::all();

if ( !$aFormData['password'] )
  unset( $aFormData['password'] );

... the rest of your code here using $aFormData ;) ...

And that's it, hope it helps!



回答7:

A much cleaner approach would be to use Eloquent Mutators

Under no circumstances would you allow a null or an empty string as password so you can safely define the following mutator in your Account model.

// Only accept a valid password and 
// hash a password before saving
public function setPasswordAttribute($password)
{
    if ( $password !== null & $password === '' )
    {
        $this->attributes['password'] = bcrypt($password);
    }
}

The above mutator will only set a password attribute if it is not null and an empty string. It also hashes the password before saving so you do not need to do it in your controller action or application elsewhere.



回答8:

Best approche is to use mutators as Noman Ur Rehman said above, but he had mistake in his code. Right one will be:

public function setPasswordAttribute($password){
   if ( $password !== null && $password !== '' )
      $this->attributes['password'] = Hash::make($password);
}