Eloquent - edit table rows and return new result data - old data retrieved - laravel-5

I have a form where the user can edit, create or delete shipping methods.
The user sends the form and the data is updated.
I want to return the user's shipping methods after they are edited.
But I seem to get the old data back, instead of the updated data.
$user = \App\User::where('user_id', $user->id)->first();
$user->shipping_methods->each(function($method) {
$method->delete();
});
$methods = [];
foreach ($request->input('methods') as $method) {
$methods[] = new \App\ShippingMethod($method);
}
$user->shipping_methods()->saveMany($methods);
return response()->json($user->shipping_methods->toArray());
(at the moment the code just deletes the old shipping methods and replaces them with the new ones). I am using eloquent relations to get the shipping methods.
So when I do:
return response()->json($user->shipping_methods->toArray());
how come I don't get the new results, instead I get the results from before the update? Is it using the results from the first $user->shipping_methods at line 3? Should I "refresh" the query somehow?

You have to reload the relationship:
return response()->json($user->load('shipping_methods')->shipping_methods->toArray());
You can also simplify the whole line:
return $user->load('shipping_methods')->shipping_methods;

The saveMany method of \Illuminate\Database\Eloquent\Relations\HasMany return instace of \Illuminate\Database\Eloquent\Collection and you must manually set relations
$shipping_methods = $user->shipping_methods()->saveMany($methods);
$user->setRelation('shipping_methods', $shipping_methods);
return response()->json($user->shipping_methods->toArray());

Related

associate() and 1tomany strange behavior

im using Laravel 5.3 and ive a simple controller with a "store" method, this is the "belongTo" side of the relations.
The others 2 models contain correctly the "hasMany" function.
public function store(Request $request)
{
$user_id = JWTAuth::parseToken()->authenticate()->id;
if(!Vehicle::where('id', '=', $request->vehicle_id)->exists()){
return $this->response->error('could_not_create_trip_errore_veicolo', 500);
}
if(!Ztl::where('id', '=', $request->ztl_id)->exists()){
return $this->response->error('could_not_create_trip_errore_ztl', 500);
}
$request->request->add(['user_id' => $user_id]);
$trip = new Trip($request->all());
//$trip->user()->associate($request->user_id);
//$trip->vehicle()->associate($request->vehicle_id);
//$trip->ztl()->associate($request->ztl_id);
if(true)
{
if($trip->save()){
return $this->response->created();
}else return $this->response->error('could_not_create_trip', 500);
}else return $this->response->error('could_not_create_trip_current_user_Error', 500);
}
First question is:
why if comment or uncomment the "associate" method, nothing changes.
Do I need to put these on the controller or I've not weel understand what is the meaning of this method.
Second:
If I send to my controller some data, using form for testing, what is "required" is the 3 foreign keys.
If I send a number that is not on my other "hasmany" table an error is rise, but if try to insert something like "2dsksk" where 2 is a correct ID of the "many" table and then a random string, the ID is taken by the insert as 2, this is correct?
Validation take just the "correct" number part of the data...the question is, why? and this is secure?
associate just sets the foreign key of the child. This means you will need to save your model afterwards.
$trip->user()->associate($request->user_id);
$trip->save();
For the second issue, that's likely MySQL truncating the data based on the data type of the column. If you don't want that to happen, you would likely need to set MySQL to strict mode.
http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict

Save vs update in laravel

What is the difference between save() and update() method in Laravel.
I have used save() method in case of update query but in few cases it acts as update and in few case it act as insert query function. Please let me know what exactly the difference between them.
These methods both allow you to save data to a database.
The save() method performs an INSERT when you create a new model which is currently is not present in your database table:
$flight = new Flight;
$flight->name = $request->name;
$flight->save(); // it will INSERT a new record
Also it can act like an UPDATE, when your model already exists in the database. So you can get the model, modify some properties and then save() it, actually performing db's UDPATE:
$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save(); //this will UPDATE the record with id=1
Theupdate() method allows you to update your models in more convenient way:
App\Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]); // this will also update the record
So you don't even need to assign the retrieved model to any variable. Updated properties are passed as arguments.
Examples and more info in the Laravel's docs.
There is only one thing left unsaid in what #ginopane told about the difference and it's that if you use update method on a query builder result then laravel will ignore $fillable or $guard array of your model. This is especially important if you want to use Input::all() as an argument to update:
Post::where('id', $id)->update(Input::all());
So in this case if you use App\Flight::where('active', 1)->update(Input::all()); everything in your database will be updated even if you put it in $fillable. So make sure to use save and update methods on Eloquent instance and not Query builder one. The following code will be fine even if the user submit fields that you don't want to insert or update in your databse table:
// User model
protected $fillable = ['email', 'name'];
// controller
public function update($id)
{
$user = User::findOrFail($id);
// validate the input here, use Request to do the job or whatever you like
$user->update(Input::all());
return view('some_view')->with('notice', 'user updated');
}
Now, no matter what with the FORM being passed here, only name and email will be updated.
Hope this complete #ginopane answer
save() : you can look to it as the equivalent of the INSERT in sql, it will create a new model (and insert it in the database)
To create a new record in the database, create a new model instance, set attributes on the model, then call the save method
update() : you can look to it as the equivalent of the UPDATE in sql, it will create a new model (and insert it in the database)
The save method may also be used to update models that already exist in the database. To update a model, you should retrieve it, set any attributes you wish to update, and then call the save method. Again, the updated_at timestamp will automatically be updated, so there is no need to manually set its value
code
$flight = App\Flight::find(1);
if (empty($flight)) {// you can do this condition to check if is empty
$flight= new Flight;//then create new object
}
$flight->name = 'New Flight Name';
$flight->save(); //this will UPDATE the record with id=1
for more detail doc

Laravel - Filtering previously returned results

I have a page with a form, that lets you select an exercise that I have logged data for. When you submit the form, it uses a getExerciseLog method in my LogController which takes the input name, and gets all instances of that exercise from the database and shows it in a table. The table displays fine, but now I need to filter these by newest and oldest etc, using a select field.
I have created the form for this, which posts to a "filter" method in LogController. So far I have this code:
public function getExerciseLog()
{
$exercises = Exercise::lists('exercise_name', 'exercise_name');
$exercise = Input::get('exercise');
$exercise_logs = Workout::where('exercise_name', '=', $exercise)->orderBy('created_at','desc')->get();
return view('log')->with(array(
'exercise'=>$exercise,
'exercises'=>$exercises,
'exercise_logs'=>$exercise_logs,
));
}
public function filter()
{
$filter = Input::get('filter');
if($filter == "oldest") {
$exercise_logs = Workout::where('exercise_name', '=', $exercise)->orderBy('created_at','asc')->get();
}elseif($filter == "newest") {
$exercise_logs = Workout::where('exercise_name', '=', $exercise)->orderBy('created_at','desc')->get();
}
}
Obviously this is not finished, as I'm stuck on how to return back to the 'log' view with the results filtered. At the moment, in the filter method, the $exercise_log won't return anything as $exercise is not being set, as the exercise input is not being submitted (that is the original select field for choosing the exercise you want to see data for).
Any ideas how I can get this working? Is it possible to pass the $exercise data from the getExerciseLog into the filter log, so I can use it within that method?
Thanks
Recommended way would be to sort the table using Javascript, it avoids the need of another request and full page refresh so it is much faster.
However in your case you can save the $excercise in the session like this
$request->session()->put('excercise', $excercise);
and
$exercise=session('excercise');
More info in here https://laravel.com/docs/5.2/session#basic-usage

Retrieving records from database using eloquent with optional query parameters

i have the following block of code in my Resource Controller:
$travel_company_id = Input::get('travel_company_id');
$transport_type = Input::get('transport_type');
$route_type = Input::get('route_type');
$travelRoutes = TravelRoute::where('travel_company_id', $travel_company_id)
->where('transport_type', $transport_type)
->where('route_type', $route_type)
->get();
Now what this does is it gets travelRoutes based on the parameters supplied. What i want is for it to do is perform a search based on the available parameters, that way if $route_type is empty the search will be performed only on travel_company_id and transport type.
Also if all the parameters are empty then it will simply do a get and return all available records.
I know i can do this with lots of if statements but then if i add a new parameter on the frontend i will have to add it to the backend as well, I was wondering if there was a much simpler and shorter way to do this in laravel.
The where method accepts an array of constraints:
$constraints = array_only(Input::all(), [
'travel_company_id',
'transport_type',
'route_type',
]);
$routes = TravelRoute::where($constraints)->get();
Warning: do not use Input::only() instead of array_only(). They're not the same.
Input::only() fills in any missing items with null, which is not what you want here.
This is pretty hacky and if you spend some time developing a solution I'm sure it could be much nicer. This assumes all the fields in the getSearchFields() function match the input names from the form and database.
/**
* Search fields to retrieve and search the database with. Assumed they match the
* column names in the database
*/
private function getSearchFields()
{
return ['travel_company_id', 'transport_type', 'route_type'];
}
public function search()
{
// Get a new query instance from the model
$query = TravelRoute::query();
// Loop through the fields checking if they've been input, if they have add
// them to the query.
foreach($this->getSearchFields() as $field)
{
if (Input::has($field))
{
$query->where($field, Input::get($field));
}
}
// Finally execute the query
$travelRoutes = $query->get();
}

Attaching new relation and returning the model

I have a User that has many Positions. I want to update the User (the model and the relation Position), and then return the updated result.
The input will be an array of the format
{
first_name,
last_name,
email,
... (other user information),
positions : [
{
id,
name,
descriton
},
...
]
}
My update function currently is
public function update($id)
{
// This is a validation that works fine
if ( ! User::isValid(Input::all())) return $this->withValidation(User::$errors);
$user = User::with('positions')->find($id);
$new_ids = array_pluck(Input::get('positions'), 'id');
$user->positions()->sync($new_ids);
$user->update(Input::all());
return $user;
}
My User and its permissions are updated, but I still get the old $user's relationship back (i.e. the basic information is updated, and the new positions are updated in the DB, but the returned result is the NEW basic information with the OLD positions).
Right now to fix this, I recall the User::with('positions')->find($id) at the end and return that. But why isn't my code above working?
Correct, sync doesn't update related collection on the parent model.
However you should use $user->load('positions') to reload the relation, it will call only 1 query, without fetching the user again.
Also, you can call load on the Collection:
$user->positions->load('anotherRelation');
because here positions is a Collection, which have load method to lazy load all the related models for each item in the collection.
This would not work, since positions() returns relation object, not collection:
$user->positions()->load('anotherRelation');

Resources