Using ClassExtender to Extend modResource

How to extend the modResource object with ClassExtender.

By Bob Ray  |  February 14, 2023  |  6 min read
Using ClassExtender to Extend modResource

There’s an article here (for Revo 2) and here (for Revo 3). It describes the steps for creating a custom database table to extend the modResource object.

At this writing, the information there is somewhat out of date, especially for Revo 3. The good news is that if you use the ClassExtender Extra, you can avoid having to do most of the steps in those articles.

Two Approaches

In my earlier article on extending the modUser object, I described two basic approaches to extending basic MODX objects like modUser and modResource. I’ve repeated that information here, but with reference to the modResource object, so you don’t have to look back at the previous article.

With the approach in the MODX official documentation, your new custom object (extResource) extends the standard modResource object. It inherits the id field and all other Resource fields from modResource and creates a separate alias (Data) for your custom table. It requires that your extResource class has a new class key (in the class_key field). In the docs this class key is extResource.

ClassExtender takes a slightly different approach (though you can still use it to create a setup like the one above). It does not use a different class key for the Resource object. Instead, the custom table is for a standalone object called resourceData. This object has two aliases, User and Profile. Which get the related modUser object and the related modUserProfile object. The ID of the Resource is kept in the resourcedata_id field of the custom object in the same way that the modUserProfile object has the user’s ID in the internalKey field. This is basically backwards from the way the MODX docs version does it. It has the advantage that the Resource object’s class key is not changed, which simplifies some operations, but has the disadvantage that if you have the Resource object, you can’t call $resource->getOne('Data');. You have to use the Resource’s ID and do this:

$resourceData = $modx->getObject('resourceData', array('resourcedata_id' => $id));

The rest of this article will assume that you’re using the ClassExtender approach, but much of the information applies to both approaches.

Your main job in ClassExtender is to create a schema for your new custom object and its database table. Here is the example schema that comes with the ClassExtender package. You can duplicate it and modify it to meet your needs, but it’s recommended that you try it out without changing it first to help you understand how ClassExtender works. Your duplicate Chunk should be called MyExtresourceSchema.

<?xml version="1.0" encoding="UTF-8"?>
<model package="extendedresource" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" tablePrefix="ext_"
       version="1.0.0">

    <object class="resourceData" table="resource_data" extends="xPDOSimpleObject">
        <field key="resourcedata_id" dbtype="int" precision="11" phptype="integer" null="false" attributes="unsigned"/>
        <field key="name" dbtype="varchar" precision="50" phptype="string" null="true"/>
        <field key="color" dbtype="varchar" precision="50" phptype="string" null="true"/>
        <field key="breed" dbtype="varchar" precision="50" phptype="string" null="true"/>
        <field key="age" dbtype="int" precision="10" phptype="integer" null="false" default="0"/>

        <index alias="resourcedata_id" name="resourcedata_id" primary="false" unique="true" type="BTREE">
            <column key="resourcedata_id" length="" collation="A" null="false"/>
        </index>
        <aggregate alias="Resource" class="modResource" local="resourcedata_id" foreign="id" cardinality="one"
                   owner="foreign"/>
    </object>
</model>

As you can see in the schema above, the class name is resourceData. There are five fields for the object, one index, and one alias. The first field is resourcedata_id, which holds the ID of the Resource. The other fields are name, color, breed, and age. These fields are for an imaginary business like a veterinary office that needs to record information about the animals it treats. Note that there is also an id field inherited from xPDOSimpleObject, but its value is arbitrary and generally of no use.

If you’re using Revo 3, you don’t have to include any namespace information or fully qualified paths in your schema. ClassExtender will insert that into the schema for you when it writes the file that’s actually used to create the class and table.

Once you have a schema with your fields and aliases in the MyExtResourceSchema chunk, view the “Extend modResource” Resource and submit the form. It’s recommended that you not change any of the fields in the form.

Important: Whenever you make changes to the schema, always make them to the MyExtresourceSchema Chunk, not the file, then submit the form again and everything will be recreated. You should drop the existing table in the database before submitting the form. ClassExtender will delete the class and map files and recreate them.

Once you’ve submitted the form, ClassExtender will perform the following steps:

  1. Convert the schema for Revo 3 if necessary
  2. Parse your schema
  3. Create the class and map files
  4. Create the custom database table
  5. Create a modNamespace object with the path to your files
  6. Create the appropriate modExtensionPackage object
  7. Remove references to your package in the extension_packages System Setting (if you’ve used older versions of ClassExtender)
  8. Create an autoloader for your classes

Extra Fields Chunk

There’s one more task you have to perform. You need to specify the extra fields of your custom object so that the ClassExtender Plugin can put them on the Create/Edit Resource panel in the Manager.

These fields go in a Chunk called MyExtraResourceFields. That Chunk should contain the HTML necessary to show the fields. It will look something like this (the classes are designed to match the styling of the Manager page):

<div class="x-form-item x-tab-item">
    <label class="x-form-item-label" style="width:auto; font-weight:bold; float:none; font-size:12px">Name</label>

    <div class="x-form-item x-tab-item">
        <input type="text" name="name" value="[[+name]]" class="x-form-text x-form-field"
                onClick="Ext.getCmp('modx-panel-resource').markDirty();"/>
    </div>
</div>

<div class="x-form-item x-tab-item">
    <label class="x-form-item-label" style="width:auto; font-weight:bold; float:none; font-size:12px">Color</label>

    <div class="x-form-item x-tab-item">
        <input type="text" name="color" value="[[+color]]" class="x-form-text x-form-field"
                onClick="Ext.getCmp('modx-panel-resource').markDirty();"/>
    </div>
</div>

<div class="x-form-item x-tab-item">
    <label class="x-form-item-label" style="width:auto; font-weight:bold; float:none; font-size:12px">Breed</label>

    <div class="x-form-item x-tab-item">
        <input type="text" name="breed" value="[[+breed]]" class="x-form-text x-form-field"
                onClick="Ext.getCmp('modx-panel-resource').markDirty();"/>
    </div>
</div>

<div class="x-form-item x-tab-item">
    <label class="x-form-item-label" style="width:auto; font-weight:bold; float:none; font-size:12px">Age</label>

    <div class="x-form-item x-tab-item">
        <input type="text" name="age" value="[[+age]]" class="x-form-text x-form-field"
                onClick="Ext.getCmp('modx-panel-resource').markDirty();"/>
    </div>
</div>

You can copy the code in the ExtraResourceFields Chunk to get started.

Once you’ve completed the steps above, your class is ready to use. If you need to write your own custom code, you should include the autoloader file at the top, like this:

require_once(MODX_CORE_PATH .
    'components/classextender/model/ce_autoload.php');

There’s no need to call addPackage() because MODX will call it on every request using the modExtensionPackage, the modNamespace, and path created by ClassExtender when you submitted the form.

Custom Code

If you write your own custom code to use your class in Revo 3, you’ll need to use the namespace MODX creates for you. The namespace will be the name of your package (by default: extendedresource).

You can either use a fully qualified class name:

$resourceData = new extendedresource/resourceData;

Or have a use statement like this:

/* At the top of your code */
use extendedresource/resourceData;

/* Later */
$resourceData = new resourceData;

Utility Snippets

The ClassExtender package also includes some utility Snippets you can use or modify to meet your needs. These are written for the ClassExtender approach, so if you are using the approach in the MODX docs, you will need to modify them.

If you need to modify them, duplicate them, so they won’t be overwritten by upgrades to ClassExtender.

Here’s brief rundown of the utility Snippets for Resources included with ClassExtender.

  • SetResourcePlaceholders—Sets placeholders for all Resource fields
  • getExtResources—Like getResources but includes your custom fields

For details about the utility Snippets and more information on using ClassExtender, see the documentation here.

Using the Official MODX Docs Approach

If you want to use the method described in the docs with ClassExtender, all you really need to do is use a schema like the one in the article, modified to meet your needs (mainly just giving the new object the fields you need).

Put your schema in the “MyExtendedResourceSchema” Chunk and submit the form on the “Extend modResource” Resource. ClassExtender will perform all the same steps but change the class key of the main object (which will extend modResource) and create the resourceData class files and the custom table. You’ll have to modify the utility Snippets (make duplicates) to use the custom Resource class as the primary object and use the Data alias to get the related resourceData object.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.