Using fromArray() To Write Raw Values to the Database

Easily writing raw values to the database with fromArray().

By Bob Ray  |  September 26, 2023  |  5 min read
Using fromArray() To Write Raw Values to the Database

In the previous article, we saw a way to write raw values to the MODX database with PDO. In this one, we’ll see a slightly slower, but much more convenient method that uses xPDO’s fromArray() method.

fromArray()

If you’ve written code that uses Tpl Chunks to display the fields of MODX objects, you’ve probably used xPDO’s toArray() method. Since any MODX object is also an xPDO object, you can convert all its fields to a PHP associative array by calling its toArray() method. Here are some examples:

$fields = $resource->toArray();
$fields = $chunk->toArray();
$fields = $user->toArray();

There is also a reciprocal method called fromArray() that sets the object fields from an associative array. It looks like this:

$fields = array(
    'pagetitle' => 'My Page',
    'alias' => 'my-page',
    'published' => '1',
    'hidemenu' => '0',
    'introtext' => 'This is my Page',
)

$resource = $modx->newObject('modResource');
$resource->fromArray($fields);
$resource->save();

When called this way, fromArray() calls the object's set() method for each field. That means, for example, that any date fields are converted to Unix timestamps, and JSON fields (like the properties field of elements and the extended field of the user object) will be converted to JSON strings.

What if you don’t want that? What if you already have timestamps or JSON strings to store in the fields? Or maybe, as in the previous article, you want to store user passwords that are already hashed because you’re importing them from another install of MODX.

The fromArray() method gives you a way to store the raw values, but it’s a little tricky. The fromArray() method has a number of optional arguments, shown below:

public function fromArray($fields, $keyPrefix= '',
    $setPrimaryKeys= false, $rawValues= false,
    $adhocValues= false) {}

The fromArray() code is in the xPDOObject class if you’d like to take a look at it. The first argument ($fields) is the array of fields we used above. The second ($keyPrefix) specifies a prefix to strip from the array keys. The third ($setPrimaryKeys) tells MODX to set the primary key of the object (the second and third arguments are seldom used). By default, the prefix is an empty string, and setting primary keys is false, since you rarely want to mess with the primary keys of MODX objects.

The fourth argument ($rawValues) is the one we want. When set to true, it stores the raw values from the $fields array rather than calling set().

The fifth argument ($adhocValues), is seldom used. If it’s set to true, set() is called for each member of the array. It also creates a class variable with the key and value if the key doesn’t exist already in the object. In other words, if you’re working with the modChunk object, $adhocValues is set to true, and your array contains someBogusField = 'bogus value', that key and value will be added to the modChunk object. If you try to save an object with ad hoc fields to the database, either the extra fields and their values would be ignored, or the save would fail, so this feature has fairly limited use.

If you want fromArray() to use the raw values of the $fields array, rather than calling set() for each one, you need to set the intermediate arguments to their default values. So the call would look like this:

$resource->fromArray($fields, '', false, true);

The first argument is our $fields array. The second sets the prefix to an empty string. The third is set to false to tell MODX not to set the primary key of the object. Finally, the fourth argument is true, which tells MODX not to call set() but rather to use the raw values of the $fields array for the field values.

Note: You almost never want to set the primary key of the object. If you do, and an object already exists with that key, you’ll get an error. The only time I’ve ever set the third argument to true involved an early version of GoRevo that imports data from a non-Revolution MODX install into completely empty tables in a Revolution site. This preserves the relationships between the objects. For example, resource fields like parent, template, createdby, editedby, and publishedby, depend on the parents, Templates, and users, having their original IDs.

Rewriting the User Password Code

In my previous article, we used PDO to write raw user passwords to the database. Here’s what that code would look like using fromArray():

$fields = array(
    'username' => 'SomeUserName',
    'active' => '1',
    'cachepwd' => '',
    'password' => 'SomePassword',
    'hash_class' => 'hashing.modMD5',
    'salt' => '',
);

$newUser = $this->modx->newObject('modUser');
$newUser->fromArray($fields, "", false, true);

/* for debugging */
// echo "\n\n" . print_r($newUser->toArray(), true);
$newUser->save(false);

The code above will write the raw values from the $fields array to the user fields in the new modUser object. Note that we’ve called save() with the argument, false. This tells MODX not to cache the user object and speeds things up a little.

When used on a real site, the code would usually have some validation at the beginning to make sure the user didn’t already exist. It would also check at the end to make sure the save() call was successful, and an error message if it wasn’t. It might also have an object prefix to make it work equally well for MODX 3 and MODX 3.

Here’s a version showing the validation and the prefix:

$username = 'someUsername';
$password = 'somePassword';

$fields = array(
    'username' => $username,
    'active' => '1',
    'cachepwd' => '',
    'password' => $password,
    'hash_class' => 'hashing.modMD5',
    'salt' => '',
);

/* Make it run in either MODX 2 or MODX 3 */
$prefix = $modx->getVersionData()['version'] >= 3
    ? 'MODX\Revolution\\'
    : '';

$user = $modx->getObject($prefix . 'modUser',
    array('username' => $username));

if ($user) {
    $modx->log(modX::LOG_LEVEL_ERROR,
        'User ' . $username . ' already exists');
} else {
    $newUser = $this->modx->newObject($prefix . 'modUser');
    $newUser->fromArray($fields, "", false, true);

    /* for debugging */
    // echo "\n\n" . print_r($newUser->toArray(), true);
    if (!$newUser->save(false)) {
        $modx->log(modX::LOG_LEVEL_ERROR,
            'Could not save user ' . $username);
    }
}

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.