Copying Contacts Between Accounts

Regular Participant

Copying Contacts Between Accounts

Hello,

 

I am a freelancer working on a project to consolidate contacts between a number of accounts. There is one main account where all of the contacts from other individual accounts need to be consolidated. They want to maintain contact lists. So if site A has a list A_Contacts, that list and its members should be duplicated on the main account.

 

The code is included below.

 

I have the access tokens working (thanks to Shannon!). Using the PHP wrappers, I create a ConstantContact object for the main site and then loop through the others with the appropriate username and access token. For each site I do a getLists and loop through the lists. If the list does not exist on the main site I make a new ContactList and addList to main.

 

I then loop through the contacts in the list and try to add them to the main list. I assume that account specific information should not be copied. Things like link, id, updated, status should not be copied because they are created as part of the contact creation process. Also, I want the lists to refer to the list on the main account not the other account.

 

I tried casting the Contact object to an array and then unsetting the site specific fields and then use that array to create a new contact. I got a strange error message about something Atomic.

 

In the example below, in the CopyContact function I dump the Contact into xml and then use the createStructure method to create an array. I get a "Fatal error: Call to a member function Attributes() on a non-object in Components.php on line 208".

 

I suppose I will have to brute force the solution and copy each of the elements from the Contact to an array and then create the new contact.

 

Any advice would be appreciated.

Thanks,

-Tom

 

 

 

 

NOTE: there is an array of objects ($accts) containing the user name and access token for each site that is not shown for security.

function CopyContact($cont)
{
    $xmlcontact = $cont->createXml();        // export contact to xml
    $arraycontact =  $cont->createStruct($xmlcontact);    // convert xml to associative array
    // remove site specific information
    unset($arraycontact['link']);
    unset($arraycontact['id']);
    unset($arraycontact['updated']);
    unset($arraycontact['status']);
    unset($arraycontact['lists']);
    
    return $arraycontact;
}

// get the current corporate lists
$corplists = $corpcc->getLists();
$corplistnames = array();
for ($i = 0; $i < count($corplists[lists]); $i++)
{
    $corplistnames[$corplists[lists][$i]->name] = $corplists[lists][$i];
}

for ($idx = 1; $idx < count($accts); $idx++)
{    
    echo("<br/>Starting: i: " . $idx . " Name: " . $accts[$idx]->username);
    // get per account access
    $ConstantContact = new ConstantContact ( "oauth2", $apikey, $accts[$idx]->username, $accts[$idx]->token );
    
    // get per account lists
    $buds = $ConstantContact->getLists();
    for ($i = 0; $i < count($buds[lists]); $i++)
    {
        echo("<br/> List: " . $buds[lists][$i]->name);
        if (!isset($corplistnames[$buds[lists][$i]->name]))
        {
            echo("<br/> New List");
            // create new list in corp
            $cclist = new ContactList(array("name"=>$buds[lists][$i]->name));
            $corpcc->addList($cclist);
        }
        else
        {
            $cclist = $corplistnames[$corplists[lists][$i]->name];
        }
        // copy each entry to the corp list
        $members = $ConstantContact->getListMembers($buds[lists][$i]);
        echo("<br/> DumpListMembers: ");
        echo(print_r($members[members][0],1));
        for ($memidx = 0; $memidx < count($members[members]); $memidx++)
        {
            echo("<br/> ListMember: " . $member[members][$memidx]->emailAddress . "<br/>***<br/>");
            
            $newmember = CopyContact($members[members][$memidx]);
            $newmember[lists] = $cclist->id;
            $mynew = new Contact($newmember);
            echo("<br/>Dump NewContact: " . print_r($mynew,1));
            $corpcc->addContact($mynew);
        }
    }

    unset($ConstantContact);
}

11 REPLIES 11
Employee

Hi Tom,

 

Yeah, I can see why you would be getting the Atom:entry error (400) in this case.  Contacts should be added to an account with fewer things than we return to you; you could look here for the minimal XML to post for a new contact and try to parse only those fields that should be changed in that payload.  You could also parse just the fields you want from contacts and use our bulk URL encoded method, included in both wrappers, which is less data intensive and requires far fewer calls to our server.

 

You're probably getting the non-object error because the wrapper requires the List to be an already created object before setting the list for a contact.

 

You're using our V1 (XML) wrapper here, and I think this would be easier with our V2 (JSON) API, since it gives better error messages and doesn't require so much of the useless information to be passed to us.  Added bonus: a much longer shelf life.  Here is a link to our V2 .net sdk if you're at all willing to switch over.  Sorry to give you that feedback after you began; I don't think I realized you were using the XML API.

 

Best Regards,

Shannon W.

API Support Specialist

Occasional Participant

Shannon,

 

Thanks for all the help. I converted to V2 and I am using the libraries. I only have one problem left.

 

When I create a new contact on the main account, I create an array and pass the values from the contact in the other account. The array values are being created but they are not being included in the new contact. This is where I create the new contact:

 

function CopyContact($cont)
{
    $contact_array = array(
    'first_name' => $cont->first_name,
    'middle_name' => $cont->middle_name,
    'last_name' => $cont->last_name,
    'prefix_name' => $cont->prefix_name,
    'job_title' => $cont->job_title,
    'home_phone' => $cont->home_phone,
    'work_phone' => $cont->work_phone,
    'cell_phone' => $cont->cell_phone,
    'fax' => $cont->fax,
    'company_name' => $cont->company_name,
    'email_addresses'=> array(),
    'addresses' => array(),
    'notes' => array(),
    'lists' => array(),
    'custom_fields' => array()
    );
    echo("<br/> --- " . print_r($contact_array,1) . " </br>");
    echo("<br/> -- in co: " . $cont->company_name . " title: " . $cont->job_title);

    $newcontact = new Contact;
    $newcontact->create($contact_array);
    
    for ($idx = 0; $idx < count($cont->email_addresses);$idx++)
    {
        $newcontact->addEmail($cont->email_addresses[$idx]->email_address);
    }
    for ($idx = 0; $idx < count($cont->addresses);$idx++)
    {
        $newcontact->addAddress($cont->addresses[$idx]);
    }
    for ($idx = 0; $idx < count($cont->custom_fields);$idx++)
    {
        $newcontact->addCustomField($cont->custom_fields[$idx]);
    }
    for ($idx = 0; $idx < count($cont->notes);$idx++)
    {
        $newcontact->notes[$idx] = $cont->notes[$idx];
    }
    
    return $newcontact;
}

As you can see I create the array. Then I make a Contact object and create it with the array. I then add all of the array fields in the loops at the bottom. All of the loops at the bottom work fine. When I do the print_r on the contact_array I have:

 

--- Array ( [first_name] => Clark [middle_name] => nomi [last_name] => Kent [prefix_name] => [job_title] => Super Hero [home_phone] => 909-808-7777 [work_phone] => north pole [cell_phone] => [fax] => [company_name] => SuperMan Inc [email_addresses] => Array ( ) [addresses] => Array ( ) [notes] => Array ( ) [lists] => Array ( ) [custom_fields] => Array ( ) )

 

None of these fields show up in the new contact. What am I missing here?

 

I would also like to be able to update contacts to be sure that any updates in the other accounts show up in the main. I think I would use the same process to create a new contact object and then say updateContact instead of addContact. Think that will work?

 

I would like to use the getContacts method of the ConstantContact object because it lets me limit the results by a modified since parameter. However, my testing shows that the contacts returned from this (and from most methods) do not have list information. The result is I have to do a getLists for each account, and then getContactsFromList to know what lists they belong to. This method doesn't support the modifed since parameter. The other accounts currently have over 500 contacts and are growing.  The consolidation is going to run daily but it would still be better if I could use the modified since.

 

Finally, it would be nice if, when you add a contact that already exists you could get a return code instead of a fatal error. It makes me do a lot of checking that you are already doing.

 

Thanks,

-Tom

Occasional Participant

I have one other question I forgot to put my other post. Do you have an example of using the next result from getContactsFromList (or any other method)? According to the documentation there is a limit of 50 results per call but you can pass a next value from a previous call. I'm assuming it might be something like

 

$more = true;

while ($more)

{

        $contacts = $ConstantContact->getContactsFromList($accts[$idx]->token,$lists[$i]);

        for ($cidx = 0; $cidx < count($contacts->results); $cidx++)
        {

          ....

         }

        if (!strcmp($contacts->results[50]->emailAddress,"next"))

               $params = array("page" => 50, "next" => $contcacts->results[50]);

        else

               $more = false;

}

 

Thanks,

-Tom

Hi Tom,

 

I'm not sure if I missed it or if you omitted it, but you are calling the addContact method at some point to add the new contact to the second account, right?  You will need to do a getLists from the second account and assign a list from the second account to the new contact, since the wrapper will expect the contact list provided to already be an object in the wrapper as returned by us.

 

If I understand correctly, the array you printed out for the returned contact has everything you want, but when you add the contact to the second account, the details aren't captured, but the contact is added to the second account.

 

As for the modified since contacts, I would recommend using that modified since query parameter on getContacts, then use the getContact ByID method for any contacts returned.  That get by ID method will return the contact's lists.  This is much less data intensive and more efficient than recreating a whole list.

 

Sorry I don't have an example of the next link being used for the new PHP sdk, but I do believe you take the next link returned from the getContacts returned body and pass that into the same method as a parameter.

 

Best Regards,

Shannon W.

API Support Specialist

Regular Participant

Shannon,

 

Yes, there is another part of the code that adds the contact to the main account, after adding the lists. The contact gets added and to the appropriate lists. But none of the information in the function I showed gets copied. All of the sub fileds work (address, notes, custom fields etc,) that get processed through the loops work but not the fields that I am passing in through the array.

 

So let me put the question in another way: if you were creating a contact from scratch and wanted to include those parameters (home phone, first, middle last name etc.) how would you do it? MY understanding is that I would create an associative array of parameters and pass that array in.

 

I'm hoping I can use the same type of associative array - create a contact and then do updateContact.

 

I'll try to rearrange my logic. I would prefer to only get updated contacts.

 

Thanks for the quick replies to an unusual type of app!

-Tom

Hi Tom,

 

You're very welcome.  That was a good way to rephrase.  It's a little strange that the email address is getting added to the account, since the email address is stored inside an array, but that the other array contents aren't populated.  I see that the non-array elements are built and associated with $cont, but that the array elements aren't at the beginning of your function.  If you haven't already, I'd echo the values of what you're adding for the arrays (like $cont->custom_fields[$idx]) to the screen to see if they have anything stored in them.  Here is a small script to show the use of the array-building methods for a contact; As you can see I'm using the same methods but I can be sure the contents aren't empty since they're just strings.

 

$contact = new Contact();

$contact->first_name = "david";
$contact->addCustomField("Custom text");
$contact->addAddress("5 Main St");
$contact->addList("1");
$contact->addEmail("test@example.com");

$responseContact = $cc->addContact($accessToken, $contact, $actionByVisitor = true); 

 

 

Best Regards,

Shannon W.

API Support Specialist

Regular Participant

Shannon,

 

We're making progress. I am trying to use the GetContacts with the modified-since parameter. The difficulty is that the lists that get returned are empty except for th id. I then try to do a getLists based on the id and about 3 times out of 5, I get an exception for invalid list.

 

my logic is:

 

$contacts = $ConstantContact->getContacts($acc_token,$params);

for ($i = 0; $i < count($contacts->results); $i++)

{

    $contact = $ConstantContact->getContact($acc_token.$contacts->results[$i]->id);

    $clists = $contact->lists;

   for ($cidx = 0; $cidx < count($clists); $cidx++)

   {

       $list_obj = $ConstantContact->getList($clists[$cidx]->id);

   }

}

 

The list array returned from getContacts only has ids, no names. So I have to do the getList to get the names. But three out of five times it says invalid.

 

It would be more efficient than looping through each list and checkiung every contact but it only copies about 20% of the contacts. I get 99% with more less efficient method.

 

BTW, it seems that interactive users can input invalid email addresses but API users can't. I have about 20 contacts ( out of about 2,500) that generate an invalid email address even though I am copying them from another account.

 

Thanks,

-Tom

Hi Tom,

 

Sorry for the trouble; I don't see anything wrong with your logic.  You could try trimming and converting the id ($cidx) to a string, even though theoretically, it should be useable as-is.  What is the exact error message you're getting?

 

Best Regards,

Shannon W.

API Support Specialist

Regular Participant

Before I try to get the lists I dump the lists array from the contact. It looks like this: --- Array ( [0] => Ctct\Components\Contacts\ContactList Object ( [id] => 4 [name] => [status] => ACTIVE [contact_count] => ) [1] => Ctct\Components\Contacts\ContactList Object ( [id] => 26 [name] => [status] => ACTIVE [contact_count] => ) )

 

The error I get on the get lists:"error_key":"http.status.not_found","error_message":"No matching resource was found for the supplied URL."

 

I don't know why the list array in the contact object only has ids, no names. But most of the time when I try to do a getList with the access token and the list id, I get an exception.

 

Thanks for all the help.

-Tom

 

 

Hi Tom,

 

I have assembled your example logic onto my development server and haven't had any issues getting it to run and print out matching lists. Here is the exact code I used to get it working:

 

 

<?php
require 'src/Ctct/autoload.php';
use Ctct\ConstantContact;
use Ctct\Services;
$acc_token="redacted";
$ConstantContact = new ConstantContact('redacted');

$params = "?modified_since=2013-05-01T19:00:00.000Z";
$contacts = $ConstantContact->getContacts($acc_token,$params);
for ($i = 0; $i < count($contacts->results); $i++)
{
    $contact = $ConstantContact->getContact($acc_token,$contacts->results[$i]->id);
    $clists = $contact->lists;
   for ($cidx = 0; $cidx < count($clists); $cidx++)
   {
       $list_obj[] = $ConstantContact->getList($acc_token,$clists[$cidx]->id);
   }
}
echo "<pre>".print_r($list_obj,1)."</pre>";
die(1);
?> 

 

 

One thing that might give some answers is to isolate the list ID that is causing the problem, and check for that same list ID in your Constant Contact account.

 

To check a list ID, log in to your Constant Contact account and navigate to the list in question and click on it to view the contacts. After this, it will show a URL similar to this in the address bar:

https://ui.constantcontact.com/rnavmap/em/contacts/browse?listId=1&srchwithinlst=true

 

What you're after is the listId=1, which contains the ID for the list with that name. By doing this, you can check to make sure that the list ID that is showing up via API actually exists within your Constant Contact account.

 

Lastly, if you would like to have us look at the code that is performing this operation in a more private manner, you can contact our API support team via email at webservices@constantcontact.com so that the communication is private vs. a public forum. If you do want to email us, it would be greatly helpful if you could include both of the usernames for the accounts that you are copying contacts between so that we can effectively research the issue.

 

Let me know if you have more questions!

 

Sincerely,

Elijah G.

API Support Specialist

 

Elijah G.
API Support Engineer
Frequent Participant

Here's a sample code that I wrote to demonstrate pagination of getContactsFromList(). I assume it works the same for oher methods. I know this thread is old but I'm hoping to save time to people who like me, come here searching for the same thing.

 

Developer Portal

View API documentation, code samples, get your API key.

Visit Page