Integracja salesforce rest api w php   biblioteka open source

W ostatnich miesiącach coraz więcej klientów zgłasza się do nas z zapytaniem o możliwość outsource’owania do XSolve projektu, w którym szczególny nacisk miałby zostać położony na integrację z wykorzystywanym przez nich i niezwykle popularnym na świecie systemem CRM Salesforce. Oczywiście nie boimy się wyzwań i taka integracja nie stanowi dla nas większego problemu, jednak zauważyliśmy, że istniejące rozwiązania w PHP nie spełniają naszych standardów i są napisane w technologiach, które śmiało można nazwać przestarzałymi.

 

Oficjalna wersja SDK dostarczana przez samego Salesforce’a jest napisana w PHP 5, którego aktywne wsparcie zostało zamknięte z końcem ubiegłego roku. Poza tym znaleźliśmy w tej bibliotece kilka problemów:

  •  nie wykorzystuje autoloadingu (PSR-4),

  •  nie jest zgodna z PSR-1/2,

  •  służy do komunikacji z Soap API, brak jest klienta REST, który naszym zdaniem byłby lepszy pod względem, między innymi, wydajności dla podstawowych scenariuszy, które chcieliśmy pokryć (głównie operacje CRUD, podstawowa synchronizacja danych),

  •  nie jest rozwijana od marca 2015, co oznacza że wspiera API maksymalnie do wersji 27, przy obecnej 38.

 

Oczywiście nie zakończyliśmy poszukiwań na oficjalnym SDK, ale przyjrzeliśmy się również gotowym rozwiązaniom stworzonym przez społeczność open source. Większość nie jest już rozwijanych ani utrzymywanych. Wyjątkiem jest tutaj eventfarm/restforcephp, który podczas wykonywania researchu nie był tak rozwinięty jak w momencie, w którym piszemy ten artykuł.

 

Rozwiązaniem tego problemu mogłoby być integrowanie się z RESTowym API za każdym razem tworząc dedykowane rozwiązanie dostosowane do konkretnego projektu. Jednak nie byłoby to wydajne ze względu na konieczność duplikowania kodu oraz czas potrzebny do dostosowywania zmian do nowszych wersji API Salesforce.

 

Dlatego właśnie zdecydowaliśmy się na rozpoczęcie prac nad biblioteką, którą obecnie można znaleźć pod adresem https://github.com/xsolve-pl/salesforce-client.

 

Technologie

 

W naszym zamyśle tworzony klient API powinien był być w miarę możliwości lekki, z maksymalnie ograniczoną liczbą zewnętrznych zależności, aby umożliwić względnie łatwą instalację i uniknąć, na ile to możliwe, konfliktów z innymi zależnościami projektu, do którego SDK byłoby dodawane.

 

Jeśli chodzi o wersję języka PHP, naturalnym wyborem była obecnie wspierana wersja 7. Rozważaliśmy również dostosowanie projektu do PHP 7.1. Zrezygnowaliśmy jednak z tego, ponieważ w chwili, w której rozpoczynaliśmy prace nad biblioteką, ta wersja języka miała zaledwie kilka dni.

 

 Przyglądając się liście zależności projektu można zauważyć, że dla celów serializacji danych do postaci, którą można przesyłać pomiędzy aplikacją a RESTowym API wykorzystujemy JMS Serializer ( http://jmsyst.com/libs/serializer ). Jest to bardzo dobrze znana, stabilna i szeroko przez nas wykorzystywana w wielu projektach biblioteka, której zadaniem jest przekształcanie własnych obiektów przy pomocy odpowiedniej konfiguracji (mapowania) do danego formatu tekstowego (domyślnie wspierane: JSON, XML i YAML) i odwrotnie - z formatu tekstowego do postaci obiektów.

 

Znana z innych języków programowania funkcjonalność enumeracji zapewniana jest przez niewielką bibliotekę eloquent/enumeration. Dzięki niej możliwe jest używanie typehintów dla enumeratorów, co pozwala uniknąć konieczności każdorazowego sprawdzenia poprawności przekazanej jako enumerator wartości.

 

Salesforce Client nie posiada własnej implementacji klienta HTTP, aby zapewnić elastyczność rozwiązania operuje na interfejsach z biblioteki HTTPlug. Istnieje dla niej wiele gotowych adapterów, a napisanie własnego jest bardzo proste. Przykładowo można skorzystać z nakładki na popularnego guzzle - php-http/guzzle6-adapter.

 

Jako podstawową wersję wersję uwierzytelnienia w Salesforce API wykorzystaliśmy flow z użyciem loginu oraz hasła, jednak w razie konieczności można łatwo dodać inną implementację. Dodatkowo, aby uniknąć odpytywania serwera za każdym razem o nowy token konieczne jest gdzieś go przechować. W naszej bibliotece można znaleźć gotowe rozwiązania do przechowywania access tokena: w Redisie z wykorzystaniem blablacar/redis-client czy najprostszej implementacji storage’a wykorzystującego pamięć podręczną i przechowującego dane tokena w obiekcie. Nic nie stoi jednak na przeszkodzie, aby stworzyć własną implementację TokenStorageInterface i zapisać go w inny sposób - na przykład w relacyjnej bazie danych.

 

Zakres funkcjonalny

 

Projekt stworzenia biblioteki rozpoczęliśmy od przeanalizowania najczęstszych przypadków użycia tak, aby pokryć zakresem funkcjonalnym jak najwięcej najbardziej popularnych zapytań do API. Wspólnie doszliśmy do wniosku, że takim najczęściej występującym problemem jest konieczność synchronizowania obiektów występujących w naszych aplikacjach z ich odpowiednikami w Salesforce CRM. Następnym krokiem było stworzenie listy obiektów z Salesforce, które są najczęściej wykorzystywane przy integracji oraz elementów które naszym zdaniem mogły znacząco przyspieszyć pracę zarówno z biblioteką jak i samym API.

 

W pierwszej iteracji powstała swojego rodzaju podstawa projektu - prosty klient REST umożliwiający uwierzytelnienie się w API Salesforce, zapisanie access tokena i wysłanie dowolnego zapytania do endpointa i zwrócenie wyniku.

 

W drugim sprincie skoncentrowaliśmy się na maksymalnym uproszczeniu korzystania z API przez ustrukturyzowanie popularnych zapytań oraz stworzenie abstrakcji od najpopularniejszych obiektów występujących w Salesforce. Wynikiem tego było:

  •  powstanie QueryBuildera który w łatwy sposób pozwala tworzyć zapytania SOQL;

  •  wprowadzenie ujednoliconego interfejsu repozytorium dla operacji CRUD;

  •  dodanie obsługi wybranych wcześniej standardowych obiektów (SObjects): Account, Case, Contact, Contract, Lead, Opportunity, Order, Pricebook2,  PricebookEntry, Product2, Solution.

 

Biblioteka pokrywa operacje CRUD na modelach, jednak nie byliśmy w stanie stworzyć mapowania dla każdego obiektu, który znajduje się w Salesforce i jest dostępny w API - po prostu jest ich zbyt wiele. Jeśli w naszej bibliotece nie znalazłeś modelu, którego akurat potrzebujesz, to w dokumentacji znajdziesz informację w jaki sposób możesz go stworzyć. Zachęcamy również do podzielenia się jego implementacją przez otwarcie pull requesta do repozytorium ;)

 

Oczywiście można również korzystać z biblioteki bez mapowania obiektu, więcej na ten temat znajdziesz w dokumentacji.

 

Przykłady użycia

 

Często działania wykonywane przez użytkowników w aplikacji powinny mieć również odzwierciedlenie w Salesforce, aby móc lepiej je śledzić i planować przyszłe działania. Przykładowo gdy prowadzimy sieć sklepów:

  1. Klient złoży zamówienie w jednym z nich i chcielibyśmy dodać informacje z wszystkimi danymi zamówienia.

  2.  Ktoś założy konto w naszym sklepie i chcielibyśmy dodać go jako potencjalnego klienta.

  3.  Inna firma złożyła zamówienie cykliczne i chcemy mieć informacje w CRM o tym, kiedy należy zakończyć dostawy.

  4.  Wyszukać czy podany adres email znajduje się już w naszych kontaktach w salesforce.

 

Oczywiście przykłady można mnożyć i zależą one od projektu, jednak wszystkie wymienione wyżej akcje są możliwe do wykonania przy użyciu naszej biblioteki, co więcej nie potrzeba wiedzieć jak odpowiednio przygotować zapytanie, lub jakie pola posiada obiekt. Aby nie cytować dokumentacji, którą można znaleźć na GitHubie, poniżej przedstawiony jest przykład dla punktu 2 z wykorzystaniem SObjectRepository:

Równie łatwo możemy zrealizować zadanie z punktu 4. Służą do tego QueryBuilder i QueryExecutor:

 

 

 

Plany na przyszłość

 

W najbliższej przyszłości planujemy dodać kolejne modele reprezentujące SObjects, natomiast z uwagi na fakt, że najczęściej wykorzystywanym przez nas frameworkiem PHP jest Symfony3, to oczywistym krokiem zdaje się przygotowanie bundle’a i zintegrowanie z kontenerem Dependency Injection, aby jeszcze bardziej ułatwić korzystanie z biblioteki. Poza konfiguracją DI w skład bundle’a może również wejść integracja z eventami Doctrine 2. Dałoby to możliwość łatwej synchronizacji danych pomiędzy aplikacją a Salesforce.

 

Zachęcamy do kontrybuowania w projekcie, zgłaszania błędów, pomysłów i poprawek. Czekamy na Twój cenny feedback i mamy nadzieję, że Salesforce Client okaże się dla Ciebie przydatny!

 

 

Autorzy:

Paweł Krynicki (Senior Software Developer w XSolve)

Adrian Waler (Junior Software Developer w XSolve)