Ticket 1540800: “I want the Flake Ice collection and the PPM collection to show first on the list of products. Can I add something to the shortcode for this?”

Short answer

Not out of the box. A SureCart product list can only be sorted by newest, name or price. There is no setting, filter or shortcode option that puts one collection ahead of another. So I wrote a small snippet that adds one: a show_first setting for the shortcode. Everything on this page is running live on this test store.

See it working

On the After page try the Sort dropdown and the search box: the chosen collections stay on top and the sort is applied inside each group.

The shortcode (your ids)

Add show_first="..." with the ids of the collections you want on top, in the order you want them. Replace the two placeholders with the ids of Flake Ice and PPM. You find a collection id in the address bar when you open it under SureCart > Products > Collections: ...page=sc-product-collections&action=edit&id=THIS-PART. Same rules as before: everything inside quotes, commas only, no spaces, one line.

[sc_product_collection collection_id="d8766f4b-4556-4931-93e5-26b5e3903c53,faaf2492-b4d9-4b69-9ea9-a2847f889693,16c1fd81-d950-49e1-891e-c796769172d9,0ecfdfd5-f2c8-4d9a-9914-a4cf7a9cf140" show_first="FLAKE-ICE-COLLECTION-ID,PPM-COLLECTION-ID" columns="4" sort_enabled="1" search_enabled="1" pagination_enabled="0" limit="50"]

Collection slugs work too, so show_first="flake-ice,ppm" is fine if you prefer. Flake Ice and PPM do not have to be in collection_id for this to work, but they do on your page, which is what you want.

This is the exact shortcode running on the After page above:

[sc_product_collection collection_id="a11da7fc-1621-44b7-a68d-e667edd1be8e,211dfe18-7780-4a1b-a24a-a31a8ee509d4,bd7b05de-182a-4519-a0d1-ece8df84950c,0ac73272-5751-49cd-9077-09c7eb4604b0" show_first="0ac73272-5751-49cd-9077-09c7eb4604b0,bd7b05de-182a-4519-a0d1-ece8df84950c" columns="4" sort_enabled="1" search_enabled="1" pagination_enabled="1" ajax_pagination="1" limit="50"]

Install (about two minutes)

  1. Open the snippets plugin you installed last time (WPCode, Code Snippets or WPCodeBox), or your child theme’s functions.php.
  2. Add a new PHP snippet, paste the code below, set it to run everywhere, and activate it.
  3. Add show_first="..." to your shortcode and save the page.
  4. If nothing changes, clear your site cache (and Cloudflare if you use it) and reload.

The snippet

<?php
/**
 * SureCart: show chosen collections first in a Product List.
 *
 * Adds a `show_first` setting to the [sc_product_collection] and [sc_product_list] shortcodes:
 *
 *   [sc_product_collection collection_id="A,B,C,D" show_first="A,B" columns="4" limit="50"]
 *
 * Products in collection A come first, then products in B, then everything else in the list.
 * The order you write them in is the order the groups appear in. Inside each group the normal
 * sorting still applies (newest first by default). Search, the Collections filter, the Sort
 * dropdown and pagination keep working.
 *
 * `show_first` accepts collection ids (the id in the address bar when you open a collection
 * under SureCart > Products > Collections) or collection slugs. Mix them if you like.
 *
 * Install: add as a PHP snippet (WPCode, Code Snippets, WPCodeBox) or in your child theme's
 * functions.php. Custom code, not an official part of SureCart. Test on staging or take a backup first.
 */

/**
 * KEEP_WHEN_SORTED
 * true  = the chosen collections stay on top even when a shopper picks something in the Sort dropdown
 *         (the sort is applied inside each group).
 * false = as soon as a shopper picks a Sort option, the whole list follows that sort and the
 *         "show first" grouping is dropped until they clear it.
 */
if ( ! defined( 'SCSF_KEEP_WHEN_SORTED' ) ) {
	define( 'SCSF_KEEP_WHEN_SORTED', true );
}

/**
 * Optional. For a Product List that is built with blocks (no shortcode), list the page here:
 *   'page-slug' => 'collection-id-or-slug,another-one'
 * The page slug is the last part of the page address, e.g. 'shop' for https://example.com/shop/.
 */
function scsf_page_rules() {
	return array(
		// 'shop' => 'flake-ice,ppm',
	);
}

/**
 * Remember which collections should come first for the list being rendered right now.
 */
function scsf_state( $set = null ) {
	static $current = null;
	if ( func_num_args() ) {
		$current = $set;
	}
	return $current;
}

/**
 * Turn "id,id,slug" into an ordered list of WordPress term_taxonomy_ids for the sc_collection taxonomy.
 */
function scsf_resolve( $raw ) {
	static $cache = array();
	$raw = trim( (string) $raw );
	if ( '' === $raw ) {
		return array();
	}
	if ( isset( $cache[ $raw ] ) ) {
		return $cache[ $raw ];
	}
	$out = array();
	foreach ( array_filter( array_map( 'trim', explode( ',', $raw ) ) ) as $token ) {
		$term = null;
		if ( preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $token ) ) {
			// A SureCart collection id: find the WordPress term that mirrors it.
			$found = get_terms(
				array(
					'taxonomy'   => 'sc_collection',
					'hide_empty' => false,
					'number'     => 1,
					'meta_key'   => 'sc_id',
					'meta_value' => $token,
				)
			);
			$term  = ( ! is_wp_error( $found ) && ! empty( $found ) ) ? $found[0] : null;
		} else {
			$term = get_term_by( 'slug', sanitize_title( $token ), 'sc_collection' );
		}
		if ( $term && ! is_wp_error( $term ) ) {
			$out[] = (int) $term->term_taxonomy_id;
		}
	}
	$cache[ $raw ] = array_values( array_unique( $out ) );
	return $cache[ $raw ];
}

/**
 * 1. When a Product List starts rendering, read its show_first setting (shortcode attribute or page rule).
 */
add_filter(
	'render_block_data',
	function ( $parsed_block ) {
		if ( 'surecart/product-list' !== ( $parsed_block['blockName'] ?? '' ) ) {
			return $parsed_block;
		}
		$raw = $parsed_block['attrs']['show_first'] ?? '';
		if ( '' === $raw && is_singular() ) {
			$rules = scsf_page_rules();
			$slug  = get_post_field( 'post_name', get_queried_object_id() );
			$raw   = $rules[ $slug ] ?? '';
		}
		scsf_state( scsf_resolve( $raw ) );
		return $parsed_block;
	}
);

/**
 * 2. When that Product List has finished rendering, forget the setting so other lists are untouched.
 */
add_filter(
	'render_block_surecart/product-list',
	function ( $content ) {
		scsf_state( null );
		return $content;
	}
);

/**
 * 3. Put the chosen collections first in the product query. Everything else about the query is untouched.
 */
add_filter(
	'posts_orderby',
	function ( $orderby, $query ) {
		global $wpdb;
		$tt_ids = scsf_state();
		if ( empty( $tt_ids ) || 'sc_product' !== $query->get( 'post_type' ) ) {
			return $orderby;
		}
		if ( ! SCSF_KEEP_WHEN_SORTED ) {
			// A shopper picked something in the Sort dropdown: let that sort win.
			foreach ( array_keys( $_GET ) as $key ) { // phpcs:ignore WordPress.Security.NonceVerification
				if ( preg_match( '/(^|-)orderby$/', (string) $key ) ) {
					return $orderby;
				}
			}
		}
		$when = '';
		foreach ( array_values( $tt_ids ) as $rank => $tt_id ) {
			$when .= sprintf(
				' WHEN EXISTS (SELECT 1 FROM %s scsf WHERE scsf.object_id = %s.ID AND scsf.term_taxonomy_id = %d) THEN %d',
				$wpdb->term_relationships,
				$wpdb->posts,
				(int) $tt_id,
				(int) $rank
			);
		}
		$rank_sql = 'CASE' . $when . ' ELSE ' . count( $tt_ids ) . ' END';
		return $rank_sql . ( $orderby ? ', ' . $orderby : '' );
	},
	10,
	2
);

Good to know

  • Order of the ids = order of the groups. Put Flake Ice first if it should be above PPM.
  • A product that is in both chosen collections lands in the first group it matches.
  • Inside each group the normal sorting applies: newest first by default, or whatever the shopper picks in the Sort dropdown. If you would rather have the Sort dropdown override the grouping completely, change SCSF_KEEP_WHEN_SORTED from true to false at the top of the snippet.
  • It only touches lists that have show_first (or a page rule). Every other product list on the site is left alone.
  • For a list built with the Product List block instead of a shortcode, add the page slug to scsf_page_rules() in the snippet, like the commented example.
  • Custom code, not an official part of SureCart. Add it on a staging copy first or take a backup.

Side note: pagination under a collection shortcode

While testing I noticed that the page numbers under an [sc_product_collection] shortcode are counted from your whole catalogue, not from the collections in the list. With 27 products in the list, a limit of 50 and 100+ products in the store, you may see page links 2 and 3 that lead to empty pages. Everything fits on one page anyway, so the clean fix for now is pagination_enabled="0", which is already in the shortcode above. I have reported the counting issue to the team.

Scroll to Top