Nested loop caused slow in performance - laravel

foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
foreach ($id_cart as $id) {
$r = Cart::find($id);
/// column to update value
$r->save();
}
}
This is how my loop looks like.
E.g. $id_prs consist of 10 data, while each id_prs may consist of 20 data etc.
From there, i found will take longer time to loop when a lots of data from each $id_cart.
How can i solve the performance issue, is there any solution?

This is known as n+1 problem, every time you iterate inside foreach additional query is created.
So this line is the evil, because it's querying data every iteration.
$r = Cart::find($id);
You can make it better like this:
$cart = Cart::all();
foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
foreach ($id_cart as $id) {
// Get it from collection rather than query it from database
$cart->where('id', $id)->first()->save();
}
}
Where you acctualy query all carts inside variable as a collection, and you just manipulating with that collection localy without touching database (only when you hit save method).

Your code and variables are not clear enough. But a better solution is:
foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
$items = Cart::whereIn('id', $id_cart) -> get();
foreach($items as $item) {
Cart::where('id', $item -> id) -> limit(1) -> update([
// Columns to update
]);
}
}

If you want to update same value for all the records
$allIds = [];
foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
foreach ($id_cart as $id) {
$allIds[] = $id;
}
}
$cart = Cart::whereIn('id',$allIds)->update(['columns'=>'value']);

Related

Refactor Query Builder Laravel

I want to do repetitive get data and foreach on several tables (see example below). Is there a way to write the code in a cleaner way instead of repeating the same code for all the tables?
$xs = DB::table('table1')->where('text', 'like', '%string')->get();
foreach ($xs as $x) {
..
}
$ys = DB::table('table2')->where('text', 'like', '%string')->get();
foreach ($ys as $y) {
..
}```
My approach is using array and foreach
$tables = ['table1', 'table2'];
results = [];
foreach($tables as $table) {
$data = DB::table($table)->where('text', 'like', '%string')->get();
foreach($data as $d) {
// your logic here
}
$results[] = ; // return a value from each query to array
}
You can write a base function and pass tableName to it and execute certain action
public function getData($tableName) {
$query = DB::table($tableName)->where('text', 'like', '%string')->get();
foreach ($query as $row) {
...
}
// return result;
}
$tables = ['table1', 'table2', 'table3'];
$queryResponse = [];
foreach($tables as $tableName) {
$queryResponse[$tableName] = $this->getData($tableName);
}

laravel more than one result from single query

I am trying to get all rows and distinct column from single query. but paginate method is only giving result but not pagination option like total prev next etc..
$offers = Offer::whereHas('users', function ($q) use ($authUser) {
$q->where('user_id', $authUser->parent_id);
$q->where('publisher_id', '=', $authUser->id);
});
and distinct column
$websites = $offers->distinct()->get(['website']);
with pivot columns (just wanted to show my full query)
$offers->orderBy($sortBy, $orderBy)->paginate($perPage)->map(function ($offer) {
if (!empty($offer->users)) {
$manager = $publisher = '';
foreach ($offer->users as $user) {
$manager = $user->pivot->user_id;
$publisher = $user->pivot->publisher_id;
}
$offer->manager = $manager;
$offer->publisher = $publisher;
}
return $offer;
});
Return
return response()->json([
'offers' => $offers,
'websites' => $websites
], 200);
hope my question will make sense.
Thanks.
You should run getCollection() before mapping to get the paginator's underlying collection.
(https://laravel.com/api/7.x/Illuminate/Pagination/LengthAwarePaginator.html#method_getCollection)
$offers->orderBy($sortBy, $orderBy)->paginate($perPage)
->getCollection()
->map(function ($offer) {
// ...
return $offer;
});
I'm answering based on it being $offers:
Your usage of map() is copying the modified results of your paginate() call to a new collection and that collection does not include the pagination information. That's why you no longer have pagination information.
Since there result of paginate() is already a usable collection, you can use each() instead of map() which will alter the objects in-place.

Sorting collections in Laravel

I've got a little problem with my Laravel controller and sorting it:
public function index()
{
$achievements = Achievement::all();
$news = News::all();
$livingspaces = Livingspace::all();
$therapies = Therapy::all();
$events = Event::all();
$collection = collect();
foreach ($achievements as $achievement) {
$collection->push($achievement);
}
foreach ($news as $new) {
$collection->push($new);
}
foreach ($livingspaces as $livingspace) {
$collection->push($livingspace);
}
foreach ($events as $event) {
$collection->push($event);
}
foreach ($therapies as $therapy) {
$collection->push($therapy);
}
$sortedData = $collection->sortBy('category')->sortByDesc('created_at');
return response()->json([
'sortedData' => $sortedData
], 200);
}
It's not sorting at all. It should sort if after the data in the created_at timestamp which comes out of the box when creating a new migration for a Laravel controller. But I can't sort the data. I think it has something to do with pushing the data from the DB directly into the collection and it's not looking for "created_at" at all. It's not giving any errors or anything its just not doing anything. The same goes for sortBy.
sortByDesc method has the same signature as the sortBy method, but will sort the collection in the opposite order. By default sortBy to do the ascending operation. If you want to sort by decending:
$sortedData = $collection->sortBy('category', true);

Showing the lowest pid / parent id number first

I have this function below which get the pid numbers from my database. The pid is parent id.
public function test() {
$arr = array();
foreach ($this->make_parent_list() as $result) {
$arr[] = $result;
}
print_r(implode(',', $arr));
}
The out put is as follows 4, 1
But I need it to print the lowest number first. 1, 4
Question How can I make sure when i view the parent id / pid that it
will show the lowest number first I have tried
$this->db->order_by('pid', 'desc'); and $this->db->order_by('pid',
'asc');
public function make_parent_list() {
$this->db->where('fid', '5');
$query = $this->db->get('forum');
$return = array();
foreach ($query->result() as $category)
{
$this->db->where('fid', $category->pid);
$this->db->order_by('pid', 'desc');
$query = $this->db->get('forum');
$return[$category->pid] = $category->pid;
foreach ($query->result() as $category)
{
$return[$category->pid] = $category->pid;
}
}
return $return;
}
Solved
I had to created a $results variable and then wrap in sort() outside foreach loop
$arr = array();
$results = $this->make_parent_list();
sort($results);
foreach ($results as $result) {
$arr[] = $result;
}
echo implode(',', $arr);
Now out put 1,4
I think you got a bit lost... The order_by asc,desc does work. Your function has order_by desc and an unnecessary foreach etc.
Assumptions: The forum table has at least the two columns pid and fid. I created a table with
fid , pid
5 1
5 4
If you simplify your function to...
// Given the fid, return an array of pid values in ascending order
public function make_parent_list() {
$this->db->where('fid', '5'); // Hardcoded for testing.
$this->db->order_by('pid', 'asc');
$query = $this->db->get('forum');
$pid_list = array();
foreach ($query->result() as $category) {
$pid_list[] = $category->pid;
}
return $pid_list;
}
Then you only need
$results = $this->make_parent_list();
echo implode(',', $results);
That will give you a result of 1,4.
If you change asc to desc in the order_by in the function then you will get 4,1.

codeigniter LEFT JOIN array issue

Can someone tell me how to write this properly?
function get_tech() {
$this->db->select('u.id
,u.first_name
,us.id
,us.group_id');
$this->db->from('users u');
$this->db->join('users_groups us','us.id = u.id','left');
$records = $this->db->where('us.group_id', '3');
$data=array();
foreach($records->result() as $row)
{
$data[$row->id] = $row->first_name;
}
return ($data);
}
I'm trying to populate a drop down menu using an array, but i need to only grab users that are part of users_group/group_id = 3
therefore in my very limited knowledge I'm needing:
select X from Users LEFT JOIN users_groups WHERE group_ID = 3
You need to call $this->db->get() in order to actually run your query.
function get_tech() {
$this->db->select('u.id
,u.first_name
,us.id
,us.group_id');
$this->db->from('users u');
$this->db->join('users_groups us','us.id = u.id','left');
$this->db->where('us.group_id', '3');
$records = $this->db->get();
$data = array();
foreach($records->result() as $row){
$data[$row->id] = $row->first_name;
}
return $data;
}

Resources