Shopping Cart Snippets

I have a query in regards a very very simple shopping snippets I have created. There are 3 in total.

  • addToCart
  • shoppingCart
  • viewCart

The addToCart snippet is a very simple button which adds the item to a shopping cart. (I’m only selling one item)

The shoppingCart snippet is a simple shopping cart displaying the item, which it does when it’s added.

And the final snippet is viewCart. It’s a simple table that lists the item before you press pay now.

When I add an item, the shopping cart doesn’t update instantly. But when i refresh or go to another page, it’s visible. The same with the clear cart button on the viewCart snippet. It will delete it from the viewcart but you’ll have to refresh the page to see the changes in the shoppingCart snippet.

To update the shopping cart instantly without refreshing the page, chatGPT says I can use AJAX. I need to modify the addToCart snippet to return JSON data. They then gave me JavaScript code to handle the AJAX request. Part of the JavaScript code is an URL for the snippet.

url: 'addToCartSnippet.php', // Path to your addToCart snippet

My question is, how do I get the URL for the snippets?

This is my addToCart snippet

<?php
// Start or resume the session
session_start();

// Check if the cart array is not initialized in the session, initialize it
if (!isset($_SESSION["cart"])) {
    $_SESSION["cart"] = array();
}

// Check if the request is to add an item to the cart
if (isset($_POST["add_to_cart"])) {
    // Validate and sanitize input
    $product_id = filter_input(INPUT_POST, "product_id", FILTER_SANITIZE_NUMBER_INT);
    $quantity = filter_input(INPUT_POST, "quantity", FILTER_VALIDATE_INT, array("options" => array("min_range" => 1)));

    if ($product_id !== false && $quantity !== false) {
        // Check if the product is already in the cart
        if (isset($_SESSION["cart"][$product_id])) {
            // If it is, increment the quantity by the selected multiplier
            $_SESSION["cart"][$product_id]["quantity"] += $quantity;
        } else {
            // If it's not, add it to the cart with the selected quantity
            $_SESSION["cart"][$product_id] = array(
                "quantity" => $quantity,
                "product_name" => "Advertisement" . $product_id,
                "product_price" => 250.00,
            );
        }

        // Return updated cart data as JSON
        echo json_encode($_SESSION["cart"]);
        exit; // Stop further execution
    } else {
        // Handle invalid input (e.g., display an error message)
        echo json_encode(array("error" => "Invalid input. Please check your input values."));
        exit; // Stop further execution
    }
}
?>

And this is my JavaScript

<script>
    $(document).ready(function() {
        $('.add-to-cart').click(function(e) {
            e.preventDefault(); // Prevent default form submission

            var form = $(this).closest('form'); // Find the closest form
            var formData = form.serialize(); // Serialize form data

            $.ajax({
                type: 'POST',
                url: 'addToCartSnippet.php', // Path to your addToCart snippet
                data: formData,
                dataType: 'json',
                success: function(response) {
                    // Update the cart dynamically
                    // (You may need to adjust this part based on your HTML structure)
                    $('.top-cart-number').text(response.quantity);
                    // Optionally, show a success message or perform any other action
                },
                error: function(xhr, status, error) {
                    // Handle errors
                    console.error(xhr.responseText);
                    alert('Error: ' + xhr.responseText);
                }
            });
        });
    });
</script>

The simplest method to create an" AJAX endpoint" is to create a new resource, with template = (empty) and “Content Type” = JSON. Then call your snippet uncached in the content.

The endpoint URL is then the URL of this resource.

1 Like

Can you tell me why I might be having this issue. Below is my full code for the addToCart snippet but for some strange reason, when I look at that page…I see a return;1 at the end of the snippet. You’ll see it in the bottom left hand corner of the image attached. Any idea why this might be happening?

// Error checker
  error_reporting(E_ALL);
  ini_set("display_errors", 1);

  // Use secure and HttpOnly flags for session cookies
  session_set_cookie_params([
      'secure' => true,
      'httponly' => true,
  ]);

  // Start or resume the session
  session_start();

  // Regenerate the session ID to prevent session fixation
  session_regenerate_id(true);

  // Check if the cart array is not initialized in the session, initialize it
  if (!isset($_SESSION["cart"])) {
    $_SESSION["cart"] = array();
  }

  // Check if the request is to add an item to the cart
  if (isset($_POST["add_to_cart"])) {
    // Validate and sanitize input
    $product_id = filter_input(INPUT_POST, "product_id", FILTER_SANITIZE_NUMBER_INT);
    $quantity = filter_input(INPUT_POST, "quantity", FILTER_VALIDATE_INT, array("options" => array("min_range" => 1)));

    if ($product_id !== false && $quantity !== false) {
      // Check if the product is already in the cart
      if (isset($_SESSION["cart"][$product_id])) {
        // If it is, increment the quantity by the selected multiplier
        $_SESSION["cart"][$product_id]["quantity"] += $quantity;
      } else {
        // If it's not, add it to the cart with the selected quantity
        $_SESSION["cart"][$product_id] = array(
          "quantity" => $quantity,
          "product_name" => "Fógraíocht " . $product_id,
          "product_price" => 250.00,
        );
      }
    } else {
      // Handle invalid input (e.g., display an error message)
      echo "Invalid input. Please check your input values.";
    }
  }
?>
<div class="single-product">
  <div class="product">
    <div class="row gutter-40">
      <div class="col-md-7">
        <div class="product-image">
          <div class="slide"><img src="[[!++site_url]]assets/images/ad/ad-rectangle.jpg" alt="Ad Space"></div>
          <div class="sale-flash badge bg-danger p-2">D&iacute;olach&aacute;n!</div>
        </div>
      </div>
      <div class="col-md-5 product-desc">
        <div class="d-flex align-items-center justify-content-between">
          <div class="product-price"><ins>&euro;250.00</ins></div>
          <?php if (!empty($_SESSION['cart'])): ?>
            <form class="cart mb-0 d-flex justify-content-between align-items-center" action="fograiocht/cisean/" method="get">
              <button type="submit" class="add-to-cart button m-0">View Cart</button>
            </form>
          <?php endif; ?>
        </div>
        
        <div class="line"></div>
        
        <form class="cart mb-0 d-flex justify-content-between align-items-center" method="post">
          <div class="quantity">
            <input type="button" value="-" class="minus">
            <input type="number" step="1" min="1" name="quantity" id="quantity" value="1" title="Qty" class="qty">
            <input type="button" value="+" class="plus">
          </div>
          <button type="submit" name="add_to_cart" class="add-to-cart button m-0">Add to Cart</button>
        </form>
        
        <div class="line"></div>
        
        <p>Ar mhaith leat do chomhlacht n&oacute; su&iacute;omh a fh&oacute;gairt anseo? T&aacute; sp&aacute;sanna le d&iacute;ol. Is m&oacute; f&oacute;gra a th&oacute;gann t&uacute;, is m&oacute; lascaine at&aacute; ar f&aacute;il. Beidh an f&oacute;gra le feice&aacute;il ar gach leathanach beo don su&iacute;omh seo.</p>
        <ul class="iconlist">
          <li><i class="fa-solid fa-caret-right"></i> 720x90 pixel & 300x300 pixel</li>
          <li><i class="fa-solid fa-caret-right"></i> 2 F&oacute;gra- Lascaine 10%</li>
          <li><i class="fa-solid fa-caret-right"></i> 3+ F&oacute;gra - Lascaine 20%</li>
        </ul>
      </div>
      <div class="w-100"></div>                  
    </div>
  </div>
</div>

So is all this HTML markup (<div class="single-product"><div class="product">...) part of the snippet?
I don’t think you can do that. A snippet should all be PHP code.

If you want to return HTML markup from a snippet, put the HTML in a chunk for example and use return $modx->getChunk('chunk_name', $chunk_properties) in the code.
Or maybe set a placeholder in the snippet
($modx->setPlaceholder('placeholder_name', $placeholder_value);)
and put the HTML with a placeholder tag in the resource content.

I’m also not so sure about all this session stuff you are doing.
With the default settings, MODX should already have created the session when a snippet runs.

Yes I have the HTML code within the snippet because I have some PHP inside the HTML too i.e the code below will only display a View Cart button if there’s something in the cart.

<?php if (!empty($_SESSION['cart'])): ?>
 <form class="cart mb-0 d-flex justify-content-between align-items-center" action="fograiocht/cisean/" method="get">
  <button type="submit" class="add-to-cart button m-0">View Cart</button>
 </form>
<?php endif; ?>

I have it like this as I had it done on PHP pages first to see if it would work before adding them to a snippet

Yes, but the snippet is supposed to be only PHP code.

You could use normal MODX output modifiers for this in the chunk:

// In the snippet
return $modx->getChunk('chunk_name', ['cart_empty' => empty($_SESSION['cart'])]);
<!-- In the chunk -->
[[+cart_empty:isnot=`1`:then=`<form class="cart mb-0 d-flex justify-content-between align-items-center" action="fograiocht/cisean/" method="get">
  <button type="submit" class="add-to-cart button m-0">View Cart</button>
 </form>`]]

or maybe use a multiline string in the snippet

$output = "..."; // The string with the markup to output;

$view_cart_form = <<<EOD
<form class="cart mb-0 d-flex justify-content-between align-items-center" action="fograiocht/cisean/" method="get">
    <button type="submit" class="add-to-cart button m-0">View Cart</button>
</form>
EOD;

if (!empty($_SESSION['cart'])) {
    $output .= $view_cart_form; // add the "view cart" markup to the output
}
return $output;

If you really want to keep the code like it is at the moment, then add

<?php
return '';

at the end to get rid of the return; 1.

Ok Harry…I’ve messing around and trying to learn. So I have 2 pages. 1 called shop.html and the other called cart.html

In the shop.html page I have this code below which is an addToCart snippet and an addToCart_Button chunk.

<div class="container">
  <!-- Add to Cart Snippet
  ============================================= -->
  [[!addToCart]]
  
  <!-- Add to Cart Button Chunk
  ============================================= -->
  [[$addToCart_Button]]
</div>

This is the addToCart snippet

<?php
// Error checker
  error_reporting(E_ALL);
  ini_set("display_errors", 1);

  // Use secure and HttpOnly flags for session cookies
  session_set_cookie_params([
      'secure' => true,
      'httponly' => true,
  ]);

  // Start or resume the session
  session_start();

  // Regenerate the session ID to prevent session fixation
  session_regenerate_id(true);

  // Check if the cart array is not initialized in the session, initialize it
  if (!isset($_SESSION["cart"])) {
    $_SESSION["cart"] = array();
  }

  // Check if the request is to add an item to the cart
  if (isset($_POST["add_to_cart"])) {
    // Validate and sanitize input
    $product_id = filter_input(INPUT_POST, "product_id", FILTER_SANITIZE_NUMBER_INT);
    $quantity = filter_input(INPUT_POST, "quantity", FILTER_VALIDATE_INT, array("options" => array("min_range" => 1)));

    if ($product_id !== false && $quantity !== false) {
      // Check if the product is already in the cart
      if (isset($_SESSION["cart"][$product_id])) {
        // If it is, increment the quantity by the selected multiplier
        $_SESSION["cart"][$product_id]["quantity"] += $quantity;
      } else {
        // If it's not, add it to the cart with the selected quantity
        $_SESSION["cart"][$product_id] = array(
          "quantity" => $quantity,
          "product_name" => "ITEM SOLD" . $product_id,
          "product_price" => 250.00,
        );
      }
    } else {
      // Handle invalid input (e.g., display an error message)
      echo "Invalid input. Please check your input values.";
    }
  }

And this is the addToCart_Button chunk

<form class="cart" method="post">
  <div class="quantity">
    <input type="button" value="-" class="minus">
    <input type="number" step="1" min="1" name="quantity" id="quantity" value="1" title="Qty" class="qty">
    <input type="button" value="+" class="plus">
  </div>
  <button type="submit" name="add_to_cart" class="add-to-cart button">Add to Cart</button>
</form>

Then on the cart.html page I have a snippet called viewCart and a chunk called viewCart_Table

This is the viewCart snippet

<?php
  error_reporting(E_ALL);
  ini_set('display_errors', 1);
  
  // Start or resume the session
  session_start();
  
  // Check if the cart array is not initialized in the session, initialize it
  if (!isset($_SESSION["cart"])) {
      $_SESSION["cart"] = array();
  }
  
  // Check if the "Clear Cart" button is clicked
  if (isset($_POST["clear_cart"])) {
      // Clear the cart
      $_SESSION["cart"] = array();
  }
?>

<?php foreach ($_SESSION["cart"] as $product): ?>
  <?php
    $quantity = $product["quantity"];
    $discount = ($quantity == 1) ? 0 : ($quantity == 2 ? 0.10 : 0.20);
    $fullPrice = $product["quantity"] * $product["product_price"];
    $discountAmount = $fullPrice * $discount;
    $discountedPrice = $fullPrice - $discountAmount;
  ?>
<?php endforeach; ?>

This is the viewCart_Table

<table class="table cart mb-5">
  <thead>
    <tr>
      <th class="cart-product-name">Name</th>
      <th class="cart-product-price">Price</th>
      <th class="cart-product-quantity">Quantity</th>        
      <th class="cart-product-fullprice">Full Price</th>
      <th class="cart-product-discount">Discount</th>
      <th class="cart-product-subtotal">Discountws Price</th>
    </tr>
  </thead>
  <tbody>
      <tr class="cart_item">
        <td class="cart-product-name">Product Name</td>
        <td class="cart-product-price"><span class="amount">Price Here</span></td>
        <td class="cart-product-quantity">
          <div class="quantity">
            <input type="button" value="-" class="minus">
            <input type="text" name="quantity" value="Quantity Vakue Here" class="qty">
            <input type="button" value="+" class="plus">
          </div>
        </td>
        <td class="cart-product-fullprice"><span class="amount">Full Price Here</span></td>
        <td class="cart-product-discount"><span class="amount">Discount Percentage Here</span></td>
        <td class="cart-product-subtotal"><span class="amount color lead fw-medium">Discounted Price Here</span></td>
      </tr>
  </tbody>    
</table>

My question is how do I take the data from the shop.html page that I’ve saved and add it to the table on the cart.html page? I know you mentioned placeholders but I’m not 100% sure how you do that.

Because the table has a variable amount of rows, you usually create an output like this completely in the snippet with the help of two chunks:

Snippet

...
$rows = "";
foreach ($_SESSION["cart"] as $product) {
    $rowProperties = $product;
    $rowProperties["discount"] = ($product["quantity"] == 1) ? 0 : ($product["quantity"] == 2 ? 0.10 : 0.20);
    $rowProperties["fullPrice"] = $product["quantity"] * $product["product_price"];
    $rowProperties["discountAmount"] = $rowProperties["fullPrice"] * $rowProperties["discount"];
    $rowProperties["discountedPrice"] = $rowProperties["fullPrice"] - $rowProperties["discountAmount"];

    $rows .= $modx->getChunk('cartRow', $rowProperties);
}
return $modx->getChunk('cartWrapper', ["rows" => $rows]);

Chunk “cartRow”

<tr class="cart_item">
    <td class="cart-product-name">[[+product_name]]</td>
    <td class="cart-product-price"><span class="amount">[[+product_price]]</span></td>
    <td class="cart-product-quantity">
        <div class="quantity">
            <input type="button" value="-" class="minus">
            <input type="text" name="quantity" value="[[+quantity]]" class="qty">
            <input type="button" value="+" class="plus">
        </div>
    </td>
    <td class="cart-product-fullprice"><span class="amount">[[+fullPrice]]</span></td>
    <td class="cart-product-discount"><span class="amount">[[+discountAmount]]</span></td>
    <td class="cart-product-subtotal"><span class="amount color lead fw-medium">[[+discountedPrice]]</span></td>
</tr>

The placeholders ([[+xy]]) get replaced with the values of the second parameter in the getChunk() call.

Chunk “cartWrapper”

<table class="table cart mb-5">
  <thead>
    <tr>
      <th class="cart-product-name">Name</th>
      <th class="cart-product-price">Price</th>
      <th class="cart-product-quantity">Quantity</th>        
      <th class="cart-product-fullprice">Full Price</th>
      <th class="cart-product-discount">Discount</th>
      <th class="cart-product-subtotal">Discountws Price</th>
    </tr>
  </thead>
  <tbody>
      [[+rows]]
  </tbody>    
</table>
1 Like