b310 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I'm modifying some code to allow me to have duplicate items in a shopping cart with different values.

For example, let's say you want to buy a green sweater and the same sweater in blue. Right now, if I go to my items an add the sweater as a green color and then go and try to add the same sweater as a blue, the blue sweater appears in the cart and not the green. Apparently, my coding only allows one entry for each item. The cart does not allow to have the same item appear more than once.

I was hoping someone can give me some help.

Here's all the code that's needed for the cart.

This code calls for the Add choice...
elsif ($choice eq "add") # add item to shopping cart { add_item ($dbh, $cart_ref, param ("item_id"), param("quantity"), p +aram("color")); $page .= format_cart_html ($cart_ref, 1); }
This code shows the products and adds to the cart...
sub get_product_table { my $sth = shift; my @row; my @nav_link; my @color; while (my $ref = $sth->fetchrow_hashref ()) { my $serve_url = sprintf ("serve_image.cgi?item_id=%s;picture", es +cape ($ref->{item_id})); @color = split (/,/, $ref->{color}); # generate a form allowing a quantity of the item to be added # to the cart push (@row, start_form(-method=>'POST', -action=>url()), hidden( -name => "choice", -override => 1, -default => "add" ), hidden( -name => "item_id", -override => 1, -default => escapeHTML($r +ef->{item_id})), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td (img ({-src => $serve_url, -alt => escapeHTML($ref->{item_id})}) +), td ({-width=>"80", -valign=>"top", -align => "center"}, escapeHTML( + $ref->{item_id} )), td ({-width=>"190", -valign=>"top"}, escapeHTML( $ref->{description +} ))), td (" "), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td ({-colspan => "3", -align => "right", -style=>"font-weight: 700; + color:blue;"}, "Price: \$", escapeHTML( sprintf( "%.2f", $ref->{pri +ce}))), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td ({-colspan => "3", -align => "right"}, "Colors:", popup_menu( -n +ame => "color", -values => \@color))), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td ({-colspan => "3", -align => "right"}, "Quantity:", popup_menu( +-name => "quantity", -values => ["1", "2", "3", "4", "5", "6", "7", " +8", "9"])), td (image_button( -name => "Add Item", -src=>"../images/add.jpg", - +border=>"0" )))), Tr ({-valign=>"top"}, td ({-colspan=>"7", -width=>"100%"}, hr)), end_form() ); } $sth->finish (); return undef unless @row; # no items? return (table ({-border => 0, -align => "center", -style=>"font-fa +mily: verdana; font-size: 10pt;"}, @row)); }
This is the add item subroutine...
sub add_item { my ($dbh, $cart_ref, $item_id, $qty, $color) = @_; # If the item isn't already in the cart, look it up from the datab +ase # and store it in the cart as a new entry with a quantity of zero. if (!exists ($cart_ref->{$item_id})) { my $sth = $dbh->prepare ("SELECT * FROM catalog_pet WHERE item_id = ?"); $sth->execute ($item_id); my $item_ref = $sth->fetchrow_hashref (); $sth->finish (); return if !defined ($item_ref); # this shouldn't happen... $cart_ref->{$item_id} = {}; # create new entry, indexed by +item ID $cart_ref->{$item_id}->{description} = $item_ref->{description +}; $cart_ref->{$item_id}->{price} = $item_ref->{price}; $cart_ref->{$item_id}->{qty} = 1; } $cart_ref->{$item_id}->{qty} = $qty; $cart_ref->{$item_id}->{color} = $color; }
Finally, this produces the cart...
sub format_cart_html { my ($cart_ref, $show_links) = @_; my $total_price = 0; my @row; if (!keys (%{$cart_ref})) { return (p ("Shopping cart is empty.")); } $page .= h3 ({-align=>"center", -style=>"font-family: +verdana; font-size: 16pt; color: blue;"}, ("Shopping Cart")); push (@row, Tr ({-align => "CENTER", -valign=>"TOP", -BGCOLOR=>" +silver", -style=>"font-family: verdana; font-size: 10pt;"}, th ({-width=>"50"},("Item")), th ({-width=>"50"},("Qty")), th ({ +-width=>"90"},("Color")), th ({-width=>"190"},("Description")), th ({-width=>"94"},("Unit Price")), th ({-width=>"94"},("Price")) )); foreach my $item_id (sort (keys (%{$cart_ref}))) { my $item_ref = $cart_ref->{$item_id}; my $total_item_price = $item_ref->{qty} * $item_ref->{price}; $total_price += $total_item_price; # generate a link allowing the item to be deleted from the car +t my $url = sprintf ("%s?choice=delete;item_id=%s", url (), escape ($item_id)); push (@row, start_form(-method=>'GET', + -action=>url()), hidden( -name => "choice", -override => 1, -default => "update" ), hidden( -name => "item_id", -override => 1, -default => escapeHTML( $i +tem_id )), Tr ({-valign=>"center", -style=>"font-f +amily: verdana; font-size: 10pt;"}, td ({-align => "center"},(escapeHTML ($ +item_id))), td ({-align => "center"},( textfield( - +name => "quantity", -size => "1", -override => 1, -value => $item_ref +->{qty}))), td (escapeHTML ($item_ref->{color})), td (escapeHTML ($item_ref->{description})), td ({-align => "right"}, escapeHTML (sprintf ("%.2f", $item_ref->{price +}))), td ({-align => "right"}, escapeHTML (sprintf ("%.2f", $total_item_price +))), td (a ({-href => $url}, img ({-src => ".. +/images/delete1.jpg", -border => "0"}))), td (image_button( -name => "update", -src +=>"../images/update1.jpg", -border=>"0" )), end_form() )); } push (@row, Tr ({-align => "CENTER", -valign=>"center", -style=>" +font-family: verdana; font-size: 10pt;"}, td ({-colspan => "2"}, ""), td ({-colspan => "3", -align=>"right", -style=>"font-w +eight: 700;"}, "Total"), td ({-align => "right", -style=>"font-weight: 700;"}, escapeHTML (sprintf ("%.2f", $total_price))) )); return (table ({-align => "CENTER", -border => 0}, @row)); }
Thank you ahead of time for all and any help.

Replies are listed 'Best First'.
Re: Need duplicate item entries in shopping cart
by perrin (Chancellor) on Feb 19, 2003 at 17:57 UTC
    You are using a compound key (item_id + color) but storing it by item_id alone. That won't work. Your cart is a hash, which means it won't store duplicates. Your add_item code checks to see if that item_id is there in the cart already before adding it, which will also prevent the same item with two colors from being added twice.

    Frankly, this is so obvious that I think you must be asking a different question. Are you asking what a better data structure would be? I'd suggest you give each color a unique item_id, and create a higher-level entity (say, "product") to represent several items that are color variations of the same basic thing. Then your item_id will be unqiue and can be stored in the way you're doing it now.

      If anyone can help me with writing my add_item with as an array, I sure could use it.
      Thanks.
      Hi Perrin,

      I modified my add_item subroutine to incorporate the arrays. Can you see if I'm on the right track?

      I believe that I'm way off base. Especially when it comes to the quantity field. Quantity is a parameter that is being passed. It's not in the database table so I don't know how to incorporate that into the array.

      sub add_item { my @item_ref; my @cart_ref; my ($dbh, $cart_ref, $item_id, $qty, $color) = @_; # If the item isn't already in the cart, look it up from the datab +ase # and store it in the cart as a new entry with a quantity of zero. if (!exists ($cart_ref->{$item_id})) { my $sth = $dbh->prepare ("SELECT item_id, description, categor +y, price, picture, thumbnail, mime_type, color FROM catalog_pet WHERE item_id = ?"); $sth->execute ($item_id); my $item_ref = $sth->fetchrow_arrayref (); $sth->finish (); return if !defined ($item_ref); # this shouldn't happen... @cart_ref->[0] = {}; # create new entry, indexed by item ID @cart_ref->[1] = $item_ref->{description}; @cart_ref->[3] = $item_ref->{price}; @cart_ref->[8] = 1; } @cart_ref->[8] = $qty; @cart_ref->[7] = $color; push (@cart_ref, @item_ref); }
        Well, first of all you have to change ALL of your code that uses this cart structure -- not just this one sub -- if you want to switch the cart from a hash to an array. I actually think this is probably the wrong approach and you would be better off making unique item ids for every one of your colors. You could also cheat by making a generated key to use in your shopping cart: my $item_key = $item_id . '|' . $color or something like that.

        Just to give you an idea of what you need to do to that code to change it to arrays, here is another take on your add_item sub:

        sub add_item { my ($dbh, $cart_ref, $item_id, $qty, $color) = @_; # If the item isn't already in the cart, look it up from the datab +ase # and store it in the cart as a new entry with a quantity of zero. my $saw_item = 0; foreach my $item (@{$cart_ref}) { if (($item->{'id'} == $item_id) && $item->{'color'} eq $color) +) { $saw_item = 1; break; } } if (!$saw_item) { my $sth = $dbh->prepare ("SELECT item_id, description, categor +y, price, picture, thumbnail, mime_type, color FROM catalog_pet WHERE + item_id = ?"); $sth->execute ($item_id); my $row = $sth->fetchrow_hashref (); return if !defined ($row); # this shouldn't happen... my $item_ref = {}; $item_ref->{description} = $row->{description}; $item_ref->{price} = $row->{price}; $item_ref->{qty} = 1; $item_ref->{color} = $color; } $item_ref->{qty} = $qty; push (@{$cart_ref}, @item_ref); }
        Note that it is no longer possible to look up a single item in the cart by ID with this structure: you have to loop through all the items.
Re: Need duplicate item entries in shopping cart
by jasonk (Parson) on Feb 19, 2003 at 17:48 UTC

    Your add_item subroutine adds the item to cart_ref using the item_id as a key, which means your cart can never contain more than one entry for any one item_id. I would probably use an arrayref rather than a hashref for cart_ref, so you can just push new items into the cart allowing more than one of a given item_id.

      Hi Jasonk,

      I modified my add_item subroutine from hashref to arrayref and three things are occurring:
      (1) when I select the first color on my list I'm receiving the following message: Can't coerce an array into a hash.
      (2) when I select an item of another color, my view cart only shows item_id, quantity, and color; the description and price is not carried foward.
      (3) when I try to select the same item again with a different color; it's still overwriting placing the most recent choice into the cart and removing the previous choice.

      Here is how my new add_item subroutine appears when I made your modification:
      sub add_item { my ($dbh, $cart_ref, $item_id, $qty, $color) = @_; # If the item isn't already in the cart, look it up from the datab +ase # and store it in the cart as a new entry with a quantity of zero. if (!exists ($cart_ref->{$item_id})) { my $sth = $dbh->prepare ("SELECT * FROM catalog_pet WHERE item_id = ?"); $sth->execute ($item_id); my $item_ref = $sth->fetchrow_arrayref (); $sth->finish (); return if !defined ($item_ref); # this shouldn't happen... $cart_ref->{$item_id} = {}; # create new entry, indexed by +item ID $cart_ref->{$item_id}->{description} = $item_ref->{description +}; $cart_ref->{$item_id}->{price} = $item_ref->{price}; $cart_ref->{$item_id}->{qty} = 1; } $cart_ref->{$item_id}->{qty} = $qty; $cart_ref->{$item_id}->{color} = $color; }
      Any other thoughts or ideas would be great! Thanks