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.
resourceData
for historic reasons. It would normally be named ResourceData. Class names are not case-sensitive in PHP, but not having them be consistent will often cause trouble of some kind. It’s recommended that you keep it at resourceData
. If you use a class name other then resourceData
, be sure you use it everywhere.
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:
- Convert the schema for Revo 3 if necessary
- Parse your schema
- Create the class and map files
- Create the custom database table
- Create a
modNamespace
object with the path to your files - Create the appropriate
modExtensionPackage
object - Remove references to your package in the
extension_packages
System Setting (if you’ve used older versions of ClassExtender) - 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.