Manually Sorting Commerce Products

Craft Commerce adds powerful products elements that don’t natively support manual drag-and-drop sorting like Craft’s structure entries or categories.

You can, however, create a custom field that allows store managers to hand sort products.

Add a Commerce Products field to a Global Set #

1. Create a Commerce Products field. #

Navigate to SettingsFields and press New field to create a new field, selecting Commerce Products for its Field Type.

The name, handle, and sources can be whatever you’d like, just remember the handle—orderedProducts here—because we’ll need it again in a moment.

When you’re finished, press Save.

Ordered Products Field

2. Add your field to a Global Set. #

Next, we’ll add our newly-created field to a Global Set. You can add the field to an existing Global Set if you’d like, but we’ll create a new one called “Store Customization”.

Navigate to SettingsGlobals and press New global set, choosing a name and handle and dragging the field from step #1 into the Field Layout for the Global Set.

Press Save.

Store Customization Global Set

Now you can navigate to Globals and see this new field in the Global Set we just created.

Press Add a product and select all of your products to that field.

Store Customization Global Set Fields

You can then drag the products into whatever order you’d like and press Save.

3. Use your field in templates. #

Anywhere you’d like to use this custom sort on the front end, replace craft.products() queries with your custom Global Set handle and field handle:

{# Fetch products from our custom field #}
{% set products = storeCustomization.orderedProducts.all() %}

<ol>
{% for product in products %}
    <li><a href="{{ product.url }}">{{ product.title }}</a></li>
{% endfor %}
</ol>

Once you’ve fetched the products from this custom field, they’ll be returned in exactly the order that’s set in the control panel.

Automatically Add New Products #

If we stopped here, store managers would still need to update this field to add each newly-created product.

We can use a custom module to listen for new Commerce products and append them to that field automatically.

See Using Events in a Custom Module for an example of setting up a custom module for the first time.

In your module’s init() method, add the following code to listen for Product::EVENT_AFTER_SAVE and update the Global Set field with a newly-saved product:

use craft\commerce\elements\Product;
use craft\events\ModelEvent;
use yii\base\Event;
use Craft;

Event::on(
    Product::class,
    Product::EVENT_AFTER_SAVE,
    static function(ModelEvent $event) {
        $globalSet = Craft::$app
            ->getGlobals()
            ->getSetByHandle('storeCustomization');

        // Make sure our Global Set exists and we have a new product
        if ($globalSet && $event->isNew) {
            // Get “Ordered Products” field’s existing product IDs
            $productIds = $globalSet->orderedProducts->ids();
            // Append the new product ID
            $productIds[] = $event->sender->id;
            // Set “Ordered Products” field value
            $globalSet->setFieldValue('orderedProducts', $productIds);
            // Save “Ordered Products” field with its updated values
            Craft::$app->getElements()->saveElement($globalSet);
        }
    }
);

Navigate to CommerceProducts and press New product. Enter required fields and press Save.

That new product should now be added to the product list under GlobalsStore CustomizationOrdered Products, ready to be sorted into whatever position you’d prefer.

Deleted products will automatically be removed from the custom field.