Only update field if form value exists - laravel

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.

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());

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'));

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
}

$data = $request->password ? $request->all():$request->except('password');
$user->update($data);
This will only update the password if it's not null

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.

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!

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.

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

Related

Hash password after Validator::make

I want to hash the password after validating the data.
My code:
public function create(Request $request){
$data = Validator::make($request->only(['name', 'email', 'password']), [
'name' => 'required|min:3:max:20',
'email' => 'required|email|unique:users',
'password' => 'required|min:6',
]);
if ($data->fails()) {
//Do something
}else{
User::create($data);
}
}
So how to hash the password after validation?
I tried to override the password inside $data, But it's not working
$data->safe()->only('password') = Hash::make($data->safe()->only('password'));
I can't use $request['password'] as I won't be able to validate it and check if it's empty ..etc.
An alternative approach would be to use an Eloquent Mutator to automatically hash the password field when it is set.
// User Model
public function setPasswordAttribute($value): void
{
$this->attributes['password'] = Hash::make($value);
}
I personally like this approach because you won't have to worry about it in the controllers. You can just set it once and forget about it. :)
The quick answer to your question would be to use the Eloquent make function.
$user = User::make($data);
$user->password = Hash::make($password);
$user->save();
Where $password is where ever you have the password stored. In your case:
$password = $data->safe()->only('password')
There may be a more efficient way, based on your exact intent. In general, the above solution will work.
The make function creates an Eloquent model, but does not store it in the database. That's why you can edit the values and then call $user->save()
Use a mutator method to set the password. Override the method by adding:
public function setPasswordAttribute($value)
{
$this->attributes['password'] = 'some random password generator';
}
there is document:
https://laravel.com/docs/8.x/eloquent-mutators#defining-a-mutator

change password when is set

i want to change password in my update method , when is $request->data['password'] is set and is not null
if is not set old password be replace
this is my code but i'm faced with error !
$user=Model::whereId($id)->update([
"updated_at" => Carbon::now(),
"department_id" => $department,
"username" => $request->data['username'],
'password'=>isset($request->data['password'])?bcrypt($request->data['password']):$user->password,
]);
You can use this solution
$data = [
"updated_at" => Carbon::now(),
"department_id" => $department,
"username" => $request->data['username'],
];
if (! empty($request->data['password'])) {
$data["password"] = bcrypt($request->data['password']);
}
$user = Model::whereId($id)->update($data);
isset checks if the password field exists in the request but if it exists there and is null then it will still pass through. Also your ternary is the other way around.
you can use empty for what you are trying to achieve like:
'password'=>!empty($request->data['password'])?bcrypt($request->data['password']):$user->password,
But if you want to use the ternary there then you must have retrieved your user model before that so your fallback actually has the value of the user password in the database. A similar approach is:
$user=Model::whereId($id)->first();
$user->updated_at = Carbon::now();
$user->department_id => $department;
$user->username = $request->data['username'];
if(!empty($request->data['password']){
$user->password=bcrypt($request->data['password']);
}
$user->save();
That way you don't need a fallback. Returning the user model with the latest changes you can use the fresh() function after you save()
return $user->fresh()
Use "dot" notation to access the arrays (like name="data['password']") :
$password = $request->input('data.password');
$model = Model::find($id);
$model->username = $request->input('data.username');
$model->department_id = $department;
if($password){
$model->password = Hash::make($password);
}
$model->save();
You may use the has method to determine if a value is present on the request. The has method returns true if the value is present on the request:
if ($request->has('data.password')) {
//
}
Or use whenHas. The whenHas method will execute the given closure if a value is present on the request:
$request->whenHas('data.password', function ($input) {
//
});

How to use two `with` in Laravel?

I try to show two flash messages in Laravel using with:
return redirect('cabinet/result')->with('user', $client->unique_code)->with('fio', $client->name.' '.$client->secondname. ' '.$client->patronymic);
Then I display this as:
{{ session('fio') }} {{ session('unique_code') }}
It shows me nothing
Firstly, when you pass data with the method 'with' to a view, it does not get stored in the session, it is just made available as a variable with the same name to the view that gets loaded after the redirect takes place.
You have two options:
Passing an array of key value pairs to the view
You may pass an array of data to views:
return view('greetings', ['name' => 'Victoria', 'last_name' => 'Queen']);
As you can see by the way the method is implemented in {root}/vendor/laravel/framework/src/Illuminate/View/View.php
/**
* Add a piece of data to the view.
*
* #param string|array $key
* #param mixed $value
* #return $this
*/
public function with($key, $value = null)
{
if (is_array($key)) {
$this->data = array_merge($this->data, $key);
} else {
$this->data[$key] = $value;
}
return $this;
}
the method accepts either a key value pair, or an array. All the keys of this array will be available in the view that is loaded next as php variables with the same name (of course you need to append the dollar sign to the calls in the view). So in the 'greetings' view you would retrieve them as such:
$variable1 = {{ $name }}
$variable2 = {{ $last_name }}
Flashing an array of key value pairs to the next session
You can do pretty much the same using the flashInput method that is found in {root}/vendor/laravel/framework/src/Illuminate/Session/Store.php:
/**
* Flash a key / value pair to the session.
*
* #param string $key
* #param mixed $value
* #return void
*/
public function flash($key, $value)
{
$this->put($key, $value);
$this->push('_flash.new', $key);
$this->removeFromOldFlashData([$key]);
}
You would do that as such:
$request->session()->flashInput('flashData' => ['key1' => value1, 'key2' => value2]);
The difference here is that the data would not be available as variables to your loaded view. Instead they would be stored in an associative array in the session and you would retrieve the stored values this way:
$variable1 = {{ session('flashData['key1']) }}
$variable2 = {{ session('flashData['key2']) }}
Resources
https://laravel.com/docs/5.3/views
https://laravel.com/docs/5.3/session
If you feel that this solved your problem please mark the answer as accepted :)
First make sure your queries return data.
In my projects, I use simple way for doing it.
$user = $client->unique_code; //now user has the code
$fio = $client->name.' '.$client->secondname. ' '.$client->patronymic;
//please make sure this returns your indented result.
return redirect('cabinet/result')->with('user', $user)->with('fio',$fio );
I hope this will work,
Try this code:
$user = 'user';
$fio = 'fio';
return redirect('cabinet/result')
->with('user', $user)
->with('fio', $fio);
For View:
{{ Session::get('user') }} {{ Session::get('fio') }}

Pass value to URL - REST style

<h1>Edit page of {{ $user->username }}</h1>
{{ Form::open(['route' => 'user.store']) }}
... the rest of the view
This is in my login view. The related code in the store method in the controller looks like this:
if (Auth::attempt(Input::only('username', 'password'))) {
$user = Auth::user();
return Redirect::route('user.show', ['user' => $user]);
}
and the show method:
public function show($user)
{
return View::make('user.edit', ['user' => $user]);
}
And I get .../user/%7Buser%7D as URL (and I want it to be, eg. .../user/exampleusername) and also an exception: ErrorException: Trying to get property of non-object.
When I dd($user) in the show method (or in the view, doesn't matter), I get simply string[6] {user}, which means I do not pass the $user successfully to the user.show route.
The official docs give this example: return Redirect::route('profile', array('user' => 1)); which seems relevant to my case, which I think should look like this in my code: return Redirect::route('user.show', ['user' => $user]);?
Funny, though, if in the show method I try to take the user object from the session (Auth::user()), and dump it, as here:
public function show($user)
{
$user = Auth::user();
dd($user);
...
it will still be NULL, but if I dump it in the index method:
public function index()
{
if (Auth::check()) {
dd(Auth::user());
...
, then it returns correct object, full of parameters and values... I have no idea what's going on and why in one method I have the session object, but in the other I don't.
Any suggestions on how to go around this problem?
UPDATE: I narrowed it down to this implementation in the store method:
return Redirect::route('user.show')->with('user', $user);
and in the show method:
$user = Session::get('user');
return View::make('user.edit', ['user' => $user]);
Because apparently the only place where you can pass an array that will explode into single variables is in View::make, whereas in Redirect::to, Redirect::action and Redirect::route, etc., you must use the ->with('key', $value) function. Those values then will be available in the Session singleton.
Nevertheless, I still get .../%7Buser%7D in the URL. And I don't know how to get out of this...
You need to pass the id of the user to the user.show route - not the $user itself.
return Redirect::route('user.show', [$user->id]);

Is there a shorter way than this to check values when using same Add form for Edit

I have the same code for an Add and an Edit form. Therefore in the controller I need a check to check if a) POST vars submitted (for saving), and if not then b) the original values (for editing) and if not then no value (blank for Adding). I put them in a $data array to pass to the view. Then in the form I can put:
value="<?php echo $member_id;?>"
So my question is, in Codeigniter is there a shorter way than the following to check if POST, then if not check if the original data exists, and if not then its nothing.
$data = array(
'member_id' => ( isset($_POST['member_id']) ? $_POST['member_id'] : (isset($member->member_id ) ? $member->member_id : '') )
);
I know about set_value() but looks like that wont add in the current data when editing a form, so have not used that.
You can allways make function for it.
function get_value_or_default($array, $key, $default) {
return isset($array[$key] ? $array[$key] :
isset($default) ? $default : '';
}
Or even better:
function update_from_post($object) {
$data = array();
foreach ($object as $prop_name => value) {
$value = get_value_or_default($_POST, $prop_name, $object->{$prop_name});
$data[$prop_name] = $value;
}
Assuming you have different methods in the controller for create vs edit: (you can use the same view in different methods by specifying it in $this->load->view()):
Your create method would assume it was new, and always read the $_POST variables (if $_POST)
Your edit method would first load the object from the database, and then overwrite with $_POST variables if present.
Finally, CodeIgniter has the input helper:
$this->input->post('field_name');
returns false if that field is not in $_POST.
To use your code above:
create
$data = array(
'member_id' => $this->input->post('member_id') ? $this->input->post('member_id') : '')
);
edit
$data = array(
'member_id' => $this->input->post('member_id') ? $this->input->post('member_id') : $member->member_id )
);

Resources