Formit2resource snippet with unique URL based on pagetitle

I have what might be called the basic formit2resource snippet working, but it doesn’t create an alias in the new document. I wanted to create a custom alias from the page title field and make sure there aren’t duplicates. So I cobbled together the information from this old forum thread.

However when I add in the code for creating the alias and adding number to the end I just get a blank page when submitting the form and nothing is created.

Here is the full code I have now.

<?php
$doc = $modx->getObject('modResource',array('id'=>$hook->getValue('resource_id')));

if (empty($doc)){
    $doc = $modx->newObject('modResource');
    $doc->set('createdby', $modx->user->get('id'));
}
else{
    $doc->set('editedby', $modx->user->get('id'));
}

$allFormFields = $hook->getValues(); 
foreach ($allFormFields as $field=>$value)
{

   if ($field !== 'spam' && $field !== 'resource_id'){
         $doc->set($field, $value);
    }
 
   //we need to parse the title into the alias
   if($field == 'pagetitle'){
      //replace spaces with -
      $alias = preg_replace('/[\' \']/' , '-' , $value);
 
      //remove non alpha and a common injection string 
      $alias = preg_replace('/[^a-z0-9\.\x0B\-]/i' , '' , $alias);
 
                                   //this is the standard revo regexp
                                   // \0\x0B\t\n\r\f\a&=+%#<>"~:`@\?\[\]\{\}\|\^'\
   }
}

//Check if alias already exists,and if it does it appends a number //in the end(eg already existing alias Maria becomes Maria2)
if($modx->getCount(modResource, array('alias'=>$alias))!= 0) {
$count = 2;
$newAlias = $alias;
while($modx->getCount(modResource, array('alias'=>$newAlias))!= 0) {
$newAlias = $alias;
$newAlias .= '-' . $count;
$count++;
}
$alias = $newAlias;
}

// now set the alias
$doc->set('alias', $alias);

$doc->set('template', '99');
$doc->set('parent', '1234');

$isnew = $doc->isNew();
 
if ($doc->save()) {
    if ($isnew) {
        $doc->set('alias', $doc->get('alias' . '-' . $doc->get('id')));
        $doc->save();
    }
}

$doc->save();

foreach ($allFormFields as $field=>$value)
{
    if ($tv = $modx->getObject('modTemplateVar', array ('name'=>$field)))
    {
        /* handles checkboxes & multiple selects elements */
        if (is_array($value)) {
            $featureInsert = array();
            while (list($featureValue, $featureItem) = each($value)) {
                $featureInsert[count($featureInsert)] = $featureItem;
            }
            $value = implode('||',$featureInsert);
        }	
        $tv->setValue($doc->get('id'), $value);
        $tv->save();
    }
}

$modx->cacheManager->refresh(); // suggested by gallenkamp
return true;

If I remove the following code from the middle it will post like normal and create the page, but the alias field still isn’t filled in. Any idea what is crashing the code or why the alias field isn’t being filled in?

//Check if alias already exists,and if it does it appends a number //in the end(eg already existing alias Maria becomes Maria2)
if($modx->getCount(modResource, array('alias'=>$alias))!= 0) {
$count = 2;
$newAlias = $alias;
while($modx->getCount(modResource, array('alias'=>$newAlias))!= 0) {
$newAlias = $alias;
$newAlias .= '-' . $count;
$count++;
}
$alias = $newAlias;
}

The first parameter has to be a string → $modx->getCount('modResource', ...).
Add quotes.

That does keep the page from crashing and creates a resource like before, but it doesn’t fill in the alias and something about it must not be completing to allow the redirect hook to run.

It just stays on the form submission page.

The brackets in this line are wrong. It should be like this:

$doc->set('alias', $doc->get('alias') . '-' . $doc->get('id'));

That seems to be working now.

As far as snippet flexibility, how simple or complicated is it to pass a template and parent variable to the snippet from the Formit call so it could be easily used in multiple locations without having to make another copy of the snippet? For example:

[[!FormIt?
&f2rPageTemplate=`99`
&f2rParent=`1234`
]]

To change the numbers for:

$doc->set('template', '99');
$doc->set('parent', '1234');

According to the FormIt documentation

Properties passed to the FormIt Snippet call are available in the config property of the $formit object exposed in a Hook.
$hook->formit->config['key']

So you could try if that also works for custom properties.

I guess I don’t know enough to understand what it is saying there.

Do you just put the line $hook->formit->config['key'] before whatever line needs to use a variable and then use $f2rPageTemplate wherever you want to use that variable?

Or do you have use it multiple times and replace “key” with the variable you want loaded up? For example:

$hook->formit->config['f2rPageTemplate']
$hook->formit->config['f2rParent']

Then:

$doc->set('template', $f2rPageTemplate);
$doc->set('parent', $f2rParent);

I believe $hook->formit->config is just an associative array with all the properties and their values. So something like this may work:

$template_prop = $hook->formit->config['f2rPageTemplate'] ?? 99; // 99 is the default value, if the property doesn't exist
$doc->set('template', $template_prop);

That seems to all work.

Here is the updated formit2resource snippet for anyone coming along later.

<?php
$doc = $modx->getObject('modResource',array('id'=>$hook->getValue('resource_id')));

if (empty($doc)){
    $doc = $modx->newObject('modResource');
    $doc->set('createdby', $modx->user->get('id'));
}
else{
    $doc->set('editedby', $modx->user->get('id'));
}

$allFormFields = $hook->getValues(); 
foreach ($allFormFields as $field=>$value)
{

   if ($field !== 'spam' && $field !== 'resource_id'){
         $doc->set($field, $value);
    }
 
   //Parse the title into the alias
   if($field == 'pagetitle'){
      //replace spaces with -
      $alias = preg_replace('/[\' \']/' , '-' , $value);
 
      //remove non alpha and a common injection string 
      $alias = preg_replace('/[^a-z0-9\.\x0B\-]/i' , '' , $alias);
 
                                   //this is the standard revo regexp
                                   // \0\x0B\t\n\r\f\a&=+%#<>"~:`@\?\[\]\{\}\|\^'\
   }
}

//Check if alias already exists, and if it does it appends a number
//to the end (eg, already existing alias Maria becomes Maria2)
if($modx->getCount('modResource', array('alias'=>$alias))!= 0) {
$count = 2;
$newAlias = $alias;
while($modx->getCount('modResource', array('alias'=>$newAlias))!= 0) {
$newAlias = $alias;
$newAlias .= '-' . $count;
$count++;
}
$alias = $newAlias;
}

//Get value for page template number to use for the resource.
$template_prop = $hook->formit->config['f2rPageTemplate'] ?? 1; // 1 is the default value, if the property doesn't exist

//Get value for parent document to save resource under.
$parent_prop = $hook->formit->config['f2rParent'] ?? 1; // 1 is the default value, if the property doesn't exist

//Set the alias
$doc->set('alias', $alias);

$doc->set('template', $template_prop);
$doc->set('parent', $parent_prop);

$doc->save();

foreach ($allFormFields as $field=>$value)
{
    if ($tv = $modx->getObject('modTemplateVar', array ('name'=>$field)))
    {
        /* handles checkboxes & multiple selects elements */
        if (is_array($value)) {
            $featureInsert = array();
            while (list($featureValue, $featureItem) = each($value)) {
                $featureInsert[count($featureInsert)] = $featureItem;
            }
            $value = implode('||',$featureInsert);
        }	
        $tv->setValue($doc->get('id'), $value);
        $tv->save();
    }
}

$modx->cacheManager->refresh(); // suggested by gallenkamp
return true;

And the Formit call is basically something like this:

[[!FormIt? 
&hooks=`formit2resource,redirect`
&resource2formitfields=`parent,pagetitle,description`
&redirectTo=`1234`
&f2rPageTemplate=`99`
&f2rParent=`1234`
]]

I guess there is one more issue. How can one set the resource group to a document when creating it from a form.

I tried the following in the above code and none worked:

$doc->set('document_group', "2");

and

$doc->set('groupId', "2");

and

$doc->set('resource_group', "2");

Are you just guessing possible names for this field? Don’t do that. Look at the schema (or the database table) to see what fields are available for a modResource:

In this case, the resource doesn’t have a field for the resource group, because a resource can be in multiple resource groups.

Which resource belongs to which resource group is stored in the class modResourceGroupResource (in MODX 3 → MODX\Revolution\modResourceGroupResource) which corresponds to the database table modx_document_groups:

1 Like

$doc->joinGroup

is, what you are looking for.

1 Like

Thanks for all the information.

Using that to get into the ballpark and using Microsoft’s CoPilot AI (which kind of has an amazing grasp of MODx and PHP) to help with the syntax.

In addition to needing to be part of a resource group I also realized the particular resource I was creating was in a collections area but still showing up in the resource tree until it was opened and resaved. I found another conversation in the forums talking about the show_in_tree field.

Eventually, I (and the A.I.) came up with the following results which adds a couple more snippet calls for adding the resource to a resource group and also setting whether a resource is part of a collection. I don’t this will work with more than one resource groups though, maybe there is a way to properly handle that.

The related FormIt snippet calls are something along the lines of:

[[!FormIt?
&hooks=`formit2resource`
&f2rPageTemplate=`99`
&f2rParent=`1234`
&f2rResourceGroup=`2`
&f2risCollection=`yes`
]]

The snippet code:

<?php
// Get the resource by ID
$doc = $modx->getObject('modResource', array('id' => $hook->getValue('resource_id')));

if (empty($doc)) {
    // Create a new resource if it doesn't exist
    $doc = $modx->newObject('modResource');
    $doc->set('createdby', $modx->user->get('id'));
} else {
    // Set the editor if the resource exists
    $doc->set('editedby', $modx->user->get('id'));
}

// Get all form field values
$allFormFields = $hook->getValues();
foreach ($allFormFields as $field => $value) {
    if ($field !== 'spam' && $field !== 'resource_id') {
        // Set the field value
        $doc->set($field, $value);
    }

    // Parse the title into the alias
    if ($field == 'pagetitle') {
        // Replace spaces with dashes
        $alias = preg_replace('/[\' \']/', '-', $value);

        // Remove non-alphanumeric characters
        $alias = preg_replace('/[^a-z0-9\.\x0B\-]/i', '', $alias);
    }
}

// Ensure the alias is unique
if ($modx->getCount('modResource', array('alias' => $alias)) != 0) {
    $count = 2;
    $newAlias = $alias;
    while ($modx->getCount('modResource', array('alias' => $newAlias)) != 0) {
        $newAlias = $alias;
        $newAlias .= '-' . $count;
        $count++;
    }
    $alias = $newAlias;
}

// Set the alias, template, and parent fields
$template_prop = $hook->formit->config['f2rPageTemplate'] ?? 1;
$parent_prop = $hook->formit->config['f2rParent'] ?? 1;
$resource_group_id = $hook->formit->config['f2rResourceGroup'] ?? 0;
$doc->set('alias', $alias);
$doc->set('template', $template_prop);
$doc->set('parent', $parent_prop);

// Conditionally set the show_in_tree field based on f2risCollection
if (isset($hook->formit->config['f2risCollection'])) {
	$show_in_tree = ($hook->formit->config['f2risCollection'] === 'yes') ? 0 : 1;
	$doc->set('show_in_tree', $show_in_tree);
}

// Save the resource
$doc->save();

// Assign the resource to the resource group if a valid group is specified
if ($resource_group_id > 0) {
    $resourceGroupResource = $modx->newObject('modResourceGroupResource');
    $resourceGroupResource->fromArray(array(
        'document_group' => $resource_group_id,
        'document' => $doc->get('id'),
    ));
    $resourceGroupResource->save(); // Save the assignment
}

// Set template variables
foreach ($allFormFields as $field => $value) {
    if ($tv = $modx->getObject('modTemplateVar', array('name' => $field))) {
        // Handle checkboxes & multiple select elements
        if (is_array($value)) {
            $featureInsert = array();
            while (list($featureValue, $featureItem) = each($value)) {
                $featureInsert[count($featureInsert)] = $featureItem;
            }
            $value = implode('||', $featureInsert);
        }
        $tv->setValue($doc->get('id'), $value);
        $tv->save();
    }
}

// Refresh cache
$modx->cacheManager->refresh();

return true;