Laravel sort object by Key - php

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.

Related

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

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

How to remove pivot keyword from json using laravel?

I am trying to fetch data from games table which has pivot table user_games. Below code if works fine for me
$UserGames = User::with(['games' => function ($query){
$query->withPivot('highscore','level');
}])->find(request()->user()->id);
I am getting following json response
{
"data": [
{
"id": 2,
"name": "culpa",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27",
"pivot": {
"user_id": 2,
"game_id": 2,
"highscore": 702,
"level": 3
}
}
]
}
But I wanted to remove pivot keyword from above json and pull pivot detail into root as like below my desire response
{
"data": [
{
"id": 2,
"name": "culpa",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27",
"user_id": 2,
"highscore": 702,
"level": 3
}
]
}
Can someone kindly guide me how to fix the issue. I would appreciate. Thank you so much
You can utilise hidden and appends on the pivot model to re-structure the returned data.
class PivotModel extends model
{
protected $hidden = ['pivot'];
protected $appends = ['user_id'];
public function getUserIdAttribute()
{
return $this->pivot->user_id;
}
}
Reference for hidden
Reference for appends
You can convert the json into an array than reconvert it to json.
$UserGames = User::with(['games' => function ($query){
$query->withPivot('highscore','level');
}])->find(request()->user()->id);
$UserGames = json_decode($UserGames, true);
$pivot = $UserGames['data'][0]['pivot'];
unset($UserGames['data'][0]['pivot']);
$UserGames = json_encode(array_merge($UserGames[0], $pivot));
You can override the User model's jsonSerialize method which is called in the toJson method, this is the initial method body:
public function jsonSerialize()
{
return $this->toArray();
}
And you can do something like this:
public function jsonSerialize()
{
$attrs = $this->toArray();
if (isset($attrs['pivot'])) {
$attrs = array_merge($attrs, $attrs['pivot']);
unset($attrs['pivot']);
}
return $attrs;
}

Add extra key in laravel eloquent result

I have a laravel query builder result that looks like this
{
"data": [
{
"id": "",
"awardID": 2,
"title": "Dummy title",
"status": "active",
"raceStart":"",
"raceEnd:":""
}
]
}
What i want to output is something like this
{
"data": [
{
"id": "",
"awardID": 2,
"title": "Dummy title",
"status": "active",
"period": {
"raceStart":"",
"raceEnd:":""
}
}
]
}
This would have been much easier if the period was a table with a 1 to 1 relationship with parent table but this is not the case here.
How can this be achieved?
Check if this will work. I haven't tried though but according to documentation we can add accessor and mutators. But it will change every response you are doing with the model.
Using Eloquent
// Your Model
class Race extends Model
{
{...}
protected $appends = ['period'];
// accessor
public function getPeriodAttribute($value)
{
$this->attributes['period'] = (object)[];
$this->attributes['period']['raceStart'] = $this->attributes['raceStart'];
$this->attributes['period']['raceEnd'] = $this->attributes['raceEnd'];
unset($this->attributes['raceStart']); = $value;
unset($this->attributes['raceEnd']);
return $this->attributes['period'];
}
}
Now when you will access $race->period will give the raceStart and raceEnd value.
Ref: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators
else another option is after query, do a map
{...}
->map(function($data) {
$data->period = (object)[];
$data->period['raceStart'] = $data->raceStart;
$data->period['raceEnd'] = $data->raceEnd;
unset($data->raceStart);
unset($data->raceEnd);
return $data;
});
Ref: https://laravel.com/docs/5.4/eloquent-collections#introduction
Using QueryBuilder
$races = DB::table('races')->get();
$races = array_map(function ($data) {
$data->period = (object)[
"raceStart" => $data->raceStart,
"raceEnd" => $data->raceEnd
];
unset($data->raceStart);
unset($data->raceEnd);
return $data;
}, $races->data);

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);
?>

PHP - recursively iterate over json objects

I need to iterate over objects in PHP and to apply a certain function on each and every single value in this object.
The objects are absolutely arbitrary. They can include vars, another objects, arrays, arrays of objects and so on...
Is there a generic method to do so? If yes, how?
Usage example:
RESTful API which receives requests in JSON format.
json_decode() is executed on request body and creates an arbitrary object.
Now, it is good, for example, to execute mysqli_real_escape_string() on every value in this object before further validations.
OBJECT EXAMPLE:
{
"_id": "551a78c500eed4fa853870fc",
"index": 0,
"guid": "f35a0b22-05b3-4f07-a3b5-1a319a663200",
"isActive": false,
"balance": "$3,312.76",
"age": 33,
"name": "Wolf Oconnor",
"gender": "male",
"company": "CHORIZON",
"email": "wolfoconnor#chorizon.com",
"phone": "+1 (958) 479-2837",
"address": "696 Moore Street, Coaldale, Kansas, 9597",
"registered": "2015-01-20T03:39:28 -02:00",
"latitude": 15.764928,
"longitude": -125.084813,
"tags": [
"id",
"nulla",
"tempor",
"do",
"nulla",
"laboris",
"consequat"
],
"friends": [
{
"id": 0,
"name": "Casey Dominguez"
},
{
"id": 1,
"name": "Morton Rich"
},
{
"id": 2,
"name": "Marla Parsons"
}
],
"greeting": "Hello, Wolf Oconnor! You have 3 unread messages."
}
If you just need to walk over the data and won't need to re-encode it, json_decode()'s second parameter, $assoc will cause it to return an associative array. From there, array_walk_recursive() should work well for what you're after.
$data = json_decode($source_object);
$success = array_walk_recursive($data, "my_validate");
function my_validate($value, $key){
//Do validation.
}
function RecursiveStuff($value, $callable)
{
if (is_array($value) || is_object($value))
{
foreach (&$prop in $value) {
$prop = RecursiveStuff($prop);
}
}
else {
$value = call_user_func($callable, $value);
}
return $value;
}
And use it like:
$decodedObject = RecursiveStuff($decodedObject, function($value)
{
return escapesomething($value); // do something with value here
});
You can just pass function name like:
$decodedObject = RecursiveStuff($decodedObject, 'mysqli_real_escape_string');

Categories