Arrays inside arrays
The values in an array can themselves be arrays. That is how PHP represents anything with structure: a table of products, an order with several lines, a menu with sub-menus, the decoded result of a JSON API. An array of arrays is called a multidimensional or nested array.
The most common shape is a list of rows, where each row is an associative array with the same keys, exactly like a database table:
<?php
$products = [
["id" => 1, "name" => "Mechanical Keyboard", "category" => "Electronics", "price" => 89.5, "stock" => 60],
["id" => 2, "name" => "Ceramic Mug Set", "category" => "Home", "price" => 18.75, "stock" => 120],
["id" => 3, "name" => "Yoga Mat", "category" => "Sports", "price" => 24.99, "stock" => 80],
["id" => 4, "name" => "Noise-Cancelling Headphones", "category" => "Electronics", "price" => 199.99, "stock" => 25],
];
echo $products[1]["name"] . "\n";
echo $products[3]["price"] . "\n";
echo count($products) . " products\n";Ceramic Mug Set
199.99
4 productsRead the keys from left to right: $products[1] is the second row, and ["name"] picks a field in it. If you have done the MySQL course, this is the same data as the products table in the sample shop, and it is exactly what PHP gives you back when it fetches rows from a database.
Looping through rows
A foreach over the list gives you one row at a time:
<?php
$products = [
["name" => "Mechanical Keyboard", "price" => 89.5, "stock" => 60],
["name" => "Ceramic Mug Set", "price" => 18.75, "stock" => 120],
["name" => "Noise-Cancelling Headphones", "price" => 199.99, "stock" => 25],
];
foreach ($products as $product) {
$value = $product["price"] * $product["stock"];
printf("%-28s stock value %9.2f\n", $product["name"], $value);
}Mechanical Keyboard stock value 5370.00
Ceramic Mug Set stock value 2250.00
Noise-Cancelling Headphones stock value 4999.75Deeper nesting
Nesting can go as deep as the data needs. An order has a customer and a list of items, and each item has its own fields:
<?php
$order = [
"id" => 1042,
"customer" => ["name" => "Asha Verma", "city" => "Delhi"],
"items" => [
["product" => "Mechanical Keyboard", "qty" => 1, "price" => 89.5],
["product" => "Ceramic Mug Set", "qty" => 2, "price" => 18.75],
],
];
echo "Order {$order['id']} for {$order['customer']['name']}\n";
echo "Second item: " . $order["items"][1]["product"] . "\n";
$total = 0;
foreach ($order["items"] as $item) {
$total += $item["qty"] * $item["price"];
}
echo "Total: $total\n";Order 1042 for Asha Verma
Second item: Ceramic Mug Set
Total: 127To print a nested value inside double quotes, use braces: "{$order['customer']['name']}".
Changing nested values
Assign through the full chain of keys. PHP creates any missing inner arrays for you:
<?php
$order = ["items" => [["product" => "Mug", "qty" => 1]]];
$order["items"][0]["qty"] = 3; // change a value
$order["items"][] = ["product" => "Bottle", "qty" => 1]; // add a row
$order["shipping"]["method"] = "courier"; // creates "shipping"
print_r($order);Array
(
[items] => Array
(
[0] => Array
(
[product] => Mug
[qty] => 3
)
[1] => Array
(
[product] => Bottle
[qty] => 1
)
)
[shipping] => Array
(
[method] => courier
)
)When you loop, remember from for and foreach loops that the loop variable is a copy. To change rows inside a foreach, use the key: $products[$i]["stock"] -= 1;.
Pulling out a column
array_column() takes one field from every row. With a second key argument it also builds a lookup array:
<?php
$products = [
["id" => 1, "name" => "Mechanical Keyboard", "price" => 89.5],
["id" => 2, "name" => "Ceramic Mug Set", "price" => 18.75],
["id" => 3, "name" => "Yoga Mat", "price" => 24.99],
];
print_r(array_column($products, "name"));
print_r(array_column($products, "price", "id"));
echo "Total of all prices: " . array_sum(array_column($products, "price")) . "\n";Array
(
[0] => Mechanical Keyboard
[1] => Ceramic Mug Set
[2] => Yoga Mat
)
Array
(
[1] => 89.5
[2] => 18.75
[3] => 24.99
)
Total of all prices: 133.24The second form, prices keyed by id, lets you look up a price straight away with $prices[3] instead of searching the list.
Grouping rows
A very common task: group records by one of their fields, for example products by category. Build a new array whose keys are the categories and whose values are lists:
<?php
$products = [
["name" => "Mechanical Keyboard", "category" => "Electronics"],
["name" => "Ceramic Mug Set", "category" => "Home"],
["name" => "Noise-Cancelling Headphones", "category" => "Electronics"],
["name" => "Yoga Mat", "category" => "Sports"],
];
$byCategory = [];
foreach ($products as $product) {
$byCategory[$product["category"]][] = $product["name"];
}
foreach ($byCategory as $category => $names) {
echo "$category: " . implode(", ", $names) . "\n";
}Electronics: Mechanical Keyboard, Noise-Cancelling Headphones
Home: Ceramic Mug Set
Sports: Yoga MatThe line inside the loop does a lot: the first time a category appears, $byCategory["Home"] does not exist, so PHP creates it as an empty array and [] appends the name. This is the PHP equivalent of GROUP BY in SQL, and the same pattern works for any field: orders by customer, posts by month, or log lines by error level.
Common mistakes
- Leaving out a level:
$products["name"]when you need$products[0]["name"]. - Changing the loop copy instead of the original row.
- Reading a deep key that may be missing.
??works through the whole chain:$order["customer"]["phone"] ?? "none". - Building huge nested arrays in memory for data that should stay in a database and be queried.
Exercise
Using the four-product list from the start of this lesson (with category, price and stock), print the number of products and the total stock in each category.
Show one possible answer
<?php
$products = [
["name" => "Mechanical Keyboard", "category" => "Electronics", "price" => 89.5, "stock" => 60],
["name" => "Ceramic Mug Set", "category" => "Home", "price" => 18.75, "stock" => 120],
["name" => "Yoga Mat", "category" => "Sports", "price" => 24.99, "stock" => 80],
["name" => "Noise-Cancelling Headphones", "category" => "Electronics", "price" => 199.99, "stock" => 25],
];
$summary = [];
foreach ($products as $product) {
$category = $product["category"];
$summary[$category]["count"] = ($summary[$category]["count"] ?? 0) + 1;
$summary[$category]["stock"] = ($summary[$category]["stock"] ?? 0) + $product["stock"];
}
foreach ($summary as $category => $totals) {
echo "$category: {$totals['count']} products, {$totals['stock']} units\n";
}Electronics: 2 products, 85 units
Home: 1 products, 120 units
Sports: 1 products, 80 units"1 products" reads badly; a real page would choose the word with a ternary, as in Ternary, null coalescing and nullsafe operators.
Key points
- An array can contain arrays. A list of associative arrays is the usual way to hold rows, like a database table.
- Chain the keys to reach a value: $orders[0]['items'][1]['price'].
- Nested foreach loops visit every level; array_column() pulls one field out of every row.
- Grouping rows by a field means building a new array keyed by that field.