PHP: Remove invalid array value? - php

I'm using two arrays, one is a POST string (product), and another is a predefined set of values(products).
They work together as:
$products[$_POST['product']]
This is okay if the POST value is exactly one of the following:
$products = array(
'Pineaple' => 500, 'Banana' => 50, 'Mango' => 150,
'Milk' => 500, 'Coffe' => 1200, 'Butter' => 300,
'Bread' => 450, 'Juice' => 780, 'Peanuts' => 800,
'Yogurt' => 450, 'Beer' => 550, 'Wine' => 2500,
);
Otherwise it will pop Undefined Index messages. Since this is a SESSION, I need to somehow keep a list of items, but remove the invalid ones from the array and echo a message.
Please check the screenshot: http://img36.imageshack.us/img36/9121/20110430004019.jpg
This is the script I'm using:
<?php
session_start();
//Getting the list
$_SESSION['list'] = isset($_SESSION['list']) ? $_SESSION['list'] : array();
//stock
$products = array(
'Pineaple' => 500, 'Banana' => 50, 'Mango' => 150,
'Milk' => 500, 'Coffe' => 1200, 'Butter' => 300,
'Bread' => 450, 'Juice' => 780, 'Peanuts' => 800,
'Yogurt' => 450, 'Beer' => 550, 'Wine' => 2500,
);
//Saving the stuff
$new_item = array(
'item' => $_POST['product'],
'quantity' => $_POST['quantity'],
'code' => $_POST['code'],
'price' => $products[$_POST['product']] * $_POST['quantity'],
);
$new_product = true;
foreach($_SESSION['list'] as $key => $item) {
if ($item['item'] == $new_item['item']) {
$_SESSION['list'][$key]['quantity'] += $new_item['quantity'];
$_SESSION['list'][$key]['price'] =
$products[$new_item['item']] * $new_item['quantity'];
$new_product = false;
}
}
if ($new_product) {
$_SESSION['list'][] = $new_item;
}
//listing
echo "<b>SHOPPING LIST</b></br>";
foreach($_SESSION['list'] as $key => $item) {
echo 'Product .'. $key. ' '. $item['item'], ' ',
$item['quantity'], ' units: ', $item['price']. '<br />';
}
echo "</br> <a href='index.html'>Return to index</a> </br>";
//Printing session
var_dump($_SESSION);
//session_destroy();
?>

You should use isset or array_key_exists to check whether the key exists before reading it:
if (isset($products[$_POST['product']])) {
// product in stock
} else {
// product not in stock
}
And if you use the same key for your product list in $_SESSION['list'], you don’t need to iterate the whole list to find the same product in the list:
if (isset($_SESSION['list'][$_POST['product']])) {
// update product in list
$_SESSION['list'][$_POST['product']]['quantity'] += $_POST['quantity'];
$_SESSION['list'][$_POST['product']]['price'] = $products[$_POST['product']] * $_POST['quantity'];
} else {
// add product to list
$_SESSION['list'][$_POST['product']] = array(
'item' => $_POST['product'],
'quantity' => $_POST['quantity'],
'code' => $_POST['code'],
'price' => $products[$_POST['product']] * $_POST['quantity'],
);
}
Note that this will always only add items to the list. You should add some functionality to also update and remove items. And don’t forget to validate the data before using it (e.g. so that people don’t get refund if they enter a negative quantity).

I would imagine you want to use the array_intersect_assoc function. it returns the items that are in both of your arrays.

Related

Making changes on a different property of recursive function

I'm currently developing this code that traverse a hierarchical array which should compute the sub-total of a property called cur_compensation. My issue is that the changes I do is not getting save
private function computeSubTotal($hierarchy){
foreach($hierarchy["_children"] as $key => $value){
if(isset($value["_children"]))
{
static::computeSubTotal($value);
}
else{
foreach($hierarchy["_children"] as $employee){
$employee_cur_compensation = $employee["cur_compensation"] ?? 0;
if (!isset($hierarchy["cur_compensation"])) {
$hierarchy["cur_compensation"] = 0;
}
$hierarchy["cur_compensation"] += $employee_cur_compensation;
}
return $hierarchy;
}
}
return $hierarchy;
}
This is the function so what it does it goes to the deepest node, the deepest node is a value that does not have any _children which mean it doesn't have any sub department (the hierarchy is sorted that the sub department are always on top)
The issue I have, once it reaches the bottom it computes the cur_compensation by looping through the employees of that department and adding it on the department "cur_compensation" property.
The issue is that, it doesn't save any of my changes.
So the purpose of the function is to add up the 'cur_compensation' of each employee/sub-department.
For example ->
$rows = array(
array(
'name' => "Main",
'id' => 1,
'parent_id' => 0,
'cur_compensation' => 0,
'_children' => array(
array(
'name' => "Dept A",
'id' => 2,
'parent_id' => 1),
),
array(
'name' => "Dept B",
'id' => 3,
'parent_id' => 1,
'_children' => array(
array(
'name' => "Dept C",
'cur_compensation' => 30000,
'id' => 4,
'parent_id' => 3),
array(
'name' => "Employee C",
'cur_compensation' => 30000,
'id' => 7,
'parent_id' => 3
)
)),
array(
'name' => "Employee A",
'cur_compensation' => 20000,
'id' => 5,
'parent_id' => 1
),
array(
'name' => "Employee B",
'cur_compensation' => 30000,
'id' => 6,
'parent_id' => 1
)
)
)
);
The result I want to get would be:
$rows = array(
array(
'name' => "Main",
'id' => 1,
'parent_id' => 0,
'cur_compensation' => 120000,
'_children' => array(
array(
'name' => "Dept A",
'id' => 2,
'cur_compensation' => 0,
'parent_id' => 1),
),
array(
'name' => "Dept B",
'id' => 3,
'parent_id' => 1,
'cur_compensation' => 60000,
'_children' => array(
array(
'name' => "Dept C",
'cur_compensation' => 30000,
'id' => 4,
'parent_id' => 3),
array(
'name' => "Employee C",
'cur_compensation' => 30000,
'id' => 7,
'parent_id' => 3
)
)),
array(
'name' => "Employee A",
'cur_compensation' => 30000,
'id' => 5,
'parent_id' => 1
),
array(
'name' => "Employee B",
'cur_compensation' => 30000,
'id' => 6,
'parent_id' => 1
)
)
)
);
So you would notice that Main and Dept B got the cur_compensation based on the _children property
There's a few things to make note on here - so I'm going to add comments to your existing code, then provide an example of how you could change it.
(I've formatted the code in each case)
class Example {
// filler code so that we can call
public function process($array){
return $this->computeSubTotal($array);
}
private function computeSubTotal($hierarchy) {
// we're not checking whether "_children" property exists before looping on it
foreach ($hierarchy["_children"] as $key => $value) {
if (isset($value["_children"])) {
// we're calling the method, but not doing anything with the return value.
static::computeSubTotal($value);
// we can set the original array value instead which will provide a modified copy
// this can be resolved by uncommenting the line below
// $hierarchy["_children"][$key] = static::computeSubTotal($value);
// also note that if this "child" doesn't have any *grand*children
// then we won't get an updated value due to how this is structured
// to fix this, you could remove the else wrapping so that the code
// below runs always
} else {
// double looping - we're already looping this array
// this will cause the end value to increase exponentially
foreach ($hierarchy["_children"] as $employee) {
$employee_cur_compensation = $employee["cur_compensation"] ?? 0;
if (!isset($hierarchy["cur_compensation"])) {
$hierarchy["cur_compensation"] = 0;
}
$hierarchy["cur_compensation"] += $employee_cur_compensation;
}
// returning whole array inside the loop is not ideal
// we have already adjusted the main array
// comment out this return to prevent that from happening
return $hierarchy;
}
}
return $hierarchy;
}
}
$example = new Example;
// calling this on $rows won't give us anything back
// since $rows doesn't contain the property "_children"
$rows = $example->process($rows);
// in this case, you would want to process each array result
// only on this primary array
foreach($rows as $index => $value){
$rows[$index] = $example->process($value);
}
echo json_encode($rows, JSON_PRETTY_PRINT);
Taking those comments into account, you would end up with something like this:
private function computeSubTotal($hierarchy) {
// we're not checking whether "_children" property exists before looping on it
foreach ($hierarchy["_children"] as $key => $value) {
if (isset($value["_children"])) {
$hierarchy["_children"][$key] = static::computeSubTotal($value);
}
// double looping - we're already looping this array
// this will cause the end value to increase exponentially
foreach ($hierarchy["_children"] as $employee) {
$employee_cur_compensation = $employee["cur_compensation"] ?? 0;
if (!isset($hierarchy["cur_compensation"])) {
$hierarchy["cur_compensation"] = 0;
}
$hierarchy["cur_compensation"] += $employee_cur_compensation;
}
}
return $hierarchy;
}
That's closer but still, it's not quite correct due to the double looping.
I've made a simpler version that is hopefully easy to follow:
private function computeSubTotal($hierarchy) {
if (!isset($hierarchy["_children"])) {
return $hierarchy;
}
// define this outside the loop for clarity
if (!isset($hierarchy["cur_compensation"])) {
$hierarchy["cur_compensation"] = 0;
}
foreach ($hierarchy["_children"] as $key => $value) {
// don't need to check for "_children" property
// as it's now handled in this function
$updated = static::computeSubTotal($value);
// reference the $updated array to increment
// the "cur_compensation" field
$hierarchy["cur_compensation"] += $updated["cur_compensation"] ?? 0;
// update original array
$hierarchy["_children"][$key] = $updated;
}
return $hierarchy;
}
// call like
foreach ($rows as $index => $value) {
$rows[$index] = static::computeSubTotal($value);
}
You will still need to change how you're passing the $rows variable due to it now containing a "_children" property (as shown in the examples) - either pass each element or add additional logic in that function to handle that.
You need to pass the array as a reference.
https://www.php.net/manual/en/language.references.pass.php
PHP passes the array to the function as a pointer, but when you try to update the array, PHP first makes a full copy of the array and updates the copy instead of the original.
Change your function signature to the following and it should be good.
private function computeSubTotal(&$hierarchy){
P.S. You are calling computeSubTotal statically, but the function is not static itself.

Looping thru array for sum of one element

I have a shopping cart with three items like this:
$cart = [
1031 => [
'id' => '1031',
'model' => 'tr16',
'price' => 100,
'discount_applied' => '',
'promo_name' => '',
'promo_id' => ''
],
1032 => [
'id' => '1032',
'model' => 'tr16g',
'price' => 100,
'discount_applied' => '',
'promo_name' => '',
'promo_id' => ''
],
1034 => [
'id' => '1034',
'model' => 'tr17g',
'price' => 100,
'discount_applied' => '',
'promo_name' => '',
'promo_id' => ''
]
];
I have an array of IDs representing items eligible for a discount like this:
$itemIds = [
0 => [
0 => 1031
],
1 => [
0 => 1032
]
];
I loop thru each array and change the price where the ID in $cart matches ID in $itemIds by applying a 20% discount, and also add new elements. That code looks like this:
foreach($cart as &$item) {
foreach($itemIds as $ids) {
foreach($ids as $key => $value) {
if ($item['id'] == $value)
{
$item['discount_applied'] = $item['price'] * 0.2;
$item['price'] = $item['price'] * .80;
$item['promo_name'] = 'Test promo';
$item['promo_id'] = 36;
}
}
}
}
Printing the cart to the screen before and after the loop shows it's working as expected.
However, I encounter a problem when trying to loop through the modified $cart and calculate the sum of individual discounts.
My loop looks like this:
$cart['total_discount'] = 0;
foreach($cart as $item)
{
$cart['total_discount'] += $item['discount_applied'];
}
echo 'Total discount:' . $cart['total_discount'];
I expect to see the sum of discounts = 40, but instead = 60. Printing the cart to the screen before and after the loop shows that items 1031 and 1032 have a value of 20 in discount_applied and that item 1034 has no value.
Any help in identifying where I have an error or errors is appreciated.
Here's all the code if you want to copy/paste.
$cart = [
1031 => [
'id' => '1031',
'model' => 'tr16',
'price' => 100,
'discount_applied' => '',
'promo_name' => '',
'promo_id' => ''
],
1032 => [
'id' => '1032',
'model' => 'tr16g',
'price' => 100,
'discount_applied' => '',
'promo_name' => '',
'promo_id' => ''
],
1034 => [
'id' => '1034',
'model' => 'tr17g',
'price' => 100,
'discount_applied' => '',
'promo_name' => '',
'promo_id' => ''
]
];
$itemIds = [
0 => [
0 => 1031
],
1 => [
0 => 1032
]
];
echo '<h2>Cart BEFORE discount</h2>'; echo '<pre>';print_r($cart); echo '</pre>';
foreach($cart as &$item) {
foreach($itemIds as $ids) {
foreach($ids as $key => $value) {
if ($item['id'] == $value)
{
$item['discount_applied'] = $item['price'] * 0.2);
$item['price'] = $item['price'] * .80;
$item['promo_name'] = 'Test promo';
$item['promo_id'] = 36;
}
}
}
}
echo '<h2>Cart AFTER discount</h2>'; echo '<pre>';print_r($cart); echo '</pre>';
$cart['total_discount'] = 0;
foreach($cart as $item)
{
echo $item['discount_applied'] . '<br>';
$cart['total_discount'] += $item['discount_applied'];
}
echo 'Total discount:' . $cart['total_discount'];
Your use of &$item in the initial loop needs to be used in your final loop also. The loop where you adding up the discount total. You will also need to see if the discount value is a number since, in the code you posted, the 3rd item in the cart will have a blank value for the discount which will throw a non-numeric error when trying to add it to the discount total.
Your final loop modified to work:
$cart['total_discount'] = 0;
foreach($cart as &$item) {
echo '*' . $item['discount_applied'] . '*<br />'; // just to show what is or isn't there
$cart['total_discount'] += (is_numeric($item['discount_applied']) ? $item['discount_applied'] : 0);
}
echo 'Total discount:' . $cart['total_discount'];

PHP: SESSION data not being stored

This data comes from a POST form, the idea is to simply add more lines whenever a new product is added.
The current output is:
l. Banana 3 units: 150
Please take a look into the script (specially the foreach loop):
<?php
session_start();
//Getting the list
$list= $_SESSION['list'];
//stock
$products = array(
'Pineaple' => 500, 'Banana' => 50, 'Mango' => 150,
'Milk' => 500, 'Coffe' => 1200, 'Butter' => 300,
'Bread' => 450, 'Juice' => 780, 'Peanuts' => 800,
'Yogurt' => 450, 'Beer' => 550, 'Wine' => 2500,
);
//Saving the stuff
$_SESSION['list'] = array(
'item' => ($_POST['product']),
'quantity' => ($_POST['quantity']),
'code' => ($_POST['code']),
);
//price
$price = $products[($_SESSION['list']['item'])] * $_SESSION['list']['quantity'];
$_SESSION['list']['price'] = $price;
//listing
echo "<b>SHOPPIGN LIST</b></br>";
foreach($_SESSION as $key => $item)
{
echo $key[''], '. ', $item['item'], ' ', $item['quantity'], ' units: ', $item['price'];
}
//Recycling list
$_SESSION['list'] = $list;
echo "</br> <a href='index.html'>Return to index</a> </br>";
//Printing session
var_dump($_SESSION);
?>
change this code:
//Saving the stuff
$_SESSION['list'] = array(
'item' => ($_POST['product']),
'quantity' => ($_POST['quantity']),
'code' => ($_POST['code']),
);
to
//Saving the stuff
$_SESSION['list'][] = array(
'item' => ($_POST['product']),
'quantity' => ($_POST['quantity']),
'code' => ($_POST['code']),
);
and remove this code:
//Recycling list
$_SESSION['list'] = $list;
Now, you'll get a new entry in $_SESSION every time you POST to the page.
Also, if you want your output to look like:
l. Banana 3 units: 150
2. Orange 5 units: 250
etc
then you'll need to change the echo in the foreach() loop from
echo $key[''] . // everything else
to just
echo ($key+1) . // everything else
Since key will be your array index starting at 0, you'll need to do a +1 every iteration, otherwise your list would look like
0. Banana 3 units: 150
1. Orange 5 units: 250
As iandouglas wrote, you are overwriting the session variable every time. The following code also fixes some undefined index issues and already existing products.
// Test POST data.
$_POST['product'] = 'Pineaple';
$_POST['quantity'] = 1;
$_POST['code'] = 1;
//Getting the list
$_SESSION['list'] = isset($_SESSION['list']) ? $_SESSION['list'] : array();
//stock
$products = array(
'Pineaple' => 500, 'Banana' => 50, 'Mango' => 150,
'Milk' => 500, 'Coffe' => 1200, 'Butter' => 300,
'Bread' => 450, 'Juice' => 780, 'Peanuts' => 800,
'Yogurt' => 450, 'Beer' => 550, 'Wine' => 2500,
);
//Saving the stuff
$new_item = array(
'item' => $_POST['product'],
'quantity' => $_POST['quantity'],
'code' => $_POST['code'],
'price' => $products[$_POST['product']] * $_POST['quantity'],
);
$new_product = true;
foreach($_SESSION['list'] as $key => $item) {
if ($item['item'] == $new_item['item']) {
$_SESSION['list'][$key]['quantity'] += $new_item['quantity'];
$_SESSION['list'][$key]['price'] = $products[$new_item['item']] * $new_item['quantity'];
$new_product = false;
}
}
if ($new_product) {
$_SESSION['list'][] = $new_item;
}
//listing
echo "<b>SHOPPIGN LIST</b></br>";
foreach($_SESSION['list'] as $key => $item) {
echo 'key '. $key. ' '. $item['item'], ' ', $item['quantity'], ' units: ', $item['price']. '<br />';
}

PHP: Foreach echo not showing correctly

The output should look like this:
1. Yougurt 4 units price 2000 CRC
But I'm currently getting this:
item. Y Y unitsYquantity. 3 3 units3code. S S unitsSprice. units
This is the script:
<?php
session_start();
//Getting the list
$list[]= $_SESSION['list'];
//stock
$products = array(
'Pineaple' => 500, 'Banana' => 50, 'Mango' => 150,
'Milk' => 500, 'Coffe' => 1200, 'Butter' => 300,
'Bread' => 450, 'Juice' => 780, 'Peanuts' => 800,
'Yogurt' => 450, 'Beer' => 550, 'Wine' => 2500,
);
//Saving the stuff
$_SESSION['list'] = array(
'item' => ($_POST['product']),
'quantity' => ($_POST['quantity']),
'code' => ($_POST['code']),
);
//price
$price = $products[($_SESSION['list']['item'])] * $_SESSION['list']['quantity'];
$_SESSION['list']['price'] = $price;
//listing
echo "<b>SHOPPIGN LIST</b></br>";
foreach($_SESSION['list'] as $key => $item)
{
echo $key, '. ', $item['item'], ' ', $item['quantity'], ' units', $item['price'];
}
//Recycling list
$_SESSION['list'] = $list;
echo "</br> <a href='index.html'>Return to index</a> </br>";
//Printing session
print_r($_SESSION);
?>
The problem is that you are nested 1 level deeper in the arrays than you think you are. To make it clear, the $_SESSION may look lik this (just before entering the foreach):
array(1) {
["list"] => array(3) {
["item"] => string(8) "Pineaple"
["quantity"] => int(30)
["price"] => int(15000)
}
}
(you can use var_dump($var) or print_r($var) methods to see the value: http://php.net/manual/en/function.var-dump.php http://php.net/manual/en/function.print-r.php)
When iterating over $_SESSION["list"], you pass the loop 3 times. In the first iteration, $key is "item", $value "Pineaple".
echo $key, '. ', $item['item'], ' ', $item['quantity'], ' units', $item['price'];
"item . P P units <empty>"
Why?
String "item" is obvious, it's just printed out.
$item['item'] -> 'item' is cast to (int)0, so the first character of $item (Pineaple) is printed: P
(The examples of string->int conversion rules are for example here: http://www.php.net/manual/en/language.types.string.php#language.types.string.conversion)
$item['quantity'] -> the same as above
$item['price'] -> since the price is much higher than the length of the string, empty string is printed:
$myvar = "hi";
echo $myvar[12234]; // prints empty string
In every iteration you get this output, just the first word is changing. Put echo "<br />" at the end of the iteration and you will see it.
I hope this helps you a bit.

PHP: Array > Sum all the numeric elements?

I found a reference in the PHP manual, but my interpretation is pretty much wrong:
$total = (sum ( array $_SESSION['list'][$item]['price'] ));
I want to sum the price of all the items in the array SESSION.
Please check the screenshot: http://img684.imageshack.us/img684/6070/20110430010324.jpg
This is the code I'm using:
<?php
session_start();
//Getting the list
$_SESSION['list'] = isset($_SESSION['list']) ? $_SESSION['list'] : array();
//stock
$products = array(
'Pineaple' => 500, 'Banana' => 50, 'Mango' => 150,
'Milk' => 500, 'Coffe' => 1200, 'Butter' => 300,
'Bread' => 450, 'Juice' => 780, 'Peanuts' => 800,
'Yogurt' => 450, 'Beer' => 550, 'Wine' => 2500,
);
//Saving the stuff
$new_item = array(
'item' => $_POST['product'],
'quantity' => $_POST['quantity'],
'code' => $_POST['code'],
'price' => $products[$_POST['product']] * $_POST['quantity'],
);
$new_product = true;
foreach($_SESSION['list'] as $key => $item) {
if ($item['item'] == $new_item['item']) {
$_SESSION['list'][$key]['quantity'] += $new_item['quantity'];
$_SESSION['list'][$key]['price'] = $products[$new_item['item']] * $new_item['quantity'];
$new_product = false;
}
}
if ($new_product) {
$_SESSION['list'][] = $new_item;
}
//listing
echo "<b>SHOPPING LIST</b></br>";
foreach($_SESSION['list'] as $key => $item) {
echo 'Product .'. $key. ' '. $item['item'], ' ', $item['quantity'], ' units: ', $item['price']. '<br />';
}
echo "</br> <a href='index.html'>Return to index</a> </br>";
//Printing session
var_dump($_SESSION);
//session_destroy();
?>
$total = 0;
foreach($_SESSION['list'] as $item) {
$total += $item['price'];
}
Or if you prefer a functional style (PHP 5.3):
$total = array_reduce($_SESSION['list'], function($a, $b) {
return $a['price'] + $b['price'];
});

Categories