How to use updateOrCreate with hasMany relationship? - php

How to use updateOrCreate with hasMany relationship. For example I have first model Code:
class Code {
public function item()
{
return $this->hasOne(UserItem::class, 'code_id')
}
}
And for the nested relationship UserItem:
class UserItem {
public function serials()
{
return $this->hasMany(ItemSerial::class, 'user_item_id', 'id');
}
}
And I need to updateOrCreate values of serials relationship. I tried this:
foreach ($data['item_serials'] as $serial) {
$code->item->serials()->updateOrCreate([
'serial' => $serial
]);
}
But this doesn't work how I need, because it changes both serial values to same value. This is how serials table looks like:
id user_item_id serial
1 1 lorem
2 1 ipsum
And then I recieve request:
'item_serials' =>
array (
0 => 'test1',
1 => 'test2',
2 => 'test3'
),
And with this request I want to update serials table like this:
id user_item_id serial
1 1 test1
2 1 test2
3 1 test3
I hope I explained understandably. How I should approach this?

Update or create takes two arguments
1-updated data
2-condition
like
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
see the docs https://laravel.com/docs/8.x/eloquent

Add an array as the first argument inside updateOrCreate(), it'll be used to retrieve/find an existing serial if it does update but if not then create it.
foreach ($data['item_serials'] as $serial) {
$code->item->serials()->updateOrCreate(
['serial' => $serial],
['serial' => $serial]
);
}

Related

Bulk insert or update for the given where

I have a function that gets $homes and $member_id as parameters. $homes is an array, $member_id is an integer.
$homes = array( 'home13', 'home21', 'home34', 'home44' );
$member_id = 5;
How do I insert/update into the following DB?
in the format:
id home_name member_id
1 home13 5
2 home21 5
2 home34 5
2 home44 5
The function I used:
public function store($homes, $member_id) {
foreach( $homes as $home ) {
$data[] = [ 'member_id' => $member_id, 'home_name' => $home ];
}
Home::insert( $data );
}
which works fine while inserting. However, I need to update all records for a given $member_id if the member_id already exists.
For example, if the $member_id is 5 and $homes is now array( 'home54' ), all other home_name that are previously stored for the $member_id 5 should be deleted. so that the following would only remain.
id home_name member_id
1 home54 5
You can use Laravel's upsert() method
Example:
Home::upsert([
['home_name' => 'home54', 'member_id' => 5, 'id' => 1],
['home_name' => 'home13', 'member_id' => 5, 'id' => 2],
['home_name' => 'home21', 'member_id' => 5, 'id' => 3],
], ['member_id', 'id'], ['home_name']);
This method consists of three arguments.
Values to insert/update
Columns that uniquely identify the values
The values that need to be updated
Please note that you need to use a unique composite key to use upsert() method.
Example from Seeder class:
$table->unique(['member_id','id']);
Reference from Laravel docs: https://laravel.com/docs/9.x/eloquent#upserts
You can use updateOrCreate method to achieve this
public function store($homes, $member_id) {
foreach( $homes as $home ) {
Home::updateOrCreate( 'member_id' => $member_id, 'home_name' => $home );
}
}

How I get data by group User ID for foreach controller function in Laravel

How can I get data by grouping user_id for a foreach loop in a controller's function in Laravel. For example, user_id = 2, 5 rows available, user_id = 1, 10 rows available. Then show 2 arrays.
$lists = lists::wherestatus(1)->groupby('user_id')->get();
foreach($lists as $list){
$list = functionHere;
}
What function can I create for this on the controller for grouping?
I need more information, but based on what you shared, you should be able to do this (removing the foreach):
$lists = Lists::whereStatus(1)->get()->groupBy('user_id');
The difference is that if you use groupBy before get, you are grouping your query by user_id, so instead of getting 5 rows for user_id = 2 and 10 for user_id = 1, you are going to get 2 rows and just the latest data, so you need to use Collection's groupBy.
What you want to do is group all the information by user_id but have each row, a schema like this:
[
'1' => [ // user_id
['user_id' => '1', 'column1' => 'random value'],
['user_id' => '1', 'column1' => 'other value'],
// 8 remaining rows
],
'2' => [ // user_id
['user_id' => '2', 'column1' => 'other nice value'],
// 4 remaining rows
],
]
you should first in List model set:
public function scopeStatus(){
return $this->where('status','1');
}
and in your controller:
$products = List::status()->groupby('user_id')->get();

Laravel, sync data with Pivot

Laravel 5.3, I have this 2 models:
User:
public function newFunctions()
{
return $this
->belongsToMany('App\NewFunctions', 'user_newfunctions')
->withPivot(['function_count', 'days_count']);
}
NewFunctions:
public function users()
{
return $this
->belongsToMany('App\User', 'user_newfunctions', 'new_function_id', 'user_id')
->withPivot(['function_count', 'days_count']);
}
I now how can I save new data to User, with this:
$user = User::findOrFail($id);
$user->name = $request->input('name');
$user->save();
But now I have to update some values of a pivot table. the pivot table is this:
user_id | new_functions_id | function_count | days_count
---------------------------------------------------------
814 | 1 | 5 |2019-07-19 12:26:19
814 | 3 | 7 |2019-07-19 12:26:19
I have more than 1 row per user_id. I was trying to use:
$user
->newFunctions()
->sync([
'days_count' => $test_date,
'function_count' => $test_int_number
]);
But I'm getting error like:
Ilegal offset type
because is trying to update with this:
array(
'records' => array(
'days_count' => object(Carbon), 'function_count' => '66'),
'results' => array(),
'id' => object(Carbon),
'attributes' => array()
)
)
in BelongsToMany.php
So:
How could I update the values for each user_id on the pivot table?
And how should use syncto update just 'function_count' and 'days_count'? they come from request.
->sync() isn't used like that; it's used to attach() and detach() related new_function_ids until only the ids in sync() are present. You're probably looking for updateExistingPivot()
An example of ->sync() would be using the array:
$user->newFunctions()->sync([
"new_function_id" => 1,
"function_count" => 6,
"days_count" => "2019-07-08 12:00:00",
]);
This would remove the record where new_function_id is 3, and updating the values where new_function_id is 1.
To update function_count and days_count for either new_function_id of 1 or 3, use ->updateExistingPivot() (pass the id you want to update as the first parameter):
$user
->newFunctions()
->updateExistingPivot("1", [
"function_count" => 6,
"days_count" = "2019-07-08 12:00:00"
]);
// or $user->newFunctions()->updateExistingPivot("3", ...);
This will update the pivot table where new_function_id is 1, while leaving the row where new_function_id is 3.
Edit: If you're looking to update all existing records in the pivot table, you'll need to do this in a loop, call a sync with all current records in a single array, or run a manual query.

How to use sync on a 3 model relationship? - Laravel

I have 3 models:
Match Team Player
And i want to create a table with the following structure:
id | match_id | team_id | player_id
So that i can associate the 3 models i refered.
I created a 4th model MatchPlayers for the table i referred and I can use the 'search' functions without a problem. Like this:
$match->matchPlayers()->first()->team()->get()
And it returns the excpected result, but I cant do a
$match->matchPlayers()->sync([])
So, how should i solve this? Is my relationship wrong or the sync method isnt allowed on a 3 model relationship and I shoud use other method?
Thanks in advance
Edit:
Match.php
public function teamPlayers(){
return $this->hasMany('\Modules\Matchs\Entities\MatchPlayer');
}
Team.php
public function matchTeamPlayers(){
return $this->hasMany('\Modules\Matchs\Entities\MatchPlayer');
}
Player.php
public function matchTeamPlayers(){
return $this->hasMany('\Modules\Matchs\Entities\MatchPlayer');
}
MatchPlayer.php
public function player(){
return $this->belongsTo('\Modules\Players\Entities\Player');
}
public function match(){
return $this->belongsTo('\Modules\Matchs\Entities\Match');
}
public function team(){
return $this->belongsTo('\Modules\Teams\Entities\Team');
}
If you've followed the Laravel documentation on Pivot tables and Many-Many relationships found here, and it's still not working, you might have more luck with "Attach". For example;
$matchPlayer = MatchPlayer::create([...]);
$match->matchPlayers()->attach($matchPlayer)
A good example of sync vs attach can be found here
Using a fourth model for this kind of relationship makes sense, as it gives you a navigation property for the third relation on your pivot table. This way you can form more complex queries this way.
For your particular problem, syncing based on match_id and team_id, I would simply do something like this:
$matchId = 123;
$teamId = 234;
$rows = [
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 345],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 346],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 347],
];
// remove all previously stored connections
MatchPlayer::where('match_id', $matchId)
->where('team_id', $teamId)
->delete();
// insert the news ones
// (you could also use MatchPlayer::create() per item or
// $matchPlayer->save(), it doesn't matter)
MatchPlayer::insert($rows);
If this operation occurs very frequently, you will potentially burn through a lot of id values of the pivot table. In this case you could also perform a more efficient sync, which is slightly more complex:
$matchId = 123;
$teamId = 234;
$rows = [
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 345],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 346],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 347],
];
// delete all players that are not among the new data anymore
MatchPlayer::where('match_id', $matchId)
->where('team_id', $teamId)
->whereNotIn('player_id', array_pluck($rows, 'player_id'))
->delete();
// remove rows from new data that already exist
$exist = MatchPlayer::where('match_id', $matchId)
->where('team_id', $teamId)
->pluck('player_id')
->toArray();
$rows = array_filter($rows, function ($value, $key) use ($exist) {
return ! in_array($value['player_id'], $exist);
});
// then we store the remaining data
MatchPlayer::insert($rows);

Eloquent ORM many to many relationship best practices

I have 2 tables
cars (id, title)
parts (id, title)
and I want to assosiate every car with a lot of parts and the price of this part for the specific car...
I believe the best way is to associate them through a table:
car_parts(id, car_id, part_id, price)
How do I define such a relation in Laravel's Eloquent?
I want to do
$car = Car::find(1);
$parts = $car->parts;
and I want to get an array of objects like so
{
'id' => 1,
'title' => 'rear flash',
'price' => '10.00',
},{
...
}
if I try the
public function parts(){
return $this->belongsToMany('Part', 'car_parts', 'car_id', 'part_id');
}
I dont get the price...
TIA
The price is available on the pivot model if you include it in withPivot:
public function parts ()
{
return $this->belongsToMany('Part', 'car_parts', 'car_id', 'part_id')
->withPivot('price');
}
Then you can map over the parts collection to get the arrays you want:
$parts = Car::find(1)->parts->map(function ($part)
{
return [
'id' => $part->id,
'title' => $part->title,
'car_id' => $part->pivot->car_id,
'part_id' => $part->pivot->part_id,
'price' => $part->pivot->price,
];
});

Categories