How to merge elements of a collection in one array using Eloquent - php

I'm trying the following:
I'm getting all clinic_tests related to my patients using the following function:
public function getPatientsClinicTests(Specialist $id)
{
$patientClinicTests = $id->patients()
->with('PatientClinicTests', 'PatientClinicTests.Patient.User')
->get()
->pluck('PatientClinicTests')
->filter(function ($value) { return !empty($value); });
$result = [];
foreach ($patientClinicTests as $array) {
$result = array_merge($result, $array->toArray());
}
return $result;
}
First group of code:
$patientClinicTests = $id->patients()
->with('PatientClinicTests', 'PatientClinicTests.Patient.User')
->get()
->pluck('PatientClinicTests')
->filter(function ($value) { return !empty($value); });
Brings me a collection of arrays as follows:
[
[
{
"id": 16,
"patient_id": 7,
"medical_condition_id": null,
"patient": {
"id": 7,
"user_id": 7,
"pigment_id": 14,
"id_medical_history": "6219116421",
"user": {
"id": 7,
"name": "Austen Wiegand",
}
}
},
.....
],
[
{
"id": 22,
"patient_id": 1,
"medical_condition_id": null,
"patient": {
"id": 7,
"user_id": 1,
"pigment_id": 14,
"id_medical_history": "6219116421",
"user": {
"id": 7,
"name": "Gregor Wiegand",
}
}
},
.......
]
]
As I need to return one array of elements I combine the arrays I got as follows:
$result = [];
foreach ($patientClinicTests as $array) {
$result = array_merge($result, $array->toArray());
}
return $result;
This returns one array as follows:
[
{
"id": 16,
"patient_id": 7,
"medical_condition_id": null,
"patient": {
"id": 7,
"user_id": 7,
"pigment_id": 14,
"id_medical_history": "6219116421",
"user": {
"id": 7,
"name": "Austen Wiegand",
}
}
},
{
"id": 22,
"patient_id": 1,
"medical_condition_id": null,
"patient": {
"id": 7,
"user_id": 1,
"pigment_id": 14,
"id_medical_history": "6219116421",
"user": {
"id": 7,
"name": "Gregor Wiegand",
}
}
},
.......
]
I would like to know if there is a smarter option to return as one array of elements using Eloquent instead a foreach statement.
Thanks a lot for your help!

you could use all() method to convert your collection to an array:
$patientClinicTests = $id->patients()
->with('PatientClinicTests', 'PatientClinicTests.Patient.User')
->get()
->pluck('PatientClinicTests')
->filter(function ($value) { return !empty($value); })->all();
please note that if you want to iterate over your array you should rebuild the array indexes after filtering ... you can do that using 'values' method:
$patientClinicTests = $id->patients()
->with('PatientClinicTests', 'PatientClinicTests.Patient.User')
->get()
->pluck('PatientClinicTests')
->filter(function ($value) { return !empty($value); })->values()->all();
more details in:
https://laravel.com/docs/7.x/collections#introduction

maybe you can use a collect method over the array to allign it in a single array
https://laravel.com/docs/7.x/collections
the link may be helpful
$collection = collect($patientClinicTests);
$collections = $collection->values()->all();
maybe this will work

Flatten method helps me to give a single array without using foreach statement an array_merge() function:
$patientClinicTests = $id->patients()
->with('PatientClinicTests', 'PatientClinicTests.Patient.User')
->get()
->pluck('PatientClinicTests')
->filter(function ($value) { return !empty($value); })->flatten()->all();
I will test using table joining as it has been recommended

Related

PHP Map infinite objects

I have some struggles how to map infinite object in Laravel.
So I have one table Categories that I'm getting in controller like:
$find_parent = Category::where('slug', $slug)->with('childrenRecursive')->first();
childrenRecursive() works fine in that case.
And return of this object would be like (minified):
{
"id": "1cbd459a-ccc0-435b-b9a9-0433e2e9285b",
"parent_id": "f0d29100-d2bc-48c8-89cf-985e0c03b8ac",
"children_recursive": [
{
"id": "30bf23a7-b28c-4s78-b873-1df589eebcb1",
"parent_id": "1cbd459a-ccc0-435b-b9a9-0433e2e9285b",
"children_recursive": [
{
"id": "32312a7-b28c-4s78-b873-1df589eebcb1",
"parent_id": "30bf23a7-b28c-4s78-b873-1df589eebcb1",
}
]
},
{
"id": "32bf23a7-b28c-4s78-b873-1df589eebcb1",
"parent_id": "1cbd459a-ccc0-435b-b9a9-0433e2e9285b",
"children_recursive": []
}
]
}
So from this, I have to get all id's in every object. So I have foreach function in controller that looks like:
$find_parent = Category::where('slug', $slug)->with('childrenRecursive')->first();
$array = [];
foreach($find_parent->childrenRecursive as $l){
array_push($array, $l->id);
if($l->childrenRecursive){
$result = array_merge($array, $this->catTree($l->childrenRecursive));
}
}
return $result;
And catTree would look like:
public function catTree($list){
$tree = [];
foreach($list as $l){
array_push($tree, $l->id);
if($l->childrenRecursive){
array_merge($tree, $this->catTree($l->childrenRecursive));
}
}
return $tree;
}
So this is returning some of the objects, but not everything. What am I doing wrong here?

Laravel use function in query

In controller get data from db like this, I want to pass whole of $request to another function in this controller to get price it calculating price based of many things from $request:
$user = Auth::user();
$query = Post::query();
$query
->where('province', '=', $user->province)
->where('city', '=', $user->city);
$customers = $query->get();
$customers['calculator'] = $this->calculator($request); // call function
my problem is it return like this:
{
"0": {
"id": 1,
"hash": "RqH29tkfm1dwGrXp4ZCV",
},
"1": {
"id": 3,
"hash": "RqH29tkfm1dwGsXp4ZCV",
},
"calculator": {
"price": 1
}
}
But I need to use that function for each data, and result should be like this:
{
"0": {
"id": 1,
"hash": "RqH29tkfm1dwGrXp4ZCV",
"calculator": {
"price": 1
}
},
"1": {
"id": 3,
"hash": "RqH29tkfm1dwGsXp4ZCV",
"calculator": {
"price": 1
}
}
}
What you want is to set a calculator key for each item in the $customers collection. So you need to loop over it:
foreach ($customers as $customer) {
$customer->calculator = $this->calculator($request);
}
Notice that since the $customer is a Model you should set the calculator as a property. Internally it will be set to the attributes array.

Is there a way to merge or combine two arrays received from Laravel Query?

I am trying to achieve a combined array from the query result of two or more queries. The solution what I have tried so far is given below.
Created one variable $total_records and iterated over it again in a foreach loop to get the second set of data output. Now I want to merge output#2 into output#1 received at the first place in total_records
There is a query#1:
$total_records = RatioStatistics::where('type', '=', 'group')
->whereBetween('date', [$modified_start_date, $modified_end_date])
->join('cart_abondon_group', 'statistics.ref_id', '=', 'cart_abondon_group.id')
->select('statistics.*', 'cart_abondon_group.name')
->orderBy('statistics.ref_id', 'ASC')
->orderBy('statistics.date', 'ASC')
->get();
Output#1:
[{
"id": 324985,
"ref_id": 1,
"date": "2019-01-02T00:00:13+00:00",
"total": 434,
"available": 164,
"rented": 270,
"unusable": 0
},
{
"id": 325602,
"ref_id": 1,
"date": "2019-01-03T00:00:16+00:00",
"total": 427,
"available": 176,
"rented": 251,
"unusable": 0
}]
There is another query#2:
foreach ($total_records as $record) {
$data = [
'total_number_of_cart_abondon_of_the_day' => Cart::where('created_at', 'like', $record['date']->format('Y-m-d') . '%')
->whereNotIn('status_id', [11])
->whereHas('product', function ($q) use ($record) {
$q->where('group_id', $record['ref_id']);
})
->count(),
'total_number_of_cart_abondon_per_day_per_warehouse_user' => Cart::where('created_at', 'like', $record['date']->format('Y-m-d') . '%')
->where('warehouse_user', 1)
->whereNotIn('status_id', [11])
->whereHas('product', function ($q) use ($record) {
$q->where('group_id', $record['ref_id']);
})
->count()
];
$new_stats[] = $data;
}
Output 2:
"0": {
"total_number_of_cart_abondon_of_the_day": 27,
"total_number_of_cart_abondon_per_day_per_warehouse_user": 0
},
"1": {
"total_number_of_cart_abondon_of_the_day": 30,
"total_number_of_cart_abondon_per_day_per_warehouse_user": 0
}
Requested Output:
Either I will have to make a solution like that so that we can query everything through the first query itself without running the second query.
Or make the second query more optimised so that output#2 can be clubbed into output#1
Correct Desired Output:
[{
"id": 324985,
"ref_id": 1,
"date": "2019-01-02T00:00:13+00:00",
"total": 434,
"available": 164,
"rented": 270,
"unusable": 0,
"total_number_of_cart_abondon_of_the_day": 27,
"total_number_of_cart_abondon_per_day_per_warehouse_user": 0
}, {
"id": 325602,
"ref_id": 1,
"date": "2019-01-03T00:00:16+00:00",
"total": 427,
"available": 176,
"rented": 251,
"unusable": 0,
"total_number_of_cart_abondon_of_the_day": 30,
"total_number_of_cart_abondon_per_day_per_warehouse_user": 0
}]
When you call
foreach ($total_records as $record) {
$data = [
'total_number_of_cart_abondon_of_the_day' => Cart::where...,
'total_number_of_cart_abondon_per_day_per_warehouse_user' => Cart::where...
]
}
You're making an extra 2 sql queries per record.
Assuming RadioStatistics is statistics table's model and Cart is cart_abondon_group table's model, this can be fixed as follows:
Make a relationship method in RadioStatistics model
# statistics has a foreign key (ref_id) that references cart_abondon_group's primary key (id)
public function carts()
{
return this->belongsTo(Cart::class, 'ref_id', 'id'); // Assuming Cart is cart_abondon_group's model
}
Remake query 1 to so it takes advantage of that:
$total_records = RatioStatistics::where('type', '=', 'group')
->whereBetween('date', [$modified_start_date, $modified_end_date])
->join('cart_abondon_group', 'statistics.ref_id', '=', 'cart_abondon_group.id')
// WithCount must be before select or it won't be added to the results
->withCount([
'carts as total_number_of_cart_abondon_of_the_day' => function ($carts) {
$carts->whereRaw("cart_abondon_group.created_at like concat(date_format('statistics.date', '%d-%m-%Y'), '%')")
->whereNotIn('status_id', [11])
->whereHas('product', function ($q) use ($record) {
$q->whereRaw('group_id = statistics.id');
});
},
'carts as total_number_of_cart_abondon_per_day_per_warehouse_user' => function ($carts) {
$carts->whereRaw("cart_abondon_group.created_at like concat(date_format('statistics.date', '%d-%m-%Y'), '%')")
->where('warehouse_user', 1)
->whereNotIn('status_id', [11])
->whereHas('product', function ($q) use ($record) {
$q->whereRaw('group_id = statistics.id');
});
}
])
->select('statistics.*', 'cart_abondon_group.name')
->orderBy('statistics.ref_id', 'ASC')
->orderBy('statistics.date', 'ASC')
->get();
The tricky part is the date. I don't really get why your schema has two different formats.

Laravel sort object by Key

I have the following which I would like to order alphabetically by the Key i.e first for each array group would be "bname", followed by "created_at".
{
"leads": [
{
"lead_id": 1,
"zoho_lead": null,
"bname": "ABC Limited",
"tname": "ABC",
"source_id": 11,
"industry_id": 1,
"user_id": 1,
"created_at": "2017-09-06 15:54:21",
"updated_at": "2017-09-06 15:54:21",
"user": "Sean McCabe",
"source": "Unknown",
"industry": "None"
},
{
"lead_id": 2,
"zoho_lead": 51186111981,
"bname": "Business Name Limited",
"tname": "Trading Name",
"source_id": 11,
"industry_id": 1,
"user_id": 1,
"created_at": "2017-06-01 12:34:56",
"updated_at": null,
"user": "John Doe",
"source": "Unknown",
"industry": "None"
}
]
}
I'm trying to use ksort like so in the foreach loop:
class LeadController extends Controller
{
use Helpers;
public function index(Lead $leads)
{
$leads = $leads->all();
foreach($leads as $key => $lead){
$lead->user = User::where('id', $lead->user_id)->first()->name;
$lead->source = Source::where('id', $lead->source_id)->first()->name;
$lead->industry = Industry::where('id', $lead->industry_id)->first()->name;
$lead->ksort();
}
return $leads;
}
But I get the following error:
Call to undefined method Illuminate\\Database\\Query\\Builder::ksort()
How do I use this function, or is there a Laravel way of doing this, or a better way altogether?
Thanks.
Managed to get it to return with the Keys in alphabetical order, so below is the solution in-case someone else should require it:
public function index(Lead $leads)
{
$leadOut = Array();
$leads = $leads->all();
foreach($leads as $key => $lead){
$lead->user = User::where('id', $lead->user_id)->first()->name;
$lead->source = Source::where('id', $lead->source_id)->first()->name;
$lead->industry = Industry::where('id', $lead->industry_id)->first()->name;
//Convert to Array
$leadOrder = $lead->toArray();
//Sort as desired
ksort($leadOrder);
//Add to array
$leadOut[] = $leadOrder;
}
return $leadOut;
}
There is likely a cleaner way to do this, but it works for my instance, and perhaps additional answers may be posted that are better.
You could do something like:
return Lead::with('user', 'source', 'industry')->get()->map(function ($lead) {
$item = $lead->toArray();
$item['user'] = $lead->user->name;
$item['source'] = $lead->source->name;
$item['industry'] = $lead->industry->name;
ksort($item);
return $item;
});
This should be much more efficient as it will eager load the relationships rather than make 3 extra queries for each iteration.

Flatten multidimensional object while keeping order

I would like to flatten an object. This is what I've got so far:
{
"1": {
"id": 1,
"name": "parent",
"children": {
"4": {
"id": 4,
"name": "child1",
"parent": 1
},
"5": {
"id": 5,
"name": "child2",
"parent": 1
}
}
},
"2":{
"id": 2,
"name": "parent2"
}
}
And this is what I would like to accomplish. So keep the same order but flatten the object:
{
"1": {
"id": 1,
"name": "parent",
},
"4": {
"id": 4,
"name": "child1",
"parent": 1
},
"5": {
"id": 5,
"name": "child2",
"parent": 1
},
"2": {
"id": 2,
"name": "parent2"
}
}
So far I haven't found a solution to this. I've tried a function without much success:
protected function _flattenObject($array)
{
static $flattened = [];
if(is_object($array) && count($array) > 0)
{
foreach ($array as $key => $member) {
if(!is_object($member))
{
$flattened[$key] = $member;
} else
{
$this->_flattenObject($member);
}
}
}
return $flattened;
}
The tough part for me is to keep the same order (children below its parent). And the function mentioned above also removes all objects and almost only keeps the keys with its value, so it wasn't a great success at all.
Hopefully somebody over here knows a good solution for this.
By the way, the reason I want such flatten structure is because the system I have to work with, has trouble handling multidimensional arrays and objects. And I still want to display an hierarchy, which is possible with the flatten structure I described, because the objects actually contain a "level" key as well so I can give them some padding based on the "level" while still showing up below their parent.
EDIT:
The JSON didn't seem to be valid, so I modified it a bit.
The main problem seems to be that you are not doing anything with the returned results of your recursive function. Unless using static inside a method does some magic that I don't know of...
So this section:
if(!is_object($member))
{
$flattened[$key] = $member;
} else
{
// What happens with the returned value?
$this->_flattenObject($member);
}
Should probably be more like this:
if(!is_object($member))
{
$flattened[$key] = $member;
} else
{
// Add the returned array to the array you already have
$flattened += $this->_flattenObject($member);
}
Here is code that works. It adds a field "level" to your objects, to represent how many levels deep in the original hierarchy they were.
<?php
$obj = json_decode('[{
"id": 1,
"name": "parent",
"children": [{
"id": 4,
"name": "child1",
"parent": 1
}, {
"id": 5,
"name": "child2",
"parent": 1
}]
}, {
"id": 2,
"name": "parent2"
}]');
function _flattenRecursive($array, &$flattened, &$level)
{
foreach ($array as $key => $member) {
$insert = $member;
$children = null;
if (is_array($insert->children)) {
$children = $insert->children;
$insert->children = array();
}
$insert->level = $level;
$flattened[] = $insert;
if ($children !== null) {
$level++;
_flattenRecursive($children, $flattened, $level);
$level--;
}
}
}
function flattenObject($array)
{
$flattened = [];
$level = 0;
_flattenRecursive($array, $flattened, $level);
return $flattened;
}
$flat = flattenObject($obj);
var_dump($flat);
?>

Categories