In 20.1, we introduced two new classes to the extensibility API. They act as replacements for the model and collection classes included with Backbone. To ensure your customizations will be forward-compatible, it is advisable that you start using them by, for example, migrating existing modules to use them.

One of the key drivers for this change is our core extensibility philosophy: we want developers to break their dependence on core SuiteCommerce modules. The more customizations can be built and maintained using classes and methods found in the extensibility API, the easier it will be for both you and us to maintain code going forward.

It is worth emphasizing, that when we talk about models in this article, we are talking explicitly about frontend models. Despite the same name, SCModel refers to different things depending on whether we are talking about code in the frontend or backend.

Before pursuing this, it is recommended you read our official documentation on this: SCModel and SCCollection.

For the purposes of this tutorial, we will look at SCModel, but SCCollection operates in a similar way, so you can take what you have learned about SCModel and apply it there. We are also going to use JavaScript, rather than TypeScript, as this is what is currently used by extensions as of 20.1.

This tutorial works with a basic extension to showcase the functionality. If you want to see a copy of it, you can download the full source of it in GitHub.

Before We Begin

It is important to understand a few things about these classes before we begin.

Firstly, unlike some classes in the extensibility API, they are not components. Components are called using getComponent() on an application object (often named container or application). Conversely, classes like SCModel and SCCollection are added as dependencies to a module just like any other module (ie, included in the array of dependencies in the opening of a define() statement).

The second thing to keep in mind is that you cannot simply search and replace invocations of Backbone.Model with SCModel. While we are still using a form of inheritance, we no longer use the extend() method/keyword. Instead, we have moved towards having a more explicit super/sub class approach. The thing to keep in mind is that we are still requiring the use of ES5, which means we cannot use ES6 keywords such as class, constructor, super etc.

Finally, out of all of this, we can explicitly remove Backbone as a dependency in any module that might ordinarily use it. Thus despite being a, well, backbone technology within SuiteCommerce, it means any modules or extensions written using the new classes should not need to depend on it. We may in the future replace more of Backbone’s modules with ‘SC’ versions, which will continue to lessen our dependency on it.

Working with SCModel

By way of example, we’re going to first look at code that you might ordinarily put into a model class as this will serve as a useful comparison.

Typically a model extends Backbone.Model and adds in, at a minimum, a value for urlRoot, which specifies the path to the service file that will handle the data requests for your module. Optionally, you can include other properties, such as validation rules.

With SCModel, you must first know that the module returns a property also called SCModel — it is this to which you add your properties. Or, more specifically, we must first create a constructor based off of it, and then add our properties to that constructor’s prototype.

There is a slight inelegance to the new syntax in the sense that the process of doing this requires us to copy, overwrite, and then ‘reset’ some of super and sub classes’ properties, but it’s not complicated.

Example Code

So, I have skipped the basics of creating a new extension and its other required files. As mentioned above, there is a link to the full sample code if you need to know that stuff, but for now I just want to show you what the model code looks like:

define('Vendor.Extension.Module.Model',
[
    'SCModel',
    'underscore'
],
function
(
    SCModelModule,
    _
) {
    'use strict';
 
    var SCModel = SCModelModule.SCModel;
 
    // Create the constructor for your custom model
    // VendorExtensionModuleModel is a subclass of SCModel
    function VendorExtensionModuleModel () {
        // Call the super class's constructor
        SCModel.call(this);
 
        // Define the properties for our custom model
        this.urlRoot = function () {
            return _.getAbsoluteUrl(getExtensionAssetsPath('services/Module.Service.ss'));
        }
    }
 
    // Copy parent instance methods (ie the superclass)
    // Note that `new SCModel` would also accomplish this, but this would also call the constructor, which is not something we need to do
    VendorExtensionModuleModel.prototype = Object.create(SCModel.prototype);
 
    // Restore the constructor as it would have been overwritten
    VendorExtensionModuleModel.prototype.constructor = VendorExtensionModuleModel;
 
    // Return the AMD constructor
    return VendorExtensionModuleModel
})

First, note that we are adding the SCModel module as a dependency and (not Backbone) and despite its name, we are going to call it SCModelModule — this is because what we want is the SCModel class which is available as a property of this module.

Then, in our callback, we set a variable name for the class itself, just to make working with it easier.

The next step is to begin defining what our new model should be like. To do this, we create a new function — it can be called whatever we want within this file, but for the sake of consistency, I’ve named after the module itself.

The first thing we do within this function is to call SCModel’s constructor. This is the beginning of the super/sub class relationship: whenever we call our custom model, we’re going to call SCModel.

After that, we begin the customization process and begin to define the properties specific to our custom model. You can see that I am adding in urlRoot so that I can point to my service file. If I were to define additional properties (such as validation) I would include it here.

The next parts of additional syntax required to ensure that it functions properly in regards to the super class.

On our custom model, we set its prototype to be a copy of the base model. This is the classic inheritance concept that you’ll be familiar with: it is here that we are saying that our custom model extends the base model. Using Object.create(), we are creating a copy of the SCModel’s prototype and setting to the prototype property of our model.

Note that, functionally speaking, we could have used new SCModel as well but this isn’t strictly correct. While this would return a new version of SCModel, which would work, it is not specifically what we want to do — there may be additional costs or unexpected consequences to instancing a new version of the model. By copying its prototype, we can be sure we’re getting exactly what we want.

So, after setting the prototype, we encounter a minor problem: we have just overwritten the prototype’s constructor function with the base model’s constructor! We need to restore it so that it points to our previously defined constructor function.

Finally, because of our AMD framework, we then return the function as the output of this module.