I've just begun learning php and this is my first time encountering associative arrays. I'm trying to echo a specific value from the innermost array but failing to do so.
This is my array -
define('TAX_RATES', array(
'Single' => array(
'Rates' => array(10,12,22,24,32,35,37),
'Ranges' => array(0,9700,39475,84200,160725,204100,510300),
'MinTax' => array(0, 970,4543,14382,32748,46628,153798)
),
'Married_Jointly' => array(
'Rates' => array(10,12,22,24,32,35,37),
'Ranges' => array(0,19400,78950,168400,321450,408200,612350),
'MinTax' => array(0, 1940,9086,28765,65497,93257,164709)
),
'Married_Separately' => array(
'Rates' => array(10,12,22,24,32,35,37),
'Ranges' => array(0,9700,39475,84200,160725,204100,306175),
'MinTax' => array(0, 970,4543,14382.50,32748.50,46628.50,82354.75)
),
'Head_Household' => array(
'Rates' => array(10,12,22,24,32,35,37),
'Ranges' => array(0,13850,52850,84200,160700,204100,510300),
'MinTax' => array(0, 1385,6065,12962,31322,45210,152380)
)
)
);
I have to run a foreach loop to loop through the 'Single', 'Married_Jointly' etc. statuses to output the first row and then a for loop to output the rest of the rows. The output should look like:
Single
Taxable Income Tax Rate
$0 - $9700 10%
$9701 - $39475 $970 + 12%
.....
Married_Jointly
Taxable Income Tax Rate
$0 - $19400 10%
$19401 - $78950 $1940 + 12%
.....
Code I've tried so far:
$keys = array_keys(TAX_RATES);
for($i = 0; $i < count(TAX_RATES); $i++){
echo $keys[$i] . "<br>";
echo "<table>";
echo "<tr>";
echo "<th> Taxable Income </th>";
echo "<th> Tax Rate </th>";
echo "</tr>";
foreach(TAX_RATES[$keys[$i]] as $key => $value){
echo "<tr>";
echo "<td> $0 - $value[1] </td>";
echo "<td> $value[0]% </td>";
echo "</table> <br>";
}
}
This is the result I'm getting -
Single
Taxable Income Tax Rate
$0 - $12 10%
$0 - $9700 0%
....
Married_Jointly
Taxable Income Tax Rate
$0 - $12 10%
$0 - $19400 0%
.....
I'm having a tough time iterating over a complex associative array like this, any help would be appreciated.
I recommend breaking up your table generating into its repeatable parts. Separating your business logic from your presentation is a great way to improve readability and maintainability.
Create template strings and fill them with data as you iterate.
I'd probably pretty up the array declaration for easier "eyeballing".
define('TAX_RATES', [
'Single' => [
'Ranges' => [0, 9700, 39475, 84200, 160725, 204100, 510300],
'MinTax' => [0, 970, 4543, 14382, 32748, 46628, 153798],
'Rates' => [10, 12, 22, 24, 32, 35, 37],
],
'Married_Jointly' => [
'Ranges' => [0, 19400, 78950, 168400, 321450, 408200, 612350],
'MinTax' => [0, 1940, 9086, 28765, 65497, 93257, 164709],
'Rates' => [10, 12, 22, 24, 32, 35, 37],
],
'Married_Separately' => [
'Ranges' => [0, 9700, 39475, 84200, 160725, 204100, 306175],
'MinTax' => [0, 970, 4543, 14382.50, 32748.50, 46628.50, 82354.75],
'Rates' => [10, 12, 22, 24, 32, 35, 37],
],
'Head_Household' => [
'Ranges' => [0, 13850, 52850, 84200, 160700, 204100, 510300],
'MinTax' => [0, 1385, 6065, 12962, 31322, 45210, 152380],
'Rates' => [10, 12, 22, 24, 32, 35, 37],
]
]);
Code: (Demo -- click the eye icon to see the rendered html)
$tableTemplate = <<<TABLE
<h2>%s</h2>
<table border="1">
<tr>
<th>Taxable Income</th>
<th>Tax Rate</th>
</tr>
%s
</table>
TABLE;
$rowTemplate = <<<ROW
<tr>
<td>%s</td>
<td>%s%d%%</td>
</tr>
ROW;
foreach (TAX_RATES as $status => $data) {
$rows = [];
$count = count($data['Rates']);
for ($i = 0; $i < $count; ++$i) {
$rows[] = sprintf(
$rowTemplate,
isset($data['Ranges'][$i + 1])
? '$' . number_format($data['Ranges'][$i] + ($i ? 1 : 0)) . ' - $' . number_format($data['Ranges'][$i + 1])
: "> $" . number_format($data['Ranges'][$i]),
$data['MinTax'][$i] ? '$' . number_format($data['MinTax'][$i], 2) . ' + ' : '',
$data['Rates'][$i]
);
}
printf($tableTemplate, $status, implode("\n", $rows));
}
Notice that a literal % in the template strings must be "escaped" by another %. Otherwise, mark variable strings as %s, variable integers as %d, and floats as %f. This is a basic rundown, but there are more formatting features that you can enjoy with the printf() family of functions. (sprintf() is the "silent" version of printf() that just returns its payload instead of printing it.)
Add money formatting as you wish.
<?php
// Loop through the different groupings, Single, Married_Jointly, etc.
foreach(TAX_RATES as $group => $info) {
echo $group . "<br>";
echo "<table>";
echo "<tr>";
echo "<th> Taxable Income </th>";
echo "<th> Tax Rate </th>";
echo "</tr>";
// Loop through the rates.
foreach ($info['Rates'] as $key => $value){
// Get the lower bound of the range.
$range_lower_bound = $info['Ranges'][$key];
// If the lower bound range is not the first range,
// then increment by 1, since the original value is
// already used as the upper bound of the previous
// range.
if ($key !== 0) {
$range_lower_bound += 1;
}
// Get the upper bound, which is the next item
// along. Check that it exists; it won't if we are
// on the last item.
$range_upper_bound = $info['Ranges'][$key + 1] ?? NULL;
// Produce the range text that we will print.
$range_text = '$' . $range_lower_bound;
// If an upper bound exists, print it, otherwise
// just print '+'.
$range_text .= $range_upper_bound
? ' - $' . $range_upper_bound
: '+';
$rate = $value . '%';
// The minimum tax.
$minimum_tax = $info['MinTax'][$key];
// Prefix the rate percent with the minimum tax if
// the minimum tax is more than 0.
if ($minimum_tax > 0) {
$rate = '$' . $minimum_tax . ' + ' . $rate;
}
echo "<tr>";
echo "<td>" . $range_text . "</td>";
echo "<td>" . $rate . "</td>";
echo "</tr>";
}
echo "</table>";
}
My shopping cart works fine using sessions, for each product_id it stores, quantity, name, price, side_name and price. For example:
Product_ID
[Product_name, Product_price, Quantity, Side_name, Side_price]
which works perfectly so far but the problem I'm facing is, I would like to have multiple sides for each Product_ID but what it seems to be doing now is updating the sides each time I choose a new side. What I would it to do is add on the side rather than updating it. So I can have multiply sides for each Product_ID
Here is the code:
if($_POST["action"] == "add_side")
{
foreach($_SESSION["shopping_cart"] as $keys => $values)
{
if($_SESSION["shopping_cart"][$keys]['product_id'] == $_POST["product_id"])
{
foreach($_SESSION["shopping_cart"] as $key => $values)
{
$_SESSION["shopping_cart"][$key]['side_name'] = $_POST["side_name"];
}
}
}
}
Adding Products
if(isset($_SESSION["shopping_cart"]))
{
$is_available = 0;
foreach($_SESSION["shopping_cart"] as $keys => $values)
{
if($_SESSION["shopping_cart"][$keys]['product_id'] == $_POST["product_id"])
{
$is_available++;
$_SESSION["shopping_cart"][$keys]['product_quantity'] =
$_SESSION["shopping_cart"][$keys]['product_quantity'] +
$_POST["product_quantity"];
}
}
if($is_available < 1)
{
$item_array = array(
'product_id' => $_POST["product_id"],
'product_name' => $_POST["product_name"],
'product_price' => $_POST["product_price"],
'product_quantity' => $_POST["product_quantity"],
'side_name' => $_POST["side_name"],
'side_price' => $_POST["side_price"]
);
$_SESSION["shopping_cart"][] = $item_array;
}
}
So there are two changes that you need to make. One when creating/inserting the product, and then another one when adding sides to an existing product.
When creating the product, if there is data for a side, you will want to create a sides sub-array on the $item_array array.
$item_array = array(
'product_id' => $_POST["product_id"],
'product_name' => $_POST["product_name"],
'product_price' => $_POST["product_price"],
'product_quantity' => $_POST["product_quantity"]
);
if(!empty($_POST['side_name']))
{
$item_array['sides'] = array(array("side_name" => $_POST["side_name"], "side_price" => $_POST["side_price"]));
}
$_SESSION["shopping_cart"][] = $item_array;
Then, in the update, you'll add a new sub-array each time, instead of assigning to a fixed index.
if($_POST["action"] == "add_side")
{
foreach($_SESSION["shopping_cart"] as $keys => $values)
{
if($_SESSION["shopping_cart"][$keys]['product_id'] == $_POST["product_id"])
{
$_SESSION['shopping_cart'][$keys]['sides'][] = array('side_name' => $_POST['side_name'], 'side_price' => $_POST['side_price']);
}
}
}
Example of outputting all sides of all products:
// iterate over all the products in the cart
foreach($$_SESSION["shopping_cart"] as $product)
{
echo "Sides for productId " . $product["product_id"] . ": <br/>";
// iterate over each side of the current product
foreach($product["sides"] as $side)
{
echo "Side Name: " . $side["side_name"] . ", Side Price: " . $side["side_price"] . "<br/>";
}
echo "<br/>";
}
I am working on a website where I am trying to have the front page skip showing products that are out of stock.
The code for showing the products is the following:
$data = array(
'sort' => 'p.date_added',
'order' => 'DESC',
'start' => 0,
'limit' => $setting['limit']
);
$results = $this->model_catalog_product->getProducts($data);
foreach ($results as $result) {
if ($result['quantity'] <= 0) { continue; }
if ($result['image']) {
$image = $this->model_tool_image->resize($result['image'], $setting['image_width'], $setting['image_height']);
} else {
$image = false;
}
if (($this->config->get('config_customer_price') && $this->customer->isLogged()) || !$this->config->get('config_customer_price')) {
$price = $this->currency->format($this->tax->calculate($result['price'], $result['tax_class_id'], $this->config->get('config_tax')));
} else {
$price = false;
}
if ((float)$result['special']) {
$special = $this->currency->format($this->tax->calculate($result['special'], $result['tax_class_id'], $this->config->get('config_tax')));
} else {
$special = false;
}
if ($this->config->get('config_review_status')) {
$rating = $result['rating'];
} else {
$rating = false;
}
$this->data['products'][] = array(
'product_id' => $result['product_id'],
'thumb' => $image,
'name' => $result['name'],
'price' => $price,
'special' => $special,
'rating' => $rating,
'reviews' => sprintf($this->language->get('text_reviews'), (int)$result['reviews']),
'href' => $this->url->link('product/product', 'product_id=' . $result['product_id']),
);
}
For this foreach loop, the limit in this case is set to 6, so this code runs 6 times to show 6 items on the front page.
In order to skip Out Of Stock items I have added the line:
if ($result['quantity'] <= 0) { continue; }
Now this code does its job but when it detects an item with 0 stock, it leaves an empty space (so rather than showing 6 product spaces it shows 5)
What I would like to accomplish is when this code detects a 0 stock item is to increment the foreach loop so it runs 7 times rather than 6.
Thanks!
So I believe I have solved the issue without having to mess around with MySQL.
I just added a counter system with a break code, talk about basics.
Basically I have set the code limit an external variable $x set as my break variable.
As out of stock values enter the counter is reduced by 1 while in stock items add 1 to the counter.
Thus when the counter hits my break variable, the code shuts off.
I've got the following code to remove 1 from the qty when a remove button is pressed and if the qty=1 the item will be removed from the array at the specific index.
for example if the first item in the array has an ID of '1B' and qty of '5' and name 'item1' second item in the array has the ID of '2B' and qty of '3' and name 'item2' and the remove button for this item is pressed, the qty will change to 2(as required) but the id will change to 1B and the name to 'item1'. The same thing happens if there are more than 2 products in the $_SESSION["Cart"] array.
I'm not sure where i'm going wrong, but this is my code:
code for $_SESSION["Cart"]
$_SESSION["Cart"] = array(
array(
'name' => "namehere",
'id' => "idHere",
'qty' => 1,
'price' => "pricehere"
)
//more arrays here
);
Code for Removing item
$prodID = $_GET["removeProd"];
foreach ($_SESSION["Cart"] as $cartItem) {
//only continue if qty is more than one
//remove item if 0 qty
if ($cartItem["id"] == $prodID) {
if ($cartItem["qty"] > 1) {
$qty = $cartItem["qty"] - 1; //decrease qty by one
$cart[] = array(
'name' => $cartItem["name"],
'id' => $cartItem["id"],
'qty' => $qty,
'price' => $cartItem["price"]
);
} //end if
} else {
$cart[] = array(
'name' => $cartItem["name"],
'id' => $cartItem["id"],
'qty' => $cartItem["qty"],
'price' => $cartItem["price"]
);
} //end else
$_SESSION["Cart"] = $cart;
} //end foreach
The problem is that you're assigning $_SESSION['Cart'] = $cart on each iteration, so it will only ever contain the last item in the $_SESSION['Cart'] array. If you move it below the end of the foreach your code should work.
You could simplify this a bit by passing $cartItem by reference. That way you only modify array elements which match $prodID:
foreach ($_SESSION['Cart'] as $key => &$cartItem) {
if ($cartItem['id'] == $prodID) {
if ($cartItem['qty'] > 1) {
$cartItem['qty'] -= 1;
} else {
unset($_SESSION['Cart'][$key]);
}
}
}
unset($cartItem); // break the binding
Your code has some algorhithmic/logic flaws. This code should do what you need it to do. Please try to find out what it actually does, and where are the flaws in your approach.
foreach ($_SESSION["Cart"] as $key=>$cartItem) {
//only continue if qty is more than one
//remove item if 0 qty
if ($cartItem["id"] == $prodID) {
if ($cartItem["qty"] > 1) {
$qty = $cartItem["qty"]--;// does the same thing as x = x - 1; //decrease qty by one
$cart[$key]['qty'] = $qty;
} //end if
else {
unset($cart[$key]);
}
break;// ends foreach loop ( assuming there can be only one item of the same type in the cart )
}
} //end foreach
$_SESSION["Cart"] = $cart;