function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Edward GeeEdward Gee 

Setting lookup fields by external id in PHP with Partner API help

Has anyone tried setting a lookup field by external ID in PHP using the partner API.  Here's a code snippet of what we're trying to do but when the upsert/update is called, it just hangs our app.  We tried both the field name (returns an error saying it's the wrong type) and the relationship name to no avail.  When using the relationship name, no exception is thrown, no return code is given, it just hangs.  Any ideas?
 
$messageObject = new SObject();
$messageObject->type = 'Message__c'; 
$messageObject->fields['External_ID__c'] = $data[8]; 
$sObject->fields['Original_Message__r'] = $messageObject;

 
Best Answer chosen by Edward Gee
pconpcon
Good new and bad news.  Good news, I got it working!  Bad news, you have to modify the SforceBaseClient.php to get it to work.  The error you should be seeing on the backend is:
 
PHP Catchable fatal error:  Object of class stdClass could not be converted to string in /.../soapclient/SforceBaseClient.php on line 492

This lead me to find a board post [1] about this same issue.  Turns out that the Partner version doesn't handle converting the complex types correctly.  So by updating the _convertToAny function in SforceBaseClient.php to
 
protected function _convertToAny($fields) {
     $anyString = '';
     foreach ($fields as $key => $value) {
          if($value instanceOf stdclass){
                    $anyString = $anyString.'<'.$key.' xmlns="">';
                    $anyString = $anyString.'<type xmlns="urn:sobject.partner.soap.sforce.com">'.$value->type.'</type>';
                    $anyString = $anyString.$this->_convertToAny($value->fields);
                    $anyString = $anyString.'</'.$key.'>';
          }else{
                    $anyString = $anyString . '<' . $key . '
                         xmlns="">' . $value . '</' . $key . '>';
          }
     }
     return $anyString;
}

and then using the following client code
 
define("USERNAME", "user@example.com");
define("PASSWORD", 'password');
define("SECURITY_TOKEN", "token");

require_once('soapclient/SforcePartnerClient.php');

$mySforceConnection = new SforcePartnerClient();
$mySforceConnection->createConnection("partner.wsdl.xml");
$mySforceConnection->login(USERNAME, PASSWORD.SECURITY_TOKEN);


$countryObj = new stdclass();
$countryObj->type = 'Country__c';
$countryObj->fields = array(
     'Code__c' => 'TN'
);

$sObject = new stdclass();
$sObject->type = 'Employee__c';
$sObject->fields = array(
     'Name' => 'Bob Dole',
     'Country__r' => $countryObj
);

$createResponse = $mySforceConnection->create(array($sObject), 'Employee__c');

I was able to get it to work as expected.

[1] https://developer.salesforce.com/forums/?id=906F00000008sthIAA

All Answers

pconpcon
Can you also provide the insert/update/upsert call you are making?
Edward GeeEdward Gee
It's the typical upsert request you can find here https://developer.salesforce.com/page/PHP_Toolkit_20.0_Upsert_Sample_(Partner)

The code executes and completes successfully when we remove the lookup field from the list of fields we set for our custom object.  Unfortunately once we add in the beforementioned lines of code, it just hangs at the upsert.  We put exception handling and return codes around the upsert statement to no avail.  Any ideas?
pconpcon
The following setup works exactly as expected for me.

Country__c object
User-added image

Employee__c object
User-added image

PHP Code
define("USERNAME", "user@example.com");
define("PASSWORD", 'password');
define("SECURITY_TOKEN", "token");

require_once('soapclient/SforceEnterpriseClient.php');

$mySforceConnection = new SforceEnterpriseClient();
$mySforceConnection->createConnection("enterprise.wsdl.xml");
$mySforceConnection->login(USERNAME, PASSWORD.SECURITY_TOKEN);

$sObject = new stdclass();
$sObject->Name = 'Bob Dole';

$countryObj = new stdclass();
$countryObj->Code__c = 'TN';

$sObject->Country__r = $countryObj;

$createResponse = $mySforceConnection->create(array($sObject), 'Employee__c');
Edward GeeEdward Gee
Thanks for the reply.  Have you tried with the partner API though?  It's nice to know it's working with the enterprise API for you.
pconpcon
Good new and bad news.  Good news, I got it working!  Bad news, you have to modify the SforceBaseClient.php to get it to work.  The error you should be seeing on the backend is:
 
PHP Catchable fatal error:  Object of class stdClass could not be converted to string in /.../soapclient/SforceBaseClient.php on line 492

This lead me to find a board post [1] about this same issue.  Turns out that the Partner version doesn't handle converting the complex types correctly.  So by updating the _convertToAny function in SforceBaseClient.php to
 
protected function _convertToAny($fields) {
     $anyString = '';
     foreach ($fields as $key => $value) {
          if($value instanceOf stdclass){
                    $anyString = $anyString.'<'.$key.' xmlns="">';
                    $anyString = $anyString.'<type xmlns="urn:sobject.partner.soap.sforce.com">'.$value->type.'</type>';
                    $anyString = $anyString.$this->_convertToAny($value->fields);
                    $anyString = $anyString.'</'.$key.'>';
          }else{
                    $anyString = $anyString . '<' . $key . '
                         xmlns="">' . $value . '</' . $key . '>';
          }
     }
     return $anyString;
}

and then using the following client code
 
define("USERNAME", "user@example.com");
define("PASSWORD", 'password');
define("SECURITY_TOKEN", "token");

require_once('soapclient/SforcePartnerClient.php');

$mySforceConnection = new SforcePartnerClient();
$mySforceConnection->createConnection("partner.wsdl.xml");
$mySforceConnection->login(USERNAME, PASSWORD.SECURITY_TOKEN);


$countryObj = new stdclass();
$countryObj->type = 'Country__c';
$countryObj->fields = array(
     'Code__c' => 'TN'
);

$sObject = new stdclass();
$sObject->type = 'Employee__c';
$sObject->fields = array(
     'Name' => 'Bob Dole',
     'Country__r' => $countryObj
);

$createResponse = $mySforceConnection->create(array($sObject), 'Employee__c');

I was able to get it to work as expected.

[1] https://developer.salesforce.com/forums/?id=906F00000008sthIAA
This was selected as the best answer