PHP foreach wrong value not work with expected - php

Something strange is happening in this script below. The last foreach values are copied to all objects.
// Laravel eloquent payment Methods
$paymentMethods = FormaPagamento::where("cod_loja", "=", $codLoja)
->select("code_payment")
->get();
Array of orders with payment code
$orderPayment = [];
$orderPayment[] = (object)[
"code_order" => 1,
"amount_received" => "10.00",
"code_payment" => 18,
"code_motoboy" => 60
];
$orderPayment[] = (object)[
"code_order" => 2,
"amount_received" => "22.50",
"code_payment" => 1,
"code_motoboy" => 70
];
$orderPayment[] = (object)[
"code_order" => 3,
"amount_received" => "15.00",
"code_payment" => 5,
"code_motoboy" => 70
];
$orderPayment[] = (object)[
"code_order" => 4,
"amount_received" => "3.01",
"code_payment" => 5,
"code_motoboy" => 70
];
Array of motoboys
$motoboys = [];
$motoboys[] = (object)[
"code_motoboy" => 60
];
$motoboys[] = (object)[
"code_motoboy" => 70
];
foreach ($motoboys as $motoboy){ // foreach each motoboy need calculate methods payments
// remove total accumulated previous motoboy
foreach ($paymentMethods as $formaPgtoRemover){
unset($formaPgtoRemover->accumulated_total);
}
// each order
foreach ($orderPayment as $pedidoPgto){
if ($pedidoPgto->code_motoboy==$motoboy->code_motoboy){ // if this order is from this motoboy
foreach ($paymentMethods as $formaPgto){ // acumulated total order
if ($formaPgto->code_payment==$pedidoPgto->code_payment){
$totalFormaPagamento = isset($formaPgto->accumulated_total) ? $formaPgto->accumulated_total : 0.00;
$totalFormaPagamento = (float) $totalFormaPagamento + (float) $pedidoPgto->amount_received;
$formaPgto->accumulated_total = number_format((float)$totalFormaPagamento, 2, '.', '');
break;
}
}
}
}
$motoboy->payments= $paymentMethods;
}
Array expected (from each motoboy, acumulated total from same payment method, example 18.01 = $ 15.00 + $ 3.01;
[
{ "code_motoboy" : 60,
"payments" : [{"code_payment" : 18, "accumulated_total" : 10.00}]
},
{
"code_motoboy" : 70,
"payments" : [{"code_payment" : 1, "accumulated_total" : 22.50}, {"code_payment" : 5, "accumulated_total" : 18.01}]
}
]
Array returned;
[
{ "code_motoboy" : 60,
"payments" : [{"code_payment" : 1, "accumulated_total" : 22.50}, {"code_payment" : 5, "accumulated_total" : 18.01}] // php copies last ocurrence of all previuos
},
{
"code_motoboy" : 70,
"payments" : [{"code_payment" : 1, "accumulated_total" : 22.50}, {"code_payment" : 5, "accumulated_total" : 18.01}]
}
]
Not work
$motoboy->payments = $paymentMethods;
Work if I tried
$motoboy->payments = "$paymentMethods";
$motoboy->payments = json_encode($paymentMethods);
I'm confused.
PHP confused foraech inside foreach.

Related

Join 2 laravel collections

I have 2 collections :
$collection1 = [
{ name : "aaa" , score : 10 },
{ name : "bbb" , score : 20 }
];
$collection2 = [
{ name : "aaa" , score : 30 },
{ name : "bbb" , score : 10 }
];
and I want to join them to get the following result :
$collection3 = [
{ name : "aaa" , score : 40 },
{ name : "bbb" , score : 30 }
]
merge() and union() didn't do the job for me .
Combine the collections, group by the name and sum the score. This can be done like so with collections in Laravel. Note that group by returns a collection of the grouped data.
$collection1->concat($collection2);
$collection1->groupBy('name')->map(function ($scores) {
$score = $scores->first();
$score['score'] = $scores->sum('score');
return $score;
});
it can be achieve by laravel collection map() and where() function
$collection1 = collect([
[
"name" => "aaa",
"score" => 10
],
[
"name" => "bbb",
"score" => 20
]
]);
$collection2 = collect([
[
"name" => "aaa",
"score" => 30
],
[
"name" => "bbb",
"score" => 10
]
]);
$collection3 = $collection1->map(function ($item) use ($collection2) {
$found = $collection2->where('name', $item['name']);
if ($found) {
$item['score'] = $item['score'] + $found->first()['score'];
}
return $item;
});
dd($collection3);
This is the result https://phpsandbox.io/n/soft-flower-ap1e-vo2xy
$collection = collect();$collection->merge(['col1' => $col1, 'col2' => $col2 ]);
OR $states = [ 'Delhi', 'Tamil Nadu', 'Uttar Pradesh'];
foreach ($states as $state)
{ $data = DB::table('user')->select('user.*') ->where("user.city", $state)->where('user.status', 1)->get(); if (!empty($data[0])) $stateUser = $stateUser->merge([$state => $data]);}

reducing an array into another array

Inside a laravel blade template, I am trying to reduce an array like this:
$longList = [['box' => 1, 'kg' => 2], ['box' => 2, 'kg' => 2], ['box' => 3, 'kg' => 3]];
into something like this:
$reducedList = [['count' => 2, 'kg' => 2], ['count' => 1, 'kg' => 3]];
This is what I have so far:
#php
$variableWeights = isset($sale->variable_weight_json) ? collect(json_decode($sale->variable_weight_json, true)) : null;
$groups = array();
if (isset($variableWeights)) {
$groups = $variableWeights->reduce(function($carry, $item) {
$index = array_search($item['kg'], array_column($carry, 'weight'));
if (isset($index)) {
$existing = $carry[$index];
array_splice($carry, $index, 1, [
'count' => $existing['count'] + 1,
'weight' => $item['kg']
]);
} else {
array_push($carry, [
'count' => 1,
'weight' => $item['kg'],
]);
}
return $carry;
}, array());
}
#endphp
But it is giving me the error Undefined offset: 0
I am new to php. How should the code be corrected or is there a better approach to achieve the desired result?
Why dont you reduce it to something simpler like this
$reducedList = [2 => 2, 3 => 1]
where the weight is the index and the value is the count.
$reducedList = [];
foreach ($longList as $box) {
if (isset($reducedList[$box['kg']]) {
$reducedList[$box['kg']]++;
} else {
$reducedList[$box['kg']] = 1;
}
}
This way you avoid complexity but you still get the same amount of information.
I guess you can achieve it with code like:
$longList = [['box' => 1, 'kg' => 2], ['box' => 2, 'kg' => 2], ['box' => 3, 'kg' => 3]];
$reducedList = array_values(array_reduce(
$longList,
function($carry, $item) {
if (isset($carry[$item['kg']])) {
++$carry[$item['kg']]['count'];
} else {
$carry[$item['kg']] = ['count' => 1, 'kg' => $item['kg']];
}
return $carry;
},
[]
));
print_r($reducedList);
Here is a working example.
don't use isset() function. it checks only variable existing. use empty() or other condition, it will check variable existing and value. try this.
#php
$variableWeights = isset($sale->variable_weight_json) ? collect(json_decode($sale->variable_weight_json, true)) : null;
$groups = array();
if ($variableWeights->isNotEmpty()) {
$groups = $variableWeights->reduce(function($carry, $item) {
$index = array_search($item['kg'], array_column($carry, 'weight'));
if ($index != false ) {
$existing = $carry[$index]?: false;
if ($existing) {
array_splice($carry, $index, 1, [
'count' => $existing['count'] + 1,
'weight' => $item['kg']
]);
}
} else {
array_push($carry, [
'count' => 1,
'weight' => $item['kg'],
]);
}
return $carry;
}, array());
}
#endphp
You can use the sexy countBy() method (combined with the famous map() one) of the Collection class.
Try this:
$longList = [['box' => 1, 'kg' => 2], ['box' => 2, 'kg' => 2], ['box' => 3, 'kg' => 3]];
$shortList = collect($longList)
->countBy('kg')
->map(function ($count, $kg) {
return [
'kg' => $kg,
'count' => $count,
];
});
With that, you'll get this:
dd($shortList);
=> Illuminate\Support\Collection {#3380
all: [
2 => [
"kg" => 2,
"count" => 2,
],
3 => [
"kg" => 3,
"count" => 1,
],
],
}
Here you have a working demo.
isset checks if variable is set (i.e it exists and not NULL). As I (and core developers) can hardly imagine in which cases array_search can return NULL, there's a manual which says:
Returns the key for needle if it is found in the array, FALSE otherwise.
So, you need to check if $index is not false:
$index = array_search($item['kg'], array_column($carry, 'weight'));
// Note that I use `!==` because using `!=` will convert
// `0` index to false, which is not correct in your case
if (false !== $index) {

Get sum of objects with same id inside nested array php

This is my array of objects in php:
Array( [0]=>
Array (
[added_time] => 2018-12-09 14:00:00+00
[id] => 123
[places] =>
[{
"place_id" : 1,
"total" : 5,
"empty" : 0,
"weight" : 68000,
"persons" :
[{
"person_id" : 2,
"person_name" : "ABC",
"total":100
},
{
"person_id" : 3
"person_name" : "DEF",
"total":200
}]
},
"place_id" : 4,
"total" : 10,
"empty" : 0,
"weight" : 54000,
"persons" :
[{
"person_id" : 2,
"person_name" : "ABC"
"total":100
},
{
"person_id" : 6
"person_name" : "GHI",
}
]
],
),
Array (
[added_time] => 2018-12-09 15:00:00+00
[id] => 456
[places] =>
[{
"place_id" : 1,
"total" : 5,
"empty" : 0,
"weight" : 68000,
"persons" :
[{
"person_id" : 2,
"person_name" : "ABC",
"total":200
},
{
"person_id" : 3
"person_name" : "DEF",
"total":300
}]
}]
)
)
I am trying to get sum on the base of person_id like group by person_id.
My desired result should be:
Array (
[added_time] => 2018-12-09 14:00:00+00
[id] => 123
persons:array(
Array([0]=>
[person_id] : 2,
[person_name] : "ABC",
[total]:200
),
Array([1]=>
[person_id] : 3,
[person_name] : "DEF",
[total]:300
),
Array([2]=>
[person_id] : 6,
[person_name] : "GHI",
[total]:500
)
),
[added_time] => 2018-12-09 15:00:00+00
[id] => 123
persons:array(
Array([0]=>
[person_id] : 2,
[person_name] : "ABC",
[total]:200
),
Array([1]=>
[person_id] : 3,
[person_name] : "DEF",
[total]:300
)
)
)
How can I achieve this result. I can use foreach but I don't want to use it because of three iterations.
Is there any other method to achieve this in php and without any performance issue?
You can have that by using PHP function as array-merge, array-column, array-reduce.
Let divided that to 3 step:
1.Sum total for all person given list of persons. Define the the following reduce function:
function reduce($carry, $item)
{
if (!in_array($item["person_name"], array_column($carry, "person_name")))
$carry[] = $item;
else {
foreach($carry as &$person) {
if ($item["person_name"] == $person["person_name"])
$person["total"] += $item["total"];
}
}
return $carry;
}
2.For every element in your array create you output using the reduce from step 1:
function reduceElement($arr) {
$res = array("added_time" => $arr["time"], "id" => $arr["id"]);
$persons = array();
foreach($arr["places"] as $place) {
$persons = array_merge($persons, $place["persons"]);
}
$res["persons"] = array_reduce($persons, "reduce", array());
return $res;
}
3.Combine it all:
$elemA= array("id"=>1, "time"=>"2018", "places" => array(array("persons" => array(array("person_name" => "ABC", "total" => 100), array("person_name" => "DEF", "total" => 200))), array("persons" => array(array("person_name" => "ABC", "total" => 100), array("person_name" => "GHI", "total" => 100)))));
$elemB = array("id"=>2, "time"=>"2017", "places" => array(array("persons" => array(array("person_name" => "ABC", "total" => 200), array("person_name" => "DEF", "total" => 300)))));
$arr = array($elemA, $elemB);
$res = array();
foreach($arr as $obj)
$res[] = reduceElement($obj);
print_r($res);
This will give you the require out put

PHP MongoDB $set using arrayFilters

I'm trying to update the value of "position" in "attributes" using the $ set operator with arrayFilters.
The command below works as expected on the terminal
db.produtos.insert(
{
"_id": 1,
"nome":"Nome do Produto",
"descricao":"Lorem ipsum dolor sit amet.",
"atributos": [
{ "id" : 1, "posicao" : 2 },
{ "id" : 2, "posicao" : 1 },
{ "id" : 3, "posicao" : 3 }
]
}
)
db.produtos.update(
{"_id": 1 },
{
$set: {
"atributos.$[elem1].posicao": 1,
"atributos.$[elem2].posicao": 2,
"atributos.$[elem3].posicao": 3
}
},
{
arrayFilters: [
{ "elem1.id": 1 },
{ "elem2.id": 2 },
{ "elem3.id": 3 },
]
}
)
But when I try to create the same command in PHP it does not work.
In my tests, the document is found but never updated.
Just to explain, I get the "attributes.id" of a string, I mount an array with the update and another to filter
<?php
$ids = explode(',', '1,2,3');
$update = [];
$filters = [];
$i = 1;
foreach ($ids as $linha) :
$update['atributos.$[elem'.$i.'].posicao'] = $i;
$filters[]['elem'.$i.'.id'] = $linha;
$i++;
endforeach;
$conexao = new MongoDB\Driver\Manager('mongodb://localhost:27017');
$cmd = new MongoDB\Driver\Command(
[
'findAndModify' => 'produtos',
'query' => ['_id' => 1],
'update' => ['$set' => $update],
'upsert' => true,
'returnDocument' => true,
'new' => true,
'arrayFilters' => $filters,
]
);
$result = $conexao->executeCommand('teste', $cmd)->toArray();
$result = json_decode(json_encode($result), true);
echo '<pre>';
print_r($cmd);
print_r($result);

Get record from sub-document array in mongo within two ISOdates?

I have a document structure like:
{
"_id": ObjectId("575912d631b9a4457891d"),
"events": [
{
"sum" : 10;
"mul" : 100;
"date": ISODate("2016-06-13T06:55:18.004Z")
},
{
"sum" : 20;
"mul" : 10;
"date": ISODate("2016-06-15T06:56:02.810Z")
},
{
"sum" : 20;
"mul" : 10;
"date": ISODate("2016-06-15T07:56:02.810Z")
},
{
"sum" : 20;
"mul" : 10;
"date": ISODate("2016-06-18T06:56:02.810Z")
},
{
"sum" : 120;
"mul" : 10;
"date": ISODate("2016-06-20T06:56:02.810Z")
}]
}
I want to fetch all sub-document array between 2016-06-13 & 2016-06-20 in PHP from MongoDB. Any help is appreciated.!!
Thanks Everybody..!!
Got the answer from the link Using $slice with $regex together on subDocument array in mongodb.
Just use aggregate(Mongo) & the solution from above link & molded my query according to my need & got desired output.
Query Made In Yii:-
$model = USER::model()->aggregate([
['$match' => ["_id" => ObjectId("575912d631b9a4457891d")]],
['$unwind' => '$events'],
['$match' => ["events.date" => ['$gte'=>new MongoDate(strtotime("2016-06-13 00:00:00")),'$lte'=>new MongoDate(strtotime("2016-06-20 23:59:59"))]]],
['$sort' => ["events.date"=>1] ],
['$group' => ['_id' => null,
"events" => ['$push' => '$events'],
"totalSum" => ['$sum' => 'events.sum']
]]
]);
$start = new MongoDate(strtotime(‘2016-06-13 00:00:00’));
$end = new MongoDate(strtotime(‘2016-06-20 00:00:00’));
$collection->find(array(“date” => array(‘$gt’ => $start, ‘$lte’ => $end)));

Categories