Example product import script

There’s no standard import script - but here’s an example to get you going.

Note that you will need to customise the code to make it suit your source data and where the things should go. I’ll also update this a little more later.

It’s expected to be run from the command line with a config.core.php in the same directory (or one up) pointing to the MODX core.

<?php

// Initiate MODX - can skip this if you're using as a snippet
if (file_exists(__DIR__ . '/config.core.php')) {
    require_once __DIR__ . '/config.core.php';
}
elseif (file_exists(dirname(__DIR__) . '/config.core.php')) {
    require_once dirname(__DIR__) . '/config.core.php';
}
if (!defined('MODX_CORE_PATH')) {
    echo "Could not find MODX_CORE_PATH; please add a config.core.php file.\n";
    exit(1);
}

require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$modx = new modX();
$modx->initialize('web');
$modx->getService('error','error.modError', '', '');

// Load Commerce
$path = $modx->getOption('commerce.core_path', null, MODX_CORE_PATH . 'components/commerce/') . 'model/commerce/';
$params = ['mode' => $modx->getOption('commerce.mode')];
/** @var Commerce|null $commerce */
$commerce = $modx->getService('commerce', 'Commerce', $path, $params);
if (!($commerce instanceof Commerce)) {
    echo "Could not load Commerce service from {$path}.\n";
    exit(1);
}

// Get your data - this may be from a XML/CSV dump or another table. Process it to get the same array structure.
// For all available fields see core/components/commerce/model/schema/commerce.mysql.schema.xml line 78+
// Note some fields require special attention, like pricing
$data = [
    [
        'sku' => 'TV12541ASD',
        'name' => 'Big TV',
        'price' => 149900, // in cents
        'sale_price' => 129900,
        'sale_price_from' => '2019-08-07 18:11:31+02:00',
        'sale_price_until' => '2019-10-01 01:00:00+02:00',
        'stock' => 14,
        'weight' => 15.50,
        'weight_unit' => 'kg',
        'image' => 'full/url/to/image.jpg',
    ],
    [
        'sku' => 'RADIOA991',
        'name' => 'A991 Radio',
        'price' => 5900, // in cents
        'stock_infinite' => true, // v1.1+
        'weight' => 900,
        'weight_unit' => 'g',
        'image' => 'full/url/to/image.jpg',
    ],
];

// Get tax group and delivery type
// This assumes all products have the same tax group and delivery type which is configured as the default
$taxGroup = $modx->getObject('comTaxGroup', ['id' => (int)$modx->getOption('commerce.default_tax_group')]);
if (!($taxGroup instanceof comTaxGroup)) {
    echo "Tax group not found - configure that first.\n";
    exit(1);
}
$deliveryType = $modx->getObject('comDeliveryType', ['id' => (int)$modx->getOption('commerce.default_delivery_type')]);
if (!($deliveryType instanceof comDeliveryType)) {
    echo "Delivery type not found - configure that first.\n";
    exit(1);
}

// To use a specific non-default currencies, use $commerce->getCurrency('KEY');
$currency = $commerce->currency;


// Set these up accordingly
$resourceParent = 76;
$resourceTemplate = 3;
$tvName = 'products';
$tvType = 'list'; // list or matrix

foreach ($data as $sourceRow) {
    /** @var comProduct $product */
    $product = $modx->newObject('comProduct');
    $product->fromArray($sourceRow); // This assumes the source data has the right keys

    // Set delivery type and tax group
    $product->set('tax_group', $taxGroup->get('id'));
    $product->set('delivery_type', $deliveryType->get('id'));

    // Set pricing - for detailed explanation see https://docs.modmore.com/en/Commerce/v1/Developer/Products/Pricing.html
    $pricing = $product->getPricing($currency);
    $pricing->setRegularPrice(new \modmore\Commerce\Pricing\Price($currency, $sourceRow['price']));

    // have a sale price?
    if (isset($sourceRow['sale_price']) && $sourceRow['sale_price'] > 0) {
        $pricing->addPriceType(
            new \modmore\Commerce\Pricing\PriceType\Sale(
                new \modmore\Commerce\Pricing\Price(
                    $currency,
                    $sourceRow['sale_price']
                ),
                new DateTime($sourceRow['sale_price_from']), // when the sale started
                new DateTime($sourceRow['sale_price_until']) // when the sale ended
            )
        );
    }
    $product->savePricing($pricing);

    echo "Created product {$product->get('sku')} with ID #{$product->get('id')}\n";

    // use the processor to create the resource - this does all sort of processing that we don't want to rewrite
    /** @var modProcessorResponse $response */
    $response = $modx->runProcessor('resource/create', [
        'context_key' => 'web',
        'parent' => $resourceParent,
        'template' => $resourceTemplate,
        'pagetitle' => $sourceRow['name'],
        'alias' => $modx->filterPathSegment($sourceRow['name'] . '-' . rand(0,999999)), // can leave this out or set a specific alias
        'published' => true,
    ]);
    if ($response->isError()) {
        $errors = implode(', ', $response->getAllErrors());
        echo "Could not create resource: {$errors}{}\n";
    }
    else {
        $resourceId = $response->getObject()['id'];
        $resource = $modx->getObject('modResource', ['id' => $resourceId]);
        if (!($resource instanceof modResource)) {
            echo "Could not load created resource with ID {$resourceId}\n";
        }
        else {
            if ($tvType === 'list') {
                $tvValue = $product->get('id'); // Multiple products per resource? Separate IDs with a comma.
            }
            else { // matrix
                echo "Matrix portion of this example is not yet ready :(\n";
                $tvValue = '';
            }
            $resource->setTVValue($tvName, $tvValue);
        }
    }
}


echo "Done!\n";
@session_write_close();
exit(0);