How merge a collection of associative arrays? - laravel

Consider
$c = collect([['name' => 'Joe'], ['score' => 98]])
$c->flatten(1)
which gives
=> Illuminate\Support\Collection {#1153
all: [
"Joe",
98,
],
}
Which is not what I expected. I would expect it to combine them to get the following:
[
'name' => "Joe",
'score' => 98,
]
How can I do that? Preferably with a collection method.

Collection::collapse() might be the method you're looking for:
$c = collect([['name' => 'Joe'], ['score' => 98]]);
$c->collapse();
which results in an array like this:
[
"name" => "Joe",
"score" => 98,
]

Related

Search dynamic array index MongoDB

I'm relatively new to MongoDB and trying to figure out how to search for data which has dynamic keys, my dataset is below:
[
'id' => '60bb040f7e589378f644f9a2',
'name' => 'foo',
'settings' => [
'foo' => [
'type' => 'custom',
'enabled' => 'yes'
],
'bar' => [
'type' => 'custom',
'enabled' => 'no'
],
'baz' => [
'type' => 'custom',
'enabled' => 'no'
]
]
]
I want to get the search inside settings, and get all the settings which has enabled as yes.
I've tried this
return Customer::raw(function($collection) {
return $collection->aggregate([
[
'$search' => [
"wildcard" => [
'query' => 'yes',
'path' => 'settings.*.enabled'
]
]
]
]);
});
I am getting the following errror:
Unrecognized pipeline stage name: '$search'
I've also tired using $match, like this
Customer::raw(function($collection)
{
return $collection->aggregate([
[
'$match' => [
"settings.*.enabled" => 'yes'
]
],
]);
})
But it does't return any value.
Any direction on achieving this will be helpful. Thanks!
Ok this is a bit complex but is all I can come up with myself:
Customer::raw(function($collection)
{
return $collection->aggregate([
[
'$addFields' => [
"settingsArray" => [
'$objectToArray' => '$settings'
]
]
],
[
'$unwind' => [
'path' => '$settingsArray'
]
],
[
'$match' => [
'settingsArray.v.enabled' => 'yes'
]
],
[ '$unset' => 'settingsArray' ],
[
'$group' => [
'_id' => '$_id',
doc => [ '$first' => '$$ROOT' ]
]
],
[
'$replaceRoot' => [
'newRoot' => '$doc'
]
]
]);
});
This should return all documents with at least one enabled setting.
A key point about aggregation is that the pipeline works on all documents that the previous stage of the pipeline returned.
Here's the aggregation pipeline explanation:
$addFields will convert the settings object to an array of format [ { k: key, v: value } ] e.g. [ { k: 'foo', 'v': { type: 'custom', enabled: 'yes' } }, ... ]
$unwind will "unwind" the document and "replace" it with X copies of the same document one for each entry in the newly added settingsArray array. The settingsArray at this stage is not an array anymore but a single entry of type { k: key, v: value }. In short you'd get one document per setting
$match will do the actual matching for any document with an enabled setting (remember now each document will only have a single setting as settingsArray
$unset will remove the settingsArray from each document bringing it back to its original format
$group will group all documents by their _id and use the first result of each group as a doc entry. Each document will now be like { _id: <id>, doc: <original document> }
$replaceRoot will set the root of the document to doc and therefore bring the documents back to their original format.
Would love to hear if anyone else has a better solution here.

Laravel 5.6 Validation Array Data

When validating a request data, we are getting the result filtered out by the rules. For example, the following request:
$this->post(route('test'), [
'foo' => 'bar',
'john' => 'doe'
]);
And in my controller, I have the following:
$data = request()->validate([
'foo' => 'required|string'
]);
Then if I perform a dd($data);, I would get the following:
array:1 [
"foo" => "bar"
]
So far so good. However, when working with arrays, for example, the following request:
$this->post(route('test'), [
'foo' => 'bar',
'names' => [
'en' => 'Product',
'fr' => 'Produit',
'john' => 'Doe'
]
]);
And then in my controller, I have the following:
$data = request()->validate([
'foo' => 'required|string',
'names' => 'array',
'names.en' => 'required|string',
'names.fr' => 'required|string'
]);
It would return the following $data:
array:2 [
"foo" => "bar"
"names" => array:3 [
"en" => "Product"
"fr" => "Produit"
"john" => "doe"
]
]
Now my questions are:
Why is 'john' => 'doe' in my names key when I don't have it in my validation rules?
Is there a way to remove unspecified array keys from the data?
Update
I've tested the suggestion of #JonasStaudenmeir, however the result is not what I expected.
Request:
$this->post(route('test'), [
'names' => [
'en' => 'Doe',
'fr' => 'John'
]
]);
Controller:
$rules = [
'names' => 'array',
'names.en' => 'string'
];
dd(request()->only(array_keys($rules));
Expected Output:
array:1 [
"names" => array:2 [
"en" => "Doe"
]
]
Actual Output:
array:1 [
"names" => array:2 [
"fr" => "John"
"en" => "Doe"
]
]
For cases with only nested rules ('names.en') but no parent rule ('names'),
this will be fixed in 5.7: https://github.com/laravel/framework/pull/23708
Until then you can use this workaround:
$data = request()->only(array_keys($rules));
If you do you have a parent rule, this would be much more complicated to achieve.
I would argue that receiving the whole 'names' array in the validated data can be seen as the expected behavior. The idea is that the validator returns the data for each rule. In your case it returns the data for the 'names' => 'array' rule, which is the whole 'names' array.

Octobercms: Many to many relations not showing

I have 2 models that are many to many related.
Workshop
public $belongsToMany = [
'registrations' => [
'Wooltown\Events\Models\Registration',
'table' => 'wooltown_events_registration_workshop',
'count' => true,
],
];
Registration
public $belongsToMany = [
'workshops' => [
'Wooltown\Events\Models\Workshop',
'table' => 'wooltown_events_registration_workshop',
'count' => true,
],
];
I'm fetching a record like this:
$vars['registration'] = Registration::with('workshops')->find($registration->id);
I'm expecting an array of all associated workshops (in this case 2) in the result but the workshops array only shows me this:
...
"workshops": [{
"registration_id": 113,
"count": 2,
"pivot": {
"registration_id": 113
}
}]
...
What am I missing here? How do I get a collection of the associated workshop models?
You should use the counter as a separate relation:
public $belongsToMany = [
'workshops' => [
'Wooltown\Events\Models\Workshop',
'table' => 'wooltown_events_registration_workshop',
],
'workshops_count' => [
'Wooltown\Events\Models\Workshop',
'table' => 'wooltown_events_registration_workshop',
'count' => true,
],
];

Order by in aggregation ES laravel

I'm trying to order elements by field descending in Elasticquent, but don't know how. This is my code:
$laws_y = Law::searchByQuery([
],
[
"group_by_law_year" => ["terms" => ['field' => "law_year", "size" => 100]]
]);
Can anyone help?
You can do it using the order setting and order by _term: desc:
$laws_y = Law::searchByQuery([
],
[
"group_by_law_year" => [
"terms" => [
'field' => "law_year",
"order" => ["_term" => "desc" ], <--- add this line
"size" => 100
]
]
]);

ruby one-liner from two hashes

a = {"rows" => [{"id" => "231-z", "name" => 'jon', "age"=> 27, "state" => 'AL'},
{"id" => "4121-x", "name" => 'ton', "age"=> 37, "state" => 'VA'}
]
}
b = {"rows" => [{"key" => ["xyz","4121-x"], "value" =>{"sum" => 12312, "realage" => 29}},
{"key" => ["xyz","231-z"], "value" =>{"sum" => 1212, "realage" => 33}}
]
}
In hash a, age is incorrect
In hash b, realage is correct. Also in hash b id is the second value in the first array that maps to id of hash a . Those are 4121-x, 231-z correspond to hash a
I want to correct the age in hash a and swap it with the realage of hash b
I can do it in multiple steps, but is it possible to do it in one liner or very short? So finally correct hash a should look like
a = {"rows" => [{"id" => "231-z", "name" => 'jon', "age"=> 33, "state" => 'AL'},
{"id" => "4121-x", "name" => 'ton', "age"=> 29, "state" => 'VA'}
]
}
does this look reasonable?
a['rows'].each_with_index do |ah, i|
(bh = b['rows'].select {|h| h['key'].last == ah['id'] }.first) &&
a['rows'][i] = ah.update('age' => bh['value']['realage'])
end
p a
{
"rows" => [
[0] {
"id" => "231-z",
"name" => "jon",
"age" => 33,
"state" => "AL"
},
[1] {
"id" => "4121-x",
"name" => "ton",
"age" => 29,
"state" => "VA"
}
]
}
Please note it will update a only if corresponding id found in b.
Also, the rows order does not matter, nor matter the rows number, it is only important b to have a row with same id as processed row in a
Here is a Working Demo

Resources