How to find and merge two rows into one - php

I am new to Laravel so I am confused how to do this
I have a table called groups where I forget to use unique in validation . After Long time I found out there are multiple datas with same name for ex :
I have two datas with same name called Feminism but with different id
The group table have only name and description columns , but it has relation with products many to many I have other datas too which has same name I want to merge those datas into one . along with the relation to the product . Is is possible ?
Shall I have to check all the names manually like
Item::where('name','Feminism')->get();
and then merge those datas or we have some other relavant methods

You can refactor this code for your models and relations
foreach ($groups as $group) {
$duplicates = Group::where('name', $group->name)->where('id', '<>', $group->id)->get();
foreach ($duplicates as $duplicate) {
$mustBeUpdatedRelation = $duplicate->products;
$anotherRelation = $duplicate->anotherRelation;
foreach ($mustBeUpdatedRelation as $product) {
$product->categories()->detach($duplicate->id); // if product has pivot data you must get these and assign to new relation
//new relation
$product->categories()->attach($group->id);
}
$anotherRelation = $duplicate->anotherRelation;
foreach ($anotherRelation as $item) {
// like above
// detach duplicated
// attach unique
}
//finally you can delete or .... duplicated group
$duplicate->update([
'name' => "duplicated_$duplicate->name"
]);
}
}

You may use the following
$items = App\Item::with('products')->get();
$tempArr = [];
foreach ($items as $key => $item) {
if(isset($tempArr[$item->name])) {
$merged = $tempArr[$item->name]['products']->merge($item->products);
unset($tempArr[$item->name]['products']);
$tempArr[$item->name]['products'] = $merged;
} else {
$tempArr[$item->name] = $item;
}
}
foreach ($tempArr as $item) {
$item->products()->sync($item->products->pluck('id'));
}
$idsToKeep = array_column($tempArr, 'id');
App\Item::whereNotIn('id', $idsToKeep )->delete();
Use onDelete cascade when defining your pivot table migration.
This takes care of deleting the model’s relations for you:
e.g.
$table->foreign(’item_id’)
->references(’id’)->on(’items’)
->onDelete(’cascade’);

Related

Building an array in nested foreach loops in Laravel controller

I am trying to build an array of user ids from a long eloquent relationship, using nested foreach loops in a Laravel controller, but can't get it to work.
Users can have publishers, those publishers can have teams and each team has members. Users can be in multiple teams so I also need to remove duplicate IDs.
I want to end up with a count to see how many team members are associated with a user.
In my user model
public function publishers()
{
return $this->belongsToMany('App\Publisher')->withTimestamps();
}
In my publisher model
public function teams()
{
return $this->belongsToMany('App\Team')->withTimestamps();
}
and in my team model
public function members()
{
return $this->belongsToMany('App\User')->withPivot('status', 'title', 'team_role_ids')->withTimestamps();
}
and in my profile controller
foreach ($user->publishers as $userPublisher) {
foreach ($userPublisher->teams as $publisherTeam) {
$teamUserIds[] = $publisherTeam->members->pluck('id')->toarray();
}
}
$deDupedIds = array_unique($teamUserIds, SORT_NUMERIC);
$idsCount = count($deDupedIds);
But I'm getting multiple arrays and not just one compiled array and the count isn't working. Any idea what I'm doing wrong?
You are assigning a new array into $teamUserIds each iteration. That's why you are getting multiple arrays.
$teamUserIds = [];
foreach ($user->publishers as $userPublisher) {
foreach ($userPublisher->teams as $publisherTeam) {
$teamUserIds = array_merge($teamUserIds, $publisherTeam->members->pluck('id')->toarray());
}
}
$deDupedIds = array_unique($teamUserIds, SORT_NUMERIC);
$idsCount = count($deDupedIds);
you are adding arrays of id $publisherTeam->members->pluck('id')->toarray(); as a new index in $teamUserIds . but what you want to do is to merge the array of ids
so your code would be like this :
foreach ($user->publishers as $userPublisher) {
foreach ($userPublisher->teams as $publisherTeam) {
$teamUserIds = array_merge($teamUserIds , $publisherTeam->members->pluck('id')->toarray());
}
}
$deDupedIds = array_unique($teamUserIds, SORT_NUMERIC);
$idsCount = count($deDupedIds);

Laravel Pagination with array_merge

I have 2 table which name incomes and expenses and I want to show all the data in one view with laravel pagination. For this, I Merged two array with array_merge().
$incomes = Income::where('user_id', Auth::User()->id)->get()->toArray();
$expenses = Expense::where('user_id', Auth::User()->id)->get()->toArray();
foreach ($incomes as $key => $value) {
$incomes[$key]['type'] = 'income';
}
foreach ($expenses as $key => $value) {
$expenses[$key]['type'] = 'expense';
}
$results = array_merge($incomes, $expenses);
How can I paginate this $results?
You can do this in two ways:
Laravel's built in manually creating a paginator functionality.
Get the results from tables using UNION.
Using UNION
$incomes = Income::where('user_id', Auth::User()->id);
$data = Expense::where('user_id', Auth::User()->id)->union($incomes)->paginate(10);
For manually creating a paginator, please see https://laravel.com/docs/6.x/pagination#manually-creating-a-paginator
Add the following to Income Model
protected $appends = ['type'];
public function getTypeAttribute()
{
return 'Income';
}
Add the following to Expense Model
protected $appends = ['type'];
public function getTypeAttribute()
{
return 'Expense';
}
This will add the type key in the query results.

Update order in pivot table when using sync in Laravel

In Laravel 4.2, I have the following code:
$thingIds = [];
foreach ($this->things as $thing) {
$thingIds[] = $thing->id;
}
$thong->things()->sync($thingIds);
$thong->save();
Which seems to work for me, however in my pivot table I have an order column that isnt being updated/synced correctly. The order is set to 1 for each item in the pivot table, however I want the order to be representative of the order of the ids in $thingIds.
Is this possible?
Is it possible to update an additional column in the pivot table using ->sync()?
After the suggestion in the comments, I have also tried the following code:
$thingIds = [];
foreach ($this->things as $index => $thing) {
$thingIds[$thing->id] = ['order' => $index];
}
$thong->things()->sync($thingIds);
$thong->save();
And I have tried
$thingIds = [];
$order = [];
foreach ($this->things as $index => $thing) {
$thingIds[] = $thing->id;
$order[] = ['order' => $index];
}
$thong->things()->sync(array_combine($thingIds, $order));
$thong->save();
But neither of these is working (the order is still 1 for each item in the pivot table). Can anyone explain what I am doing wrong?
If I do the attaching one at a time, like so:
foreach ($this->things as $index => $thing) {
$thong->things()->attach($thing, ['order' => $index]);
}
The order comes out correctly in the pivot table. How can I get the same effect but whilst still using ->sync()?

eloquent relation in laravel with json

suppose i have two tables(Team and User) in my database . in Team table i have data like "["5","6","7","9","10","26","27"]"(json) , and in users table i have id column in which data is like serial no(Auto_increment .1,2,3 ....nth).is it possible a relation which finds a if json has that single key in it.
i have a loop statement which has the result that i want but i want that data in relation the code is :
foreach($model as $key => $value){
$user_data = User::whereIn('id',json_decode($value->member_ids))->get();
$model[] = $user_data;
}
Try this
$arr = json_decode("["5","6","7","9","10","26","27"]");
if (in_array(5, $arr )) {
// code
}

Traversing through collection and updating records using Eloquent

I am using eloquent to pull a list of App\Post's from my database and deleting their file contents from my server.
I am traversing through the list and processing a delete action, on success I want to store a value to the respective App\Post but I am getting this error:
Method save does not exist.
Here is my code:
$posts_to_purge = Post::where('post_date', '<', \Carbon\Carbon::now()->subDays(7)->toDateString())->get();
if(count($posts_to_purge) > 0)
{
foreach ($posts_to_purge as $post) {
$directory = storage_path() . '/' . $post->post_date;
$deleted_folder = File::deleteDirectory($directory);
if($deleted_folder)
{
// 'purged' is a column in my Post table
$post->purged = 1;
}
else
{
Log::error('Unable to delete \''.$directory.'\' directory');
}
} // end foreach loop
// Update collection with purged posts
$posts_to_purge->save();
}
A side question to this... is this the most efficient way to do this? As I only want to update SOME records in the collection.. should I create a separate collection and save that only?
Collections don't have really have the power to interact with the database in Laravel. You are able to define a custom collection class with a save() method however there is a simple way to do what you want.
$posts_to_purge = Post::where('post_date', '<', \Carbon\Carbon::now()->subDays(7)->toDateString())->get();
if(count($posts_to_purge) > 0)
{
foreach ($posts_to_purge as $post) {
$directory = storage_path() . '/' . $post->post_date;
$deleted_folder = File::deleteDirectory($directory);
if($deleted_folder)
{
// 'purged' is a column in my Post table
$post->purged = 1;
$post->save ();
}
else
{
Log::error('Unable to delete \''.$directory.'\' directory');
}
} // end foreach loop
// Update collection with purged posts
$posts_to_purge->save();
}
Yes this does do one query per updated model, however updates by primary key are very fast.
If you were super committed to using one query, you could simply save each updated Post's ID to an array and do something like Post::whereIn ('id', $updated_ids)->update(['purged' => 1]);

Categories