Commerce v1 Upgrades v1.0

Most upgrades are automatically applied, but sometimes you may need to update custom templates, or status configuration, to take advantage of new features or improvements. Those changes are discussed here.

The most notable changes in v1.0 revolve around the way product pricing and currencies are handled. This has resulted in some breaking changes, detailed below.

Pricing Changes

The biggest change in Commerce 1.0 compared to v0.12 is how pricing and currencies are handled.

The short version is that the price field (and related getPrice() method) on the products table is deprecated, and instead a more complex and powerful pricing pattern based on Price Types is stored in a new pricing field (with related getPricing($currency) method).

There is a lot to say about the new pricing and price types, which we've done here.

While we've tried to keep as much backwards compatibility, that's near impossible with such a massive change. In particular custom product types will likely need an update.

Pricing on Resource Products [rc3]

You should update your Resource Product implementation to use the new Pricing TV instead of a simple number field for the price. This gives you the ability to set multi-currency prices as well as the different price types introduced in 1.0.

If you don't update your Resource product implementation, the product price may no longer respond to changes.

See the updated Resource Product documentation on how to use the Pricing TV.

New/updated placeholders

When fetching a product with the get_product(s) snippets, or in another template that has access to full products, you can now choose from a variety of new/updated placeholders.

(While this list uses the MODX placeholder syntax, e.g. [[+price_formatted]], the same keys are available in twig syntax, e.g. {{ product.price_formatted }}.)

  • [[+price]] and [[+price_formatted]] contain the current lowest price in the active currency. This may be either the regular price or the price provided by a price type (e.g. sale). Make note that this does not necessarily evaluate all possible price types, as it is only based on a single quantity item and certain price types may require more information (e.g. the actual quantity or user details).
  • [[+regular_price]] and [[+regular_price_formatted]] contain the normal price for the active currency. This is the price entered as default, without evaluation additional price types.
  • [[+price_valid_until]] contains a unix timestamp with the date until a sale is valid, if such an expiration date is provided by the price type. You can use this to show a sale is "Valid until" a certain date. Use the date output filter to turn this into a human-readable date.
  • [[+price_legacy]] and [[+price_legacy_formatted]] placeholders are temporarily available which hold the value of the products' price field. You should not rely on these (they will be removed in v1.3), but if you encounter a problem with the new pricing and need the old price; these placeholders can be temporarily used. Reach out to support if you use this to get help in moving to the new pricing.

We haven't yet added a way to display quantity-based prices on a product page yet; expect that to become available by mid-end April.

The price on existing products

When product pricing is loaded for a product that does not yet have any pricing information in the new system, it will take the old price value (through the getPrice method), and use that as the "regular" price. The old getPrice method was not currency aware (at least not fully implemented); so if you enable multiple currencies the integer price is used for each currency (e.g. a price of 1500 will be set as both €15 and $15).

There is a chance for custom product types, as well as the resource product type, that this pricing information gets saved; either by the core or a user interaction. When that happens, the getPrice method will no longer be called when loading the pricing. This means that such products would do best to upgrade their implementation to make use of the price types (even if that means only supporting the regular price and not custom types).

Developers can learn more about the pricing internals here. The task can be summarised as defining the getRawPricing and setRawPricing protected methods to return an array of currencyCode => serializedProductPricingInterface. Please contact support if you need assistance with incorporating new pricing into your ucstom product..

For the resource product we intend to ship a custom TV type that brings support for all price types into the resource product, likely mid-end April.

Only show tax details when taxes != 0 (rc3)

In the frontend/checkout/partial/summary.twig template, we've tweaked the place of the tax details to make sure they're only shown when the total tax is not equal to 0. This prevents issues where no tax rates are applied (resulting in an empty tax name for €0 being shown).

To apply this to custom templates, edit frontend/checkout/partial/summary.twig and around line 95 move this code block:

{% for rate in tax_rates %}
    <tr class="c-cart-summary-totals-taxes-breakdown">
        <td class="c-cart-summary-totals-label">{{ rate.name}} ({{ rate.percentage_formatted }} {% if rate.is_inclusive %} of {% else %} over {% endif %}{{ rate.total_taxed_amount_formatted }})</td>
        <td class="c-cart-summary-totals-value">{{ rate.total_tax_amount_formatted }}</td>
    </tr>
{% endfor %}

up one line so it's included before the {% endif %} belonging to {% if order.tax != 0 %} (started on line 81).

[DEV] setFieldValue<Name> taking precedence [rc3]

The FormWidget in the dashboard lets you define setFieldValue<Name> methods on the model that will be used to set a value. (E.g. for a field named selector, it would look for setFieldValueSelector) Previously, if the field existed in the model, the FormWidget would simple call the set function and your method was not called. As of 1.0.0-rc3, if the method is defined it will take precedence instead.

[DEV] CountryField.getCountries signature change

The getCountries method on the CountryField no longer requires you to provide the Commerce class to it.

[DEV] Grids now escape all columns by default

This only affects developers of custom modules.

We're now automatically escaping all grid columns by default. This avoids any XSS vulnerabilities from making their way into the dashboard.

To allow HTML in columns (e.g. to enable links), add 'raw' => true to your array-style column definition, or use the new way of defining Columns with the raw parameter set to true.

When you've marked a column as raw, it is up to you to make sure any contents in it is properly escaped. A new utility $this->encode($value) is now available to grid widgets to help.

[DEV] Country dependency introduced breaking change

This only affects developers of custom modules.

The CommerceGuys\Intl\Country\CountryRepository utility that has been available for a while has been updated, which suffered breaking changes. That data has moved to a different package, CommerceGuys\Addressing\Country\CountryRepository, which luckily does have the same signatures.

To prevent such breakage in the future, we've implemented a new Countries utility that gives you the same functionality but in a native package that restricts your usage to native classes.