Blog

The Grav Shopping Cart Skeleton

Good news: the Shopping Cart plugin has now an official Grav Skeleton. This means it's quicker than ever to set up your site, and it's also easy to compare your site with what can be considered the "best" way to structure your pages.

The skeleton is free, public and lives on GitHub in its repo: https://github.com/flaviocopes/grav-skeleton-gravcart

How to install

It's not yet a full site download, but this will come soon.

In the meantime, just install Grav from https://getgrav.org/downloads, then remove the user/ folder.

Then download/clone from https://github.com/flaviocopes/grav-skeleton-gravcart, put that in your user/ folder, and run bin/grav install from the Grav main folder from the command line to fetch the dependencies.

You should be up and running in no time!

GravCart 1.1 released

With tons of fixes and improvements, GravCart 1.1 is now released.

If you spot any problem, let me know through the GitHub issues.

Full list of new changes since version 1.0.7:

Features

* "Add to cart"-button functionality on products list page. Updated [#35](https://github.com/flaviocopes/grav-plugin-shoppingcart/issues/35)
* If no checkout plugin is installed, alert the site owner. Currently alerting in the frontend since this alert is likely to be encountered just when building the site
* Product image in cart, optional
* Added option to set the product image size in the product page
* Added option to set the product image size in the cart
* Add option to remove cents if .00
* Ability to configure the plugin options via the Admin interface
* Ability to edit the product through the Admin Pages view. Can currently set the price and set the default picture from the page media
* Added more events to support more addons features. Documented in the Plugin documentation site
* Added the ability to show the cart widget on pages not directly managed by the plugin (e.g. default pages, blog or any other page). Documented in the Plugin documentation site

Improvements

* Add a ShoppingCart.provinceIsRequired() utility function
* Add support for Grav 1.0.x and Form 1.2.x in the 1.1 release
* Extracted the Add to Cart code in a separate partial for easier override
* Default to ignore some checkout information
* Move onBeforeAddProductToCart and  onAfterAddProductToCart events inside ShoppingCart.addProduct
* Improved rendering of cart on mobile devices
* Added support for shortcodes addon
* Translate quantity string
* Added first acceptance tests
* Add wrapper div in order confirmation
* Changed structure of the Twig files: `shoppingcart` has been removed in favor of `shoppingcart_categories`, `shoppingcart_section` is now `shoppingcart_categories`, `shoppingcart_category` is now `shoppingcart_products`. `shoppingcart_product` is the product page. Please rename your markdown pages accordingly. There's backwards compatibility, so at this point things will continue to work fine, but the old filenames are deprecated.
* Core reorganization
* More testing

Bug fixes

* Fix issue with shipping being reset while recalculated
* Fix #24 "All countries without specific rule" not working as expected. And also cleanup some useless methods
* Only require province is required, fix #25
* Fix #27 only execute controller tasks if available
* Fix labels in blueprint
* Fix saving the cart when no shipping methods are entered. Also add payment info when there's a single shipping method, instead of omitting it
* Fixed error in version number
* Fixed error in namespace
* Fixed issue with price > 999 and NaN shown when adding it to the cart
* Various small bug fixes and improvements

GravCart 1.1 beta progress

It's been a bit more than two weeks since I last wrote about version 1.1. In the meantime, some more beta versions were published.

Now it's all stable enough for a release, I'm basically waiting for Grav 1.1 to be out stable to publish it, as the Admin settings require Admin 1.1 to work in their tabbed interface.

If you can, test-drive version 1.1 beta on the Grav 1.1 beta release, and report any issue if you find one.

Here's the summary of the changes introduced since GravCart 1.1 beta 1:

Features

  • Added the product image in cart (optional)
  • Added option to set the product image size in the product page
  • Added option to set the product image size in the cart
  • Added option to remove cents if the price ends with .00

Improvements

  • Add support for Grav 1.0.x and Form 1.2.x in the 1.1 release
  • Extracted the Add to Cart code in a separate partial for easier override
  • Default to ignore some checkout information
  • Move onBeforeAddProductToCart and onAfterAddProductToCart events inside ShoppingCart.addProduct
  • Improved rendering of cart on mobile devices
  • Added support for shortcodes addon
  • Translate quantity string
  • Added first acceptance tests
  • Add wrapper div in order confirmation
  • If no checkout plugin is installed, alert the site owner. Currently alerting in the frontend since this alert is likely to be encountered just when building the site
  • Add a ShoppingCart.provinceIsRequired() utility function

Bug fixes

  • Fixed saving the cart when no shipping methods are entered. Also add payment info when there's a single shipping method, instead of omitting it
  • Fixed an error in namespace
  • Fixed issue with price > 999 and NaN shown when adding it to the cart
  • Fixed #24 "All countries without specific rule" not working as expected. And also cleanup some useless methods
  • Only require province is required, fix #25
  • Fixed #27 only execute controller tasks if available
  • Fixed labels in blueprint

That's it for now! More news soon :-)

Moving to the gravcart.com domain

TL;DR: site moved from gravshoppingcart.com to gravcart.com.

I bought the domain gravshoppingcart.com back in January. I built the Grav Shopping Cart site on that domain, and I was happy with it, until I went creating a Twitter account but found that it was too long for a Twitter handle.

So I settled on @gravcart.

Then I found I liked the gravcart term. It's shorter, better, etc etc. So I bought gravcart.com, thinking that some day I might want to use it.

The more I thought about it, the more I realized that if I want this change to be done, it should be as early as possible, so starting from today, gravshoppingcart.com redirects to gravcart.com.

And I will use the name Grav Shopping Cart and GravCart to indicate the plugin, depending on what I find more convenient. The latter is shorter and easier. The first is the official name as stated in the GPM.

Introducing Acceptance Testing

GravCart has some basic Unit testing in place, for both PHP and JavaScript code. This allows to test the functionality in isolation, one method at a time. It's pretty useful to predict low-level behavior, and to keep constant track of possible issues when changing a method implementation. Not everything is covered, but the aim is to cover all it's sensible to cover over time.

Today I worked on doing the groundwork for allowing automated Acceptance Testing for the plugin, and its addons.

What does it mean?

The most immediate level at which code quality, and the whole product, is reflected is the user-facing part of the plugin.

The user experiences the process of checking your site, choosing one of the products you sell, add it to the cart, check some options if any are setup, and proceed to the checkout.

Nothing can go bad in this flow, or the user will be prevented a purchase, and bad things will happen, in the worst case leading to a missed sale.

Your role

Your role, as Shop owner, into this, is to make sure you carefully followed the docs and filled the information where it was required. Like setting up the correct Stripe / PayPal account information. And make sure you run the site on a good server, in good shape, up to date, etc etc.

My role

My role, as the Plugin developer, is to make sure that every change I ship in a release will never break this flow. Every option, every workflow change, every setting changed should be tested, on all the major browsers and operating systems, to make sure everything will work as expected once shipped. Possibly on different PHP versions, on different Web Servers, on different hosting setups.

It's a pretty hard job, right? Especially working solo, when my already limited resources should be focused on building the product, not testing every single possible thing that might go wrong, on all possible cases I might think of.

So how can this be achieved without duplicating myself? With automated testing.

Automated testing is amazing.

It's like having an army of minions at your fingertips doing what you require, at the time you want.

Enter Codeception.

Codeception is a BDD style testing framework for PHP, and allows Unit Testing and Acceptance testing (and Functional, which is not yet a thing for GravCart) with the same tool.

Used in Grav core already, for Unit Testing, I already baked it in GravCart for Unit Testing, and today I decided to write a base Acceptance Testing automation that will, moving forward, expand to cover all possible scenarios the Plugin will support.

How does it work?

Conceptually it's pretty simple. There is an Actor, which I called FrontendTester, who simulates a customer of yours purchasing a product. One of the existing workflows tested is picking a product, adding it to the cart, and purchasing it using Stripe.

So I created a simple, sample skeleton and added a couple products and entered my Stripe test keys.

And then I instructed my tester to execute some actions. Here's the code:

$I = new FrontendTester($scenario);

$I->am('Customer');
$I->wantTo('buy a product with Stripe');
$I->lookForwardTo('conclude the purchase process');

$I->buyProductWithStripeCheckout();

buyProductWithStripeCheckout is a method which my actor knows how to do as it's stored in its own class. Here's its implementation:

    public function buyProductWithStripeCheckout()
    {
        $I = $this;
        $I->addProductToCart();
        $I->goToCheckout();
        $checkoutPage = new CheckoutPage($I);
        $checkoutPage->fillCheckout();
        $checkoutPage->selectPaymentMethod('Stripe');
        $checkoutPage->proceedToPayment();
        $checkoutPage->waitForStripeCheckoutToShowUp();
        $checkoutPage->fillStripeCheckoutForm();
        $I->checkOrderConfirmed();
    }

It's a very eloquent syntax, which is perfectly readable.

Everyone of those lines is a separate method, implemented using the Acceptance testing syntax offered by Codeception. For example addProductToCart is:

    /**
     * Add a product to the cart
     */
    public function addProductToCart()
    {
        $I = $this;
        $I->amOnPage('/');
        $I->executeJS('ShoppingCart.clearCart()');
        $I->see('Shop');
        $I->see('Geek Toys');
        $I->seeLink('Geek Toys');
        $I->click('Geek Toys');
        $I->see('Anime, Gaming, Movies, Comics, we have all your toys');
        $I->see('Stuffy Turret');
        $I->seeLink('Details');
        $I->click('Details');
        $I->see('Brick Mug');
        $I->seeLink('Add to cart');
        $I->click('.js__shoppingcart__button-add-to-cart');
    }

which again is a pretty simple description of the actions to take on the page.

The nice thing about this kind of testing is that, once properly setup, it will also execute the JavaScript code. This is done via PhantomJS WebDriver, which simplifies things a lot over using Selenium, the alternative platform that offers such possibility.

Once all is setup, I can now type

./vendor/bin/codecept run acceptance --env chrome

and just wait for the minions to do the work and report it to me.

I can also watch them doing every step with

./vendor/bin/codecept run acceptance --env chrome --debug

Or even see every screenshot of the steps by enabling the Codeception\Extension\Recorder Codeception extension.

Taking this to the next level

The next level is testing everything on every browser, on every platform. There are various services that allow this, I chose Open Sauce from Sauce Labs, because they offer an unlimited free tier for Open Source projects.

I have setup a couple platforms in my configuration, with

env:                   
    chrome_win:
         modules:
            config:
                WebDriver:
                    browser: 'chrome'
                    capabilities:
                        platform: 'Windows 10'

    firefox_win:
         modules:
            config:
                WebDriver:
                    browser: 'firefox'
                    capabilities:
                        platform: 'Windows 10'

    edge_win10:
         modules:
            config:
                WebDriver:
                    browser: 'MicrosoftEdge'
                    capabilities:
                        platform: 'Windows 10'

so I can test browsers on Windows, by running ./vendor/bin/codecept run acceptance --env edge_win10 --debug I can quickly check if my scenario works in Edge, without even having a computer that can run it.

Wrapping up

I can only imagine the amount of time this will save me while developing, and also the amount of bugs which this toolset will allow me to find before reaching your sites.

Win-win!

Released 1.1.0-beta.1

Today I published the pre-release versions for Shopping Cart core and addons. Some pretty new stuff I've worked on in the last 2 months.

They will appear soon in the Testing channel of Grav 1.1.

Let's see a summary of the most important changes.

New Core Features

  • Ability to configure the plugin options via the Admin interface
  • Ability to edit the product through the Admin Pages view. Can currently set the price and set the default picture from the page media
  • Added more events to support more addons features.
  • Added the ability to show the cart widget on pages not directly managed by the plugin (e.g. default pages, blog or any other page).

Improvements

  • Changed structure of the Twig files: shoppingcart has been removed in favor of shoppingcart_categories, shoppingcart_section is now shoppingcart_categories, shoppingcart_category is now shoppingcart_products. shoppingcart_product is the product page. Please rename your markdown pages accordingly. There's backwards compatibility, so at this point things will continue to work fine, but the old filenames are deprecated and will probably removed the future.

Of course there were also bug fixes, various code improvements and minor changes here and there. Those interested in the small details can have a look at the GitHub commits, as always.

New Addons

Working with version 1.1 of Grav Shopping Cart are some new free addons that i now "soft launch", although they will take some days to be in the GPM, you can now install them manually:

Manual Checkout

Also called "Offline payments". This gateway is useful for processing check or direct debit payments. It simply authorizes every payment.

Very useful for easy and fast testing, as there's no need to "fake" a purchase using Stripe / PayPal sandboxes. Still, be sure to test those too when testing that their specific integration works.

Email Notifications

Enables email notifications triggered when the order is confirmed. See usage instructions.

Product Variations

This addon adds an option in the Admin panel to have variations on your products. The variation options will be shown in the product page. You can set an option to be required, or optional.

There's still an issue, the "Variations" option is added to all pages currently, will be solved for its final release.

Create a theme for a Grav site

I’ve redesigned the Grav Shopping Cart website. Hope you like it!

The old site had a nice theme for a landing page, but was not thought for a bigger site. I have now purchased a new HTML theme (I’m no designer!) which is pretty nice and has many designs for inner site sections, such a the blog and the help desk. Very clean, has my favourite colors and I’m happy about it.

I’d like to talk a bit how the process was.

Moving an existing HTML theme to Grav is a straightforward process for me, I’ve done it dozens of times now and I have a quite fixed flow.

I like to work at a file level instead of using the Admin, but the same concepts apply.

At first I just wanted to include the theme’s pages as they are. No custom content, just replicate the HTML theme, but within a Grav structure.

I picked the page I want to start with, the home page.

The home page

I created a templates/home.html.twig Twig template inside the theme’s templates folder. This represents a template specific for the home page. Usually the home is a pretty unique page on the site, so it probably deserves a dedicated Twig.

I copied the whole home page code there, from <html> to </html>.

I created a pages/01.home/home.md file, and in config/system.yaml I set home as home.alias. This tells Grav to load that page as the home.

Now loading the page in the browser showed up the content, but it was missing all CSS/JS and images. That’s because the theme had them hardcoded as /img/* or /css/* links.

Adding the correct assets links

In Grav the links are broken because they point to the home route, so instead of pointing to /user/themes/mytheme/img, they point to /img in the Grav root. I could have moved that folder to the Grav root, but it’s best to keep all theme related stuff under the theme.

So I searched within the page for assets and I changed the images references from img/logo.png to <img src="{{ url('theme://img/logo.png', true) }}" />.

Stylesheets require a bit more thought as there’s an asset pipeline we’ll want to enable at some point, so I’ll move them to a stylesheets block within the <head> tag.

    {% block stylesheets %}
        {% do assets.addCss('theme://css/styles.min.css', 100) %}
    {% endblock %}
    {{ assets.css() }}

Same applies to JavaScript files, with the additional requirement that some JS is loaded in the footer.

    {% block javascripts %}
        {% do assets.addJs('theme://js/vendor/modernizr.custom.js') %}
        {% do assets.addJs('jquery',101) %}

        {% do assets.addJs('theme://js/vendor/preloader.min.js', {group: 'bottom'}) %}
        {% do assets.addJs('theme://js/scripts.js', {group: 'bottom'}) %}
    {% endblock %}
    {{ assets.js() }}

And in the footer, just before the closing </body> tag:

    {{ assets.js('bottom') }}

Fine for assets!

The page show now show up perfectly fine.

Adding another page

Now I wanted to add another page, the Blog page. I repeated the process to add a templates/blog.html.twig file, pasted the HTML source, and created a pages/02.blog/blog.md page.

Now, while images links inside the pages still need to be migrated to Grav's assets syntax (or simply change the path) I didn't want to repeat the same work I did above for CSS and JS assets.

So I identified the common parts of the pages (header and footer), and moved them to the templates/partials/base.html.twig file.

Each page template then needs to extend partials/base.html.twig and just add their unique content.

Going on from here

Many more tasks await us now:

  • Tweaking long pages in modular pages
  • Avoid repeating blocks of code, using the page frontmatter to store values in arrays and loop over them in the Twig templates
  • Adding blueprints to the theme to edit those modular pages, if I later want to easily change them through the Admin panel
  • Organize images in a common /pages/images folder, for easier handling

and much more. Just started!