Blog Sections Open

Creating Documents Programmatically with TV Values in MODX Revo

Creating resources from code is straightforward in Revo, but TVs are not regular resource fields, so they need their own save step.

The old code example already showed how to create a resource programmatically in MODX Revo, but it stopped at the point where many developers get confused: TVs are not saved with a simple set() call like normal resource fields.

The Base Resource Creation Example

$document = $modx->newObject('modResource');
$document->set('createdby', $modx->user->get('id'));
$document->set('template', 3);
$document->set('isfolder', 0);
$document->set('published', 1);
$document->set('createdon', time());
$document->set('pagetitle', 'Artificial document');
$document->set('alias', 'artificial-document');
$document->set('description', '<p>Short article description.</p>');
$document->set('introtext', '<p>Short article intro.</p>');
$document->setContent('<p>Full article text</p>');
$document->set('parent', 21);
$document->save();

Why TVs Need a Separate Step

TVs are stored through a related table, not as direct columns on the resource. That means this will not work reliably:

$document->set('my_tv', 'value');

Instead, save the resource first and then assign TVs explicitly.

The Correct Pattern

$document->save();
$document->setTVValue('my_tv', 'value');
$document->setTVValue('color', 'blue');
$document->setTVValue('price', '199');

Why This Matters

Programmatic resource creation is often used for imports, catalog synchronization, CRM-driven content, or frontend submission flows. In all of those cases, forgetting the extra TV save step leads to resources that look complete in code but arrive in the manager without the expected custom field data.

The safe rule is simple: create the resource, save it, then write TVs through setTVValue().

Newer post

Using Login Across Two Different Contexts in MODX Revo

How to think about Login and multiple contexts in Revo when one request initializes one context and another context must also participate in authentication.

Older post

Making AjaxSearch Work with Content Rendered Through Ditto

Why AjaxSearch does not automatically search the rendered output of Ditto, and what to do when most site content is assembled from hidden source documents.