I have a resource and I'm trying to set up the update controller. In my case my edit form has many inputs and I need to update the database with them but there might be columns in the database not changed by the edit form. So I have my controller like this:
public function update($id)
{
$hostess = Hostess::find($id);
$inputs=Input::all();
foreach ($inputs as $key => $value) {
$hostess->$key= $value;
}
if ($hostess->save())
{
return Redirect::route('hostesses.show', $hostess->id);
}
return Redirect::back()->withInput()->withErrors($hostess->getErrors());
}
This gives me an error because I am using PUT in my view and I get
Column not found: 1054 Unknown column '_method' in 'field list'
Because my Input::all() is getting the hidden inputs for the PUT method. I can use Input::except() for that, but is that the proper way of updating with laravel?
You can actually do something like this:
$hostess = Hostess::find($id)
$post_data = Input::all();
// or
$post_data = Input::except('_method');
// warning untested if block below
if ($hostess->update($post_data))
{
return Redirect::route('hostesses.show', $hostess->id);
}
return Redirect::back()->withInput()->withErrors($hostess->getErrors());
As short as that would update all available key and value pairs.
Do note that you have to add the columns to the $fillable property in the model to avoid the mass assignment warning.
You could do something like this:
$inputs = Input::all();
$inputs = array_except($input, '_method');
Related
I'm fetching data from database, inside foreach loop i have to add one array index list_array
$list = Lists::where('name',$request->name)->get();
$Data=[];
foreach($list as $key => $list_row)
{
$list_row['list_array'][]=$list_row['data_1'];
$list_row['list_array'][]=$list_row['data_2'];
$Data[]=$list_row;
}
It should be of array type but when i declare it as array it is not working,
message: "Indirect modification of overloaded property
App\Models\Lists
Any solution to create list_array as an array index inside foreach loop. Thanks
You are not dealing with an array, you are dealing with an App/Models/Lists model. What you probably instead want to do is adding a custom attribute to the model. This happens one step before.
// your model
class Lists
{
protected $appends = ['list_array'];
public function getListArrayAttribute()
{
$data = [
// your array data from where ever
];
return $data;
}
}
You then can access (without adding it when you want to access the data) the new attribute from within the model, like so:
// where you access your data
$lists = Lists::where('name', 'whatever')->get();
foreach($lists as $list) {
dd($lists->list_array)
}
You can read more about the so called Accessors here:
https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor (Laravel 8)
https://laravel.com/docs/9.x/eloquent-mutators#defining-an-accessor (Laravel 9)
I hope I got the intention of your question right. From your question it's not very clear what data you want to add and where it is coming from. There might be other and better ways too.
Edit:
Based on the comment from #user3653474 the Accessor function could look like this.
public function getListArrayAttribute()
{
$data = [
$this->data_1,
$this->data_2,
];
return $data;
}
data_1 and data_2 are the column names in the table of the same model.
I wanted to let the system to show error message when detect duplicated entry of full_name column without applying unique in the full_name column from public function rules() in model.
My code is like this :
if ($model->load(Yii::$app->request->post()) ) {
$model->full_name = $model->first_name .'' . $model->last_name ;
$name = StudentInfo::find()->select('full_name')->where(['full_name'=> $model->full_name]);
if($name == $model->full_name ){
echo "<script type='text/javascript'>alert('Same student name is detected');</script>";
}
else{
$model->status ="Active";
$model->call_format = Countries::find()->select('phonecode')->where(['name'=> $model->country]);
$model->date_created = new Expression('NOW()');
$user->user_type ='student';
$user->user_name = $model->full_name;
$user->user_status = $model->status;
$user->authKey = Yii::$app->security->generateRandomString(10);
$user->accessToken = Yii::$app->security->generateRandomString(10);
$user->save();
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
}
}
But it shows error like :missing required parameters: id. When i apply model->save(false) ,it seems that the sql statement wont run because of duplicate entry in full_name column. How do i fix it?
Well, there is a construct exists() for such a purposes (see Yii2: check exist ActiveRecord model in database ).
if(StudentInfo::find()->where(['full_name'=> $model->full_name])->exists()){
echo "<script type='text/javascript'>alert('Same student name is detected');</script>";
}
else{...}
it generates the EXISTS query, which is faster and you don't have to load all the data from DB.
If you don't have such a column in your table, then check it by the first/last name.
change it:
$name = StudentInfo::find()->select('full_name')->where(['full_name'=> $model->full_name]);
To:
$name = StudentInfo::find()->select('full_name')->where(['full_name'=> $model->full_name])->one();
Also, if you use the select() method, to use the update() and save() or updateCounters() ... methods, you need the row ID in the same query.
Example:
->select('id') or ->select(['id', 'full_name'])
info: Multi-parameter is an array in select()
:missing required parameters: id
could mean that it couldn't find id, not by duplicate entry in full_name column. please check again
There are two problems with your code.
$name = StudentInfo::find()->select('full_name')->where(['full_name'=> $model->full_name]);
When this line is executed the $name variable will contain instance of yii\db\ActiveQuery. You want to call some method, that will actually execute your query and return result.
You can use scalar() to get the selected value. In that case the $name will contain the content of full_name column from result.
$name = StudentInfo::find()
->select('full_name')
->where(['full_name'=> $model->full_name])
->scalar();
Or you can use count() to get the number of rows that match condition. In that case you may leave out the select() method call but you will need to modify your condition
$count = StudentInfo::find()
->where(['full_name'=> $model->full_name])
->count();
if ($count > 0) {
echo "<script type='text/javascript'>alert('Same student name is detected');</script>";
} else {
// ...
}
The other problem is that you are not checking whether your $model->save() was successful. If your $model is new instance and the id attribute is auto-generated then when $model->save fails the $model->id is empty and then you are trying to redirect user to view with empty id.
Your code should look like this:
if ($user->save() && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
If the save fails because of validation the validation errors will be stored in models and if you are using ActiveForm widget the errors will be displayed. If you are not using ActiveForm you should do something to tell user that operation failed.
Since you are saving two different models you might want to consider use of transactions to prevent a situations where $user model is saved but save of $model fails.
I have a form that is sending the values to the controller in this way:
public function postFormUpdate(ProjectUpdateRequest $request)
{
$inputs = $request->all();
$project = $this->projectRepository->update($inputs['project_id'],$inputs);
//...
}
The project repository is done this way:
public function update($id, Array $inputs)
{
return $this->save($this->getById($id), $inputs);
}
private function save(Project $project, Array $inputs)
{
// Nullable
if (isset($inputs['project_type'])) {$project->project_type = $inputs['project_type'];}
if (isset($inputs['activity_type'])) {$project->activity_type = $inputs['activity_type'];}
...
}
My problem is if the project_type is null from the form field (the project type doesn't need to be entered or can be removed), then isset($inputs['project_type']) will be false and the update will not be triggered.
What I want is if the user had set up a project type and then wants to change it and set it to null, like this, it is not working. I use the isset because sometimes I update only one field and I don't want this to generate an error because it was not part of the inputs and was not set.
What I can do is use:
if (isset($inputs['project_type']) || is_null($inputs['project_type'])) {$project->project_type = $inputs['project_type'];}
But I am looking if there is a more elegant way to do this.
Thanks.
A more elegant way would be to see if anything is set under the key.
so go with array_key_exists
if ( array_key_exists('project_type', $inputs) ){$project->project_type = $inputs['project_type'];}
You may see it in action here
I am working on this laravel project where user can upload an avatar image. My users table does not have any column yet to store the file location. So i was testing this in phpunit following the TDD series in laracast.
After the file is uploaded successfully and moved to the desired location in the server, i called the update method on the authenticated user like below:
$user = auth()->user();
$user->update(['avatar_location' => 'avatars/avatar.jpg']);
Note that avatar_location is not yet there on the users table. I expected this to fail but it didn't. I tried to find out what was going on so i followed through to the update() method in the model class:
//file Illuminate/Database/Eloquent/Model.php
public function update(array $attributes = [], array $options = [])
{
//dd($attributes); //prints 'avatar_location"=>"avatars/avatar.jpg'
if (! $this->exists) {
//dd($attributes);
return false;
}
return $this->fill($attributes)->save($options);
}
till this point the dd($attribute) prints the value that i passed to the update() method.
So i followed into the fill() method that is being called with the attribute parameter. However when i die dumped the received parameter from inside the fill() method i am not seeing the key=>value pair that i passed. Instead it was showing the other attributes of the user:
/**
* Fill the model with an array of attributes.
*
* #param array $attributes
* #return $this
*
* #throws \Illuminate\Database\Eloquent\MassAssignmentException
*/
public function fill(array $attributes)
{
//dd($attributes);
//does not print 'avatar_location"=>"avatars/avatar.jpg'
//rather, prints:
//array:4 [
// "name" => "Armand Mraz"
// "email" => "akautzer#example.net"
// "password" => "$2y$10$h7OG9/Toh31MsyFQc8lfg.wHeQC7maP4Bh37bea.DXU//IuRuXZi."
// "remember_token" => "X0udISwEEM"
]
$totallyGuarded = $this->totallyGuarded();
foreach ($this->fillableFromArray($attributes) as $key => $value) {
$key = $this->removeTableFromKey($key);
// The developers may choose to place some attributes in the "fillable" array
// which means only those attributes may be set through mass assignment to
// the model, and all others will just get ignored for security reasons.
if ($this->isFillable($key)) {
$this->setAttribute($key, $value);
} elseif ($totallyGuarded) {
throw new MassAssignmentException($key);
}
}
return $this;
}
I spent a lot of time trying to figure out why?
can anyone please explain?
And why the update method is not failing even though i am trying to update a column that does not exist?
Thanks,Yeasir
When you're updating an object, Laravel is trying to match the keys of the array of data and the list of fillable fields. All pairs of key/valye missing from the fillable fields are not considered. It's the reason why it's not failing.
You have to update the fillable field list (property $fillable) in your user model.
Have a look at the documentation.
If you add avatar_location in your fillable fields list and the field doesn't exist, in this case, it will throw an exception.
I am trying to add some fields to the column of my table. When I check my table, I see that the column could be created but the fields could not be added. However, I get no error or anything.
public function isset_row($target, $sender_table, $receiver_table, $sender_row) {
$this->load->model('Connection_model');
if ($this->Connection_model->get_custom_db($target)->get($sender_table)) {
// Add column(s)
$this->myforge = $this->load->dbforge($this->Connection_model->get_custom_db('receiver'), TRUE);
$fields = array(
$sender_row => array('type' => 'TEXT')
);
$this->myforge->add_column($receiver_table, $fields);
$query = $this->Connection_model->get_custom_db('sender')->get($sender_table);
foreach ($query->result() as $row) {
echo $row->$sender_row . '<br>'; // Returns fields from a table (string)
echo $receiver_table; // Returns table name (string)
$this->Connection_model->get_custom_db('receiver')->update($receiver_table, $row->$sender_row);
}
}
}
Edit1:
var_dump($this->Connection_model->get_custom_db('receiver')->update($receiver_table, $row->$sender_row));
This line returns bool(false)
Edit2:
With the update() function I am just saying to add the fields to the table but I am not saying to which column of the table. I think that must be the point but how can I specify the column when trying to update?
Complete your model get_custom_db(). Use get() query builder method in model. So your condition should like this
if ($this->Connection_model->get_custom_db($table_name)) {
...
}
and use get() in model
public function get_custom_db($table_name){
....
$test = $this-db->get($table_name);
return $test->result();
}
OR provide the model code, I'll figure out it for you. Thanks