We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 53432
    • 46 Posts
    Is it possible to create a new resource using the API in Modx Revolution?

    I am trying to create a snippet that will bulk-generate resources from a CSV file.

    I'd like to avoid having to manually create pages one at a time. There could be thousands.

    Any ideas would be greatly appreciated.
    • You'll need to loop through your csv file, set up an array of field properties and use that array in the runProcessor() function. It can be done as a snippet, or as an external script.

      http://forums.modx.com/thread/79111/solved-runprocessor-for-resources-in-modx-api-mode
      http://forums.modx.com/thread/74265/how-to-set-up-runprocessor-to-create-new-docs

      Or you might want to check this out http://rtfm.modx.com/extras/revo/importx
        Studying MODX in the desert - http://sottwell.com
        Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
        Join the Slack Community - http://modx.org
        • 53432
        • 46 Posts
        Awesome! Thank you, @sottwell!

          • 53432
          • 46 Posts
          Just for reference, here is the final code I am using to create a new Modx resource through the API

          // first, define what we want to create:
            $parent       = 42;
            $pagetitle    = 'Example Page';
            $longtitle    = 'Example - A New Document Created Through The Modx API';
            $alias        = 'example-page';
            $description  = 'Example description...';
            $template     = 5;
            $published    = 1;
            $content      = '<p>This is some sample content.</p>';
            $tv_img       = 'example.jpg';
          
          // secondly, create the new resource and save it:
          $doc = $modx->newObject('modDocument');
            $doc->set('parent',$parent);
            $doc->set('pagetitle',$pagetitle);
            $doc->set('longtitle',$longtitle);
            $doc->set('alias',$alias);
            $doc->set('description',$description);
            $doc->set('template',$template);
            $doc->set('published',$published);
            $doc->setContent($content);
          
            $doc->save();
          
          // thirdly, retrieve the newly created resource so we set its TV values:
            $newdoc = $modx->getObject('modDocument', array('pagetitle'=>$pagetitle));
            $id = $newdoc->get('id');
            $tv = $modx->getObject('modTemplateVar',array('name'=>'Category_Thumb'));
            $tv->setValue($id,$tv_img);
            $tv->save();
          
            • 3749
            • 24,544 Posts
            You can also use $newdoc->setTVValue($tvNameOrId,$value );

            One drawback of your method is that no system events will be fired (unless you fire them yourself). That means that any plugins that re supposed to run when a new resource is created won't execute. If you call the resource/create processor, it will fire the usual system events.

            Also, if you use the processor (with $modx->runProcessor), you can send the TV values along in the array with the resource fields. The format is kind of weird. You can look at the NewsPublsher class code to see how it's done.
              Did I help you? Buy me a beer
              Get my Book: MODX:The Official Guide
              MODX info for everyone: http://bobsguides.com/modx.html
              My MODX Extras
              Bob's Guides is now hosted at A2 MODX Hosting
              • 53432
              • 46 Posts
              BobRay, that's interesting. I will check that out. Thank you, much appreciated!
                • 42562
                • 1,145 Posts
                Hello NDweb, did you get this to work as expected? I just came up with this need, and ImportX is acting funny with 2.3. I like this route and don't want to have to use phpmyadmin.
                  TinymceWrapper: Complete back/frontend content solution.
                  Harden your MODX site by passwording your three main folders: core, manager, connectors and renaming your assets (thank me later!)
                  5 ways to sniff / hack your own sites; even with renamed/hidden folders, burst them all up, to see how secure you are not.
                  • 40045
                  • 534 Posts
                  Maybe you're able to pull out something from the following method I'm using in a script of mine, it uses the resource create processor as Bob suggested

                  	/** 
                  	 * Recursively adds resources to a specified context
                  	 *
                  	 * @param $resources An array of resources to create
                  	 *
                  	 * @return bool
                  	 */
                  	public function createResources(array $resources = array(), $context = '') {
                  		if ( !empty($context) ) {
                  			foreach ($resources as $resource) {
                  
                  				if ( !empty($resource['class_key']) ) {
                  					$classkey = $resource['class_key'];
                  				} else {
                  					$classkey = 'modResource';
                  				}
                  
                  				$resource['context_key'] = $context;
                  
                  				$resource['alias'] = !empty($resource['alias']) ? $resource['alias'] : $this->modx->call('modResource', 'filterPathSegment', array(&$this->modx, $resource['pagetitle']));
                  
                  				// extract child resources
                  				$children = isset($resource['children']) ? $resource['children'] : array();
                  				unset($resource['children']);
                  
                  				// get resource object for the following child resources and resource field checks (we need the parent id and the fields)
                  				$resobj = $this->modx->getObject($classkey, array('pagetitle' => $resource['pagetitle'], 'context_key' => $resource['context_key']));
                  
                  				if ( !$resobj ) {
                  					$response = $this->modx->runProcessor('resource/create', $resource);
                  
                  					if ( !$response->isError() ) {
                  						$this->log('info', 'Successfully created new resource ' . $resource['pagetitle'], __METHOD__, __LINE__);
                  
                  						$resarr = $response->getObject();
                  
                  						$this->log('info', '$resobj ' . print_r($resobj, 1), __METHOD__, __LINE__, 'FILE', $this->config['debug']);
                  					} else { 
                  						$this->log('error', 'Failed to create new resource ' . $resource['pagetitle'] . ', Errors: ' . implode(', ', $response->getAllErrors()), __METHOD__, __LINE__);
                  					}
                  				} else {
                  					$this->log('info', 'Resource with pagetitle ' . $resource['pagetitle'] . ' already exists, skipping...', __METHOD__, __LINE__);
                  
                  					// initialize the update variable
                  					$update = false;
                  
                  					// check if any existing resource field values need an update
                  					foreach ($resource as $field => $value) {
                  						if ( $value != $resobj->get($field) ) {
                  							$this->log('info', 'Existing resource field "' . $field . '" has not the same value as the config resource field: ' . $resobj->get($field) . ' !== ' . $value . ', updating resource object', __METHOD__, __LINE__);
                  
                  							$resobj->set($field, $value);
                  
                  							$update = true;
                  						}
                  					}
                  
                  					// updated exting resource fields
                  					if ( $update ) {
                  						$response = $this->modx->runProcessor('resource/update', $resobj->toArray());
                  
                  						if ( !$response->isError() ) {
                  							// we do not get back any details about the created access policy from the processor, so we just give the filename back
                  							$this->log('info', 'Successfully updated resource ' . $resobj->get('pagetitle'), __METHOD__, __LINE__);
                  
                  							// once an update is done all changes are taken over, no need to do this for every field
                  							continue;
                  
                  						} else { 
                  							$this->log('error', 'Failed to update resource ' . $resobj->get('pagetitle') . ', Errors: ' . implode(', ', $response->getAllErrors()), __METHOD__, __LINE__);
                  						}
                  					}
                  
                  					// convert resource object to array for the following child resources check (we need the parent id)
                  					$resarr = $resobj->toArray();
                  				}
                  
                  				// recursively create child resources
                  				if ( !empty($children) && isset($resarr['id']) ) {
                  					foreach ($children as $index => $child) {
                  						$children[$index]['parent'] = $resarr['id'];
                  					}
                  
                  					$this->createResources($children, $context);
                  				}
                  			}
                  		}
                  	}
                  


                  this method takes an array of resources + a context key (where to create those resouce) like this:

                  			$resources = array(
                  				'pagetitle' => 'Parent Resource',
                  				'published' => 1,
                  				'hidemenu'	=> 0,
                  				'children'	=> array(
                  					array(
                  						'pagetitle' => 'Child Resource 1',
                  						'published' => 1,
                  						'hidemenu'	=> 0,
                  					),
                  					array(
                  						'pagetitle' => 'Child Resource 2',
                  						'published' => 1,
                  						'hidemenu'	=> 0,
                  					),
                  					array(
                  						'pagetitle' => 'Child Resource 3',
                  						'published' => 1,
                  						'hidemenu'	=> 0,
                  					),
                  				),
                  			);
                  


                  if you can convert your CSV data to an array in this form, it should be very easy, you can specify any resource fields + values in the resource arrays, not just the shown ones...

                  you need to adapt the method above a bit to run it isolated as it's pulled from a class, e.g. all the $this-> stuff will not work like that...
                    • 53432
                    • 46 Posts
                    @donshakespeare, yes -- my code in Reply #4 does work. With two caveats: depending on your server, you may want to limit it to only importing a few hundred resources at a time, otherwise you may have memory issues with PHP. And, see BobRay's comment about system events not firing.
                    // first, define what we want to create:
                      $parent       = 42;
                      $pagetitle    = 'Example Page';
                      $longtitle    = 'Example - A New Document Created Through The Modx API';
                      $alias        = 'example-page';
                      $description  = 'Example description...';
                      $template     = 5;
                      $published    = 1;
                      $content      = '<p>This is some sample content.</p>';
                      $tv_img       = 'example.jpg';
                     
                    // secondly, create the new resource and save it:
                    $doc = $modx->newObject('modDocument');
                      $doc->set('parent',$parent);
                      $doc->set('pagetitle',$pagetitle);
                      $doc->set('longtitle',$longtitle);
                      $doc->set('alias',$alias);
                      $doc->set('description',$description);
                      $doc->set('template',$template);
                      $doc->set('published',$published);
                      $doc->setContent($content);
                     
                      $doc->save();
                     
                    // thirdly, retrieve the newly created resource so we set its TV values:
                      $newdoc = $modx->getObject('modDocument', array('pagetitle'=>$pagetitle));
                      $id = $newdoc->get('id');
                      $tv = $modx->getObject('modTemplateVar',array('name'=>'Category_Thumb'));
                      $tv->setValue($id,$tv_img);
                      $tv->save();