Rawland Hustle Rawland Hustle - 5 months ago 9
MySQL Question

Need help turning an associative array into nested JSON using PHP

I've been struggling with this for the past week and it's driving me crazy so if anyone could help me out I'd be forever grateful.

After querying my database I iterate through the data with:

while ($row=mysqli_fetch_assoc($result)) {...


This is how the rows are constructed:

Example row 1:

(
"countryId" => "2",
"countryDescription" => "Canada",
"cityId" => "3",
"cityDescription" => "Montreal",
"restaurantFranchiseId" => "2",
"restaurantFranchiseDescription" => "Kentucky Fried Chicken"
)


Example row 2:

(
"countryId" => "2",
"countryDescription" => "Canada",
"cityId" => "3",
"cityDescription" => "Montreal",
"restaurantFranchiseId" => "3",
"restaurantFranchiseDescription" => "Taco Bell"
)


Notice that only the restaurant franchise differs in the two rows above. The country and the city are the same in both rows.

I want to turn the rows in to a nested JSON-file like the one below. As you can see below, each country is a unique object. Each city is an unique object and a child element of it's corresponding country object. The restaurant franchises however aren't unique, since they aren't tied to a specific country or city.

How can create the JSON-file below from my data, which is structured as described above?

THANKS!!!

{
"Countries": [{
"countryId": "1",
"countryDescription": "USA",
"cities": [{
"cityId": "1",
"cityDescription": "Houston",
"restaurantFranchises": [{
"restaurantFranchiseId": "1",
"restaurantFranchiseDescription": "Mc Donald's"
}, {
"restaurantFranchiseId": "2",
"restaurantFranchiseDescription": "Kentucky Fried Chicken"
}, {
"restaurantFranchiseId": "4",
"restaurantFranchiseDescription": "Pizza Hut"
}]
}, {
"cityId": "2",
"cityDescription": "New york",
"restaurantFranchises": [{
"restaurantFranchiseId": "1",
"restaurantFranchiseDescription": "Mc Donald's"
}, {
"restaurantFranchiseId": "4",
"restaurantFranchiseDescription": "Pizza Hut"
}]
}]
}, {
"countryId": "2",
"countryDescription": "Canada",
"cities": [{
"cityId": "3",
"cityDescription": "Montreal",
"restaurantFranchises": [{
"restaurantFranchiseId": "1",
"restaurantFranchiseDescription": "Mc Donald's"
}, {
"restaurantFranchiseId": "3",
"restaurantFranchiseDescription": "Taco Bell"
}, {
"restaurantFranchiseId": "4",
"restaurantFranchiseDescription": "Pizza Hut"
}]
}, {
"cityId": "4",
"cityDescription": "Ottawa",
"restaurantFranchises": [{
"restaurantFranchiseId": "2",
"restaurantFranchiseDescription": "Kentucky Fried Chicken"
}, {
"restaurantFranchiseId": "3",
"restaurantFranchiseDescription": "Taco Bell"
}, {
"restaurantFranchiseId": "4",
"restaurantFranchiseDescription": "Pizza Hut"
}]
}]
}]


}

Answer

You could use this code:

$result = [];
$lastCity = [ "cityId" => null ];
$lastCountry = [ "countryId" => null ];
while ($row=mysqli_fetch_assoc($result)) {
    if ($row["countryId"] !== $lastCountry["countryId"]) {
        $result[] = [
            "countryId" => $row["countryId"],
            "countryDescription" => $row["countryDescription"],
            "cities" => []
        ];
        $lastCountry = &$result[count($result)-1];
    }
    if ($row["cityId"] !== $lastCity["cityId"]) {
        $lastCountry["cities"][] = [
            "cityId" => $row["cityId"],
            "cityDescription" => $row["cityDescription"],
            "restaurantFranchises" => []
        ];
        $lastCity = &$lastCountry["cities"][count($lastCountry["cities"])-1];
    }
    $lastCity["restaurantFranchises"][] = [
        "restaurantFranchiseId" => $row["restaurantFranchiseId"],
        "restaurantFranchiseDescription" => $row["restaurantFranchiseDescription"],
    ];
}

See it run on eval.in.

About the helper variables

The two variables $lastCity and $lastCountry are references to locations in the $result data structure (except at the start of the loop, when they are dummy values). To get such references inside the $result array, the & operator is used. It could be done without these two variables, but it would make the assignment statements quite long, as you would need to reference each time the last element in the $result array, get from that element the last element in its cities array, ...etc.

Assumptions

This algorithm needs your query result set to be ordered by country and city, i.e. a city should not first be "New York", then "Los Angeles" and then "New York" again.

Also, it is assumed that cityId values are unique. For example, a city in the US should not have the same cityId as a city in Canada. If this is the case, then the above code should be slightly adapted.