I have the following array in PHP:
[
{
"website": "example",
"url": "example.com"
},
{
"website": "example",
"url": "example.com"
}
]
Now I would like to convert this to a collection so I sort by the keys website or url. However when I do this:
$myArray = collect(websites);
I get this instead:
{
"0": {
"website": "example",
"url": "example.com"
},
"1": {
"website": "example",
"url": "example.com"
}
}
And the sorting does not work, I would like to know what I am doing wrong and how I can fix it so I have an array collection of objects I can easily sort.
Edit:
I expect the output to be the same as this:
[
{
"website": "example",
"url": "example.com"
},
{
"website": "example",
"url": "example.com"
}
]
By "sorting does not work" I meant the items are not sorted.
Edit; I understand this question is getting a lot of hits based on the title so the TLDR for those people is to use the collect() helper to create a Collection instance. In answer to the questioner's brief:
If you have
$collection = collect([
(object) [
'website' => 'twitter',
'url' => 'twitter.com'
],
(object) [
'website' => 'google',
'url' => 'google.com'
]
]);
You then have your array wrapped in an instance of the Collection class.
That means it does not behave like a typical array (- it will be array-like, but don't treat it like it is one -) until you call all() or toArray() on it. To remove any added indices you need to use values().
$sorted = $collection->sortBy('website');
$sorted->values()->all();
The expected output:
[
{#769
+"website": "google",
+"url": "google.com",
},
{#762
+"website": "twitter",
+"url": "twitter.com",
},
]
See the docs https://laravel.com/docs/5.1/collections#available-methods
The toArray method converts the collection into a plain PHP array. If the collection's values are Eloquent models, the models will also be converted to arrays.
The all method returns the underlying array represented by the collection.
In my case I was making an collection to fake a service for test purpose so I use
$collection = new Collection();
foreach($items as $item){
$collection->push((object)['prod_id' => '99',
'desc'=>'xyz',
'price'=>'99',
'discount'=>'7.35',
]);
}
Related
I have two data structures that I would like to merge together - as I understand this is called a 'deep merge'.
It should follow the same logic as Rails' deep merge, except my requirement differs in that values are only overwritten on the target hash if they match certain conditions.
As an example, given the following two data structures:
hash1 = {
"data": [
{"id": "1", "type": "foo", created_at: "<IGNORE>" },
{"id": "2", "type": "bar", created_at: "<IGNORE>" }
],
meta: {
page: 1
}
}
hash2 = {
"data": [
{"id": "1", "type": "baz", created_at: "01.01.2022", name: 'thing' },
{"id": "2", "type": "qux", created_at: "02.01.2022" }
],
meta: {
page: 1
}
}
I would like to achieve the following output:
irb(main):001:0> hash1.deep_merge(hash2)
=> {
"data": [
{"id": "1", "type": "baz", created_at: "<IGNORE>", name: 'thing' },
{"id": "2", "type": "qux", created_at: "<IGNORE>" }
],
meta: {
page: 1
}
}
In hash1, the values for created_at contain a tag <IGNORE> that should stipulate that the corresponding value from hash2 should not be merged. All the other keys and values should merge as it would if I was to use Rails' deep_merge.
Disclaimer: You didn't say what the conditions are, even so I'll help you transform this input into this output, it would be interesting to edit your question later and say the conditions.
i don't have many knowledge in ruby but i created a simple algorithm for merge two hash structures without duplicates and keep data type of key, it's not
the better algorithm or solution but i think this implemetation help to your a find other better solution for resolver your problem
Complexity O(n)
Join the hashes and transform the keys into a string
result = hash1.merge(hash2).transform_keys(&:to_s)
save meta information
meta = hash1[:meta]
convert array of symbols to array of strings
`result = result["data"].map do |res|
{
'id' => res[:id].to_s,
'type' => res[:type].to_s,
'created_at' => res[:created_at].to_s,
'name' => res[:name].to_s
}
end
`
if the hash does not have the key name remove it
`
result =
result.map {
|res| res["name" == ""] ? {
'id' => res["id"],
'type' => res["type"],
'created_at' => res["created_at"]
} : res
}
`
Build new hash :D
final_result = {"data" => result, meta: meta}
output result
i have a problem for the response, i want to change the response API because i need for my mobile APP, the feature have filter object based on date. So i hope you all can help me to solve the problem
i wanna change the response for my API
before:
{
"tasks": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
},
{
"id": 5,
"user_id": 1,
"title": "ghf",
"date": "2022-02-17 16:05:00",
"deskripsi": "fghf",
"created_at": "2022-02-09T06:05:12.000000Z",
"updated_at": "2022-02-09T06:05:12.000000Z"
},
{
"id": 6,
"user_id": 1,
"title": "fgh",
"date": "2022-02-17 18:05:00",
"deskripsi": "gh",
"created_at": "2022-02-09T06:05:40.000000Z",
"updated_at": "2022-02-09T06:05:40.000000Z"
}
]
}
here is the code for the response API above
return response([
'tasks' => Task::where('user_id', auth()->user()->id)->where('date','>',NOW())->orderBy('date','asc')->get(),
],200);
and i want to change it my response API into this response
{
"tasks": [
{
"date": "2022-02-10",
"task": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
},
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 15:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
}
]
},
{
"date": "2022-02-12",
"task": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-12 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
}
]
},
]
}
Do groupBy on the resulting Collection from the query (see docs: https://laravel.com/docs/9.x/collections#method-groupby)
For example, you could do:
$tasksGroupedByDate = Task::where(.......)
->get()
->groupBy(fn (Task $task) => $task->date->format('Y-m-d'));
(Note: above uses PHP 7.4 arrow functions. Also, add a date cast on the date column in your Task model to be able to use ->format( directly on the date field)
The above code results to:
{
'2022-01-01' => [
{ Task object },
{ Task object },
{ Task object },
],
'2022-01-02' => [
{ Task object },
{ Task object },
{ Task object },
],
}
(used Task object for brevity, but that will be ['id' => 1, 'title' => 'Task name', .....])
To morph that to the structure you want, you can use map and then values to remove the keys and turn it back to an ordered array:
$tasksGroupedByDate->map(fn ($tasksInGroup, $date) => [
'date' => $date,
'task' => $tasksInGroup
])->values();
If you want to combine everything into one method chain:
return [
'tasks' => Task::where(......)
->get()
->groupBy(fn (Task $task) => $task->date->format('Y-m-d'))
->map(fn ($tasksInGroup, $date) => [
'date' => $date,
'task' => $tasksInGroup
])
->values(),
];
It sounds like you want to create a human friendly date field based on the date column, then group by it.
While solutions do exists to accomplish this at the database level, I believe you'd still need to loop around it again afterwards to get the hierarchy structure you're looking for. I don't think it's too complicated for PHP to loop through it.
My suggestion is as follows:
Before:
return response([
'tasks' => Task::where('user_id', auth()->user()->id)
->where('date','>',NOW())->orderBy('date','asc')->get(),
],200);
After:
$out = [];
$tasks = Task::where('user_id', auth()->user()->id)
->where('date','>',NOW())->orderBy('date','asc')->get();
foreach($tasks as $task) {
$date = strtok((string)$task->date, ' ');
if (empty($out[$date])) {
$out[$date] = (object)['date' => $date, 'task' => []];
}
$out[$date]->task[] = $task;
}
$out = array_values($out);
return response(['tasks' => $out], 200);
Note in the above I'm using the function strtok. This function might look new even to the most senior of php developers.... It's a lot like explode, except it can be used to grab only the first part before the token you're splitting on. While I could have used explode, since the latter part after the token isn't needed, strtok is better suited for the job here.
$task = Task::where('user_id', auth()->user()->id)->where('date','>',NOW())->orderBy('date','asc')->get();
foreach($task as $item){
$date[] = item->date;
$result = Task::where('user_id', auth()->user()->id)->where('date','=', $date)->get();
}
return response([
'tasks' =>
['date' => $date,
'task' => $task]
],200);
maybe something like this
I have a query that looks like this where I fetch data for various businesses in a particular location and I need to be able to tell that each business has (or does not have) a female employee.
$business = Business::where('location', $location)
->with(['staff'])
->get();
return MiniResource::collection($business);
My Mini Resource looks like this:
return [
'name' => $this->name,
'location' => $this->location,
'staff' => PersonResource::collection($this->whenLoaded('staff')),
];
This is what a sample response looks like:
{
"id": 1,
"name": "XYZ Business"
"location": "London",
"staff": [
{
"name": "Abigail",
"gender": "f",
"image": "https://s3.amazonaws.com/xxxx/people/xxxx.png",
"role": "Project Manager",
},
{
"name": "Ben",
"gender": "m",
"image": "https://s3.amazonaws.com/xxxx/people/xxxx.png",
"role": "Chef",
},
]
}
I really don't need the staff array, I just want to check that a female exists in the relation and then return something similar to this:
{
"id": 1,
"name": "XYZ Business"
"country": "USA",
"has_female_employee": true;
}
Is there an eloquent way to achieve this ?
NB: In my original code I have more relations that I query but I had to limit this post to be within the scope of my problem.
If you are only looking for male or female staff members, you can achieve it like so:
$someQuery->whereHas('staff', function ($query) {
$query->where('gender', 'f');
})
If you want both genders, I wouldn't go through the hassle of achieving this in the query, but recommend reducing your results collection in your MiniResource:
return [
'name' => $this->name,
'location' => $this->location,
'has_female_employee' => $this->whenLoaded('staff')->reduce(
function ($hasFemale, $employee) {
$hasFemale = $hasFemale || ($employee->gender === 'f');
return $hasFemale;
}, false),
];
Even better would be to create it as a method on your MiniResource for readability.
Change your code like below and see
$business = Business::where('location', $location)
->with(['staff'])
->where('gender', 'f')
->get();
return [
'name' => $this->name,
'location' => $this->location,
'has_female_employee' => empty($this->whenLoaded('staff')) ? false : true,
];
I have a problem with the Laravel Query Builder. When I try to bind a variable, which would include some sort of sql code, into my binding parameter, it returns no results. If I run enableQueryLog() I can see that the query and the binding is correct. So the code provides a perfectly fine query but yet it does not perform accordingly.
I've already tried printing out all the variables that matter. I enabled a query log to see if everything is set correctly, which it is. When I put in the variable in my whereRaw() just as it is without binding, it works fine. Just not with the binding.
This is the code I run:
public function getCarbrands() {
$name = 'name != Ford';
try {
$brands = DB::table('ni_carbrands')
->whereRaw(':name',['name'=>$name])
->select('id','name')
->get();
echo json_encode( array('info' => 1 ,'status' => 'Successfully found car brands', 'details' => $brands));
} catch(Exception $e){
echo json_encode( array('info' => 0 ,'status' => 'Error finding car brands', 'e' => $e));
}
}
I know that this use of the binding feature is unnecessary, it is merely a test for some other functions I wanna build.
This is what my Query Log returns:
array:1 [▼
0 => array:3 [▼
"query" => "select `id`, `name` from `ni_carbrands` where :name"
"bindings" => array:1 [▼
0 => "name != Ford"
]
"time" => 190.25
]
]
The components of the query all seem correct, but yet it seems to have some trouble producing it.
The expected results would be something like this:
{
"info": 1,
"status": "Successfully found car brands",
"details": [
{
"id": 1,
"name": "Toyota"
},
{
"id": 2,
"name": "Fiat"
},
{
"id": 3,
"name": "Iveco"
},
{
"id": 4,
"name": "Citroën"
},
{
"id": 5,
"name": "Opel"
},
{
"id": 6,
"name": "Mercedes"
},
{
"id": 8,
"name": "Volkswagen"
},
{
"id": 9,
"name": "Renault"
},
{
"id": 10,
"name": "MAN"
},
{
"id": 11,
"name": "Nissan"
},
{
"id": 12,
"name": "Hyundai"
},
{
"id": 13,
"name": "Peugeot"
}
]
}
But the actual result is:
{"info":1,"status":"Successfully found car brands","details":[]}
I'd greatly appreciate some help.
It seems like you cant bind a string containing an operator.
Have a look into this one Can I bind a parameter to a PDO statement as a comparison operator?
And this one binding not null in pdo
Bad raw condition:
whereRaw(':name',['name'=>$name])
Just like this:
$brands = DB::table('ni_carbrands')
->select('id','name')
->whereRaw($name)
->get();
you can this use this kind of also
$brands = DB::table('ni_carbrands')
->select('id','name')
->where('name', '!=', 'Ford')
->get();
otherwise you can set where condition in dynamic field
like
->where($name, '!=', 'Ford')
$name = 'name != Ford';
$brands = DB::table('ni_carbrands')
->whereRaw(':name',['name'=>$name])
->select('id','name')
->get();
this is equivalent query
select from ni_carbrands where 'name != Ford'
of course it doesn't work, because you have so
select from ni_carbrands where name != Ford
problem in quotes
The following:
Customer::with('tickets:customer_id,subject')->get();
returns the following:
[
{
"id": 1,
"company": "Test Ltd",
"support": "Standard",
"tickets": [
{
"customer_id": "1",
"name": "Test ticket 1"
},
{
"customer_id": "1",
"name": "Test ticket 2"
}
]
}
]
How can I flatten the related field, tickets to get:
tickets: ["Test ticket 1", "Test ticket 2"]
I tried doing a ->flatten() after ->get() but that didn't work at all.
It feels like it should be something simple that I'm missing and just can't find in the docs or by googling (thanks for the word with, that screws pretty much all sane results).
I haven't tested this out but using ->map() might help you in this instance:
$customers = Customer::with('tickets:customer_id,name')
->get()
->map(function($customer, $key) {
$customer->tickets = $customer->tickets->pluck('name')->all();
return $customer;
})->all();
->pluck() will return only the value of the key specified.
Update
This can be achieved by running a query within map and avoiding eager loading:
$customers = Customer::all()
->map(function($customer, $key) {
$customer['tickets'] = $customer->tickets()->get()->pluck('name')->all();
return $customer;
})->all();