Web Services. Примеры работы

Как я уже говорил в первой статье данного цикла, веб-сервисы SugarCRM представлены двумя протоколами: SOAP и REST. Почитать об их различии можно здесь http://citforum.ru/internet/webservice/soap_rest/.

Но что же использовать? Что лучше? Однозначного ответа на этот вопрос нет. Многие гиганты веб-индустрии также предоставляют право выбора пользователю, поддерживая и протокол SOAP, и протокол REST.

Для SugarCRM я лично начинал использовать SOAP, так как было намного больше примеров использования именно данного протокола, но постепенно мне показалось более «правильным» использовать именно REST протокол.

Те, кому будет интересно использование REST протокола, могут использовать данную статью http://www.ibm.com/developerworks/library/x-sugarcrmrest/index.html как пособие. Написана она Джоном Мертиком, одним из разработчиков SugarCRM.

А мы же разберем несколько примеров использования SOAP протокола.

Для работы через Soap протокол воспользуемся стандартным классом php SoapClient.

Создадим экземпляр данного класса:

// location - путь к soap.php файлу, лежащему в корне CRM
// uri - URL адрес вашей SugarCMR
$options = array(
	"location" => 'http://127.0.0.1/sugar6/soap.php',
	"uri" => 'http://127.0.0.1/sugar6',
);
$client = new SoapClient(NULL, $options);

Залогинимся в нашу ЦРМ и получим список модулей, которые доступны для данного пользователя.

// Массив с информацией для авторизации
$user_auth = array(
	'user_name' => 'admin',
	'password' => MD5('12345'),
	'version' => '.01'
);

$response = $client->login($user_auth, 'Soap tested application');

// Сохраняем id ссесии, она обязательно потребуется при вызове любых функций
$session = $response->id;

$response = $client->get_available_modules($session);
var_dump($response->modules);

Так как я залогинился под администратором, то я увижу список всех модулей, доступных в SugarCRM.

Приступим наконец-то к чему-то более полезному. Например, извлечем все записи контрагентов, где в имени присутствует SugarTalk, и отсортируем по дате создания. Как мы помним из обзора функций WEB API, для этого используется функция get_entry_list. Получим следующий код:

// так как SugarCRM будет делать join дугих таблиц, всегда определяйте, к какой таблице относится поле в запросе,
// иначе получите стандартный ответ sql : Column in where clause is ambiguous
$response = $client->get_entry_list($session, 'Accounts', 'accounts.name LIKE "%SugarTalk%"', 'accounts.date_entered DESC');

$count_records = $response->result_count;
echo 'Количество записей : ' . $count_records . '<br>';

$accounts = $response->entry_list;
foreach ($accounts as &$acc)
{
  //Преобразуем в читабельный для нас массив
$acc->arrayFields = NameValueToArray($acc->name_value_list);
echo 'Название компании:<b>' . $acc->arrayFields['name']
. '</b>&nbsp;&nbsp;' . 'Запись изменена:<b>' . $acc->arrayFields['date_modified']  . '</b><br>';
}

В данном коде мы извлекли все записи контрагентов, попадающих под наше условие. Самые наблюдательные уже заметили функцию NameValueToArray. Код данной функции:

function NameValueToArray($items){
    $out_arr = array();
    foreach ($items as $item)
    {
    	$out_arr[$item->name] = $item->value;
    }
    return $out_arr;
}

Эта короткая функция очень полезна. Дело в том, что все значения полей CRM возвращает в виде массива объектов. obj ('name'=>'name_field', 'value' => 'field_value'), а данная функция преобразует данную конструкцию в более привычный для нас ассоциативный массив.

Далее получим id всех контактов контрагента. Для этого воспользуемся функцией get_relationships, которая возвращает нам id всех связанных записей, удовлетворяющих условию. Так как нам необходимо получить все записи, условие мы задавать не будем.

//Возьмем id для первого контрагента
$account_id = $accounts[0]->arrayFields['id'];
//Извлечем все контакты для этого контрагента
$ids = $client->get_relationships($session, 'Accounts', $account_id, 'Contacts');
foreach ($ids->ids as $contact)
{
	echo 'ID контакта: ' . $contact->id . '<br>';
}

Я получил данный результат выполнения всех этих функций:

SugarCRM WEB API результат

Давайте напоследок разберем еще один актуальный пример: извлечение записи по email-адресу.
Я думаю, всем известно, что SugarCRM хранит email-адреса в отдельной таблице email_addresses, и для работы с ними используется модуль EmailAddresses. Таким образом реализуется связь один-ко-многим.
Для извлечения записи через SOAP протокол, можно, конечно, воспользоваться стандартной схемой:

$response = $client->get_entry_list($session, 'EmailAddresses', 'email_addresses.email_address="hr.vegan.im@example.us"');

Далее получаем relationship, а затем, имея id записи, get_entry нужной нам записи.

Но можно данную схему реализовать «красивее» и в одну операцию.
Для примера будем искать предварительный контакт с email-адресом dev11@example.de.

Тогда данный запрос будет следующим:

//email для поиска
$email = 'dev11@example.de';
// формируем условие where
$filter = "leads.id in (
  select eabr.bean_id
  from email_addr_bean_rel eabr
  join email_addresses ea on eabr.email_address_id = ea.id and ea.deleted = 0
  where eabr.bean_module = 'Leads' and ea.email_address = '$email' and eabr.deleted=0)";
$response = $client->get_entry_list($session, 'Leads', $filter);
$leads = $response->entry_list;

Как видно, мы просто добавили в третий параметр (условие выборки — WHERE) необходимость извлечь только записи с id, полученными в результате подзапроса к таблице email_addr_bean_rel и email_addresses с поиском нужного нам email-адреса.

На этом я заканчиваю короткое HOW-TO по использованию SOAP протокола для связи с SugarCRM. И еще раз приведу полный код того, что мы с вами делали.

<?php
function NameValueToArray($items){
    $out_arr = array();
    foreach ($items as $item)
    {
    	$out_arr[$item->name] = $item->value;
    }
    return $out_arr;
}

// location - путь к soap.php файлу, лежащему в корне CRM
// uri - URL адрес вашей SugarCMR
$options = array(
	"location" => 'http://127.0.0.1/sugar6/soap.php',
	"uri" => 'http://127.0.0.1/sugar6',
);

$client = new SoapClient(NULL, $options);

// Массив с информацией для авторизации
$user_auth = array(
	'user_name' => 'admin',
	'password' => MD5('12345'),
	'version' => '.01'
);

$response = $client->login($user_auth, 'Soap tested application');

// Сохраняем id ссесии, она обязательно потребуется при вызове любых функций
$session = $response->id;

$response = $client->get_available_modules($session);

//var_dump($response->modules);

// Извлечем все записи контрагентов, где в имени присутствует SugarTalk, и отсортируем по дате создания
// так как SugarCRM будет делать join дугих таблиц, всегда определяйте, к какой таблице относится поле в запросе,
// иначе получите стандартный ответ sql : Column in where clause is ambiguous
$response = $client->get_entry_list($session, 'Accounts', 'accounts.name LIKE "%SugarTalk%"', 'accounts.date_entered DESC');

$count_records = $response->result_count;
echo 'Количество записей : ' . $count_records . '<br>';

$accounts = $response->entry_list;

foreach ($accounts as &$acc)
{
	$acc->arrayFields = NameValueToArray($acc->name_value_list); //Преобразуем в читабельный для нас массив
	echo 'Название компании:<b>' . $acc->arrayFields['name']
		  . '</b>&nbsp;&nbsp;' . 'Запись изменена:<b>' . $acc->arrayFields['date_modified']  . '</b><br>';
}
echo '<br><br>';
//Возьмем id для первого контрагента
$account_id = $accounts[0]->arrayFields['id'];
//Извлечем все контакты для этого контрагента
$ids = $client->get_relationships($session, 'Accounts', $account_id, 'Contacts');

foreach ($ids->ids as $contact)
{
	echo 'ID контакта: ' . $contact->id . '<br>';
}
//email для поиска
$email = 'dev11@example.de';
// формируем условие where
$filter = "leads.id in (
  select eabr.bean_id
  from email_addr_bean_rel eabr
  join email_addresses ea on eabr.email_address_id = ea.id and ea.deleted = 0
  where eabr.bean_module = 'Leads' and ea.email_address = '$email' and eabr.deleted=0)";
$response = $client->get_entry_list($session, 'Leads', $filter);
$leads = $response->entry_list;
// предполагаю что запись с данным email будет только одна, либо же использовать цикл
$lead = $leads[0];
//var_dump($lead);
?>

Высказать своё мнение о статье Вы можете в комментариях, если Вас интересуют какие либо вопросы или дополнения, то обсуждение по данной теме доступно на нашем форуме.

Есть 19 коммент. к “Web Services. Примеры работы”

  1. Булат:

    Опубликовали бы еще хак позволяющий по соапу делать поиск по email адресу

  2. Булат:

    я про 6.0 версию

    ну или просто подскажите как мне получить объект аккаунт или лид, с заданным почтовым адресом, ведь адреса хранятся в другой таблице

  3. Coldrex:

    Вылазит единственная надпись «Not A Valid Entry Point»

    С чем это связанно?

  4. Шуга Админ:

    В начале каждого файла в ЦРМ происходит проверка, правильная ли это «точка» входа, это способ защиты от несанкционированного вызова файлов. Все вызовы могут проходить только через entry point, а также специальные скрипты, такие как index.php, campaign_tracker.php и т.д. В том числе и soap.php, так что видимо вы указали не правильный http путь к файлу soap.php. Если вы уверены что все правильно, попробуйте добавить в самое начало своего скрипта следующий php код

    if(!defined('sugarEntry')) define('sugarEntry', true);  
    

  5. Coldrex:

    Теперь вылазит ошибка

    Warning: require_once(../include/nusoap/nusoap.php) [function.require-once]: failed to open stream: No such file or directory in Y:\home\test2.ru\www\SoapTest.php on line 61 Fatal error: require_once() [function.require]: Failed opening required '../include/nusoap/nusoap.php' (include_path='.;/usr/local/php5/PEAR') in Y:\home\test2.ru\www\SoapTest.php on line 61

    вроде все правильно прописал

    require_once ('../include/nusoap/nusoap.php'); $soapclient = new nusoapclient('http://localhost/sugarcrm/soap.php');

    • Шуга Админ:

      У вас проблема с путями. Вы запускаете скрипт SoapTest.php, который лежит я так понимаю в корне CRM test2.ru, и в этом скрипте, подключаете файл который находится еще выше данного корня

      require_once('../include/nusoap/nusoap.php');

      где ... означают поднятие на директорию выше.

      Что то мне подсказывает, что вы пытаетесь запустить стандартный тестовый файл SoapTest.php, который лежит в директории examples, при этом перенеся его в другую директорию и не сменив пути. Вам надо или поменять пути на новые, или же запускать его непосредственно из директории examples.

  6. Булат:

    жаль тут нет форума, чтобы можно было какие-то вещи обсудить и поделится опытом.

    Вот у меня вопрос есть по Logic Hooks

    у меня есть импорт с веба Leads через SOAP, мне не нужны дубликаты (считаем уникальным email), как мне быть? как автоматизировать. Хотелось бы иметь возможность в logic hook при попытке добавления lead с уже имеющимся email, возвращать id уже существующей записи, ну и сделать заодно там пометку, но я так понимаю что logic hook не позволяет сделать такие вещи? Очень трудно переходить с разработки под SalesForce на SugarCRM, но необходимо адаптировать продукт :(

    • Шуга Админ:

      Это очень хорошо, что вы хотите поделиться опытом, но боюсь, что таких людей немного и пока нет острой потребности в этом. Но мы обязательно подумаем над вашим предложением! :wink:

      Насчет вашего вопроса: не совсем понятно, что вы все-таки хотите. Получать записи с уникальным email через SOAP или не давать возможности добавлять записи с дублирующимся email?

      Logic hooks — это обычные функции, в которых у вас есть ссылка на текущий объект, вы можете делать с ним что угодно. Проверить на уникальность email, выставить, если надо, значение какого-либо поля и т.д. Так что никаких ограничений на их возможные области применения я не вижу.

      Если же вы хотите реализовать это через SOAP, вы можете легко добавить свои функции для использования (даже в upgrade safe стиле). В данном случае, думаю, это будет наиболее правильным решением, чем пытаться использовать существующий функционал для такой задачи.

      • Булат:

        Ну я думаю и в России потихонечку бизнес начнет всё активнее и активнее приходить в CRM, а SugarCRM это одино из лучших предложений по соотношению цена/качество (хотя второе и очень хромает, но первая составляющая почти полностью невелирует это)

        Начну с конца. Да я уже сам пришел к выводу что одно из решений это дописать функционал для SOAP в upgrade safe стиле. Но есть проблема — нужно не допустить создания дубликатов и через веб-интерфейс.

        А теперь к началу. Я хочу в идеале, чтобы при попытке создать дубликат Lead (по полю email), мне возвращалось:

        1. через SOAP: id уже сущестующего lead, при этом нового не появлялось

        2. через веб-интерфейс, хотябы просто симпатичная ошибка, а в идеале и со ссылкой на 'правильный' Lead

        • Шуга Админ:

          1) Если вам просто надо не добавлять дубликаты через SOAP, то зачем расширять протокол? Достаточно вначале послать запрос на выборку записи с данным email. В случае если эта запись уже присутствует, вы получите ее id. Если нет, то тогда уже посылаете запрос на сохранение записи.

          2) Проверка полей на дубликаты уже реализована в модуле DevToolKit. Правда, не очень в плане быстродействия. Можете пользоваться при количестве записей до 10000-20000, если нужно больше, лучше написать свой. Мы не тестировали на работу с email, не знаю, работает ли он с полями модуля email. Если нет, в таком случае есть очень простое решение, основанное на javascript:

          В editviewdefs в секции templateMeta меняем код кнопки save:

          'form' => 
          array (
          'buttons' => 
            array ( 
            0 => 
            array (
              'customCode' => '<input title="{$APP.LBL_SAVE_BUTTON_TITLE}" accessKey="{$APP.LBL_SAVE_BUTTON_KEY}" class="button" type="submit" name="button" "onclick=\'this.form.return_action.value="{$returnAction}"; this.form.action.value="Save"; return checkDuplicates();\' value="  {$APP.LBL_SAVE_BUTTON_LABEL}  " >
                              <input title="{$APP.LBL_CANCEL_BUTTON_TITLE}" accessKey="{$APP.LBL_CANCEL_BUTTON_KEY}" class="button" type="submit" name="button" "onclick=\'this.form.action.value="{$returnAction}";\' value="  {$APP.LBL_CANCEL_BUTTON_LABEL}  ">'),
            ),
          ),
          

          Как видно, мы добавили свою функцию проверки, которая будет выглядеть следующим образом:

          function checkDuplicates()
          {
            //здесь проверяем запись на дубликат через ajax
            //в случае если запись не уникальная выдаем сообщение и return false;
            //в случае если все ок запускаем стандартную SugarCRM валидацию формы
            return check_form("EditView")
          }
          

          Вам остается только реализовать ajax запрос.

        • Булат:

          ой спасибо за наводку по поводу веб-интерфейса и яваскрипта. Будем делать

          а по поводу расширения стандартного класса. Дело в том что я использую массовый импорт данных

          т.е. по SOAP у меня добавляется через set_entries бывает по 200 объектов, после этого добавляется столько же объектов кастом модуля, потом с помощью set_relationships и возвращенных set_entries идентификаторов, я их связываю. Мне нужно чтобы Lead не создался, а кастом модуль привязался, к старому Lead, уже существующему в CRM. Т.е. в идеале надо чтобы set_entries вернуло id старого объекта, уже существующего... видимо для этого и придется расширять SOAP

  7. kolya27:

    А кто нибудь вообще привязывал к get_entry_list это — link_name_to_fields_array???

    Потому что не выходит никак. И еще — почему то по модулю contacts выводит данные не группированные по contacts.id, много дублирующих, ощущение — что сколько таблиц привязано-столько раз и дублирует каждую запись, и больше чем 2500 по кол-ву за раз из базы не возьмешь. Кто нибудь сталкивался с таким!?? (это Sugar 6.2.0 Profession).

  8. Сергей Олегович Савельев:

    Версия 6.3.0RC2

    Файл soap.php в корне, как и должен быть.

    Всё всё всё, уже ложусь.)

    Fatal error: Class 'SoapClient' not found in /home/mysite/mysite.ru/docs/sugarcrm/custom/modules/mymodul/before_save.php on line 13

    <?php
    class bs_c
    {
    	function bs_m() {
    		
    		// location - путь к soap.php файлу, лежащему в корне CRM
    		// uri - URL адрес вашей SugarCMR
    		$options = array(
    			"location" => 'http://mysite.ru/sugarcrm/soap.php',
    			"uri" => 'http://mysite.ru/sugarcrm/',
    			'version' => '.01'
    		);
    		$client = new SoapClient(NULL, $options);
                    
                    ...
    

  9. Сергей Олегович Савельев:

    На часах 01:20, я заработался, но сделал вроде всё правильно, тем не менее не работает. Нужна ваша поддержка, все пути уже перепробовал, но всё не так. Пожалуйста, подскажите — В чём может быть проблема?

    • Шуга Админ:

      Здравствуйте.

      Мы не оказываем поддержку в комментариях.

      Все ваши вопросы касательно темы данной статьи — вы можете задать в соответствующем разделе форума.

Написать комментарий

Вы должны войти чтобы комментировать.