Can this PHP code be simplified to improve performance? - php

The goal of this code, is to get all brands for all stores into one array, and output this to the screen. If a brand exists in multiple stores, it will only be added once.
But I feel I have too many for loops, and that it might choke the CPU on heavy traffic.
Is there a better solution to this?
function getBrands($stores, $bl)
{
$html = "";
//Loop through all the stores and get the brands
foreach ($stores as $store)
{
//Get all associated brands for store
$result = $bl->getBrandsByStore($store['id']);
//Add all brands to array $brands[]
while ($row = mysql_fetch_array($result))
{
//If this is the first run, we do not need to check if it already exists in array
if(sizeof($brands) == 0)
{
$brands[] = array("id" => $row['id'], "name" => $row['name']);
}
else
{
// Check tosee if brand has already been added.
if(!isValueInArray($brands, $row['id']))
$brands[] = array("id" => $row['id'], "name" => $row['name']);
}
}
}
//Create the HTML output
foreach($brands as $brand)
{
$url = get_bloginfo('url').'/search?brandID='.$brand['id'].'&brand='.urlSanitize($brand['name']);
$html.= ''.$brand['name'].', ';
}
return $html;
}
//Check to see if an ID already exists in the array
function isValueInArray($values, $val2)
{
foreach($values as $val1)
{
if($val1['id'] == $val2)
return true;
}
return false;
}

From your comment, you mention "Guide table has X stores and each store has Y brands". Presumably there's a "stores" table, a "brands" table, and a "linkage" table, that pairs store_id to brand_id, in a one-store-to-many-brands relationship, right?
If so, a single SQL query could do your task:
SELECT b.`id`, b.`name`
FROM `stores` s
LEFT JOIN `linkage` l
ON l.`store`=s.`id`
LEFT JOIN `brands` b
ON b.`id`=l.`brand`
GROUP BY b.`id`;
That final GROUP BY clause will only show each brand once. If you remove it, you could add in the store ID and output the full list of store-to-brand associations.

No need to loop through two sets of arrays (one to build up the array of brands, and then one to make the HTML). Especially since your helper function does a loop through -- use the array_key_exists function and use the ID as a key. Plus you can use the implode function to join the links with ', ' so you don't have to do it manually (in your existing code you'd have a comma on the end you'd have to trim off). You can do this without two sets of for loops:
function getBrands($stores, $bl)
{
$brands = array();
//Loop through all the stores and get the brands
foreach ($stores as $store)
{
//Get all associated brands for store
$result = $bl->getBrandsByStore($store['id']);
//Add all brands to array $brands[]
while ($row = mysql_fetch_array($result))
{
if (!array_key_exists($row['id'])
{
$url = get_bloginfo('url') . '/searchbrandID=' .
$brand['id'] . '&brand=' . urlSanitize($brand['name']);
$brands[$row['id']] .= '<a href="' . $url . '" id="' .
$brand['id'] . '" target="_self">' .
$brand['name'] . '</a>';
}
}
}
return implode(', ', $html);
}
That will get you the same effect a little faster. It's going to be faster because you used to loop through to get the brands, and then loop through and build up the HTML. Don't need to do that as two separate loops so it all at once and just store the HTML as you go along. Plus since it's switched to use array_key_exists, instead of the helper you wrote that checks by looping through yet again to see if a brand is in there, you'll see more speed improvements. Hashmaps are nice like that because each element in the hashmap has a key and there are native functions to see if a key exists.
You could further optimize things by writing a better SQL statement with a distinct filter to make it so you don't have to do a while inside a foreach.

How are your tables designed? If you had a store table, a brand table, and a link table that had the relationship between stores and brands, you could just pull in the list of brands from the brand table in one query and not have to do any other logic.
Design your tables so they easily answer the questions you need to ask.

If you need to get all the brands for a certain set of stores then you should consider using a query crafted to do that instead of iterating through all the stores and getting the separate pieces of information.

Related

Query 2 tables and combine data in a single array

I am trying to combine the results from 2 queries into a single array but am having problems getting the correct code.
I have a table called PrimaryEvents that I query to add to an array. This query works ok:
$rows = query("SELECT * FROM PrimaryEvents WHERE unit = ? ORDER BY event", $unit);
I add the data to an array using the following:
foreach ($rows as $row)
{
// $event = lookup($row["event"]); (removed as spotted by sergio not required)
$event["event"] = $row["event"];
$event["validity"] = $row["validity"];
$event["eventsrequired"] = $row["eventsrequired"];
$event["unit"] = $row["unit"];
$event["role"] = $row["role"];
//now we have the data lets try and do something to it
if ($event["role"] == "0")
{
//then the user is active so change value to a YES
$event["role"] = "ALL";
}
//add our stock to the portfolio array
$portfolio[] = $event;
}
So far everything works well. I would like to query a separate table and add certain results to the array portfolio[];
$rowsCompleted = query("SELECT * FROM portfolio WHERE id = ? ORDER BY name", $_SESSION["id"]);
My original plan was to use the following additional code to add the data of interest to the portfolio[] but it doesn't work:
//now add individual stats to the array
foreach($rowsCompleted as $row)
{
if ($portfolio["event"] == $row["name"]
{
//then add our number and date to the array
$portfolio["datecompleted"] = $row["datecompleted"];
$portfolio["number"] = $row["number"];
}
//else do nothing
}
My final aim was to pull the datacompleted value and number value from the portfolio table and then add it to the array ONLY if the event name matches the name in portfolio array.
Hopefully I have described my problem and required behaviour well enough.
Thanks for nay help that can be offered, I am new to php/sql so learning as I go.
Andy
EDIT:
Also tried the following loop to try and loop through the array:
//now add individual stats to the array
foreach($rowsCompleted as $row)
{
foreach ($portfolio["event"] as $item)
{
if ($portfolio["event"] == $row["name"]
{
//then add our number and date to the array
$portfolio["datecompleted"] = $row["datecompleted"];
$portfolio["number"] = $row["number"];
}
}
//else do nothing
}
EDIT2: To try and make my question more clear: The Primary-events table lists all the events a specific user has to complete. The portfolio table tracks which events have been completed by the user (by means of date-completed and number).
I want to run a query that lists all the events from Primary-events, then into those results add the date-completed and number from the users portfolio. If the answer is blank from the portfolio then the user has not yet completed the event but I would still like to list it from the Primary-events table data.
I tried 2 queries to start with as it seemed to follow what I was trying to achieve.
EDIT3:
New code with help from Barmar
foreach ($rows as $row) {
$event = lookup($row["event"]);
$event["event"] = $row["event"];
$event["validity"] = $row["validity"];
// $event["datecompleted"] = $row["datecompleted"];
// $event["number"] = $row["number"];
$event["eventsrequired"] = $row["eventsrequired"];
$event["unit"] = $row["unit"];
$event["role"] = $row["role"];
//now we have the data lets try and do something to it
if ($event["role"] == "0") {
//then the user is active so change value to a YES
$event["role"] = "ALL";
}
//add our stock to the portfolio array
$portfolio[] = $event;
//now add individual stats to the array
foreach ($rowsCompleted as $row) {
foreach ($portfolio as &$item) {
if ($item["event"] == $row["name"]) {
//then add our number and date to the array
$item["datecompleted"] = $row["datecompleted"];
$item["number"] = $row["number"];
}
}
}
}
The page still isn't being displayed by chrome so guessing i've missed something.
EDIT4: Page displayed, I am now getting undefined index errors when the table is rendered on the html page.
Specifically the undefined index errors are for date-completed and number. I am taking it to mean that these values are not being added to the portfolio array? Do try and help I have added an Else statement (as below) to ensure that even if the event isn't available the index is:
//now add individual stats to the array
foreach($rowsCompleted as $row)
{
foreach ($portfolio as &$item)
{
if ($item["event"] == $row["name"])
{
//then add our number and date to the array
$item["datecompleted"] = $row["datecompleted"];
$item["number"] = $row["number"];
}
else
{
$item["datecompleted"] = "Never";
$item["number"] = "0";
}
}
}
EDIT 5: Near Success. Sorry to reopen this but I have just noticed behaviour I wasn't expecting: The nested loop only sets the date-completed and number for the first value to matches from the portfolio table. The follow on values are not set. It would seem like the nested loop isn't stepping through all of the portfolio values, and just exiting once the first "event" == "name".
EDIT 6: Sample data and clarification on desired functions:
portfolio sample data
|id|name |number|datacompleted
|21|event1 |3 |2014-07-07
|15|event1 |5 |2014-07-05
|21|event2 |5 |2014-05-08
|15|event1 |1 |2013-05-05
id is the id of the user that completed the event
number is the number of events completed
PrimaryEvents sample data
|id|event |validity|eventsrequired
|1 |event1 |7 |10
|1 |event2 |25 |1
|1 |event3 |12 |50
id is the id of the user that created the entry (used for historic purpose only)
The desired functionality is:
The query should create a an array to allow a html table to be created of everything within the Primary-events table. This table lists the events the user must complete.
The second query or current nested loop should gather the data from the portfolio table for the current user id, then match the event name to the name in the Primary-events array and update (if present) the number and date-completed value. (I.E populate the data for the events that the user has completed).
The current code merges the data from portfolio only for the first match, but then the nested loop seems to exit.
Hopefully this is a more clear description of what I am trying to achieve.
EDIT: I have changed the functionality to use the Left join statement below but am still having problems:
The table only contains some of the events from primary-events table and not all of them. The events being pulled over are only those that the user has completed, the ones the user has not yet completed are not being shown.
EDIT: This query seems to work:
$allEvents = query("SELECT * FROM PrimaryEvents LEFT JOIN portfolio ON (PrimaryEvents.event = portfolio.name) WHERE PrimaryEvents.event = ? AND (portfolio.id = ? Or portfolio.id is null) ORDER BY PrimaryEvents.event", $currentEvent, $_SESSION["id"]);
You need to notice that portfolio is not associative array but multidimensional array, so you cannot access it using $portfolio["event"]. You should use $portfolio[0]["event"], $portfolio[1]["event"] and so on.
It's hard to show you exact solution because I don't know how those arrays/database queries should be merged.
EDIT
It seems your query should look like this:
query("SELECT * FROM PrimaryEvents e LEFT JOIN portfolio p ON e.event = p.name WHERE e.unit = ? AND p.id = ? ORDER BY e.event", $unit,$_SESSION["id"]);
EDIT2
I haven't proposed nested loop (as it's now in modified question) because of performance loss.
You're getting closer with your second query, but still confused about what's in each array.
foreach($rowsCompleted as $row)
{
foreach ($portfolio as &$item) // Need to use reference so we can update it
{
if ($item["event"] == $row["name"])
{
//then add our number and date to the array
$item["datecompleted"] = $row["datecompleted"];
$item["number"] = $row["number"];
break;
}
}
}
To avoid the nested loop, it would be better for $portfolio to be an associative array. Change the code for your initial query to use:
//add our stock to the portfolio array
$portfolio[$event["name"]] = $event;
Then the second loop becomes:
foreach($rowsCompleted as $row)
{
$name = $row["name"];
if (isset($portfolio[$name]) {
$portfolio[$name]["datecompleted"] = $row["datecompleted"];
$portfolio[$name]["number"] = $row["number"];
}
}

How to iterate through a mysql array and append corresponding text from an PHP array

I am making a pricing table to upgrade membership.
In mySQL I have a table with limits on what each membership can allow see picture
I grab the info from the table and try to iterate through it and append some text to it from another array. I have tried thoroughly looking online for an answer but I'm probably not even searching for the right terms. For the purpose of this question i'm using 2 fields but in reality i will be iterating and appending many columns.
Here's my code:
$sql = "SELECT maxProducts,maxImages FROM memproducts_membership_settings WHERE useThis='1'";
$rs = mysql_query($sql);
while($row = mysql_fetch_array($rs,MYSQL_ASSOC))
{
foreach($row as $field)
{
$append = array('Product Slots','Images per product');
print $field.' '.$append.'<br>';
}
}
All i get is:
5 Array
2 Array
and not what i need which would be:
5 Product Slots
2 Images per product
I'm sure there is a way to do this but i just cannot figure it out, please anyone give me some pointers? Many regards :)
This is pretty basic stuff...
while($row = mysql_fetch_array($rs,MYSQL_ASSOC))
{
echo $row['maxProducts'] . ' Product Slots<br>';
echo $row['maxImages'] . ' Images per product<br>';
}
Or if you are needing to do this with dynamic fields in whatever order you return from database like your question suggests, perhaps you might consider providing a mapping of database column names to the appropriate label content like this:
$labels = array(
'maxProducts' => 'Product Slots',
'maxImages' => 'Images per product',
...
);
...
while($row = mysql_fetch_array($rs,MYSQL_ASSOC)) {
foreach ($row as $key => $value) {
echo $value . ' ' . $labels[$key] . '<br>';
}
}
Note if you are just learning PHP, you REALLY should NOT learn mysql_* functions. They are deprecated and need to die. Please learn to use PDO or mysqli.
To append values to the end of an array use array_push()
http://php.net/manual/en/function.array-push.php
There's plenty about your code and your approach that I don't like at all, but the most immediate solution to your current problem would be to iterate the array of strings from within the foreach loop, using each():
$strings = array('Product Slots','Images per product');
while($row = mysql_fetch_array($rs,MYSQL_ASSOC))
{
reset($strings);
foreach($row as $field)
{
$append = each($strings);
print $field.' '.$append[1].'<br>';
}
}
You should at very least make yourself aware of:
the deprecation of ext/mysql (use either mysqli or PDO); and
the risk of XSS attacks (escape any HTML in values from your database before returning them to the browser).
It's doing what it should... You have created an array and asked it to print its name. As print can't automatically iterate an array it just tells you what the variable type is.

Using a set of Mysql results as a comparison in PHP

This question is quite simple but I'm not sure of the terms involved to look up the answer myself. What I have is a MYSQL database containing all of my product information. I would like to make an image appear on the product pages for products that have a certain attribute. I have created this SQL query which outputs the list of product_ids for the products that I would like the image to show up on. However, I do not know how to use this set of results in the page itself.
I currently have:
$cupwinnerids = mysql_query("SELECT product_id FROM `jos_vm_product_type_1` WHERE jos_vm_product_type_1.Cup_Winner ='Cup Winners';");
while($row = mysql_fetch_array($cupwinnerids))
{
echo $row['product_id'] . ",";
}
This outputs the correct ids with a comma in between them. What I would like to do is wrap the whole thing with something like $listofids = (...) and then I can use in the product page PHP file: if $product_id is in $listofids then ... if not then ... I am just having a problem understanding how to use this selection of ids. If I try to output the list directly I just get "array". Any help would be greatly appreciated.
The problem may be that you are trying to display the array using echo, while you should use print_r.
In your case you could do something like this:
$listofids = array();
$cupwinnerids = mysql_query("SELECT product_id FROM `jos_vm_product_type_1` WHERE jos_vm_product_type_1.Cup_Winner ='Cup Winners';");
while($row = mysql_fetch_array($cupwinnerids))
{
array_push($listofids, $row['product_id']);
}
print_r($listofids);
As Ryan commented, if you want to manage the values of the array you should then use a foreach loop, that iterates through the array, something like this
foreach ($listofids as $id) {
echo $id . ", ";
}
// EDIT: you could also use echo implode($listofids,", ") which will do basically the same
If what you want is to compare if $product_id is in $listofids us in_array, that returns true if the value you are looking for is in the array, and false if it isn't:
if (in_array($product_id, $listofids)) {
echo "Product ID is in the List of Product ID's";
}
else {
echo "Product ID isn't in the List of Product ID's";
}
Why not return $cupwinnerids to your products page, and then do something like...
while($row = mysql_fetch_array($cupwinnerids)) {
if ($row['product_id'] == $product_id)
// display your image
}
It sounds like you would be better off modifying your query for returning the products so that it joins to the jos_vm_product_type_1 table -
SELECT `jos_vm_product`.*, IF(`jos_vm_product_type_1`.`product_id` IS NULL, 0, 1) AS `winner`
FROM `jos_vm_product`
LEFT JOIN `jos_vm_product_type_1`
ON `jos_vm_product`.`product_id` = `jos_vm_product_type_1`.`product_id`
AND `jos_vm_product_type_1`.`Cup_Winner` ='Cup Winners'

PHP - Nested List Broken into Even Columns (fix and updates)

I have a follow-up to a previous thread/question that I hope can be solved by relatively small updates to this existing code. In the other thread/question, I pretty much solved a need for a nested unordered list. I needed the nested unordered list to be broken up into columns based on the number of topics.
For example, if a database query resulted in 6 topics and a user specified 2 columns for the layout, each column would have 3 topics (and the related news items below it).
For example, if a database query resulted in 24 topics and a user specified 4 columns for the layout, each column would have 6 topics (and the related news items below it).
The previous question is called PHP - Simple Nested Unordered List (UL) Array.
The provided solution works pretty well, but it doesn't always divide
correctly. For example, when $columns = 4, it only divides the
columns into 3 groups. The code is below.
Another issue that I'd like to solve was brought to my attention by
the gentleman who answered the question. Rather than putting
everything into memory, and then iterating a second time to print it
out, I would like to run two queries: one to find the number of
unique TopicNames and one to find the number of total items in the
list.
One last thing I'd like to solve is to have a duplicate set of
code with an update that breaks the nested unordered list into columns
based on the number of news items (rather than categories). So, this
would probably involve just swapping a few variables for this second
set of code.
So, I was hoping to solve three issues:
1.) Fix the division problem when relying on the number of categories (unordered list broken up into columns based on number of topics)
2.) Reshape the PHP code to run two queries: one to find the number of unique TopicNames and one to find the number of total items in the list
3.) Create a duplicate set of PHP code that works to rely on the number of news items rather than the categories (unordered list broken up into columns based on number of news items)
Could anyone provide an update or point me in the right direction? Much appreciated!
$columns = // user specified;
$result = mysql_query("SELECT * FROM News");
$num_articles = 0;
// $dataset will contain array( 'Topic1' => array('News 1', 'News2'), ... )
$dataset = array();
while($row = mysql_fetch_array($result)) {
if (!$row['TopicID']) {
$row['TopicName'] = 'Sort Me';
}
$dataset[$row['TopicName']][] = $row['NewsID'];
$num_articles++;
}
$num_topics = count($dataset);
// naive topics to column allocation
$topics_per_column = ceil($num_topics / $columns);
$i = 0; // keeps track of number of topics printed
$c = 1; // keeps track of columns printed
foreach($dataset as $topic => $items){
if($i % $topics_per_columnn == 0){
if($i > 0){
echo '</ul></div>';
}
echo '<div class="Columns' . $columns . 'Group' . $c . '"><ul>';
$c++;
}
echo '<li>' . $topic . '</li>';
// this lists the articles under this topic
echo '<ul>';
foreach($items as $article){
echo '<li>' . $article . '</li>';
}
echo '</ul>';
$i++;
}
if($i > 0){
// saw at least one topic, need to close the list.
echo '</ul></div>';
}
UPDATE 12/19/2011: Separating Data Handling from Output Logic (for the "The X topics per column variant"):
Hi Hakre: I've sketched out the structure of my output, but am struggling with weaving the two new functions with the old data handling. Should the code below work?
/* Data Handling */
$columns = // user specified;
$result = mysql_query("SELECT * FROM News LEFT JOIN Topics on Topics.TopicID = New.FK_TopicID WHERE News.FK_UserID = $_SESSION[user_id] ORDER BY TopicSort, TopicName ASC, TopicSort, NewsTitle");
$num_articles = 0;
// $dataset will contain array( 'Topic1' => array('News 1', 'News2'), ... )
$dataset = array();
while($row = mysql_fetch_array($result)) {
if (!$row['TopicID']) {
$row['TopicName'] = 'Sort Me';
}
$dataset[$row['TopicName']][] = $row['NewsID'];
$num_articles++;
}
/* Output Logic */
function render_list($title, array $entries)
{
echo '<ul><li>', $title, '<ul>';
foreach($entries as $entry)
{
echo '<li>', $entry['NewsID'], '</li>';
}
echo '</ul></li></ul>;
}
function render_column(array $topics)
{
echo '<div class="column">';
foreach($topics as $topic)
{
render_list($topic['title'], $topic['entries']);
}
echo '</div>';
}
You have not shown in your both questions what the database table is, so I can not specifically answer it, but will outline my suggestion.
You can make use of aggregation functions in mysql to obtain your news entries ordered and grouped by topics incl. their count. You can do two queries to obtain counts first, that depends a bit how you'd like to deal with your data.
In any case, using the mysql_... functions, all data you selected from the database will be in memory (even twice due to internals). So having another array as in your previous question should not hurt much thanks to copy on write optimization in PHP. Only a small overhead effectively.
Next to that before you take care of the actual output, you should get your data in order so that you don't need to mix data handling and output logic. Mixing does make things more complicated hence harder to solve. For example if you put your output into simple functions, this gets more easy:
function render_list($title, array $entries)
{
echo '<ul><li>', $title, '<ul>';
foreach($entries as $entry)
{
echo '<li>', $entry['NewsID'], '</li>';
}
echo '</ul></li></ul>;
}
function render_column(array $topics)
{
echo '<div class="column">';
foreach($topics as $topic)
{
render_list($topic['title'], $topic['entries']);
}
echo '</div>';
}
This already solves your output problem, so we don't need to care about it any longer. We just need to care about what to feed into these functions as parameters.
The X topics per column variant:
With this variant the data should be an array with one topic per value, like you did with the previous question. I would say it's already solved. Don't know which concrete problem you have with the number of columns, the calculation looks good, so I skip that until you provide concrete information about it. "Does not work" does not qualify.
The X news items per column variant:
This is more interesting. An easy move here is to continue the previous topic with the next column by adding the topic title again. Something like:
Topic A Topic A Topic B
- A-1 - A-5 - B-4
- A-2 Topic B - B-5
- A-3 - B-1 - B-6
- A-4 - B-2
- B-3
To achieve this you need to process your data a bit differently, namely by item (news) count.
Let's say you managed to retrieve the data grouped (and therefore sorted) from your database:
SELECT TopicName, NewsID FROM news GROUP BY 1;
You can then just iterate over all returned rows and create your columns, finally output them (already solved):
$itemsPerColumn = 4;
// get columns
$topics = array();
$items = 0;
$lastTopic = NULL;
foreach ($rows as $row)
{
if ($lastTopic != $row['TopicName'])
{
$topic = array('title' => $row['TopicName']);
$topics[] = &$topic;
}
$topic['entries'][] = $row;
$items++;
if ($items === $itemsPerColumn)
{
$columns[] = $topics;
$topics = array();
$lastTopic = NULL;
}
}
// output
foreach($columns as $column)
{
render_column($column);
}
So this is actually comparable to the previous answer, but this time you don't need to re-arrange the array to obtain the news ordered by their topic because the database query does this already (you could do that for the previous answer as well).
Then again it's the same: Iteration over the returned result-set and bringing the data into a structure that you can output. Input, Processing, Output. It's always the same.
Hope this is helpful.

How can I generate a tree structure from a table in a database?

I'm trying to generate a tree structure from a table in a database. The table is stored flat, with each record either having a parent_id or 0. The ultimate goal is to have a select box generated, and an array of nodes.
The code I have so far is :
function init($table, $parent_id = 0)
{
$sql = "SELECT id, {$this->parent_id_field}, {$this->name_field} FROM $table WHERE {$this->parent_id_field}=$parent_id ORDER BY display_order";
$result = mysql_query($sql);
$this->get_tree($result, 0);
print_r($this->nodes);
print_r($this->select);
exit;
}
function get_tree($query, $depth = 0, $parent_obj = null)
{
while($row = mysql_fetch_object($query))
{
/* Get node */
$this->nodes[$row->parent_category_id][$row->id] = $row;
/* Get select item */
$text = "";
if($row->parent_category_id != 0) {
$text .= " ";
}
$text .= "$row->name";
$this->select[$row->id] = $text;
echo "$depth $text\n";
$sql = "SELECT id, parent_category_id, name FROM product_categories WHERE parent_category_id=".$row->id." ORDER BY display_order";
$nextQuery = mysql_query($sql);
$rows = mysql_num_rows($nextQuery);
if($rows > 0) {
$this->get_tree($nextQuery, ++$depth, $row);
}
}
}
It's almost working, but not quite. Can anybody help me finish it off?
You almost certainly, should not continue down your current path. The recursive method you are trying to use will almost certainly kill your performance if your tree ever gets even slightly larger. You probably should be looking at a nested set structure instead of an adjacency list if you plan on reading the tree frequently.
With a nested set, you can easily retrieve the entire tree nested properly with a single query.
Please see these questions for a a discussion of trees.
Is it possible to query a tree structure table in MySQL in a single query, to any depth?
Implementing a hierarchical data structure in a database
What is the most efficient/elegant way to parse a flat table into a tree?
$this->nodes[$row->parent_category_id][$row->id] = $row;
This line is destroying your ORDER BY display_order. Change it to
$this->nodes[$row->parent_category_id][] = $row;
My next issue is the $row->parent_category_id part of that. Shouldn't it just be $row->parent_id?
EDIT: Oh, I didn't read your source closely enough. Get rid of the WHERE clause. Read the whole table at once. You need to post process the tree a second time. First you read the database into a list of arrays. Then you process the array recursively to do your output.
Your array should look like this:
Array(0 => Array(1 => $obj, 5 => $obj),
1 => Array(2 => $obj),
2 => Array(3 => $obj, 4 => $obj),
5 => Array(6 => $obj) );
function display_tree() {
// all the stuff above
output_tree($this->nodes[0], 0); // pass all the parent_id = 0 arrays.
}
function output_tree($nodes, $depth = 0) {
foreach($nodes as $k => $v) {
echo str_repeat(' ', $depth*2) . $v->print_me();
// print my sub trees
output_tree($this->nodes[$k], $depth + 1);
}
}
output:
object 1
object 2
object 3
object 4
object 5
object 6
I think it's this line here:
if($row->parent_category_id != 0) {
$text .= " ";
}
should be:
while ($depth-- > 0) {
$text .= " ";
}
You are only indenting it once, not the number of times it should be indented.
And this line:
$this->get_tree($nextQuery, ++$depth, $row);
should be:
$this->get_tree($nextQuery, $depth + 1, $row);
Note that you should probably follow the advice in the other answer though, and grab the entire table at once, and then process it at once, because in general you want to minimize round-trips to the database (there are a few use cases where the way you are doing it is more optimal, such as if you have a very large tree, and are selecting a small portion of it, but I doubt that is the case here)

Categories