Struggling with product categories and cart function

Hi all,

I’m a noob to the forum, so be gentle.

Okay, so I’ve been playing locally with the brilliant Kirby (v2) for a while, building a small shop for myself on the back of great initial work covered in these previous posts:

  1. Probably the original Kirby cart plugin
  2. And a more recent update of it

These are both great pieces of work, but only offer a basic single level of product to cart functionality, without any category:

Without wanting to get too in depth in this initial post, I have a feeling (but might be totally wrong) that my problem is to do with the cart.php plugin, and perhaps the routing/config loops of the site.

Here’s the structure I’d like to enable:

Side note: I have all of the above structure and templates working, it’s just the adding of items to cart from within /category-1/product-1 that does not work. But if I drop a product back one level to /products then it adds to cart… All very puzzling to me!

Is the above structure possible, or am I being too hopeful?! I’m certainly no php expert and only tinker, so I’ve spent many hours trying to get this to work! Forgive me if I’m being dumb.

Any suggestions or advice would be much appreciated.

Many thanks in advance,

I don’t see a problem with using categories subfolders, you just need to adapt the templates accordingly, wherever the products are called.

And as regards adding a product to the cart, to me it seems all you have to change is the action url in the form in the product.php template (and maybe some other places, if the form is used on more than one site.

Not tested though.

It sounds like your issue is that nested products don’t get added to the cart, whereas products located int he main directory do. Can you post your plugin code?

Thanks for your replies @texnixe and @samnabi

Cart plugin

I’m using the same cart.php plugin code as the above 2 plugin links, which is:

function get_cart() {
    $cart = s::get('cart', array());
    return $cart;

function cart_logic($cart) {

    if (isset($_REQUEST['action'])) {
      $action = $_REQUEST['action'];
      $id = $_REQUEST['id'];
      switch ($action) {
          case 'add':
              if (isset($cart[$id])) {
              } else {
                  $cart[$id] = 1;
          case 'update':
              if (isset($_REQUEST['quantity'])) {
                  $quantity = intval($_REQUEST['quantity']);
                  if ($quantity < 1) {
                  } else {
                      $cart[$id] = $quantity;                
          case 'delete':
              if (isset($cart[$id])) {
      s::set('cart', $cart);

    if (count($cart) == 0) {

    return $cart;

function cart_count($cart) {
    $count = 0;
    foreach ($cart as $id => $quantity) {
        $count += $quantity;
    return $count;

function cart_postage($total) {
    switch ($total) {
        case ($total < 10):
            $postage = 2.5;
        case ($total < 30):
            $postage = 3.5;
        case ($total < 75):
            $postage = 5.5;
        case ($total < 150):
            $postage = 8;
            $postage = 10;
    return $postage;

Products template

In the products.php template I have this code to display the /category image and page link:

<?php foreach($products as $product): // START PRODUCTS ?>
<?php endforeach // END PRODUCTS ?>

Category template

In the category.php template page I have this code to display the /product image and page link:

<?php $products = $page->children()->visible()->flip()->paginate(12); // FIND AND FLIP ?>
<?php foreach($products as $product): // START PRODUCTS ?>
<?php endforeach // END PRODUCTS ?>

Product template

In the product.php template I have this button code to add the item to cart:

<form action="<?php echo url('shop/cart') ?>" method="post">
	<input type="hidden" name="action" value="add">
	<input type="hidden" name="id" value="<?php echo $page->uid() ?>">
	<button type="submit" name="submit">Add to cart</button>

Cart template

In the cart.php template I have this code to display added items, also based on the above 2 plugin links:

<?php $cart = cart_logic(get_cart()) ?>
<?php $products = $page->siblings()->visible() ?>
<?php $i=0; $count = 0; $total = 0; ?>
<?php foreach($cart as $id => $quantity): // START QUANTITY ?>
<?php if($product = $products->findByURI($id)): // START PRODUCTS ?>
<?php $i++; ?>
<?php $count += $quantity ?>
<?php endif; // END PRODUCTS ?>
<?php endforeach; // END QUANTITY ?>


Clearly, I have something wrong as yes: nested products don’t get added to the cart, whereas products in the main directory do. That’s the issue.

Maybe it’s the cart plugin, and my /products and /category templates messing things up?..

Any suggestions would be great. Many thanks.

1. Use the full URI as your ID field

Try changing the id field in your product.php template from this:

<input type="hidden" name="id" value="<?php echo $page->uid() ?>">

To this:

<input type="hidden" name="id" value="<?php echo $page->uri() ?>">

2. Look through the whole site for products, not just siblings of the /products/cart page

In cart.php, remove this:

<?php $products = $page->siblings()->visible() ?>

And change this:

<?php if($product = $products->findByURI($id)): // START PRODUCTS ?>

To this:

<?php if($product = page($id)): // START PRODUCTS ?>

Brilliant. After making those updates to my code – the cart works! Thank you so much @samnabi

Just one small bug now: after making those updates the product quantity options on the cart.php template breaks. E.g. adding more, less or deleting items on the cart page stops working; no updates are displayed.

It seems to be the update to page($id) conflicting with the product input data-id and their add, remove, delete actions – but I might be totally wrong!

Here’s the code:

	<input data-id="<?php echo $product->uid() ?>" data-quantity="<?php echo $quantity ?>" pattern="[0-9]*" class="quantity" type="hidden" name="quantity_<?php echo $i ?>" min="1" value="<?php echo $quantity ?>">
	<?php echo $quantity ?> x
	<a class="btn add" href="<?php echo url('cart') ?>?action=add&amp;id=<?php echo $product->uid() ?>">+</a>
	<?php if ($quantity > 1): ?>
	 <a class="btn remove" href="<?php echo url('cart') ?>?action=remove&amp;id=<?php echo $product->uid() ?>">-</a>
	<?php endif ?>
	 <?php $prodtotal = floatval($product->price()->value)*$quantity ?>
	<td><a class="btn-red delete" href="<?php echo url('cart') ?>?action=delete&amp;id=<?php echo $product->uid() ?>">Remove</a></td>

Any suggestions for what might fix this? Again, thanks for any advice… much appreciated.

I haven’t tested this, but anywhere you see $product->uid() can be changed to $product->uri(). That should fix your remaining issues.

The UID is just based on the product page itself, so something like product-name. The URI, on the other hand, takes the whole path, like content/category-name/product-name.

This lets you pull products from any category, and the cart won’t break if you have more than one product with the same name in different categories.

Yep, that works.

I tired it before the previous post but thought it was a bit weird or bad practice to utilise /basket?action=add&id=products/category-name/product-name rather than the usual cleaner /basket?action=add&id=product-name

But no complaints, the cart works now.

Thank you very much for your help on this Sam.

Job done.


Sam, I just looked through the plugins archive on the forum – and only just discovered your own excellent work on Shopkit. Wow, that could have saved me a lot of time! It looks great, very comprehensive… I may well buy a license from you at some point soon.

All the best with it.

Happy to help :slight_smile:

Yes, Shopkit has a lot more to offer. I’m doing quite a lot of active development to get it feature-complete before a 1.0 release, so send me your requests!