+ Reply to Thread
Page 1 of 5 1 2 3 ... LastLast
Results 1 to 10 of 50

Thread: Using Salesforce Outbound SOAP Messages with PHP

  1. #1
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15

    Using Salesforce Outbound SOAP Messages with PHP

    Recently I have been using the Salesforce outbound SOAP messages while I was working on some workflow's. Honestly I had no idea that this could be implemented and I am in awe right now.

    My organization needed to find a way to change the Owner Id on a custom object when a certain field changed. We implemented a workflow, which in turn would thin trigger an outbound message from Salesforce via the API. An outbound message looks like and is formatted in XML. Here is an example:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
      <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
       <OrganizationId>00DR......</OrganizationId>
       <ActionId>04kR00000004CCXIA2</ActionId>
       <SessionId xsi:nil="true"/>
       <EnterpriseUrl>https://cs2-api.salesforce.com/services/Soap/c/8.0/4e1300DR00000000XcD</EnterpriseUrl>
       <PartnerUrl>https://cs2-api.salesforce.com/services/Soap/u/8.0/4e1300DR00000000XcD</PartnerUrl>
       <Notification>
        <Id>04lR0000000CcIFIA0</Id>
        <sObject xsi:type="sf:Design_Registration__c" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
         <sf:Id>a06R00000009QZaIAM</sf:Id>
         <sf:BU_Reviewer_Lookup__c>00540000000lnc2AAA</sf:BU_Reviewer_Lookup__c>
         <sf:DP_cust_eng_email__c>me@email.com</sf:DP_cust_eng_email__c>
         <sf:DP_reg_id__c>JXD08-7CLLB7</sf:DP_reg_id__c>
         <sf:DR_Number__c>R080500453</sf:DR_Number__c>
         <sf:Field_reviewer_lookup__c>00540000000yHbGAAU</sf:Field_reviewer_lookup__c>
         <sf:Name>URT</sf:Name>
         <sf:OwnerId>00540000000mBqsAAE</sf:OwnerId>
        </sObject>
       </Notification>
      </notifications>
     </soapenv:Body>
    </soapenv:Envelope>
    The key to this is that you are allowed to include almost any field in the outbound message. You can set these values when you are creating the trigger in Salesforce.

    Once you set the fields, they are sent in the sObject portion of the SOAP message:

    Code:
    <sObject xsi:type="sf:Design_Registration__c" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
         <sf:Id>a06R00000009QZaIAM</sf:Id>
         <sf:BU_Reviewer_Lookup__c>00540000000lnc2AAA</sf:BU_Reviewer_Lookup__c>
         <sf:DP_cust_eng_email__c>me@email.com</sf:DP_cust_eng_email__c>
         <sf:DP_reg_id__c>JXD08-7CLLB7</sf:DP_reg_id__c>
         <sf:DR_Number__c>R080500453</sf:DR_Number__c>
         <sf:Field_reviewer_lookup__c>00540000000yHbGAAU</sf:Field_reviewer_lookup__c>
         <sf:Name>URT</sf:Name>
         <sf:OwnerId>00540000000mBqsAAE</sf:OwnerId>
        </sObject>
    The great thing is that you can then kick off almost any application with this with a SOAP Server that is setup locally (we have one running on our server).

    The key is that you have to send a response back to your instance of Salesforce.

    So once the SOAP Server is setup on your server, you can respond to an incoming message like this:

    PHP Code:
    //Reads SOAP incoming message from Salesforce/MAPS
    $data fopen('php://input','rb');
    $content fread($data,5000); 
    This code reads the raw data coming in from Salesforce and then sticks it in a variable called $content.

    You can then respond to the message like this:

    PHP Code:
    if ($content)
    {
        respond('true');
    }
    else
    {
        respond('false');
    }

    function respond($tf)
    {

        print '<?xml version "1.0" encoding "utf-8"?>
       <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <soapenv:Body>
               <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
                   <Ack>' . $tf . '</Ack>
                  </notifications>
              </soapenv:Body>
          </soapenv:Envelope>';
    }
    So $tf is set to true and responds to the outbound message and Salesforce knows that the message has been accepted and is removed from the queue.


    So what can you really do with this? We are using this workflow to change the OwnerId on a custom object when a certain field is changed

    I will continue to update this thread with different topics and ideas on what I have been able to accomplish with this on demand feature

    ~Mike

  2. #2
    hemmeter is offline Junior Member
    Join Date
    May 2008
    Posts
    12
    What does it take to get a SOAP server setup? What's needed architecturally to configure the end point? Just any PHP server that is ready and able to connect to Salesforce using the PHP toolkit?

  3. #3
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15
    Hey Scott !!

    listen the setup (still totally new at this) was rather quite easy

    First off, do you know how to setup the outbound workflow in Salesforce?

    I am sure you do, you are a lot more knowledgeable than I am, LOL!

    Listen there is no special setup for the SOAP server setup.

    I just have that code that I posted in the first post of this thread and a parser to read XML (the incoming SOAP message)

    for testing purposes, I wrote it out to a file and then used that to parse the XML until I felt comfortable with the data I was seeing using print_r.

    You can use PHP's built in XML DOM. I found this example at the salesforce forums:

    PHP Code:
    <?php
    /* Handles SFDC Outbound Message notification.*/
    require_once ('../includes/salesforce_login.php');


    // Get raw post data
    $data fopen('php://input','rb');
    $content fread($data,5000);

    // Parse values from soap string
    $dom = new DOMDocument();
    $dom->loadXML($content);
    $resultArray parseNotification($dom);
    $sObject $resultArray["sObject"];

    // Output field names and values to file
    $fields_string "";

    foreach (
    $sObject->fieldnames as $field)
    {
        
    $fields_string .= $field " => " $sObject->fields->$field "\n";
    }


    // Write message and values to a file
    $fh fopen('soap.xml','a');
    fwrite($fh,$content);
    $ret fclose($fh);


    // Sends SOAP response to SFDC
    if ($ret)
    {
        
    respond('true');
    }
    else
    {
        
    respond('false');
    }
    //functions

    /* Parse a Salesforce.com Outbound Message notification SOAP packet
    * into an array of notification parms and an sObject.   */
    function parseNotification($domDoc)
    {
        
    // Parse Notification parameters into result array
        
    $result = array("OrganizationId" => "","ActionId" => "","SessionId" => "","EnterpriseUrl" => "","PartnerUrl" => "","sObject" => null);

        
    $result["OrganizationId"] = $domDoc->getElementsByTagName("OrganizationId")->item(0)->textContent;
        
    $result["ActionId"] = $domDoc->getElementsByTagName("ActionId")->item(0)->textContent;
        
    $result["SessionId"] = $domDoc->getElementsByTagName("SessionId")->item(0)->textContent;
        
    $result["EnterpriseUrl"] = $domDoc->getElementsByTagName("EnterpriseUrl")->item(0)->textContent;
        
    $result["PartnerUrl"] = $domDoc->getElementsByTagName("PartnerUrl")->item(0)->textContent;

        
    // Create sObject and fill fields provided in notification
        
    $sObjectNode $domDoc->getElementsByTagName("sObject")->item(0);
        
    $sObjType $sObjectNode->getAttribute("type");
        if (
    substr_count($sObjType,"sf:"))
            
    $sObjType substr($sObjType,3);
        
    $result["sObject"] = new SObject($sObjType);
        
    $result["sObject"]->type $sObjType;

        
    $sObjectNodes $domDoc->getElementsByTagNameNS('urn:sobject.enterprise.soap.sforce.com','*');
        
    $result["sObject"]->fieldnames = array();
        foreach (
    $sObjectNodes as $node)
        {
            if (
    $node->localName == "Id")
            { 
    // Id goes under sObject->Id
                
    $result["sObject"]->Id $node->textContent;
            }
            else
            { 
    // ... the rest go under sObject->fields
                
    $fieldname $node->localName;
                
    $result["sObject"]->fields->$fieldname $node->nodeValue;
                
    array_push($result["sObject"]->fieldnames,$fieldname);
            }
        }

        return 
    $result;
    // end parseNotification


    function respond($tf)
    {

        print 
    '<?xml version = "1.0" encoding = "utf-8"?>
       <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <soapenv:Body>
               <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
                   <Ack>' 
    $tf '</Ack>
                  </notifications>
              </soapenv:Body>
          </soapenv:Envelope>'
    ;
    }
    ?>
    to get me started. I had some small troubles and also purchased a off the shelf XML parser that reads the SOAP message better than the DOM XML built in class.

    So basically it is easy to setup and get working, I can take this offline sometime if you want

    ~Mike

    PS: good to see you here, I need to add a link to your site on my main page, I forgot to do that

  4. #4
    hemmeter is offline Junior Member
    Join Date
    May 2008
    Posts
    12
    Awesome. Got it working! Now I need to figure out what to implement, but this is a really nice option to have.

  5. #5
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15
    Outstanding man!! Glad that helped.

    Listen if you come up with Ideas, fixes or things that work and you post them on your site, would you be willing to post a link here or the code and let me know what you did or found?

    FYI > I added a link from my main menu to your site


    ~Mike

  6. #6
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15
    Scott we have taken this service and added a whole new object via the outbound messaging. It is really like combining a whole bunch of queries using the data coming in from the salesforce outbound messaging and querying multiple tables

    Then we take that data and create a whole new data flow for a new Object in Salesforce. It works like a charm

    So if they change a status on the custom object, it kicks off a SOAP outbound message that you could really do anything with!!

    Stick it into a custom reporting application, A database, or back into Salesforce

    I am thinking this is like the new trigger function in APEX, but keeping your process outside of Salesforce and not having to learn APEX coding (even though I need too)

    Have you had a chance to mess around with this at all

    ~Mike

  7. #7
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15
    One item that I did not take into consideration was error reporting and what it can cause in your responses to salesforce's outbound messaging queue

    Let me explain

    Last week I was running into errors when sending 10 fields in an outbound message and forgot to account for empty fields in the data elements.

    So let's say you send the following from the Account Object:

    * FirstName
    * LastName
    * Name
    * Email
    * UserName
    * Phone
    * Mobile


    And in some cases not all the data is populated, well if your error_reporting is turned on and you do not set it correctly to ignore notice's, you can run into some serious problems trying to get the outbound message queue from clearing out.

    I was sending notices back to Salesforce with the True response and noticing that the message queue was filled up with many requests.

    A fellow developer at work told me to check what was in the output buffer, basically what I was sending back to salesforce

    I had no idea that the notices were being sent back, but once I added the mail() function to email myself the ob_get_contents(), I saw the other data that I was returning to Salesforce, what a mess that was!

    a simple line at the top of the script to turn off notice reporting was all that I needed

    PHP Code:
    error_reporting(E_ALL E_NOTICE); 
    Stupid mistake, but I thought that I would share it with the community because you could run into the same problems

    Anyway just a little snippet to take care of clearing out the buffer before sending your response to Salesforce

  8. #8
    ambiguator is offline Junior Member
    Join Date
    Aug 2008
    Posts
    14

    Thanks for the great advice - now a followup question

    This thread has been my primary resource for outbound messaging via SalesForce. Thanks for all the great input.

    My client has 2 primary means of accessing their SalesForce data:
    1. Through the web at login.salesforce.com, na2.salesforce.com, etc. using SF's snazzy Ajax interface.
    2. Through a custom CMS web app designed to talk to salesforce through the web service API.

    My question is: apart from having 2 different logins, how do I tell the 2 kinds of requests apart?

    Let's take this scenario: I modify the Contact object through the CMS (case 2 above). This forces the CMS to push the changes to the salesforce database. I have an outbound message being triggered for all changes to a Contact object. The outbound message gets triggered and notifies the CMS that the Contact object has changed, even though the CMS initiated the change in the first place.

    How do I keep salesforce from generating the outbound message when it was triggered by an event via a webservice API call?

    Thanks in advance,
    /a

  9. #9
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15
    Quote Originally Posted by ambiguator View Post
    This thread has been my primary resource for outbound messaging via SalesForce. Thanks for all the great input.

    My client has 2 primary means of accessing their SalesForce data:
    1. Through the web at login.salesforce.com, na2.salesforce.com, etc. using SF's snazzy Ajax interface.
    2. Through a custom CMS web app designed to talk to salesforce through the web service API.

    My question is: apart from having 2 different logins, how do I tell the 2 kinds of requests apart?

    Let's take this scenario: I modify the Contact object through the CMS (case 2 above). This forces the CMS to push the changes to the salesforce database. I have an outbound message being triggered for all changes to a Contact object. The outbound message gets triggered and notifies the CMS that the Contact object has changed, even though the CMS initiated the change in the first place.

    How do I keep salesforce from generating the outbound message when it was triggered by an event via a webservice API call?

    Thanks in advance,
    /a

    When you create an outbound message, you can set who you want to send the outbound message as, a user id, like me (see the attached picture)

    Maybe you could use 2 different users to send as, thus two different identifications that you can use to differentiate the two outbound messages.

    I did not know that you could kick off a outbound message via an API call, but you can limit it to only being called once!

    when you set the Work Flow rule, there is a section:

    Evaluate rule How do I choose?
    1. When a record is created, or when a record is edited and did not previously meet the rule criteria (choose this one)
    2. Only when a record is created
    3. Every time a record is created or edited
    Hope that helps sir and I hope that I understood your question(s)

    If Not then do let me know

  10. #10
    mike's Avatar
    mike is offline Administrator
    Join Date
    May 2007
    Location
    Wylie, Texas
    Posts
    557
    Blog Entries
    15
    Yesterday I came across a major problem with one of our outbound soap processes that I would like to bring up.

    I knew that Salesforce assigned a processing Id with their outbound message, which makes since. I always thought that this was a 1 for 1 relationship, but I am totally mistaken.

    We had a discrepancy in some of our counts in two different objects and could not figure it out. Further testing revealed that Salesforce can send multiple records with one processing Id. Look at the XML code below from one processing Id:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
      <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
       <OrganizationId>*****************</OrganizationId>
       <ActionId>04k400000004CYMAA2</ActionId>
       <SessionId xsi:nil="true"/>
       <EnterpriseUrl>https://cs2-api.salesforce.com/services/Soap/c/8.0/</EnterpriseUrl>
       <PartnerUrl>https://cs2-api.salesforce.com/services/Soap/u/8.0/</PartnerUrl>
       <Notification>
        <Id>04lR00000004z4uIAA</Id>
        <sObject xsi:type="sf:OpportunityLineItem" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
         <sf:Id>00k40000004PHXtAAO</sf:Id>
        </sObject>
       </Notification>
       <Notification>
        <Id>04lR00000004z4qIAA</Id>
        <sObject xsi:type="sf:OpportunityLineItem" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
         <sf:Id>00k40000004P5IhAAK</sf:Id>
         </sObject>
       </Notification>
       <Notification>
        <Id>04lR00000004z4vIAA</Id>
        <sObject xsi:type="sf:OpportunityLineItem" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
         <sf:Id>00k40000004PK51AAG</sf:Id>
        </sObject>
       </Notification>
     </soapenv:Body>
    </soapenv:Envelope>
    I wrote the script on the basis of the 1 to 1 relationship and hence, it could not. I will rewrite it on Monday morning so it can iterate through multiple line items and process all the SOAP inbound messages. This will allow for mutliple records to process

    Just be aware that there is a 1 to many relationship when it comes to SOAP messages from Salesforce and adjust your processes accordingly.


    I will keep this thread updated with the changes and let you know how it goes

    ~Mike

+ Reply to Thread
Page 1 of 5 1 2 3 ... LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

SEO by vBSEO 3.5.1