We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 38878
    • 255 Posts
    I've been working on a CMP that uses only 3rd party data via rest api and no database data whatsoever. There are very few examples on this and many of the built-in CMP functionality does not apply. I have been working through the initial bootstrapping of the CMP and have made some good progress on pulling data from the API thanks to muzzstick (see http://forums.modx.com/thread/97924/cmp-dev--index-undefined-config-question#dis-post-529733).

    I am at the point of updating data, and in this case via the inline grid editor and have to use specific field names in order to submit the posts to the api. When I use basic field names like "id", "name" "description", etc the grid renders fine but updates to the API fail because the field names submitted are not in the correct format. When I modify the field names to required format, the grid doesn't render. Below is my simplest scenario and why I started with this one. Once I nail this the rest will fall into place:

    Here is one of my grids:
    Ebents.grid.Organizers = function(config) {
        config = config || {};
        Ext.applyIf(config,{
            url: Ebents.config.connectorUrl
            ,baseParams: { action: 'mgr/organizers/getlist' } 
            ,fields: ['organizer.id','organizer.name','organizer.description']//<- notice the dot style naming - these are required for the post submission to the rest gateway
            ,trackMouseOver:true 
            ,disableSelection:true 
            ,loadMask: true
            ,save_action: 'mgr/organizers/updateFromGrid'
            ,autosave: true
            ,paging: true
            ,remoteSort: false
            ,anchor: '97%'
            ,columns: [{
                    header: _('ebents.organizer_id')
                    ,dataIndex: 'organizer.id'//<-- rendered fine when it was just "id"
                    ,sortable: true
                    ,width: 70
                },{
                    header: _('ebents.organizer_name')
                    ,dataIndex: 'organizer.name'//<-- rendered fine when it was just "name"
                    ,sortable: true
                    ,width: 150
                    ,editor: { xtype: 'textfield' }
                },{
                    header: _('ebents.organizer_description')
                    ,dataIndex: 'organizer.description'//<-- rendered fine when it was just "description"
                    ,sortable: false
                    ,width: 250
                    ,editor: { xtype: 'textarea' }
                }
            ]
            ,tbar:[{
                text: _('ebents.organizer_create')
               ,handler: { xtype: 'ebents-window-organizer-create' ,blankValues: true }
            }]
        });
        Ebents.grid.Organizers.superclass.constructor.call(this,config)


    And the related getList.class.php called by the connector:
    class GetOrganizersListProcessor extends modProcessor {
        
        public function initialize() {
            $this->setDefaultProperties(array(
                'start' => 0,
                'limit' => 10,
            ));
            return true;
        }
        public function process() {
            // call to main class to retrieve my organizers from theEventbrite API
            $this->mgr_client = new Ebents($this->modx);
            
            $this->args = array('id' => 'me', 'data' => 'organizers');
            $this->output = $this->mgr_client->getData('users', $this->args);
            //error_log(print_r($this->output), true);
            //lazy convert results from stdClass to array as the api returns a stdClass object
            $organizers = array();
            $organizers = json_decode(json_encode($this->output->organizers), true);
            
            $count = count($organizers);
            
            $organizers = array_slice($organizers,$this->getProperty('start'),$this->getProperty('limit'),true);
            $list = array();
            /* process output data for grid columns */
            foreach ($organizers as $organizer) {
                $entryArray = array(
                    'organizer.id' => $organizer['id'],
                    'organizer.name' =>  $organizer['name'],
                    'organizer.description' =>  $organizer['description']['text'],
                );
                $list[] = $entryArray;
            }
            //convert to JSON for grid consumption
            return $this->outputArray($list,$count);
        }
    }
    return 'GetOrganizersListProcessor';


    And the JSON response from getlist:
    {"success":true,"total":"1","results":[{"organizer.id":"8310024813","organizer.name":"Joe Organizer","organizer.description":"Joe is the Organizerest Organizer."}]}


    Looking at this, it should render fine as it rendered before I modified the field names to "organizer.XXXXX". But the grid is blank with no errors at all in the console. Is there some predefined data model for the store that needs to be modified or extended? The response is valid JSON though maybe the store/grid pukes on dots. I have tried as a test to intercept the data payload in the initialize function and rewrite the key names before submission, but the post seemed to ignore it (and it felt like a hack) so I yanked it.

    My updatefromgrid.class.php:
    class UpdateOrganizerFromGridProcessor extends modProcessor {
        public $id;
        public $params = array();
        public $args = array();
    
        public function initialize() {
            $data = $this->getProperty('data');
            if (empty($data)) return $this->modx->lexicon('invalid_data');
            $data = $this->modx->fromJSON($data);
            if (empty($data)) return $this->modx->lexicon('invalid_data');
            
            $this->id =  $data['id'];
            
            //error_log($data);
            
            //error_log("Org ID : " .  $this->id);
            $this->params = array (
                'organizer.name' =>  $data['organizer.name'],
                'organizer.description' => $data['organizer.description'],
            );
            $this->args = array('id' => $this->id, 'params' => $this->params);
            error_log(print_r($this->args, true));
            
            return parent::initialize();
        }
    
        public function process() {
    
            // call to main class to save changes to theEventbrite API
            $this->mgr_client = new Ebents($this->modx);
            
            $this->output = $this->mgr_client->postData('organizers', $this->args);
        
            $response = json_decode(json_encode($this->output), true);
            //convert to JSON 
            error_log($this->outputArray($response));
            return $this->outputArray($response);
        }
    }
    return 'UpdateOrganizerFromGridProcessor';


    Again, the reason I changed the field names is because the API expects the names in the dot format. The update was actually posting but the field names were wrong so the API gateway ignored the submission. And there is my dilemma. Any ideas would be great.

    Thanks!

    This question has been answered by harveyev. See the first response.

      • 44195
      • 293 Posts
      Hello again wink
      Can you not just convert back to what the api wants in the update processor?

      Edit:
      Sorry I just saw that you said you tried it.
      I think this is the best way though. [ed. note: muzzstick last edited this post 8 years, 8 months ago.]
        I'm lead developer at Digital Penguin Creative Studio in Hong Kong. https://www.digitalpenguin.hk
        Check out the MODX tutorial series on my blog at https://www.hkwebdeveloper.com
        • 4172
        • 5,888 Posts
        As far, as I can tell, javascript doesn't like dots in fieldnames.

        you will need to convert the dots to underscores, or something and convert them back to dots in the update-processor.
          -------------------------------

          you can buy me a beer, if you like MIGX

          http://webcmsolutions.de/migx.html

          Thanks!
        • I'm guessing when it sees organizer.id, it will look at the value organizer, and search for the key id in that object.

          If you prepare the data like this in your getlist processor:

          $entryArray = array(
                'organizer' => array(
                   'id' => $organizer['id'],
                   'name' => $organizer['name'],
                   'description' => $organizer['description'],
                 )
          );
          


          and set the fields array in your grid config to this:

                  ,fields: [{name: 'organizer', type: 'object'}]
          


          you might get it to work.

          That's definitely out of the box though, and might not be a lot of development fun. An easier solution might be to have processors that just return id, name and description, and transforming that back into what the API needs in an update processor.
            Mark Hamstra • Developer spending his days working on Premium Extras and a MODX Site Dashboard with the ability to remotely upgrade MODX and extras to make the MODX world a little better.

            Tweet me @mark_hamstra, check my infrequent blog at markhamstra.com, my slightly more frequent ramblings at MODX.today or see code at Github.
            • 38878
            • 255 Posts
            Thanks guys. I went back to simple field names in the js to get it rendering and am now attempting to modify the data before post. The API will take a JSON payload as input so I'm trying that but alas, still no luck. My updateFromGrid.class.php looks like this:
            class UpdateOrganizerFromGridProcessor extends modProcessor {
                
                public function initialize() {
                    $data = $this->getProperty('data');
                    if (empty($data)) return $this->modx->lexicon('invalid_data');
                    $data = $this->modx->fromJSON($data);
                    if (empty($data)) return $this->modx->lexicon('invalid_data');
                    
                    $this->id =  $data['id'];
                    $this->params = array ();
                    // build JSON content for form submission...cooking key names
                    $this->formData = array (
                        'organizer.name' =>  $data['name'],
                        'organizer.description' => $data['description'],
                    );
                    $this->formJSON = $this->modx->toJSON($this->formData); 
                    $this->args = array('id' => $this->id, 'params' => $this->params);
                    return parent::initialize();
                }
            
                public function process() {
                    // call to main class to save changes to the Eventbrite API
                    $this->mgr_client = new Ebents($this->modx);
                    $this->output = $this->mgr_client->postData('organizers', $this->args, $this->formJSON);
                    $response = json_decode(json_encode($this->output), true);
                    return $this->outputArray($response);
                }
            }
            return 'UpdateOrganizerFromGridProcessor';


            I convert the form data I'm submitting to JSON in the API call so I could send in the post headers. Otherwise it was populating the $data array values from the connector which is the simple names. The postData function in my main class looks like this:
                function postData($method, $args, $JSONdata) {
                  error_log("JSON Payload : " . $JSONdata);
                  // Get the URI we need.
                  $uri = $this->build_uri($method, $args);
                  // Construct the full URL.
                  $request_url = $this->endpoint . $uri;
                  // This array is used to authenticate our request.
                  $options = array(
                    'http' => array(
                      'header'  => "Content-type: application/json\r\n",
                      'method'  => 'POST',
                      'content' => $JSONdata,
                      'header'  => "Authorization: Bearer " . $this->token
                    )
                  );
                  // Call the URL and get the data.
                  error_log("URL: " . $request_url);
                  error_log("Content: " . $options['http']['content']);
                  $resp = file_get_contents($request_url, false, stream_context_create($options));
                  // parse our response
                    if($resp){
                        $resp = json_decode( $resp );
            
                        if( isset( $resp->error ) && isset($resp->error->error_message) ){
                           error_log( $resp->error->error_message );
                        }
                    }
                    // Return it as arrays/objects.
                    return $resp;
                }


            The $JSONData output is correct as passed to the postData function:
            {"organizer.name":"Joe Organizer","organizer.description":"Joe is the Uberest Organizer."}


            When I refresh it reverts back to the original value. Hard to see what the post value actually submitting via the browser as the data reflected in the console is the data as submitted to the connector (which is the original field names from the store). [ed. note: harveyev last edited this post 8 years, 8 months ago.]
            • discuss.answer
              • 38878
              • 255 Posts
              Okay after banging on this I finally got it. I bailed on the JSON and went straight up form data. Also the actual URL wasn't forming correctly as I was missing a slash right before the query string. So we move on. Final processor class is below. Thanks all for your input.

              class UpdateOrganizerFromGridProcessor extends modProcessor {
              
                  public function initialize() {
                      $data = $this->getProperty('data');
                      if (empty($data)) return $this->modx->lexicon('invalid_data');
                      $data = $this->modx->fromJSON($data);
                      if (empty($data)) return $this->modx->lexicon('invalid_data');
                      
                      $this->id =  $data['id'];
                      
                      $this->params = array (
                          'organizer.name' =>  $data['name'],
                          'organizer.description.html' => $data['description'],
                          'organizer.logo.id' => $data['logo_id'],
                      );
                      
                      $this->args = array('id' => $this->id, 'data'=> '', 'params' => $this->params);
                      return parent::initialize();
                  }
              
                  public function process() {
              
                      // call to main class to save changes to the Eventbrite API
                      $this->mgr_client = new Ebents($this->modx);
                      $this->output = $this->mgr_client->postData('organizers', $this->args);
                      $response = json_decode(json_encode($this->output), true);
                      return $this->outputArray($response);
                  }
              }
              return 'UpdateOrganizerFromGridProcessor';
              • I think I need to chime in because this is related with your other issue in ExtJs's grid.

                Why don't you extend:

                • GetOrganizersListProcessor from modObjectGetListProcessor?
                • UpdateOrganizerFromGridProcessor from UpdateOrganizerProcessor?

                modProcessor is somewhat an abstract class, a raw class.
                Then modObjectProcessor extends it.
                You better use modObjectProcessor's child classes to do the common jobs:

                By doing that, you standardize the output of the processors.
                You only need to direct extend the modObjectProcessor for other task, like: batch action, etc.
                Example: https://github.com/virtudraft/CrossContextsSettings/blob/master/core/components/crosscontextssettings/processors/mgr/settings/updatefromgrid.class.php#L28

                MODx's grid in ExtJs expects values with the format of modObjectGetListProcessor's response.

                Back to your update-from-grid processor.

                You better try to do this:
                update.class.php
                UpdateOrganizerProcessor extends modObjectUpdateProcessor {
                    /// more code
                }
                

                updatefromgrid.class.php
                UpdateOrganizerFromGridProcessor extends UpdateOrganizerProcessor {
                   // @example https://github.com/goldsky/Lingua/blob/master/core/components/lingua/processors/mgr/langs/updatefromgrid.class.php#L27
                }
                

                  Rico
                  Genius is one percent inspiration and ninety-nine percent perspiration. Thomas A. Edison
                  MODx is great, but knowing how to use it well makes it perfect!

                  www.virtudraft.com

                  Security, security, security! | Indonesian MODx Forum | MODx Revo's cheatsheets | MODx Evo's cheatsheets

                  Author of Easy 2 Gallery 1.4.x, PHPTidy, spieFeed, FileDownload R, Upload To Users CMP, Inherit Template TV, LexRating, ExerPlan, Lingua, virtuNewsletter, Grid Class Key, SmartTag, prevNext

                  Maintainter/contributor of Babel

                  Because it's hard to follow all topics on the forum, PING ME ON TWITTER @_goldsky if you need my help.
                  • 38878
                  • 255 Posts
                  Quote from: goldsky at Aug 13, 2015, 03:53 PM
                  I think I need to chime in because this is related with your other issue in ExtJs's grid.

                  Why don't you extend:

                  • GetOrganizersListProcessor from modObjectGetListProcessor?
                  • UpdateOrganizerFromGridProcessor from UpdateOrganizerProcessor?

                  modProcessor is somewhat an abstract class, a raw class.
                  Then modObjectProcessor extends it.
                  You better use modObjectProcessor's child classes to do the common jobs:

                  By doing that, you standardize the output of the processors.
                  You only need to direct extend the modObjectProcessor for other task, like: batch action, etc.
                  Example: https://github.com/virtudraft/CrossContextsSettings/blob/master/core/components/crosscontextssettings/processors/mgr/settings/updatefromgrid.class.php#L28

                  MODx's grid in ExtJs expects values with the format of modObjectGetListProcessor's response.

                  Back to your update-from-grid processor.

                  You better try to do this:
                  update.class.php
                  UpdateOrganizerProcessor extends modObjectUpdateProcessor {
                      /// more code
                  }
                  

                  updatefromgrid.class.php
                  UpdateOrganizerFromGridProcessor extends UpdateOrganizerProcessor {
                     // @example https://github.com/goldsky/Lingua/blob/master/core/components/lingua/processors/mgr/langs/updatefromgrid.class.php#L27
                  }
                  


                  Thanks for the info. But, I'm not using the MODX db and only interfacing with a 3rd party API. Not sure what I gain by using objectProcessors for that.
                  • Quote from: harveyev at Aug 14, 2015, 01:36 AM
                    Thanks for the info. But, I'm not using the MODX db and only interfacing with a 3rd party API. Not sure what I gain by using objectProcessors for that.

                    The processors are prepared mostly for MODExt, so if you dig enough, you'll find some hidden features (hooks, validation, prepared statements) in them and the connection between them.

                    I keep using them even for front-end Ajax, or cross database, or for other alien PHP API.
                    In my case, I just like to keep a same standard for my internal development team.
                    For a complex web-app, a custom class can extend another custom class, and so forth.
                    Overriding a specific method is the key on that practice.
                    That said, we've never declared a new instance in processor, but rather in connector.

                    Example, assuming your mgr_connector.php:

                    <?php
                    
                    require_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/config.core.php';
                    require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
                    require_once MODX_CONNECTORS_PATH . 'index.php';
                    
                    $corePath = $modx->getOption('ebents.core_path', null, $modx->getOption('core_path') . 'components/ebents/');
                    require_once $corePath . 'model/ebents.class.php';
                    $modx->ebents = new Ebents($modx);
                    
                    $modx->lexicon->load('ebents:processor', 'ebents:cmp');
                    
                    /* handle request */
                    $path = $modx->getOption('processorsPath', $modx->ebents->config, $corePath . 'processors/');
                    $modx->request->handleRequest(array(
                        'processors_path' => $path,
                        'location' => '',
                    ));


                    So, on this example, $this->modx->ebents exists for all of your processors.

                        public function process() {
                            // call to main class to save changes to the Eventbrite API
                            $this->output = $this->modx->ebents->postData('organizers', $this->args);
                            $response = json_decode(json_encode($this->output), true);
                            return $this->outputArray($response);
                        }
                    


                    But again, it's only a pattern, you can either use it or ignore it. [ed. note: goldsky last edited this post 8 years, 8 months ago.]
                      Rico
                      Genius is one percent inspiration and ninety-nine percent perspiration. Thomas A. Edison
                      MODx is great, but knowing how to use it well makes it perfect!

                      www.virtudraft.com

                      Security, security, security! | Indonesian MODx Forum | MODx Revo's cheatsheets | MODx Evo's cheatsheets

                      Author of Easy 2 Gallery 1.4.x, PHPTidy, spieFeed, FileDownload R, Upload To Users CMP, Inherit Template TV, LexRating, ExerPlan, Lingua, virtuNewsletter, Grid Class Key, SmartTag, prevNext

                      Maintainter/contributor of Babel

                      Because it's hard to follow all topics on the forum, PING ME ON TWITTER @_goldsky if you need my help.
                      • 38878
                      • 255 Posts
                      I understand and really appreciate your input. I definitely don't need to re-declare in my processors and will remove that. I've looked through the modObjectProcessor before and saw much if it deals with db query functions, etc which I don't need, and why I chose to go with modProcessor as it gives me what I need and not much I don't. For example here is my create processor where really all I am doing is setting up the url to post and returning the results:

                      class CreateOrganizerProcessor extends modProcessor {
                          public function initialize() {
                              //formats the nvpairs used in the query string for submit
                              $this->params = array (
                                  'organizer.name' =>  $_POST['name'],
                                  'organizer.description.html' => $_POST['description'],            
                              );
                              $this->args = array('data'=> '', 'params' => $this->params);
                              return parent::initialize();
                          }
                      
                          public function process() {
                              $this->output = $this->modx->ebents->postData('organizers', $this->args);
                              return $this->output;
                          }
                      }
                      return 'CreateOrganizerProcessor';


                      How would it differ if I used modObjectCreateProcessor?

                      If it's a matter of standardization and practice, I get it. But in looking at my processors, I'm not sure I understand what I'm gaining otherwise. I looked hard for references to CMPs with only 3rd party API data and couldn't find anything and this being my first real CMP dev effort, I'm wandering in the woods a bit which is fine with me. If what I'm doing flies in the face of "convention" regarding CMP dev, of course I don't want that. It's just that extending existing classes that include methods I don't need ( and will never use in this solution) doesn't seem efficient.