Skip to content
  1. Extras
  2. MiniShop3
  3. Development
  4. Events
  5. Product

Product events (catalog) ​

Modifying product price, weight and fields on catalog output.

Feature

These events support a plugin chain — each plugin can read the previous result from $modx->eventData and pass its own result to the next one.

msOnGetProductPrice ​

Fired when getting product price. Lets you modify the price on the fly.

Parameters ​

ParameterTypeDescription
msProductDatamsProductDataProduct data object
dataarrayExtra product data
pricefloatCurrent price

Modifying data (with chain support) ​

php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductPrice':
        $price = $modx->eventData['msOnGetProductPrice']['price']
            ?? $scriptProperties['price'];
        $data = $scriptProperties['data'];

        if ($data['parent'] == 5) {
            $price = $price * 0.85;
        }

        $modx->eventData['msOnGetProductPrice']['price'] = $price;

        $values = &$modx->event->returnedValues;
        $values['price'] = $price;
        break;
}

Personal prices ​

php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductPrice':
        $price = $modx->eventData['msOnGetProductPrice']['price']
            ?? $scriptProperties['price'];
        $productData = $scriptProperties['msProductData'];

        if ($modx->user->isMember('VIP')) {
            $price = $price * 0.8; // -20%
        }

        if ($modx->user->isMember('Wholesale')) {
            $wholesalePrice = $productData->get('wholesale_price');
            if ($wholesalePrice > 0) {
                $price = $wholesalePrice;
            }
        }

        $modx->eventData['msOnGetProductPrice']['price'] = $price;
        $values = &$modx->event->returnedValues;
        $values['price'] = $price;
        break;
}

Dynamic markup by currency rate ​

php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductPrice':
        $price = $modx->eventData['msOnGetProductPrice']['price']
            ?? $scriptProperties['price'];

        $rate = $modx->cacheManager->get('usd_rate') ?? 90;

        $productData = $scriptProperties['msProductData'];
        if ($productData->get('currency') === 'USD') {
            $price = $price * $rate;
        }

        $modx->eventData['msOnGetProductPrice']['price'] = $price;
        $values = &$modx->event->returnedValues;
        $values['price'] = round($price, 2);
        break;
}

msOnGetProductWeight ​

Fired when getting product weight. Lets you modify weight on the fly.

Parameters ​

ParameterTypeDescription
msProductDatamsProductDataProduct data object
dataarrayExtra product data
weightfloatCurrent weight

Modifying data ​

php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductWeight':
        $weight = $modx->eventData['msOnGetProductWeight']['weight']
            ?? $scriptProperties['weight'];
        $data = $scriptProperties['data'];

        $packagingWeight = 0.5;
        $weight = $weight + $packagingWeight;

        $modx->eventData['msOnGetProductWeight']['weight'] = $weight;
        $values = &$modx->event->returnedValues;
        $values['weight'] = $weight;
        break;
}

Volumetric weight ​

php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductWeight':
        $weight = $modx->eventData['msOnGetProductWeight']['weight']
            ?? $scriptProperties['weight'];
        $productData = $scriptProperties['msProductData'];

        $length = $productData->get('length') ?? 0;
        $width = $productData->get('width') ?? 0;
        $height = $productData->get('height') ?? 0;

        if ($length > 0 && $width > 0 && $height > 0) {
            $volumeWeight = ($length * $width * $height) / 5000;
            $weight = max($weight, $volumeWeight);
        }

        $modx->eventData['msOnGetProductWeight']['weight'] = $weight;
        $values = &$modx->event->returnedValues;
        $values['weight'] = $weight;
        break;
}

msOnGetProductFields ​

Fired when getting product fields. Lets you add or modify any fields.

Parameters ​

ParameterTypeDescription
msProductDatamsProductDataProduct data object
dataarrayProduct fields array

Modifying data ​

php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductFields':
        $data = $modx->eventData['msOnGetProductFields']['data']
            ?? $scriptProperties['data'];

        $data['discount_percent'] = 0;
        if ($data['old_price'] > 0 && $data['price'] > 0) {
            $data['discount_percent'] = round(
                (($data['old_price'] - $data['price']) / $data['old_price']) * 100
            );
        }

        $stock = $data['stock'] ?? 0;
        if ($stock <= 0) {
            $data['availability'] = 'out_of_stock';
            $data['availability_text'] = 'Out of stock';
        } elseif ($stock < 5) {
            $data['availability'] = 'low_stock';
            $data['availability_text'] = 'Low stock';
        } else {
            $data['availability'] = 'in_stock';
            $data['availability_text'] = 'In stock';
        }

        $modx->eventData['msOnGetProductFields']['data'] = $data;
        $values = &$modx->event->returnedValues;
        $values['data'] = $data;
        break;
}
php
<?php
switch ($modx->event->name) {
    case 'msOnGetProductFields':
        $data = $modx->eventData['msOnGetProductFields']['data']
            ?? $scriptProperties['data'];
        $productData = $scriptProperties['msProductData'];

        $reviewCount = $modx->getCount('msProductReview', [
            'product_id' => $data['id'],
            'published' => 1,
        ]);
        $data['review_count'] = $reviewCount;

        $c = $modx->newQuery('msProductReview');
        $c->where(['product_id' => $data['id'], 'published' => 1]);
        $c->select('AVG(rating) as avg_rating');
        if ($c->prepare() && $c->stmt->execute()) {
            $data['avg_rating'] = round($c->stmt->fetchColumn(), 1);
        }

        $modx->eventData['msOnGetProductFields']['data'] = $data;
        $values = &$modx->event->returnedValues;
        $values['data'] = $data;
        break;
}

Full example: discounts and promotions ​

php
<?php
/**
 * Plugin: Discounts and promotions
 * Events: msOnGetProductPrice, msOnGetProductFields
 */

switch ($modx->event->name) {

    case 'msOnGetProductPrice':
        $price = $modx->eventData['msOnGetProductPrice']['price']
            ?? $scriptProperties['price'];
        $data = $scriptProperties['data'];

        $discounts = [];

        $categoryDiscounts = [
            5 => 10,
            10 => 15,
        ];
        if (isset($categoryDiscounts[$data['parent']])) {
            $discount = $categoryDiscounts[$data['parent']];
            $price = $price * (1 - $discount / 100);
            $discounts['category'] = $discount;
        }

        if ($modx->user->isAuthenticated()) {
            $price = $price * 0.95;
            $discounts['member'] = 5;
        }

        if ($modx->user->isMember('VIP')) {
            $price = $price * 0.9;
            $discounts['vip'] = 10;
        }

        $modx->eventData['discounts'] = $discounts;
        $modx->eventData['msOnGetProductPrice']['price'] = $price;

        $values = &$modx->event->returnedValues;
        $values['price'] = round($price, 2);
        break;

    case 'msOnGetProductFields':
        $data = $modx->eventData['msOnGetProductFields']['data']
            ?? $scriptProperties['data'];

        $discounts = $modx->eventData['discounts'] ?? [];
        if (!empty($discounts)) {
            $data['applied_discounts'] = $discounts;
            $data['total_discount'] = array_sum($discounts);
        }

        $badges = [];
        if ($data['new']) {
            $badges[] = ['type' => 'new', 'text' => 'New'];
        }
        if ($data['popular']) {
            $badges[] = ['type' => 'popular', 'text' => 'Bestseller'];
        }
        if (!empty($data['total_discount'])) {
            $badges[] = [
                'type' => 'sale',
                'text' => '-' . $data['total_discount'] . '%',
            ];
        }
        $data['badges'] = $badges;

        $modx->eventData['msOnGetProductFields']['data'] = $data;
        $values = &$modx->event->returnedValues;
        $values['data'] = $data;
        break;
}

msOnGetPublicSeo ​

Fired in PublicSeoService after the SEO set and the TV overlay map (ms3_public_seo_tv_map) are assembled, just before the final whitelist. Works for both products and categories. On list and tree requests include_seo=0 by default; on get it is 1.

Parameters ​

ParameterTypeDescription
seoarrayThe current SEO whitelist
payloadarrayThe catalog row (product or category)
og_typestringproduct, or website for a category

Modifying ​

A plugin returns a patch in $modx->event->returnedValues['seo']. Unknown keys are dropped by the whitelist. og.title / og.description copy title / description unless the patch sets them explicitly in seo.og.

php
<?php
switch ($modx->event->name) {
    case 'msOnGetPublicSeo':
        $seo = $scriptProperties['seo'];
        $payload = $scriptProperties['payload'];
        $ogType = $scriptProperties['og_type'];

        if ($ogType === 'product' && !empty($payload['vendor'])) {
            $seo['description'] = trim(($seo['description'] ?? '') . ' — ' . $payload['vendor']);
        }

        $values = &$modx->event->returnedValues;
        $values['seo'] = $seo;
        break;
}