More efficient hierarchy system - php

I am trying to code a hierarchy detection script, I've already written the script, but can only go down 4 levels. Is there a way to condense this to a few lines that will work with an infinite amount of levels?
This script is basically the same code copy and pasted 4 times
<?php
function listCategories($name, $disable_status = 0, $show_nums = 0) {
echo "<select name='".$name."'>";
$result = mysql_query("SELECT * FROM categories") or die(mysql_error());
while($row = mysql_fetch_array($result)) {
if($row['parent_id']==0) {
$result2 = mysql_query("SELECT * FROM categories WHERE parent_id=".$row['id']) or die(mysql_error());
echo "<option value='".$row['id']."'";
if($disable_status==1&&isParent($row['id'])){
echo " disabled='disabled'";
}
echo ">".$row['name']."</option>";
while($row2 = mysql_fetch_array($result2)) {
$result3 = mysql_query("SELECT * FROM categories WHERE parent_id=".$row2['id']) or die(mysql_error());
echo "<option value='".$row2['id']."'";
if($disable_status==1&&isParent($row2['id'])){
echo " disabled='disabled'";
}
echo ">- ".$row2['name']."</option>";
while($row3 = mysql_fetch_array($result3)) {
$result4 = mysql_query("SELECT * FROM categories WHERE parent_id=".$row3['id']) or die(mysql_error());
echo "<option value='".$row3['id']."'";
if($disable_status==1&&isParent($row3['id'])){
echo " disabled='disabled'";
}
echo ">-- ".$row3['name']."</option>";
while($row4 = mysql_fetch_array($result4)) {
echo "<option value='".$row4['id']."'>[".$row4['id']."] --- ".$row4['name']."</option>";
}
}
}
}
}
echo "</select>";
}
function isParent($cat_ID) {
$result = mysql_query("SELECT * FROM categories WHERE parent_id=".$cat_ID) or die(mysql_error());
if(mysql_num_rows($result)==0) {
return FALSE;
} else {
return TRUE;
}
}
My table structure for categories is
id, name, parent_id
If the category has no parent, the parent_id will be 0, or else, it would be the id of the category of it's parent.
All help is appreciated.

I guess something along these lines should do (untested, must be adapted to your needs):
$q = mysql_query("SELECT id, parent_id, name FROM categories");
while ($r = mysql_fetch_row($q)) {
$names[$r[0]] = $r[2];
$children[$r[0]][] = $r[1];
}
function render_select($root=0, $level=-1) {
global $names, $children;
if ($root != 0)
echo '<option>' . strrep(' ', $level) . $names[$root] . '</option>';
foreach ($children[$root] as $child)
render_select($child, $level+1);
}
echo '<select>';
render_select();
echo '</select>';
an even funkier way of doing this is by using SQL stored procedures, but it may be way overkill in this case...

This isn't an answer but instead of using many selects you can use many left joins to traverse 4 hierarchies. Instead of so many row of codes it's just this single query:
select a.id,a.parent_id,b.id,b.parent_id,c.id,c.parent_id,d.id,d.parent_id from a left join b ON a.id = b.parent_id left join c on b.id=c.parent_id left join d on c.id=d.parent_id;
You can use my query with a single table, too, because you can alias a table name:
left join mytable as c on c.id=d.parent_id ... ...
But unfortunetley mysql doesn't support recursive selects. Maybe try another database?

Related

Compare MySQL data with another table

This code below echoes out 8 names from tableOne, but I want to compare those names with 8 names in another table. I want to compare the rows echoed in $row['weight'] with tableTwo, and if the results don't match, then add a <span class="strike"> </span> to the result echoed in $row['weight'].
How do I go about adding an if/else to $row['weight'] compare each name with the names in another table?
$result = mysqli_query($con,"SELECT * FROM tableOne LIMIT 0, 8");
$i = 1;
while($row = mysqli_fetch_array($result)) {
echo $i. " - " . $row['weight'] . '<br>';
$i++;
}
Here is some simple code to get you started:
$result = mysqli_query($con,"SELECT * FROM tableOne LIMIT 0, 8");
$result2 = mysqli_query($con,"SELECT * FROM tableTwo LIMIT 0, 8");
$i = 1;
while($row = mysqli_fetch_array($result)) {
$value1 = $row['weight'];
$row2 = mysqli_fetch_array($result2);
$value2 = $row2['weight'];
echo $i . " - table 1: ";
echo $value1;
echo ", table 2: - ";
if ($value2 != $value1) {
echo '<span class="strike">$value2</span>';
} else {
echo $value2;
}
echo '<br>';
$i++;
}
You can make the code smarter, to handle cases where there aren't 8 values to compare, and to display the values in an HTML table too, but hopefully this can get you started.
Try this:
$sql="select * from tableone to left join tabletwo tw on to.weight=tw.weight ";
This query will return all the rows in tableone which matches and empty rows for the ones where the weight dont match.
So in your php code:
while($row = mysqli_fetch_array($result)) {
if(empty($row['weight'])){
echo '<span class="strike">$row[weight]</span>';
}
}
This would work with any dynamic number of records in the tables.
How about keeping the values ($row[]) in one array($tableOne) and doing the same thing for table two ($tableTwo) and then perform your comparison in foreach()? (For the sake of simplicity, if you don't want any joins)

While inside While from different MySQL table

i have 2 tables:
Cat
id,cat_name
SubCat
id, subcat_name, under_cat
i want to make a list that loads the sub category into the correct Category
the problem is with the second loop, i cant make it work
what am i doing wrong?
<?
$query = "SELECT * FROM `Cat`";
$result = $mysqli->query($query);
while ($row = $result->fetch_assoc()){
echo '<li><a href="#">'
. $row['cat_name'] .
'</a><ul>';
$query2 = "SELECT * FROM `SubCat` WHERE under_cat = '". $row['cat_id'] ."'";
$result2 = $mysqli->query($query2);
while ($row2 = $result->fetch_assoc()){
echo '<li><a href="#">'
. $row2['subcat_name'] .
'</a></li>';
}
echo '</ul></li>';
}
?>
EDIT:
two lists from same category
this is the code:
<?
$query = "SELECT * FROM Cat LEFT OUTER JOIN SubCat ON SubCat.under_cat = Cat.cat_id;";
$result = $mysqli->query($query);
while ($row = $result->fetch_assoc()){
echo '<li><a href="#">'
. $row['cat_name'] .
'</a><ul>';
if($row['cat_id'] === $row['under_cat']){
echo '<li>'.$row['subcat_name'].'</li>';
}
echo '</ul></li>';
}
?>
I hate doing SELECT * because of their maintenance problems, but to work with what you did, just do a JOIN:
"SELECT * FROM Cat LEFT OUTER JOIN SubCat ON SubCat.under_cat = Cat.cat_id;"
This will return the result set you want, but you will need to modify your PHP in response to create the list appropriately, something like this:
if($row['cat_id'] === $id){
echo '<li>'.$row2['subcat_name'].'</li>';
} else {
echo '</ul></li><li>'.$row['cat_name'].'<ul>';
iF(!empty($row['subcat_name'])){
echo '<li>'.$row['subcat_name'].'</li>';
}
}
$id = $row['cat_id'];
This will add the items to the list if it is the same as the previous entry, else it will start a new list. The !empty thing is a quick attempt at handling NULLs, as in where there were no matches brought back from SubCat but you had entries in Cat. If this would never be the case, you can eliminate the if statement and convert your query to this:
"SELECT * FROM Cat INNER JOIN SubCat ON SubCat.under_cat = Cat.cat_id;"
Why go through all this effort you ask? Because this will provide the result set you want while only querying once. The way you had it structured prior would query a bazillion times, which kills the server and greatly reduces performance.
Hopefully this helps both your problem and you're overall implementation knowledge.

Grouping row elements into one column using SQL

My current table is fetching content from the database like this but I would like to combine the rows so that all the Room Preference's are displayed in one row rather than over several. How can I go about doing this? As a result the table below would have only two rows.
The Room Preference information is being inferred from ts_roompref.
Here is my code so far:
$sql = "SELECT
*
FROM ts_request
INNER JOIN ts_day
ON ts_request.day_id = ts_day.id
INNER JOIN ts_period
ON ts_request.period_id = ts_period.id
INNER JOIN ts_allocation
ON ts_request.id = ts_allocation.request_id
INNER JOIN ts_roompref
ON ts_request.id = ts_roompref.request_id
WHERE ts_request.round=:round
AND ts_request.dept_id=:dept
ORDER BY ts_request.module_id ASC";
}
$stm = $pdo->prepare( $sql );
$stm->execute( array( ':round' => 'P', ':dept' => $loggedin_id ) );
$rows = $stm->fetchAll();
foreach ($rows as $row)
{
echo '<tr align="center">';
echo '<td>'.$row['module_id'].'</td>';
echo '<td>'.$row['day'].'</td>';
echo '<td>'.$row['period'].'</td>';
echo '<td>';
if ($row['room_id']=="0")
{
echo "Any";
}
else
{
echo $row['room_id'];
}
echo '</td>';
echo '<td>'.$row['status'].'</td>';
echo '</tr>';
Well, I guess your trying to do it in SQL, but if you wanted to do it in your PHP you could loop over the rows watching for changes to Module Code. Pseudo code something like...
$newrows = array()
$tmpCode = ""
foreach $rows as $row
if $tmpCode != $row['module_code'] {
$tmpCode = $row['module_code']
$newrows[] = $row
} else {
$newrows[count($newrows) - 1]['room_preference'] .= $row['room_preference']
}
}
...rough, but you get the idea.
If you want to do it in SQL, can't you do a GROUP_CONCAT on room_preference and GROUP BY the other 4 fields?

Generating a list of products based on database content

I've created a database which contains one table for each product series, basically I'm trying to list all the distinct models (table rows in each table), in a list where the first row is the table_name. Why does this not work?
$result = mysql_query("SELECT DISTINCT TABLE_NAME FROM
INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME
IN ('id') AND TABLE_SCHEMA='products-ropox'");
while($row = mysql_fetch_array($result))
{
$serie = $row["TABLE_NAME"];
echo "<ul>";
echo "<li class='ldd_heading'><a class='link'
href='products.php?category=".$serie."'>"
.ucfirst($serie)."</a></li>";
$query = mysql_query("SELECT DISTINCT model FROM $serie
ORDER by model ASC");
while($row = mysql_fetch_array($query))
{
echo "<li><a href='products.php?category=".$serie.
"&model=".$row['model']."'>".$row['model']."</a></li>";
}
echo "</ul>";
}
The first loop works well, but the second query generates an error...
Rename the second $row and you need mysql_fetch_assoc not mysql_fetch_array. If you want to use mysql_fetch_array you'll need to use $row[0]
$result = mysql_query("SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME IN ('id') AND TABLE_SCHEMA='products-ropox'");
while($row = mysql_fetch_array($result))
{
$serie = $row["TABLE_NAME"];
echo "<ul>";
echo "<li class='ldd_heading'><a class='link' href='products.php?category=".$serie."'>".ucfirst($serie)."</a></li>";
$query = mysql_query("SELECT DISTINCT model FROM $serie ORDER by model ASC");
while($row2 = mysql_fetch_array($query))
{
echo "<li><a href='products.php?category=".$serie."&model=".$row2['model']."'>".$row2['model']."</a></li>";
}
echo "</ul>";
}
Also a good pratice is to stick a or die(mysql_error()) after a query to output an error if there is one in your query.

PHP/MySQL Query Results

Having an issue with a query..my wscategories stores the main categories of an online store, and my wssubcategories table stores the sub categories of the table with the foreign key called "parentcategory". I'm trying to list the main categories, with the subcategories underneath a la:
Tops
T-Shirts
Wovens
Sweaters
Pants
Shorts
Jeans
The query/result I wrote is the following:
$query = "SELECT DISTINCT a.categoryname AS maincategory, b.categoryname AS
smallcategory FROM wscategories a, wssubcategories b WHERE a.SECTION = 'girls' AND
b.parentcategory = a.id";
$result = mysql_query($query) or die(mysql_error());
while ($row = mysql_fetch_array($result))
{
echo $row['maincategory'];
echo "<br/>";
echo $row['smallcategory'];
echo "<br/>";
}
Which returns:
Tops
T-Shirts
Tops
Sweaters
Tops
Wovens
Etc. I want the script to just display the main category once, and then the subcategories underneath vs. displaying it multiple times. Any help?
try something like this
// note we need to order by maincategory for this to work
//
$query = "SELECT DISTINCT a.categoryname AS maincategory, b.categoryname AS
smallcategory FROM wscategories a, wssubcategories b WHERE a.SECTION = 'girls' AND
b.parentcategory = a.id
ORDER BY maincategory ASC,
smallcategory ASC
";
$result = mysql_query($query) or die(mysql_error());
// keep track of previous maincategory
$previous_maincategory = NULL;
while ($row = mysql_fetch_assoc($result))
{
// if maincategory has changed from previouscategory then display it
if ($previous_maincategory != $row['maincategory']) {
echo $row['maincategory'];
}
echo "<br/>";
echo $row['smallcategory'];
echo "<br/>";
// record what the previous category was
$previous_maincategory = $row['maincategory'];
}
Set a flag after you echo the main category, like this:
$echoed_main = false;
while ($row = mysql_fetch_array($result)) {
if (!$echoed_main) {
echo $row['maincategory'];
echo "<br/>";
$echoed_main = true;
}
echo $row['smallcategory'];
echo "<br/>";
}

Categories