Laravel 5.2 Many to Many withPivot save - laravel

I'm having problem on this many to many. My code is
$purchase_order = PurchaseOrder::find(1);
$item = Item::find(1);
$purchase_order->items()->save($item);
This one works well. I have a pivot table name items_purchase_order it creates
item_id 1 and purchase_order_id 1
The problem is the pivotColumn on how to add it.
$array = [
'qty' => 4,
'unit_price' => '23'
];
My PurchaseOrder Model
return $this->belongsToMany('App\Item', 'items_purchase_orders', 'purchase_order_id', 'item_id')->withPivot('qty','unit_price');
I have tried other examples on here but to no avail. Like this one
$purchase_order = PurchaseOrder::find(1);
$item = new Item($array);
$purchase_order->items()->save($item);
This becomes ambiguous because there is no item id. The items are already created and the purchase order is the one I'm going to create. I have tried the reverse one with the Item Model.
return $this->belongsToMany('App\PurchaseOrder', 'items_purchase_orders', 'item_id', 'purchase_order_id')->withPivot('qty','unit_price');
//pivot table added with purchase_order columns
$array = [
'po_number' => '454jk', // I minimized it to one column. Purchase Orders Column
'qty' => 4, // Pivot Column
'unit_price' => '23' // Pivot Column
];
$item = Item::find(1);
$purchase_order= new PurchaseOrder($array);
$item->purchaseOrder()->save($purchase_order);
Still it doesn't work.
I think I found this 1
$shop->products()->attach(1, ['products_amount' => 100, 'price' => 49.99]); Yeah it work! Reference http://laraveldaily.com/pivot-tables-and-many-to-many-relationships/

Related

Laravel - Factory seeding unique data on pivot table

Event::factory(5)
->hasAttached(
Team::factory()->count($this->faker()->numberBetween(0, 60)),
[
'team_name' => $this->faker()->unique()->name,
'score' => $this->faker()->numberBetween(0, 50)
],
'participants'
)
->create([
'user_id' => $user,
'quiz_id' => $quiz
]);
The above snippet of code creates 5 events for said $user using said $quiz. It will have a random amount of participants which is a pivot table (Team and Event). On that pivot table there is a team_name and score column. Because teams can change their team name we want to know the team name at the time of participating and also the score in which they got for the event.
With the current code, because $this->faker()->numberBetween(0, 60), $this->faker()->unique()->name and $this->faker()->numberBetween(0, 50) are not evaluated within an iteration, all the pivot table data is the same.
How can I make this data different per pivot row?
TIA
Figured it out;
Event::factory(5)
->hasAttached(
Team::factory()->count($this->faker()->numberBetween(0, 60)),
function() {
return [
'team_name' => $this->faker()->unique()->name,
'score' => $this->faker()->numberBetween(0, 50)
];
},
'participants'
)
->create([
'user_id' => $user,
'quiz_id' => $quiz
]);

Pass array into a single column in Laravel

i am trying to pass some values into a single column in laravel database table.
The values are like this 20,45,67,89
but i want them to enter into the colume like this
===USER_ID====
20
45
67
89
I have tried like below, but not working..any suggestions ?
foreach ($request->val2 as $value){
$str_explode = explode(",",$value);
DB::table('retirement')->insertGetId([
'user_id' => $str_explode,
'amount' => $request->val1,
'week' => $request->week
]);
}
Hope this will work
foreach ($request->val2 as $value){
$str_explode = explode(",",$value);
$insert = [];
foreach($str_explode as $str){
$insert[] = [
'user_id' => $str,
'amount' => $request->val1,
'week' => $request->week
];
}
DB::table('retirement')->insert($insert);
I'm not sure i understood your question clearly, i'm assuming you want to insert array to a column:
did you try to set the column in migration to Json?
did you set the $casts in the model to json or array?
protected $casts = [ 'user_id' => 'array' ];
then when you do this, you can have an array added to that column like
Posts::create(['user_id'=>[1,2,3,4]]);
normally the user_id field is set to unsignedBigInt(), that type will not accept anything but integers, you gotta check the migration column type first.
explode() is returning an array, not a single value, that's why it will fail. Instead, you should loop through all values like this:
foreach ($request->val2 as $value){
$str_explode = explode(",",$value);
foreach($str_explode as $str){
DB::table('retirement')->insertGetId([
'user_id' => $str,
'amount' => $request->val1,
'week' => $request->week
]);
}
}
As a side advice, as you are not saving the id returned by insertGetID, you can simply use insert. Moreover, it's usually a good practice to use create because this way you will also save timestamps for created and updated.

Laravel relationship with method "with" returns null

Today I wanted to do some clean code so just started selecting columns for with relationship. With this code:
\App\Genre::with([
'family'
])->where([
'slug' => $slug,
'is_active' => true
])->first();
everything is working fine. But when I start selecting columns for "with" method:
\App\Genre::with([
'family' => function ($query) {
$query->select('name_pl', 'name_lat');
}])->where([
'slug' => $slug,
'is_active' => true
])->first();
I got that family is null (but it should be an object with columns: name_pl, name_lat). What I am doing wrong?
family method in Genre class looks like this:
public function family () {
return $this->belongsTo(Family::class);
}
I am using Laravel 5.4
Pretty sure you need to add a related column to the list of selected columns, otherwise Laravel won't b able to match the data to eager-load.
Assuming that Genre has a family_id and Family has an id primary key column specified, you need this:
$query->select('id', 'name_pl', 'name_lat'); // See the id added here?
Should do the trick.
For clarity, the matching I mentioned is this one:
select * from genre
select * from family where id in (1, 2, 3, 4, 5, ...)
-- where the comma-separated list of IDs consists of the unique family_id values retrieved in the first query.
Why don't you try:
\App\Genre::with('family:name_pl,name_lat')->where([
'slug' => $slug,
'is_active' => true
])->first();

Laravel insert or update multiple rows

Im new in laravel, and im trying to update my navigation tree.
So i want to update my whole tree in one query without foreach.
array(
array('id'=>1, 'name'=>'some navigation point', 'parent'='0'),
array('id'=>2, 'name'=>'some navigation point', 'parent'='1'),
array('id'=>3, 'name'=>'some navigation point', 'parent'='1')
);
I just want to ask - is there posibility in laravel to insert(if new in array) or update my current rows in database?
I want to update all, because i have fields _lft, _right, parent_id in my tree and im using some dragable js plugin to set my navigation structure - and now i want to save it.
I tried to use
Navigation::updateOrCreate(array(array('id' => '3'), array('id'=>'4')), array(array('name' => 'test11'), array('name' => 'test22')));
But it works just for single row, not multiple like i tried to do.
Maybe there is another way to do it?
It's now available in Laravel >= 8.x
The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
I wonder why this kind of feature is not yet available in Laravel core (till today). Check out this gist The result of the query string would look like this: here
I am putting the code here just in case the link breaks in the future, I am not the author:
/**
* Mass (bulk) insert or update on duplicate for Laravel 4/5
*
* insertOrUpdate([
* ['id'=>1,'value'=>10],
* ['id'=>2,'value'=>60]
* ]);
*
*
* #param array $rows
*/
function insertOrUpdate(array $rows){
$table = \DB::getTablePrefix().with(new self)->getTable();
$first = reset($rows);
$columns = implode( ',',
array_map( function( $value ) { return "$value"; } , array_keys($first) )
);
$values = implode( ',', array_map( function( $row ) {
return '('.implode( ',',
array_map( function( $value ) { return '"'.str_replace('"', '""', $value).'"'; } , $row )
).')';
} , $rows )
);
$updates = implode( ',',
array_map( function( $value ) { return "$value = VALUES($value)"; } , array_keys($first) )
);
$sql = "INSERT INTO {$table}({$columns}) VALUES {$values} ON DUPLICATE KEY UPDATE {$updates}";
return \DB::statement( $sql );
}
So you can safely have your arrays inserted or updated as:
insertOrUpdate(
array(
array('id'=>1, 'name'=>'some navigation point', 'parent'='0'),
array('id'=>2, 'name'=>'some navigation point', 'parent'='1'),
array('id'=>3, 'name'=>'some navigation point', 'parent'='1')
)
);
Just in case any trouble with the first line in the function you can simply add a table name as a second argument, then comment out the line i.e:
function insertOrUpdate(array $rows, $table){
.....
}
insertOrUpdate(myarrays,'MyTableName');
NB: Be careful though to sanitise your input! and remember the timestamp fields are not touched. you can do that by adding manually to each arrays in the main array.
I've created an UPSERT package for all databases: https://github.com/staudenmeir/laravel-upsert
DB::table('navigation')->upsert(
[
['id' => 1, 'name' => 'some navigation point', 'parent' => '0'],
['id' => 2, 'name' => 'some navigation point', 'parent' => '1'],
['id' => 3, 'name' => 'some navigation point', 'parent' => '1'],
],
'id'
);
Eloquent Style
public function meta(){ // in parent models.
return $this->hasMany('App\Models\DB_CHILD', 'fk_id','local_fk_id');
}
.
.
.
$parent= PARENT_DB::findOrFail($id);
$metaData= [];
foreach ($meta['meta'] as $metaKey => $metaValue) {
if ($parent->meta()->where([['meta_key', '=',$metaKey]] )->exists()) {
$parent->meta()->where([['meta_key', '=',$metaKey]])->update(['meta_value' => $metaValue]);
}else{
$metaData[] = [
'FK_ID'=>$fkId,
'meta_key'=>$metaKey,
'meta_value'=> $metaValue
];
}
}
$Member->meta()->insert($metaData);
No, you can't do this. You can insert() multiple rows at once and you can update() multiple rows using same where() condition, but if you want to use updateOrCreate(), you'll need to use foreach() loop.
I didn't find a way to bulk insert or update in one query. But I have managed with only 3 queries. I have one table name shipping_costs. Here I want to update the shipping cost against the shipping area. I have only 5 columns in this table id, area_id, cost, created_at, updated_at.
// first get ids from table
$exist_ids = DB::table('shipping_costs')->pluck('area_id')->toArray();
// get requested ids
$requested_ids = $request->get('area_ids');
// get updatable ids
$updatable_ids = array_values(array_intersect($exist_ids, $requested_ids));
// get insertable ids
$insertable_ids = array_values(array_diff($requested_ids, $exist_ids));
// prepare data for insert
$data = collect();
foreach ($insertable_ids as $id) {
$data->push([
'area_id' => $id,
'cost' => $request->get('cost'),
'created_at' => now(),
'updated_at' => now()
]);
}
DB::table('shipping_costs')->insert($data->toArray());
// prepare for update
DB::table('shipping_costs')
->whereIn('area_id', $updatable_ids)
->update([
'cost' => $request->get('cost'),
'updated_at' => now()
]);
in your controller
use DB;
public function arrDta(){
$up_or_create_data=array(
array('id'=>2, 'name'=>'test11'),
array('id'=>4, 'name'=>'test22')
);
var_dump($up_or_create_data);
echo "fjsdhg";
foreach ($up_or_create_data as $key => $value) {
echo "key ".$key;
echo "<br>";
echo " id: ".$up_or_create_data[$key]["id"];
echo "<br>";
echo " Name: ".$up_or_create_data[$key]["name"];
if (Navigation::where('id', '=',$up_or_create_data[$key]["id"])->exists()) {
DB::table('your_table_ name')->where('id',$up_or_create_data[$key]["id"])->update(['name' => $up_or_create_data[$key]["name"]]);
}else{
DB::insert('insert into your_table_name (id, name) values (?, ?)', [$up_or_create_data[$key]["id"], $up_or_create_data[$key]["name"]]);
}
}

firstOrCreate in model

I am either adding to my table or editing an existing row.
I've looked into firstOrCreate but I can't seem to get it to work, it always creates a new row.
return $this->firstOrCreate(array('id' => $input['id'], 'title' => $input['title'], 'sell' => $input['sell'], 'article' => $input['article'], 'thumb' => $input['thumb'], 'gallery' => $input['galleryData'], 'date' => $input['date'], 'published' => $input['published']));
Things like title change when the user edits them, is there a way to search the table based on the id, if it exists then update, if not, then create?
If the title changes, then using this method is illogical. The method you mention check for an item with ALL the given specifications.
What you should be doing instead:
// find the item, given the ID
$item = Item::firstOrNew(array('id' => $input['id']));
// add the fields from your input
$item->title = $input['title'];
$item->sell = $input['sell'];
$item->article = $input['article'];
$item->thumb = $input['thumb'];
$item->gallery = $input['galleryData'];
$item->date = $input['date'];
$item->published = $input['published'];
// Save the thing
$item->save();

Resources