Attaching new relation and returning the model - laravel

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

Related

Laravel Create multiple records in Pivot table

I'm trying to create a function in our Laravel 5.8 app that would add multiple records to a pivot table. At present we have the following setup;
Users
Training Courses
Users Training Courses (pivot table for the above relationships, with a few extra fields)
I want to be able to show all users in the database, then check their name, pick a training course and hit "Add" and it'll create a record in the pivot table for each user that was selected.
I can't figure out where to start with this - it seems like I need to have a "for each user selected, run the store function" loop in the controller, but I have no idea where to start.
I wasn't sure if there was an easy way to do this in eloquent or not. Is there a simple way to do this?
Eloquent does this automatically if you set up the relationships correctly and you don't have to worry about pivot tables.
class Users
{
public function trainingCourses()
{
return $this->hasMany(TrainingCourses::class);
}
}
class TrainingCourses
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Then you can use the save() method to create the relationship. But I find it better to wrap this function inside a helper method that you can use throughout your code:
class Users
{
...
public function assignTrainingCourse(TrainingCourse $trainingCourse)
{
return $this->trainingCourses()->save($trainingCourse);
}
}
In your code, you could then do something as simple as this:
$user = User::find(1);
$trainingCourse = TrainingCourse::find(1);
$user->assignTrainingCourse($trainingCourse);
Building on this, suppose you have the following route to assign a training course, where it expects a trainingcourse_id in the request:
Route::post('/users/{user}/trainingcourses', 'UserTrainingCoursesController#store');
Thanks to route model binding, Laravel can inference the parent model (user) from the URL, and your controller might look like this:
// UserTrainingCoursesController.php
public function store(User $user)
{
$trainingCourse = TrainingCourse::find(request()->input('trainingcourse_id'));
$user->assignTrainingCourse($trainingCourse);
return back();
}
Of course, you'll want to put some validation in here, but this should get you started.

How to return a model with his relations in Laravel?

I have a ManyToMany relation with a pivot table. Between my model Deck and PlayCard how can I return my deck with his Playcard inside?
Something like this:
id: 1,
...
play_cards: [
{
id: 1, ...
},
{
id: 2, ...
}
]
I tried to use the with() function, but it doesn't work.
This is my function:
public function addToDeck(Request $request)
{
$play_card = Auth::user()->playCards()->where('uid', $request->card_uid)->first();
$deck = Auth::user()->decks()->where('token', $request->deck_token)->first();
if (!$play_card || !$deck) {
return ResponseService::respondWithErrors(
400,
$this->routes_messages[__FUNCTION__],
['Error Deck or Uid unknow.']
);
}
if ($play_card->decks()->find($deck->id)) {
return ResponseService::respondWithErrors(
400,
$this->routes_messages[__FUNCTION__],
['Card already in this deck.']
);
}
$deck->playCards()->attach($play_card);
$deck->save();
return ResponseService::respondOK(
200,
$this->routes_messages[__FUNCTION__],
$deck
);
}
In the code you've shown, the $deck in the successful response won't show any related playcards because you never loaded the relationship on the deck. You accessed the relationship query to add the new playcard, but you never actually ran the query to get the playcards for the deck.
However, using with to load the initial playcards won't help you much either. Your response will include the original playcards, but it won't include the new one you just added. Modifying related records doesn't affect records that are already loaded.
In this instance, after you attach the new card to the deck's related cards, you will need to reload the relationship for the card to show up in the response.
// Add the card to the deck.
$deck->playCards()->attach($play_card);
// Load (or reload) the new set of related playcards. This will populate
// the $deck->playCards attribute so it will show up in your response.
$deck->load('playCards');
On a side note, there is no reason to save the $deck. You didn't modify anything on it. If you're attempting to update the updated_at timestamp on the deck, that still won't work, since it won't actually update any fields if the model isn't dirty. If that is your goal, however, you can use the touch() method ($deck->touch()).

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

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

how to display the current logged on users picture from a relationship laravel

I want to retrieve the user's photo and display it in a thumbnail form which i have stored in public/assets/uploads/thumbnail/. I tried auth()->user()->user_detail->file_name but I can't get it to work. How do you do it ?
you have to first define a relationship if they are stored in different table
like i did in model
public function imagedata() {
return $this->hasMany(Images::class, 'listID', 'id');
}
and after that when you get the user just call this method like this
$listingimg = Listings::findOrfail($id);
and for calling the relationship
foreach (listingimg as $singleIlisting) {
$singleIlisting->imagedata;
}
modify the code according your needs as if needed and by the way relatio is one to many

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

Resources