Making an api request OnDocFormSave

Gist: I need to make a request to a geocoding api OnDocFormSave and insert the received lat and long values into the proper TVs, but I can’t make it work. I’ve tried: cURL, modRest, and file_get_contents.

Long Story:
How to make an HTTP request I tried mocking up something similar to this, but it didn’t work. I’m making a get request to a geocoding service called OpenCage. I can make the request fine with Javascript, using fetch or whatever, but because of rate limiting, I need to get this to work when the client saves the resource.

I’ve been able to get the event working (i’ve messed around with that a couple of times before), and inside of the event switch statement code is running, I’m concatenating strings, checking TV values, loading services, etc. But when it comes time to make the request it does not go through.

with file_get_contents, I get the https:// wrapper blah blah blah, and I’m not ALLOWED to disable it, I know how and I tried, but its out of my hands. I know, it whomps.

With the curl option, I was able to get the resource to make the request, but as the document was saving, it would just show the loading modal over and over. I checked the browser console, and it had a message uncaught JSON.loader, json could not load or something along those lines. I can go back and recreate it if it has to be specific. I wasn’t able to find much in the sencha forums/docs. I am making sure to break out of the switch statement , and have been using this as a pseudo guide.

Then I found modRest, and that was awesome, but I got to the same point as curl. I wasn’t able to get either to emit an error either. Like i said, file contents just gave me the https:// wrapper nonsense.

Hopefully y’all have some ideas? I’ve been learning a lot about system events doing this, so I’m very excited to keep going. I’m sad that I’m gonna have to switch back to javascript to meet this dead line.

What’s the exact error message when you get the “https wrapper blah blah blah”?

Oh sure! here it is:

[2020-02-28 12:49:23] (ERROR @ .../includes/elements/modplugin/35.include.cache.php : 52) PHP warning: file_get_contents(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0
[2020-02-28 12:49:23] (ERROR @ .../includes/elements/modplugin/35.include.cache.php : 52) PHP warning: file_get_contents(https://api.opencagedata.com/geocode/v1/json?q=${address query}&key=0bf8bc4281ac47cc8c5c9e441b99d9bf&no_annotations=1&limit=1&countrycode=us): failed to open stream: no suitable wrapper could be found

I figured it was super common from how many stack overflow answers there were. The answer was to change the option allow_url_fopen in the php.ini from Off/0 to On/1. Doing this in my cpanel didn’t help, and when i reached out to support for my hosting service, they weren’t willing to change it.

I saw some suggestions to change it using php, ini_set('allow_url_fopen', 1); but this didn’t work either. Guessing its cause server overrides files or something like that, but support wasn’t very helpful.

Helps to be specific at times. :wink:

curl or modRest gets around that limitation. Try reverting to that and inspecting the ajax request - perhaps there’s an error in the response?

haha, thats true! The golden rule: write help requests you want others to leave for you.

Alright, so bare with me, I’m popping in and out with this one since I’m at work. I swapped back to the modRest option and made a lot of progress (oddly enough an old error message said could not load modRest class, totally missed that one, but got it first try this time). So I wrote up a bunch of pseudo tests to try and get an understanding of what is going on.

Here’s what I came up with:

event is running
logic is passing (two tests: right template?, does tv have a value?)
Here is the address encoded: {encoded_address}
Here is the request: https://api.opencagedata.com/geocode/v1/json?q={encoded_address}&key={key}&{params}
Attempting load rest class (anything below this line means the class is loaded)
_______________________
Test to see if request worked: (start of modRest pseudotests)
Has resonse error:
# in my code I have a $response->responseError in a $modx->log(level_error, ...); #
Recoverable error: Object of class stdClass could not be converted to string
Has response info? # nothing
What is returned? Array
Array has length? 0
Does $array 0 exist? # nothing
Request has occurred

So this Object of class stdClass could not be converted to string error is has something to do with handling the response after the get request.
But I have the appropriate $response->process(); from the modRest documentation. I’m very interested to see how this turns out.

Actual code if interested

This is wrapped in a switch ($modx->event->name) case 'OnDocFormSave': ... case too.

if ($resource->get('template') != 13) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, 'not the right template');
    break;
}
if (empty($resource->getTVValue('address'))) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, 'No value in address TV');
    break;
}

$address = urlencode($resource->getTVValue('address'));
$request = $GEOCODE_URL . $address . '&key=' . $GEOCODE_API . $GEOCODE_PARAMS;
$modx->log(xPDO::LOG_LEVEL_ERROR, 'logic is passing');
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Here is the address encoded: '.$address);
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Here is the request: '.$request);

$modx->log(xPDO::LOG_LEVEL_ERROR, 'Attempting load rest class');
$modx->log(xPDO::LOG_LEVEL_ERROR, '_______________________');
$client = $modx->getService('rest', 'rest.modRest');

$response = $client->get($api_request);

if ($response->responseError) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, $response->responseError);
    break;
}
$info = $response->responseInfo;
$error = $response->responseError;
$array = $response->process();

$modx->log(xPDO::LOG_LEVEL_ERROR, 'Test to see if request worked: ');
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Has response error: '.$error);
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Has response info? '.$info);
$modx->log(xPDO::LOG_LEVEL_ERROR, 'What is returned? '.$array);
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Array has length? '.count($array));
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Does $array 0 exist? '.$array[0]);
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Request has occurred');

// calcLatLng();
break;

According to that error, $response->responseError is an object which you can’t just log like that. Try wrapping it in a print_r with 2nd parameter as true.

Okay, cool, that at least sounds like a beginners mistake on my part, haha. I’ve gotta get some other stuff done today at work, but I didn’t want to leave this hanging. Hopefully I’ll be able to make some progress today

The idea got nixed at work, so i had to port it over to my personal testing area. Since I wasn’t under any deadline pressure, I was able to break it down into smaller pieces.

First, I just focused on getting the values from a tv and formatting/playing around with them. Code:

if ($mode == 'new') return;

if ($resource->get('template') != 6) return;
$originalValue = $resource->getTVValue('original-tv');

if (empty($originalValue) || gettype($originalValue) != 'string') return;

$formattedValue = strtoupper($originalValue);

if (!$resource->setTVValue('formatted-tv', $formattedValue)) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, 'formatted value could not be set');
}

return;

I’m sure there is some smarter php way to write some of this stuff. I’m coming from a JS heavy (functional) background. With that covered, I moved into just making the request itself and getting that to show up in the console, using print_r($var) like mark said.

So I made a new plugin and added on my required fetch (rest) code. Code:

$rest_client = $modx->getService('rest', 'rest.modRest');

$response = $rest_client->get($request);

$response_array = $response->process();

if (empty($response_array)) return;

$results = $response_array['results'];

Then I’m able to just loop through the response and find the most confident result, and add that information to the required TVs. Code:

foreach($results as $item) {
    $confidence = $item['confidence'];
    
    if ($confidence < 6) continue;
    
    $lat = $item['geometry']['lat'];
    $lng = $item['geometry']['lng'];
    $formatted_address = $item['formatted'];
    $coordinates = array(
        'lat' => $lat,
        'lng' => $lng
    );
    break;
}

if (!empty($formatted_address)) {
    $resource->setTVValue('address', $formatted_address);
}
if (!empty($coordinates['lat']) && !empty($coordinates['lng'])) {
    $resource->setTVValue('lat', $coordinates['lat']);
    $resource->setTVValue('lng', $coordinates['lng']);
}

So, I achieved what I came out here to do. But since I’ve personally used posts 8+ years old from the previous forums, is there anything that can be done differently/any red flags? Don’t wanna lead anyone a stray :slight_smile:

All the code put together, including logging messages
<?php
$event_name = $modx->event->name;

if ($event_name !== 'OnDocFormSave') return;

if ($resource->get('template') !== 6) return;

$address = $resource->getTVValue('address');

if (empty($address)) return;

$GEOCODE_URL='https://api.opencagedata.com/geocode/v1/json?q=';
$GEOCODE_PARAMS='&no_annotations=1&countrycode=us';

$address_encoded = urlencode($address);

$request = $GEOCODE_URL . $address_encoded . $GEOCODE_PARAMS;

$modx->log(xPDO::LOG_LEVEL_ERROR, 'Loading rest class');

$rest_client = $modx->getService('rest', 'rest.modRest');

$response = $rest_client->get($request);

$response_info = $response->responseInfo;
$response_error = $response->responseError;
$response_array = $response->process();

if (!empty($response_info)) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, 'response info');
    $modx->log(xPDO::LOG_LEVEL_ERROR, print_r($response_info));
}

if (!empty($response_error)) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, 'response error');
    $modx->log(xPDO::LOG_LEVEL_ERROR, print_r($response_error));
}

if (empty($response_array)) return;
$modx->log(xPDO::LOG_LEVEL_ERROR, 'response array');
$modx->log(xPDO::LOG_LEVEL_ERROR, print_r($response_array['results']));

$results = $response_array['results'];

foreach($results as $item) {
    $confidence = $item['confidence'];
    
    if ($confidence < 6) continue;
    
    $lat = $item['geometry']['lat'];
    $lng = $item['geometry']['lng'];
    $formatted_address = $item['formatted'];
    $coordinates = array(
        'lat' => $lat,
        'lng' => $lng
    );
    break;
}

if (!empty($formatted_address)) {
    $resource->setTVValue('address', $formatted_address);
}
if (!empty($coordinates['lat']) && !empty($coordinates['lng'])) {
    $resource->setTVValue('lat', $coordinates['lat']);
    $resource->setTVValue('lng', $coordinates['lng']);
}

return;

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.