Blog / Adding a customizable shopping cart to Bolt CMS

One of the best things about the Bolt CMS for PHP is that it enables you to use MVC patterns to add custom functionality to your sites. In this guide, I’ll show you how to use Bolt’s “local extension” system to create your own customized cart.

Contents

Sample Code

Sample code can be found here on GitHub: https://github.com/designspike/bolt-local-extension-example-cart. It covers everything featured in this article.

Regular vs local extensions

Bolt has two ways to extend your site.

  1. Regular extensions – These are extensions you develop as standalone modules that anyone can add to their Bolt installation. Typically, you’d host it on github or similar, then submit it to https://extensions.bolt.cm/. You can install these via the Extend page in Bolt’s admin area.
  2. Local extensions – This is what we’re talking about in this article. Local extensions are pretty straghtforward and that’s why they’re great. You create a folder like /extensions/local/yourname/yourextension and Bolt will automatically detect and load the extension. To test your work, just refresh the page. This is actually how a regular extension would start out before it’s published.

Local extensions are perfect for carts or other highly-specific functionality that must be customized to a site’s individual needs. With a local extension, it’s easy to start working and write the code you need for the job at hand.

A shopping cart is a great example of the robustness of Bolt’s extension system, since carts do a lot of things.

Set up routes

To make your cart work, you’ll need to map out all the URLs that the browser will access to view and update the cart contents.


protected function registerFrontendRoutes(ControllerCollection $collection)
{
    $collection->post('/cart/add',    [$this, 'callbackCartAdd']);
    $collection->post('/cart/update', [$this, 'callbackCartUpdate']);
    $collection->post('/cart/clear',  [$this, 'callbackCartClear']);
    $collection->post('/cart/send',   [$this, 'callbackCartSend']);
    $collection->get( '/cart',        [$this, 'callbackCartDisplay']);
}

Using code like the above, you map a URL to a method in your extension. For example, the callbackCartAdd() method will be called when the visitor’s browser makes a post request to /cart/add. And when they go to /cart, it will display the cart contents.

Modify the cart (add, update, remove)

If you use sessions to hold the cart contents, modifying the cart is as easy as updating the session variable.


public function callbackCartAdd(Application $app, Request $request)
{
    $cart = $app['session']->get('cart');
    $identifier = $request->request->get('identifier');
    $cart[$identifier] = [
        "id"     => (int) $request->request->get('id'),
        "name"   =>       $request->request->get('name'),
        "number" =>       $request->request->get('number'),
        "unit"   =>       $request->request->get('unit'),
        "qty"    => (int) $request->request->get('qty')
    ];
    if ($cart[$identifier] <= 1) {
        unset($cart[$identifier]);
    }
    $app['session']->set('cart', $cart);
    return new RedirectResponse('/cart');
}

The method accesses the session, adds a new entry to the cart array, then sends the visitor back to the cart URL.

Add custom twig functions to display cart contents or quantity

You’ll probably want to display the contents of your cart to visitors at some point. To make this easier, you can add a custom twig function that returns the cart contents.


protected function registerTwigFunctions()
{
    return [
        'cart_contents' => 'cartContentsFunction',
        'cart_count' => 'cartCountFunction',
    ];
}

public function cartContentsFunction()
{
    return $this->getContainer()['session']->get('cart');
}

public function cartCountFunction()
{
    $cart = $this->getContainer()['session']->get('cart');
    if (!is_array($cart)) {
        return 0;
    }
    $sum = 0;
    foreach ($cart as $line_item) {
        $sum += $line_item['qty'];
    }
    return $sum;
}

registerTwigFunctions gives you an easy way to create new functions you can use in your site’s twig templates. For example, to use the code above, you can do this:


Number of items in the cart: {{ cart_count() }}
Cart contents:
<ul>
    {% for identifier, product in cart_contents() %}
        <li>{{ product.name }} ({{ product.qty }})</li>
    {% endfor %}
</ul>

Validate the checkout

While validating the checkout, you can add feedback messages to a “flashbag” just like in other Symfony or Silex applications:


$app['session']->getFlashBag()->add('danger', 'Name is required.');

Then, after redirecting the visitor back to the checkout page:


if ($app['session']->getFlashBag()->has('danger')) {
    return new RedirectResponse('/cart');
}

You can display the errors in cart.twig:


{% for type, messages in app.session.flashbag.all() %}
    {% for message in messages %}
        <p class="bg-{{ type }}">{{ message }}</p>
    {% endfor %}
{% endfor %}

Send an email receipt

Sending the receipt consists of two steps.

First, the receipt needs to be rendered to html. For this example I decided to include the receipt template as part of the extension. Though it could have been added as part of the Bolt theme instead.

To make the email_order.twig accessible, you can register the folder it’s contained in:


protected function registerTwigPaths()
{
    return [
        'templates' => ['position' => 'prepend', 'namespace' => 'Cart']
    ];
}

Then make a /templates folder within your extension’s folder, and drop email_order.twig in it. If you had other templates specific to the cart, they could go in the same /templates folder.

Then rendering the twig template to html is pretty easy:


$body = $app['twig']->render('@Cart/email_order.twig', [
    'cart'     => $app['session']->get('cart'),
    'checkout' => $app['session']->get('checkout'),
]);

To send the email, you can use Bolt’s built-in SwiftMailer service.


$message = \Swift_Message::newInstance($subject)
    ->setFrom($app['config']->get('general/mailoptions/senderMail'))
    ->setTo($app['config']->get('general/order_receipt_recipients'))
    ->setBody($body, 'text/html');
if (filter_var($request->request->get('email'), FILTER_VALIDATE_EMAIL)) {
    $message->setReplyTo(
        $request->request->get('email'),
        $request->request->get('name')
    );
}
$sent = $app['mailer']->send($message);
if (!$sent) {
    throw new \Exception("Message wasn't actually sent.");
}

The above code also illustrates how to access Bolt configuration values (config.yml) from within an extension.

Will

Web developer

More Posts

Follow Me:
Twitter