Skip to content

Catalog Models ​

This reference documents all Twig-accessible properties and methods for catalog organization models: categories, manufacturers, and custom groups. These models are typically accessed via the Catalog component.

Category ​

Categories organize products into a browsable hierarchy with unlimited nesting depth. The category tree powers navigation menus, product listing pages, and breadcrumb trails.

Core Properties ​

PropertyTypeDescription
idintPrimary key
namestringCategory name
slugstringURL slug for this category
fullslugstringFull URL path including parents (e.g., electronics/phones)
baseidstringBase identifier
codestringCategory code for programmatic reference
titlestringSEO/page title (falls back to name)
descriptionstringFull description (HTML)
short_descriptionstringBrief summary
is_hiddenboolWhether hidden from frontend
custom_pagestring|nullCustom CMS page override (pagefinder reference)
sort_orderintDisplay order
parent_idint|nullParent category ID
created_atCarbonCreation date
updated_atCarbonLast update date

Relationships ​

PropertyTypeDescription
productsCollection<Product>Products in this category
parentCategory|nullParent category
childrenCollection<Category>Direct child categories
imagesCollection<File>Category images

Methods ​

MethodReturnsDescription
pageUrl($pageName)stringCMS page URL for this category (respects custom_page override)
countProducts()intNumber of products in this category
listProducts($options)PaginatorPaginated product listing
availableFilters()arrayProduct filter facets available for this category (see Catalog Filtering)
priceRange()array{min, max} base values (cents) for products in this category
getBreadcrumbPath()array|nullParent chain for breadcrumbs
getParents()arrayAll ancestor categories

Scopes ​

ScopeDescription
applyVisibleFilters out hidden categories

listProducts Options ​

The listProducts() method is a convenience proxy for Product::listFrontEnd() with this category pre-filled. It accepts the same options:

OptionTypeDefaultDescription
pageint1Page number
perPageint30Products per page
sortstringcreated_at descSort field with direction (e.g., name asc, price desc, random)
searchstring''Search term
manufacturersarray|nullnullManufacturer IDs to filter by
ratingsarray|nullnullStar ratings to filter by (e.g., [4, 5])
priceMinint|nullnullMinimum price in base value (cents)
priceMaxint|nullnullMaximum price in base value (cents)
filtersarray|nullnullProduct filter facets as {code: [values]} (e.g., {color: ['Red']})

Multiple values within a filter facet use OR logic; different facets use AND logic. Visibility and site filtering are applied automatically.

priceRange ​

Returns the minimum and maximum product price within the category as base values (cents). Useful for initializing a price filter slider.

twig
{% set priceRange = category.priceRange() %}
{# priceRange.min = 999 (i.e., $9.99) #}
{# priceRange.max = 29999 (i.e., $299.99) #}

availableFilters ​

Returns an array of product filter facets available for this category. Each facet contains a code, name, type (control type: checkbox, dropdown, radio, or color), and values array. Only filter definitions marked as filterable and with matching products in this category are included. Results are scoped to the current locale.

twig
{% set facets = category.availableFilters() %}
{# [
#     { code: 'color', name: 'Color', type: 'color', values: [
#         { value: 'Red', count: 5 },
#         { value: 'Blue', count: 3 }
#     ]},
#     { code: 'size', name: 'Size', type: 'dropdown', values: [
#         { value: 'Large', count: 4 },
#         { value: 'Small', count: 2 }
#     ]}
# ] #}

See Catalog Filtering for the full sidebar implementation.

Complete Examples ​

Category Navigation ​

twig
{# Multi-level category menu #}
{% set categories = catalog.allCategories() %}
<nav>
    <ul>
        {% for category in categories %}
            <li>
                <a href="{{ category.pageUrl('shop/category') }}">
                    {{ category.name }}
                    <span class="count">({{ category.countProducts() }})</span>
                </a>
                {% if category.children is not empty %}
                    <ul>
                        {% for child in category.children %}
                            <li>
                                <a href="{{ child.pageUrl('shop/category') }}">
                                    {{ child.name }}
                                </a>
                            </li>
                        {% endfor %}
                    </ul>
                {% endif %}
            </li>
        {% endfor %}
    </ul>
</nav>

Category Page with Product Listing ​

twig
<h1>{{ category.name }}</h1>

{% if category.description %}
    <div class="category-description">
        {{ category.description|raw }}
    </div>
{% endif %}

{# Subcategories #}
{% if category.children is not empty %}
    <div class="subcategories">
        {% for child in category.children %}
            <a href="{{ child.pageUrl('shop/category') }}">
                {% set image = child.images.first() %}
                {% if image %}
                    <img src="{{ image.path|resize(200, 200) }}" alt="{{ child.name }}" />
                {% endif %}
                <span>{{ child.name }}</span>
            </a>
        {% endfor %}
    </div>
{% endif %}

{# Products with sorting #}
{% set products = category.listProducts({
    page: pagination,
    perPage: 12,
    sort: 'price asc'
}) %}

<div class="product-grid">
    {% for product in products %}
        {% partial 'shop/product-card' product=product %}
    {% endfor %}
</div>

{# Pagination #}
{{ products.render()|raw }}
twig
{% set breadcrumbs = category.getBreadcrumbPath() %}
{% if breadcrumbs %}
    <nav aria-label="breadcrumb">
        <ol class="breadcrumb">
            <li><a href="{{ 'shop'|page }}">Shop</a></li>
            {% for crumb in breadcrumbs %}
                <li>
                    <a href="{{ crumb.pageUrl('shop/category') }}">
                        {{ crumb.name }}
                    </a>
                </li>
            {% endfor %}
            <li class="active">{{ category.name }}</li>
        </ol>
    </nav>
{% endif %}

Custom Category Page ​

Individual categories can override the default category page by setting the custom_page field (a pagefinder reference) in the backend. When set, pageUrl() automatically resolves to the custom CMS page: no theme template changes needed.

The custom CMS page must include the [catalog] component with lookup = "category" and a matching URL pattern:

ini
url = "/shop/category-landing/:fullslug*?/:baseid?"

[catalog]
lookup = "category"
identifier = "baseid"

TIP

Theme templates that call category.pageUrl('shop/category') will automatically use the custom page when one is set. The override is transparent.


Manufacturer ​

Manufacturers represent brands or companies that produce products. Each product can belong to one manufacturer.

Core Properties ​

PropertyTypeDescription
idintPrimary key
namestringManufacturer name
slugstringURL slug
baseidstringBase identifier
descriptionstringDescription (HTML)
is_hiddenboolWhether hidden from frontend
created_atCarbonCreation date
updated_atCarbonLast update date

Contact Properties ​

PropertyTypeDescription
address_line1stringStreet address
citystringCity
zipstringPostal/ZIP code
emailstringContact email
website_urlstringWebsite URL
phonestringPhone number
faxstringFax number

Relationships ​

PropertyTypeDescription
productsCollection<Product>Products by this manufacturer
logoFile|nullManufacturer logo image
countryCountry|nullCountry (via RainLab.Location)
stateState|nullState/province (via RainLab.Location)

Accessing Manufacturers ​

The catalog component supports manufacturer lookup:

ini
[catalog]
lookup = "manufacturer"
identifier = "slug"

This sets the manufacturer page variable, giving you access to all manufacturer properties and its products.

Complete Examples ​

Manufacturer Page ​

twig
title = "Manufacturer"
url = "/shop/manufacturer/:slug"

[catalog]
lookup = "manufacturer"
identifier = "slug"
==
{% if not manufacturer %}
    {% do abort(404) %}
{% endif %}

<div class="manufacturer-detail">
    {% if manufacturer.logo %}
        <img src="{{ manufacturer.logo.path|resize(300, 200) }}"
             alt="{{ manufacturer.name }}" />
    {% endif %}

    <h1>{{ manufacturer.name }}</h1>

    {% if manufacturer.description %}
        <div>{{ manufacturer.description|raw }}</div>
    {% endif %}

    {% if manufacturer.website_url %}
        <a href="{{ manufacturer.website_url }}" target="_blank">
            Visit Website
        </a>
    {% endif %}

    <h2>Products by {{ manufacturer.name }}</h2>
    <div class="product-grid">
        {% for product in manufacturer.products %}
            {% partial 'shop/product-card' product=product %}
        {% endfor %}
    </div>
</div>

Manufacturer Info on Product Page ​

twig
{% if product.manufacturer %}
    <div class="manufacturer-info">
        {% if product.manufacturer.logo %}
            <img src="{{ product.manufacturer.logo.path|resize(100, 60) }}"
                 alt="{{ product.manufacturer.name }}" />
        {% endif %}
        <a href="{{ 'shop/manufacturer'|page({ slug: product.manufacturer.slug }) }}">
            {{ product.manufacturer.name }}
        </a>
    </div>
{% endif %}

Manufacturer List ​

twig
[catalog]
==
{% set manufacturers = catalog.customGroupQuery() %}
{# Or query directly: #}

<h1>Our Brands</h1>
<div class="brand-grid">
    {% for manufacturer in manufacturers %}
        <a href="{{ 'shop/manufacturer'|page({ slug: manufacturer.slug }) }}">
            {% if manufacturer.logo %}
                <img src="{{ manufacturer.logo.path|resize(200, 120) }}"
                     alt="{{ manufacturer.name }}" />
            {% endif %}
            <span>{{ manufacturer.name }}</span>
        </a>
    {% endfor %}
</div>

CustomGroup ​

Custom groups are curated, flat collections of products used for specific sections of your site: featured products, new arrivals, best sellers, etc. Products within a group can be manually sorted.

Core Properties ​

PropertyTypeDescription
idintPrimary key
namestringGroup name
codestringUnique code for template lookup
created_atCarbonCreation date
updated_atCarbonLast update date

Relationships ​

PropertyTypeDescription
productsCollection<Product>Products in this group (sorted by sort_order)

Accessing Custom Groups ​

Use the catalog component's findCustomGroup() method to look up a group by its code:

twig
{% set group = catalog.findCustomGroup('featured-products') %}

Or use customGroupQuery() to query all groups:

twig
{% set groups = catalog.customGroupQuery().get() %}

Complete Examples ​

twig
{% set featured = catalog.findCustomGroup('featured-products') %}
{% if featured and featured.products is not empty %}
    <section class="featured-products">
        <h2>Featured Products</h2>
        <div class="product-grid">
            {% for product in featured.products %}
                {% partial 'shop/product-card' product=product %}
            {% endfor %}
        </div>
    </section>
{% endif %}

Multiple Groups on Homepage ​

twig
{% set featured = catalog.findCustomGroup('featured') %}
{% set newArrivals = catalog.findCustomGroup('new-arrivals') %}
{% set bestSellers = catalog.findCustomGroup('best-sellers') %}

{% for group in [featured, newArrivals, bestSellers] %}
    {% if group and group.products is not empty %}
        <section>
            <h2>{{ group.name }}</h2>
            <div class="product-grid">
                {% for product in group.products %}
                    {% partial 'shop/product-card' product=product %}
                {% endfor %}
            </div>
        </section>
    {% endif %}
{% endfor %}

Commerce Theme Custom Group Partial ​

The commerce theme includes a reusable shop/custom-group partial:

twig
{% partial 'shop/custom-group' groupCode='featured-products' %}

This partial looks up the group by code and renders a product grid. If the group doesn't exist, it displays a message prompting you to create it in the backend.


Querying Products ​

Both categories and custom groups give you access to product collections, but for more advanced queries, use the catalog component's query builders.

Product Query Builder ​

twig
{# Custom product query #}
{% set products = catalog.productQuery()
    .applyVisible()
    .where('is_on_sale', true)
    .orderBy('price', 'asc')
    .limit(8)
    .get() %}

{% for product in products %}
    {% partial 'shop/product-card' product=product %}
{% endfor %}

Category Query Builder ​

twig
{# All top-level categories #}
{% set topLevel = catalog.categoryQuery()
    .applyVisible()
    .whereNull('parent_id')
    .orderBy('sort_order')
    .get() %}

Custom Group Query Builder ​

twig
{# All custom groups #}
{% set groups = catalog.customGroupQuery().get() %}
{% for group in groups %}
    <h3>{{ group.name }} ({{ group.products.count() }} products)</h3>
{% endfor %}