Join 137,383 PHP Programmers for FREE! Get instant access to thousands of PHP experts, tutorials, code snippets, and more! There are 2,115 people online right now. Registration is fast and FREE... Join Now!
Hi all got a little trouble trying to get my head around something,
im creating a webshop and at the moment im getting a little brain tied and stuck it goes a little something like this
a user of the cms can input categories, now there are 5 main categories, this will never change, and each on of these categories can have sub categories and within these sub categories is where the products will stay. now of course a sub category can exist across different categories, and here lies one of the problems, as this is the first time out cms in house has had to come across this we havent had time to readjust this, so for me to enter a subcategory across 2 different main categories i have to create two versions of the sub instead of a nice little checkbox where i could just choose which one it belongs to,
i can get over this for now its just majorly time consuming inputting around 30 sub categories that most belong to 4 out of the 5 categories, and it really isnt how it should be done.
now if a person wants to search by one of those main categories i need it to putt all bottom level of the sub categories that belong to it, not the individual products as thats the next click through, but i would like it to list the bottom level sub category and show an image of this, so i guess i need to scan through the whole categories in the db and then find the bottom level one that belongs to say category cat.
so ive been trying to work my head around a nice way of doing this. i must admit i sometimes struggle turning things into reusable functions, which i believe this is what this should be
so this is what ive got so far and it works, but i mean it doesnt seem like its going to be fast when i fill the thing with loads more categories etc
CODE
#as we are searching by sector the first thing we need to do is to pull all the subcategories of that sector out and show them, so for instance #tactical has sub categories of MJM, PPE we need to show these with a small description of tactical and the cats under #from there they click into the product list page and then into individual pages. #first thing is to loop through all the categories and then we can work through them all and find the relevant bottom level cats $categories = array (); $q = "SELECT * FROM shop_categories"; $x = $db->Select($q);
if ($x){ #if there are some results we will loop through them all and add them to an array foreach ( $x as $category){ if ($category['parent'] != '0' ){ #the parent category will only be 0 if its a top level category
if ($category['parent'] == $_GET['parent']){ #if the category parent equals the current parent then add it to the categories array $categories[] = $category['id']; } #end of if category = parent else { #next thing to do is go through and find the higher level category they belong to until they reach the parent then add them $q = "SELECT * from shop_categories where id = ".$category['parent'].""; $x = $db->Select($q); if ($x) {
#if there is a match then we have found the level above so need to track back until parent = 0 foreach ($x as $uplevel){
$q = "SELECT * from shop_categories where id = ".$uplevel['parent'].""; $x = $db->Select($q); if ($x) {
#if we get this far then we are at the bottom level $categories[] = $x[0]['id']; } } } else { # there are not subs after this and we have reached the parent category $categories[] = $uplevel['id']; } } } }
foreach ($categories as $product){ echo $product; } }
now i look at this right now and i think ohh my god its ugly!! if i had a child like this i wouldnt know what i would do with it!! lol. kidding.
but i hope you can understand what im trying to do here, and if anybody might have a better idea of traversing up the tree then please let me know.
ok i got the code wrong and ive fixed it to work, but it has its limitation, it only goes back 2 levels now this is where i would love to be able to convert it into a reusable function that i can call within it and reuse, but im finding it very difficult to do this
if ($x){ #if there are some results we will loop through them all and add them to an array foreach ( $x as $category){ if ($category['parent'] != '0' ){ #the parent category will only be 0 if its a top level category
if ($category['parent'] == $_GET['parent']){ #if the category parent equals the current parent then add it to the categories array $categories[] = $category['id']; } #end of if category = parent else { #next thing to do is go through and find the higher level category they belong to until they reach the parent then add them $q = "SELECT * from shop_categories where id = ".$category['parent'].""; $x = $db->Select($q); if ($x) {
$currentitem = $category['id']; if ($x[0]['parent'] == '0'){
} else { #if the parent isnt 0 we need to find the level back foreach ($x as $uplevel){
$q = "SELECT * from shop_categories where id = ".$uplevel['parent'].""; $x = $db->Select($q); if ($x) { if ($x[0]['parent'] == '0'){ } else { #i think we need to step again here, but i need to make this into a recursive function that i can reuse
#if we get this far then we are at the bottom level, so add the current item variable to it $categories[] = $currentitem; } } } } } else { # there are not subs after this and we have reached the parent category $categories[] = $uplevel['id']; } } } }
} else { echo "<h1>No Current Categories</h1>"; }
i guess im not sure where i would go about doing it.
The PHP documentation has a sample of how to create a recursive function. See example 4.
As for storing the data, you've got several options. Your parent field could contain a comma-delimited list of parent categories, which is queried using a LIKE clause, and then parsed out in PHP. This is a rather resource-intensive approach though.
A better approach would be to ditch the parent field, and create a new table for storing category relationships. It could be a simple two-field table (cat_id, parent_id), with a single entry for each relationship. While it won't eliminate the need for a row for every relationship, it will eliminate the need to make category updates to all of the rows. And with a little coding, creating the relationship entries could be a simple matter of checking some boxes in an HTML form.
A better approach would be to ditch the parent field, and create a new table for storing category relationships. It could be a simple two-field table (cat_id, parent_id), with a single entry for each relationship.
Here's an example I wrote recently that demonstrates what DilutedImage is talking about. I use four fields in the second table (custom_fields) instead of two, but that's so I can sort the fields according to different criteria.
The code seems long, but that's because it creates and populates the tables before sorting them. Don't let that scare you!
if ($uname != '') { $newaddresses = new Addresses(); $newaddresses->setup($uname,$pswd); if ($newaddresses->connected) { echo "<h1>Note that subgroups and their contents are sorted too, but the contents are sorted according<br>to the field_position field in the database, allowing field positions to be moved at will.</h1>"; ?> <form method="post" action="<?php echo $php_self ?>"> <input type="radio" name="state" value="up" <?php echo $ch1; ?> onclick="this.form.submit();">Sort in Ascending order <br> <input type="radio" name="state" value="DESC" <?php echo $ch2; ?> onclick="this.form.submit();" >Sort in Descending order </form>
class Addresses { var $server="127.0.0.1"; var $mydatabase="MyContacts"; var $mytable="addresses"; var $myfields="custom_fields"; var $myindex="id"; var $myfieldindex="contact_id"; var $Firstname; var $Lastname; var $addr1; var $addr2; var $city; var $thestate; var $zip; var $fields; var $fieldcount; var $connected= false;
function setup($username,$password) { $link = mysql_connect($this->server, $username, $password);// or die(mysql_error()) or die("A MySQL error has occurred.<br />Your Query: " . $your_query . "<br /> Error: (" . mysql_errno() . ") " . mysql_error());; $sel= mysql_select_db($this->mydatabase);// or die(mysql_error()) or die("A MySQL error has occurred.<br />Your Query: " . $your_query . "<br /> Error: (" . mysql_errno() . ") " . mysql_error()); if ($link && $sel) { $this->connected=true; } if ($link && !$sel) {
ok its dealing with one table, and the table has the following fields
CODE
id name about intro rank parent visible thumbnail metak metat meatd
it just seems im doing the loop over and over, ive now got it to pull all the bottom level categories for each main category, which is what i wanted it to do, but it does look like too much code if im honest and reading through it there must be a better way of doing this, as you can tell im not the best at refactoring my code at the moment, im just getting my head around OOP, so this is what i have got
CODE
<?php switch ($_GET['sector']){ case "tactical": $color='#045827'; break; case "commercial": $color='#6F1461'; break;
<?php if (($_GET['record']=='0') || (!isset($_GET['record']))){
#as we are searching by sector the first thing we need to do is to pull all the subcategories of that sector out and show them, so for instance #tactical has sub categories of MJM, PPE we need to show these with a small description of tactical and the cats under #from there they click into the product list page and then into individual pages. #first thing is to loop through all the categories and then we can work through them all and find the relevant bottom level cats
$q = "SELECT * FROM shop_categories"; $x = $db->Select($q);
if ($x){ #if there are some results we will loop through them all and add them to an array foreach ( $x as $category){ #the parent category will only be 0 if its a top level category so no need to add if ($category['parent'] != '0' ){
if ($category['parent'] == $_GET['parent']){
#if the category parent equals the current parent then we need to see if it has any children if it doesn't add it, else don't and loop down $q = "SELECT id from shop_categories where parent = '".$category['id']."'"; $x = $db->Select($q);
if ($x) { #dont add it as it has children } else { #if the category parent equals the current parent and no children add it to the array $categories[] = $category['id']; } } #end of if category = parent else {
#next thing to do is go through and find the previous level category they belong to until they reach the parent then add them $q = "SELECT * from shop_categories where id = ".$category['parent'].""; $x = $db->Select($q); if ($x) {
$currentitem = $category['id']; if ($x[0]['parent'] == '0'){
# this is the bottom category } else { #for if $x
#if the parent isnt 0 we need to find the level back foreach ($x as $uplevel){
$q = "SELECT * from shop_categories where id = ".$uplevel['parent'].""; $z = $db->Select($q); if ($z) { if (($z[0]['parent'] == '0') && ($z[0]['id'] == $_GET['parent'])){ $categories[] = $currentitem; }
} } } } else { # there are not subs after this and we have reached the parent category $categories[] = $uplevel['id']; } } } } $_SESSION['categories'] = $categories; } else { echo "<h1>No Current Categories</h1>"; } # print_r($_SESSION); if ($_SESSION['categories']){ echo "<ul id='itemlist'>";
foreach ($_SESSION['categories'] as $product){
$q= "SELECT * FROM shop_categories where id = '".$product."'"; #$q="SELECT *,shop_products.name as prodname FROM shop_products JOIN shop_product_images on shop_product_images.shop_products_id=shop_products.id JOIN shop_products_cats on shop_products.id=shop_products_cats.products_id JOIN shop_categories on shop_products_cats.products_categories_id = shop_categories.id where shop_categories.name ='".$_GET['sector']."' GROUP BY shop_products.id"; $x=$db->Select($q); if ($x){ //create the list of categories here and the image list below;
foreach ($x as $item){ echo "<a href='".ROOT."$item[name]/sector_$item[name]/page_product/product_$item[products_id]/index.html'><li>".$item['name']."</li></a>"; }
}
} echo "</ul><br /><br />"; } } #need to now use the array again and draw the images
//start of the displaying product Categories as images /*$q= "SELECT * FROM shop_categories where parent = '".$_GET['parent']."'"; #$q="SELECT *,shop_products.name as prodname FROM shop_products JOIN shop_product_images on shop_product_images.shop_products_id=shop_products.id JOIN shop_products_cats on shop_products.id=shop_products_cats.products_id JOIN shop_categories on shop_products_cats.products_categories_id = shop_categories.id where shop_categories.parent ='".$_GET['parent']."' GROUP BY shop_products.id "; $x=$db->Select($q); */ $size=sizeof($_SESSION['categories']); //find out how many pages we require $pages=$size/9; if (isset($_GET['record'])){ $record = $_GET['record']; if ($record == 0){ $end = 6; } else { $end = $record + 9; } } else { $record = 0; $end = 6; } $column = 0; echo "<div class='row'>"; #loop through the array and pull the information for ($i = $record; $i<$end; $i++){ $q = "SELECT * FROM shop_categories where id = ".$_SESSION['categories'][$i].""; $x=$db->Select($q); if ($x){ foreach ($x as $item){ ?> <?php if ($column == '3'){ ?>
</div><div class='row'>
<?php $column = 0; } ?> <?php #draw the information out onto the screen this being the titl and image associated with the section ?> <div class='product'> <h3><?=$item[name]?></h3> <a href='<?=ROOT?><?=$item['name']?>/sector_<?=$sector?>/page_product/product_<?=$item[products_id]?>/index.html'><img src="<?=ROOT?>images/categories/<?=$item['thumbnail']?>" alt="<?=$item['title']?>" title="<?=$item['title']?>" style="height:135px;width:180px;"/></a> <?php }
?>
<div class="sectors"> <?php #now we need to pull the categories each one belongs to echo $q = "SELECT * FROM shop_categories where name LIKE '".$item['name']."' "; $x = $db->Select($q); if ($x){
#if there are some results we will loop through them all and add them to an array foreach ( $x as $category){ #the parent category will only be 0 if its a top level category so no need to add if ($category['parent'] != '0' ){
#if the category parent equals the current parent then we need to see if it has any children if it doesn't add it, else don't and loop down $q = "SELECT id from shop_categories where parent = '".$category['id']."'"; $x = $db->Select($q);
if ($x) { #dont add it as it has children } else { #if the category parent equals the current parent and no children add it to the array $categorylist[] = $category['parent']; } } #end of if category = parent else {
#next thing to do is go through and find the previous level category they belong to until they reach the parent then add them $q = "SELECT * from shop_categories where id = ".$category['parent'].""; $x = $db->Select($q); if ($x) {
$currentitem = $category['id']; if ($x[0]['parent'] == '0'){
# this is the bottom category } else { #for if $x
#if the parent isnt 0 we need to find the level back foreach ($x as $uplevel){
$q = "SELECT * from shop_categories where id = ".$uplevel['parent'].""; $z = $db->Select($q); if ($z) { if (($z[0]['parent'] == '0') && ($z[0]['id'] == $_GET['parent'])){ $categorylist[] = $z[0]['id']; }
} } } } else { # there are not subs after this and we have reached the parent category $categorylist[] = $uplevel['parent']; } } } } #loop through the category list and take out the repeats
print_r($newcatlist); #print_r($categorylist); foreach ($categorylist as $image){ echo $image; switch ($image){ case "1": $sectorz="Tactical & Security"; $image="circletact.png"; break; case "2": $sectorz="Private/Corporate"; $image="circlepriv.png"; break; case "3": $sectorz="Emergency Services"; $image="circleemer.png"; break; case "4": $sectorz="Adventure"; $image="circleadv.png"; break; case "5": $sectorz="Maritime"; $image="circle.png"; break; } } echo "<img src='images/framework/webshop/$image' alt='$sectorz' title='$sectorz' class='nobordercir' />";
}
/*$q="SELECT * FROM shop_products_cats JOIN shop_categories ON shop_categories.id = shop_products_cats.products_categories_id JOIN shop_product_images WHERE shop_products_cats.products_id = '".$item[products_id]."' GROUP BY shop_products_cats.products_categories_id"; */
<? } $column++; } echo "</div>"; //if the size of the returned result is more than 6 then draw the navigation. if ($size>6){ ?>
<div id="navigation"> <div class='block'> <?php //getting the correct color arrows depeding on sectors they are in switch ($sector){ case "tactical": $prev='prevtact.png'; $next='nexttact.png'; break;
case "civil": $prev='prevpriv.png'; $next='nextpriv.png'; break;
case "commercial": $prev='nextemer.png'; $next='prevemer.png'; break;
case "adventure": $prev='prevadve.png'; $next='nextadve.png'; break;
Since you appear to only want the root of the lineage, why not store the information in the database? Every time you create a name, you store their parent, so you could easily store their root parent too.
CODE
id name about intro rank parent ROOT_PARENT visible thumbnail metak metat meatd