Using ClassExtender to Extend modUser

How to extend the modUser object with the ClassExtender Extra.

By Bob Ray  |  February 2, 2023  |  7 min read
Using ClassExtender to Extend modUser

As I said in my previous article, you may have seen the somewhat complex set of steps for creating a custom database table to extend the modUser object. That information is here (for Revo 2) and here (for Revo 3).

As I also mentioned, that data 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

Also, in the previous article, I described two basic approaches to extending the modUser object. I’ve repeated that information here, so you don’t have to look back at the previous article:

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

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 user object. Instead, the custom table is for a standalone object called userData. This object has two aliases, User and Profile. Which get the related modUser object and the related modUserProfile object. The ID of the user is kept in the userdata_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 user object’s class key is not changed, which simplifies some operations, but has the disadvantage that if you have the user object, you can’t call $user->getOne('Data');. You have to use the user’s ID and do this:

$userData = $modx->getObject('userData', array('userdata_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.

The Schema

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 as is. That will help you understand how ClassExtender works.

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

    <object class="userData" table="user_data" extends="xPDOSimpleObject">
        <field key="userdata_id" dbtype="int" precision="11" phptype="integer" null="false" attributes="unsigned"/>
        <field key="firstName" dbtype="varchar" precision="50" phptype="string" null="true"/>
        <field key="lastName" dbtype="varchar" precision="50" phptype="string" null="true"/>
        <field key="title" dbtype="varchar" precision="100" phptype="string" null="true"/>
        <field key="company" dbtype="varchar" precision="100" phptype="string" null="true"/>
        <field key="registrationDate" dbtype="datetime" phptype="datetime"/>

        <index alias="userdata_id" name="userdata_id" primary="false" unique="true" type="BTREE">
            <column key="userdata_id" length="" collation="A" null="false"/>
        </index>
        <aggregate alias="User" class="modUser" local="userdata_id" foreign="id" cardinality="one" owner="foreign"/>
        <aggregate alias="Profile" class="modUserProfile" local="userdata_id" foreign="internalKey" cardinality="one" owner="foreign"/>
    </object>
</model>

As you can see in the schema above, the class name is userData. There are six fields for the object and two aliases. The first field is userdata_id, which holds the ID of the user. The other fields are first_name, last_name, title, company, and registration_date (which is set automatically when the user registers). The two aliases are User, which points to the related modUser object, and Profile, which points to the related modUserProfile object. Note that there is also an id field inherited from xPDOSimpleObject, but its value is arbitrary and of no use.

If you’re using Revo 3, you don’t have to include any namespace information 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.

The ClassExtender Form

Once you have a schema with your fields and aliases, you put it in a Chunk called MyExtUserSchema, then view the “Extend modUser” Resource and submit the form. It’s recommended that you not change any of the fields in the form.

Whenever you make changes to the schema, always make them to the 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

One more task you have to do is to specify the extra fields of your custom object so that the ClassExtender Plugin can put them on the Create/Edit User panel in the Manager.

These fields go in a Chunk called MyExtraUserFields. That Chunk should contain the HTML necessary to show the fields. It will look something like this:

<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">First Name</label>
    <div class="x-form-item x-tab-item">
        <input type="text" name="firstName" value="[[+firstName]]" class="x-form-text x-form-field" onClick="Ext.getCmp('modx-panel-user').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">Last Name</label>

    <div class="x-form-item x-tab-item">
        <input type="text" name="lastName" value="[[+lastName]]" class="x-form-text x-form-field" onClick="Ext.getCmp('modx-panel-user').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">Title</label>

    <div class="x-form-item x-tab-item">
        <input type="text" name="title" value="[[+title]]" class="x-form-text x-form-field" onClick="Ext.getCmp('modx-panel-user').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">Company</label>

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

You can copy the code in the ExtraUserFields 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: extendeduser).

You can either use a fully qualified class name:

$userData = new extendeduser/userData;

Or have a use statement like this:

/* At the top of your code */
use extendeduser/userData;

/* Later */
$userData = new userData;

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 users included with ClassExtender.

  • SetUserPlaceholders—Sets placeholders for all user fields
  • getExtusers—Like getResources for users
  • ExtUserUpdateProfile—Add your custom fields during the update profile process
  • ExtUserRegisterPosthook—Add your custom fields during user registration
  • UserSearchForm—Search for users by 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 related Userdata object the fields you need).

Put your schema in the “MyExtendedUserSchema” Chunk and submit the form on the “Extend modUser” Resource. ClassExtender will perform all the same steps but will change the class key of the main object (which will extend modUser) and create the userData class files and the custom table. You’ll have to modify the utility Snippets (make duplicates) to use the custom user class as the primary object and use the Data alias to get the related userData 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.