Forum Moderators: coopster

Message Too Old, No Replies

Help with foreach loop over two queries

Loop within a loop?

         

TheKG

8:56 pm on Jan 16, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



I am fairly new to php and have read so many posts and tutorials, but still cannot seem to find one that addresses the problem I have. I hope someone here can tell me if what I am attempting is possible and, if so, how.

Table 1 - style_img. Contains the style numbers and images.
Table 2 - products. Contains all information about each style, size, style no., sku, etc.

My objective is to create an ordering page for each style. Each style is available in 9 or 10 sizes and 28 - 32 colors. I have been attempting to code for 1 cell of a table, then use a for or foreach to loop through all the colors/sizes to display one color with corresponding sizes in each cell. I then wanted to apply pagination to display 3 cells across the page; 4 or 5 rows on each page.

The linked page is a sample I created by using only one color in my code so I could better explain what I am attempting.

<snipped url>

Here's the code I used to create that page:


<?php
include('admin/misc.inc');
$cxn = mysqli_connect($host,$user,$passwd,$dbname) or die ("couldn't connect to server" . mysqli_error());
$input = "input type=\"text\" size=\"2\" name=";
$colors = array('Black', 'Celadon', 'Chocolate', 'Ciel');


echo "<table width:750px cellpadding='0' cellspacing='0'>";
echo "<tr>";

$result = mysqli_query($cxn,"SELECT * FROM style_img WHERE style_no='4112'")or die ("problem with query" . mysqli_error());

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

echo "<td width='250'>";
echo "<img src='../images/{$row['photo_sm']}' border='0' align='left'>";
echo "</p>";
}

$result2= mysqli_query($cxn,"SELECT * FROM products WHERE style_no='4112' ORDER BY size_rank") or die ("problem with second query" . mysqli_error());


while($row = mysqli_fetch_array($result2))
{
echo "<p><font face='Arial Narrow' size='2'><$input\"{$row['price']}_{$row['sku']} {$row['vendor_name']} {$row['color_name']} {$row['desc']} #{$row['style_no']}-{$row['color_code']}-{$row['size']}(nontaxable)_{$row['shipping']}\"> {$row['size']}@ \${$row['price']} each</p>";
}
echo "</td>";

echo "</tr>";

echo "</table>";
?>

The problem I have is if I use "foreach", the photo appears next to each size (i.e. 9 sizes and 9 photos) or all the garment photos are displayed followed by the sizes grouped as all XS, all S, etc. I've been reading about one to many relationships and arrays in arrays as well as loop within a loop. Quite honestly, I'm going loopy trying to get this to work. It's been weeks now and I've had no success.

If someone could please just point me in the right direction or let me know that this is IMPOSSIBLE, I'd appreciate it.

Please also let me know if you need more information.

[edited by: coopster at 10:30 pm (utc) on Jan. 16, 2009]
[edit reason] no personal urls please TOS [webmasterworld.com] [/edit]

cameraman

6:40 am on Jan 17, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Welcome to Webmaster World, TheKG.

I'm having a bit of a hard time following your objective. Does the code you posted produce the content that you want for one cell? Do you just need to replicate this for other values of style_no?

If so, then you would put foreach($style_array as $style_no) { (assuming of course you've got the styles in an array called $style_array) above your first $result = line, replace the hard-coded number in your queries with $style_no, then the closing brace for the foreach after the </td> echo.

That's phase 1. That should get you however many <td>s all across in one row.

Phase 2 is to keep track of the <td> count so you can do separate rows. For that, look at this separately (that is, paste it into a new file, dissect it, play with it, etc):
<table>
<?php
$ary = range(1,13); // This is just some test content
$i = 0;
$cols = 3;
// Your stuff is going to go right here, replacing the next 4 lines:
foreach($ary as $a) {
if(!($i%$cols))
echo " <tr>\n";
echo " <td>$a</td>\n";
$i++;
if(!($i%$cols))
echo " </tr>\n";
} // EndForEach array item
if($i%$cols) {
while(($i++)%$cols)
echo " <td>&nbsp;</td>\n";
echo " </tr>\n";
} // EndIf need to fill a row
?>
</table>

The % is the modulus operator, it returns the remainder of a mathematical division. The lines above echo a <tr> if there's no remainder, which means the start of a row. Then the content is spat out, then a </tr> is echoed if the desired number of columns is reached. Afterward, the counter is checked to see if any empty <td>s need to be echoed to fill out a row.

Put pagination aside for a bit, mess around with this and report back. You should be able to get one big page that has all of your styles, 3 cells across.

TheKG

3:31 pm on Jan 17, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



camerman...thanks for the welcome.
Sorry about the url I posted; it was the only way I knew how to convey what I was trying to do.

Actually, each page is to display one style number. In this case, style #4112. On this page I want to display each color in a separate cell with the photo on the left, then the list of available sizes to the right of that photo. For example, the first cell should show the photo of the black pants, then next to it in a column, XS, S, M, XL, 2XL, 3XL, 4XL, 5XL. The next cell should show the same for the celadon pant, and so on for each color.

I have 1 array - $colors. When I use a foreach on that array, I get all the photos in a row, then the sizes display with all the XS in all colors grouped together, all S grouped together, and so on.

Need to ask a really stupid question now...in my table structure for the style_img, since there were no unique entries in any fields, I used "id" as the primary key and have these numbered. In my table structure for the products, the primary key is sku. To relate the tables for a one to many relationship, should I also have an "id" field in the products table that uses the same "id" number from the style_img table? For example, in style_img table, if the "id" number associated with the 4112 black photo is 3, should all the sizes (XS - 5XL) of the 4112 in black also have a 3 in an "id" field?

I sure hope you followed that...I confuse myself sometimes.

cameraman

4:35 pm on Jan 17, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



In the style_img table, does "id" indicate the color, for example is 1 = black, 2 = celadon, etc? If so, then you'll be using the id instead of a foreach on the colors array (you could still use the color array if you wanted to echo the color name). If it's a continuous number (which is what I gather since you used it for primary key) and every 5th one is black, you could still figure out the color. If that's not necessarily the case because some colors weren't available in a particular style, then you can't really figure out the color programmatically (from the style_img table anyway - I see you have color_name in the products table).

In the products table do you have an entry for 4112 XS in black and another for 4112 XS in celadon (I mean in general; the answer would still be yes if in particular 4112 XS celadon isn't available). If so, then yes, you would want to add a column to the products table to match the color id in the style_img table. Then you would add to your second sql statement to select the correct id for that style (sytle_no = AND id = ).

I believe you need to nest your while's.
while($srow = mysqli_fetch_assoc($result))
{
echo image; // $srow['photo_sm']
$result2= mysqli_query($cxn,"SELECT * FROM products WHERE ((style_no='4112') AND (id={$row['id']})) ORDER BY size_rank") or die ("problem with second query" . mysqli_error());

while($row = mysqli_fetch_array($result2))
{
echo numbers;
} // endwhile result2
} // endwhile result

Because they're nested, I changed the name of the variable that receives the style_img data - while in this particular case it would be just fine to reuse that variable, you could get yourself really messed up in the future, so it's better to form the habit right now.

The way you have your table structured, the "numbers" (price, sku, size, et al) will appear below the picture. To keep that from happening, the "better" way is to use CSS and floating DIVs, but you could easily spend a couple of days hashing that out, so I suggest nesting a table. Each of your table cells above will contain a table, the first column being the photo and the second being the data from $result2.

TheKG

5:54 pm on Jan 17, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



Thanks. I think I follow what you've posted. It'll take me a while to test this. I reply with the results.

TheKG

8:47 pm on Jan 19, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



The products table does have an individual entry for each style in each color in each size. The style_img table has an individual entry for each style in each color (but not size...here size does not matter :)). The id number in style_img is unique, i.e. 4112-chocolate is 3; 4112-black is 8, etc.

I added the 'id' field in the products catalog so that every size in style 4112 in chocolate has a 3; black has an 8, etc.

Taking a closer look at the code, I guess I don't follow. What goes in here (id={$row['id']}))? If I want each color to display by referencing the 'id' number, do I list each one? I tried leaving it just the way it's written, but receive an error.

Sorry, guess I'm more confused than I knew!

enigma1

1:26 pm on Jan 20, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



If the style table has a column for the products (that would be a proper approach), then each row in the styles table has a corresponding product. If so then the outer loop must be for the products and the inner loop for the styles.

And so the products table should not have any styles info in this case.

Then the outer loop is for the products and the inner loop for the styles.

$result = mysqli_query($cxn,"SELECT * FROM products")or die ("problem with query" . mysqli_error());
while($row = mysqli_fetch_assoc($result))
..........
$result2= mysqli_query($cxn,"SELECT * FROM styles WHERE products_id=$row['products_id'] ORDER BY size_rank") or die ("problem with second query" . mysqli_error());
..........
}

Again depends how the db structure is. If you have an intermediate table like styles_to_products then that's the one to use for the inner loop.

TheKG

7:46 pm on Jan 20, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



enigma1 -- Having a hard time following your post. The tables I am using are style_img and products. The products table has all the information for each product I sell; style, color, size, sku, etc. The style_img table has the image files which include the style number and color. The sizes are listed ONLY in the products table and the photo_img is in ONLY the style_img table. They are associated by the 'id' column in each as I describe in my post above.

enigma1

10:11 pm on Jan 20, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Ok so you may do it using 2 independent queries. The first one will retrieve the product information,

$result2= mysqli_query($cxn,"SELECT * FROM products WHERE style_no='4112' ORDER BY size_rank") or die ("problem with second query" . mysqli_error());

while the 2nd will retrieve just one image that is associated with the product.

$result = mysqli_query($cxn,"SELECT * FROM style_img WHERE style_no='4112'")or die ("problem with query" . mysqli_error());

For the first query you will need a loop to retrieve all the sizes for a given style_no or id whatever you have as reference. But for the 2nd you need just the id that identifies the image. And before printing anything you store the results of products into a separate array so you can access it later. Like

$products_array = array();
while($row = mysqli_fetch_assoc($result2)) {
$products_array[] = $row;
}

$styles_array = array();
if($row = mysqli_fetch_assoc($result)) {
$styles_array = $row;
}

// Print the images on the left
echo $styles_array['photo_sm'];
for($i=0, $j=count($products_array); $i<$j; $i++ )
echo $products_array[$i]['color_name'] . ' ' . $products_array[$i]['sku'] . ' ' .............etc
}

So this should print one image for each product of the same style.

Usually you define the tables based on relationship among entities. So a product has many styles you could use one db table that holds all the product info including the image and then another that holds the styles. The products would drive the style because new records will create a new primary key value for products that can then be used inside the styles table.

TheKG

7:54 pm on Jan 24, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



Can't thank you both enough!

Just when I thought I was going to pop an eyeball...it all came together. I ended up using cameraman's suggested code with nested while statements. Changed the (id={$row['id']})) to (id={$srow['id']})) and it worked like a charm.

Then I wrestled on Phase 1 of his suggestion to display in columns. It took some experimentation, but I finally got the codes in the right places and viola, the columns appeared.

Next step...pagination. This is for the styles that will come in up to 35 colors. I was thinking that that might take too long to load if it was on one page.

Your opinions, please. Should I just let them all display, or would pagination be better?

cameraman

8:49 pm on Jan 24, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Hot dog, great.

I think the big thing to consider on pagination is load time for the page. The breaking point would be subjective and arbitrary; I know that if a page takes too long to load when I'm looking for something, I'm off to the next one of the 800 million results that the search engine rounded up for me, but I can't name a specific number of megabytes.

Occasionally the vertical length might be a consideration, but where that point might be is likely a tossup as well.

TheKG

9:33 pm on Jan 26, 2009 (gmt 0)

10+ Year Member Top Contributors Of The Month



Now that I've gotten this far, before I finalize the coding and begin creating tons of pages, what about security? I've read several posts about php security and sql injection. Is there something I need to do with my code to protect against the invaders?