Esta biblioteca PHP contém
Ele também contém vários exemplos/classes parciais que implementam um processo de sincronização de dados de contato de um sistema de origem no banco de dados de contatos/leads da Sharpspring. Isso funciona com um cache local de leads da Sharpspring, para minimizar chamadas de atualização para a API REST da Sharpspring.
A classe cliente pode ser usada de forma independente, embora esta biblioteca não tenha sido escrita para isso. Se você quiser construir seus próprios parâmetros e decodificar o resultado sozinho: vá em frente. Instancie-o; chame o método call(). Você não precisa do resto da biblioteca.
O objetivo da classe Connection é ajudar você a não ficar confuso sobre a comunicação com a API REST da Sharpspring. Ele tenta ajudar com isso das seguintes maneiras:
(A classe LocalLeadCache não é discutida aqui.)
use SharpSpring RestApi Connection ;
use SharpSpring RestApi CurlClient ;
// One thing this library does not make super easy: starting. Separation of
// concerns is considered more important, so (since the actual API call was
// abstracted into CurlClient) creating a new connection takes 2 lines instead
// of 1:
$ client = new CurlClient ([ ' account_id ' => . . . , ' secret_key ' => . . . ]);
$ api = new Connection ( $ client );
// Get all leads updated after a certain time (notation in 'local' timezone,
// though there is no formal definition of what 'local' entails).
$ leads = $ api -> getLeadsDateRange ( ' 2017-01-15 10:00:00 ' );
O código lança exceções para qualquer coisa estranha que encontrar... exceto por uma coisa: propriedades extras que ele vê na resposta, além dos valores da matriz esperados pelo método API/Conexão específico que você está chamando. Estes são ignorados por padrão; não se espera que eles sejam encontrados. Se você deseja que eles sejam registrados, passe um objeto logger compatível com PSR-3 como o segundo argumento para o construtor Connection.
Nos 'objetos' (arrays) da API REST da Sharpspring, os campos personalizados são referidos pelo nome do sistema, que muda por conta. Para permitir a gravação de código mais geral, o objeto Connection possui um mapeamento da propriedade customizada para o nome do sistema de campo. Quando esse mapeamento é definido (com sua própria escolha de nomes de propriedades), quaisquer parâmetros de 'objetos' em chamadas de API REST terão seus nomes de propriedades customizadas traduzidos automaticamente para os nomes de sistema de campo correspondentes.
Digamos que você tenha leads para sua loja de calçados, com um campo personalizado para o tamanho do calçado que você criou por meio da UI da Sharpspring, cujo nome do sistema saiu como shoe_size_384c1e3eacbb3. Os dois exemplos a seguir são equivalentes:
$ api -> createLead ([
' firstName ' => ' Roderik ' ,
' emailAddress ' => ' [email protected] ' ,
' shoe_size_384c1e3eacbb3 ' => 12 ,
]);
$ api -> setCustomProperties ( ' lead ' , [ ' shoeSize ' => ' shoe_size_384c1e3eacbb3 ' ]);
$ api -> createLead ([
' firstName ' => ' Roderik ' ,
' emailAddress ' => ' [email protected] ' ,
' shoeSize ' => 12 ,
]);
// Note that system names will still be OK; after setCustomProperties is called,
// you can still send in [...,'shoe_size_384c1e3eacbb3' => 12, ...]. Just don't
// set values for _both_ the field name _and_ its property alias, because then
// the library does not guarantee which of the two will be used.
A conversão automática só é feita para 'objetos' nos parâmetros de chamada da API. Os resultados retornados de chamadas de API não são adulterados. Se quiser que os nomes do sistema de campos personalizados nos resultados da API sejam convertidos novamente em seus nomes de propriedades personalizadas, você precisará fazer isso explicitamente:
$ api -> setCustomProperties ( ' lead ' , [ ' shoeSize ' => ' shoe_size_384c1e3eacbb3 ' ]);
$ leads = $ api -> getLeads ([ ' emailAddress ' => ' [email protected] ' ]);
$ lead = reset ( $ leads );
$ my_lead = $ api -> convertSystemNames ( ' lead ' , $ lead );
Usar matrizes para representação de 'objetos' da API é ótimo. Mas você pode preferir usar objetos/classes para eles. (Ele fornece preenchimento automático do IDE, o que também minimiza a chance de nomes de propriedades com letras maiúsculas incorretas que a API REST não suporta).
A classe base é ValueObject e neste momento existe uma classe Lead que implementa todos os campos conhecidos (com comentários sobre onde a documentação da API da Sharpspring está desatualizada).
O exemplo a seguir é igual ao acima:
/**
* If you have custom fields, you will want to define your own subclass:
*/
class ShoeStoreLead extends Lead
{
// Define your own properties:
public $ shoeSize ;
}
$ api -> setCustomProperties ( ' lead ' , [ ' shoeSize ' => ' shoe_size_384c1e3eacbb3 ' ]);
// This is the create call from above. Note createLead() accepts ValueObjects as
// well as arrays.
$ lead = new ShoeStoreLead ();
$ lead -> firstName = ' Roderik ' ;
$ lead -> emailAddress = [email protected]';
$ lead -> shoeSize = 12 ;
$ api -> createLead ( $ lead );
// And this is the 'get' call which puts the result into a new object:
$ leads = $ api -> getLeads ([ ' emailAddress ' => ' [email protected] ' ]);
$ lead = reset ( $ leads );
$ my_lead = $ api -> convertSystemNames ( ' lead ' , $ lead );
$ my_lead_obj = new ShoeStoreLead ( $ my_lead );
Obviamente, se você não tiver nenhum campo personalizado, este exemplo ficará muito mais simples (porque você não precisa subclassificar Lead ou usar setCustomProperties() / convertSystemNames()).
No exemplo acima, o ValueObject não sabe nada sobre o mapeamento de suas propriedades para nomes de sistemas de campos; o objeto Connection trata disso para operações de criação/atualização e, após as operações 'get', você precisa convertê-las explicitamente de volta em nomes de propriedades personalizadas antes de construir o objeto.
Há também outra maneira: você pode definir o mapeamento no ValueObject em vez da Conexão.
$ mapping = [ ' shoeSize ' => ' shoe_size_384c1e3eacbb3 ' ];
// $api->setCustomProperties('lead', $mapping) is not called here.
// For create:
$ lead = new ShoeStoreLead ([], $ mapping );
$ lead -> firstName = ' Roderik ' ;
$ lead -> emailAddress = [email protected]';
$ lead -> shoeSize = 12 ;
$ api -> createLead ( $ lead );
// Note you could also add all the properties in the first argument of the
// constructor, instead of setting them individually - although that more or
// less defeats the purpose of using a ValueObject in the first place. Setting
// 'shoeSize' works just as well as 'shoe_size_384c1e3eacbb3', in that first
// argument. Just don't set values for _both_ the field name _and_ its property
// alias, because then the library does not guarantee which of the two will be
// used.
// For 'get':
$ leads = $ api -> getLeads ([ ' emailAddress ' => ' [email protected] ' ]);
$ lead = reset ( $ leads );
$ my_lead_obj = new ShoeStoreLead ( $ my_lead , $ mapping );
Portanto: para ValueObjects que possuem campos customizados, existe a opção de configurar um mapeamento da conexão, ou configurá-lo no ValueObject. Este último tem a vantagem de que os dados recuperados da API REST são automaticamente convertidos no construtor, mas a desvantagem de que o mapeamento precisa ser definido sempre que um objeto é construído.
Existe outra maneira: codificar o mapeamento dentro do objeto, como:
// Override the parent's (empty) property mapping variable:
protected $_customProperties = ['shoeSize' => 'shoe_size_384c1e3eacbb3'];
... ou fazer com que o construtor da sua subclasse ValueObject personalizada o configure (ou o derive de algum lugar). Provavelmente será um código específico para sua situação.
Escolha sua abordagem preferida.
O comportamento mais estranho da API REST da Sharpspring foi documentado ou parcialmente mitigado/oculto por esta biblioteca. No entanto, se você pretende realizar um trabalho sério com base na API, há algumas coisas das quais você deve pelo menos estar ciente e decidir se precisa levá-las em consideração.
Valores com caracteres não padrão (aproximadamente: caracteres que seriam codificados por htmlspecialchars()) são armazenados na Sharpspring de forma diferente dependendo se são inseridos através da API REST ou inseridos através da UI. (E para a UI, as coisas também diferem entre campos padrão e personalizados.) O '<' é ainda mais estranho: às vezes é armazenado com codificação dupla. Os detalhes sangrentos estão em encoding.md. A única maneira pela qual esta biblioteca conseguiu mitigar esse comportamento é o CurlClient sempre decodificar qualquer campo em HTML, seja necessário ou não. Como a decodificação HTML acontece de forma transparente, você provavelmente não verá esse comportamento, mas um aplicativo sério ainda deve considerar se isso é um problema.
A chamada updateLead pode alterar endereços de e-mail de um lead existente enviando (pelo menos) o valor 'id' existente junto com o endereço de e-mail alterado. No entanto, se o e-mail alterado já for usado em outro lead existente, a API descartará silenciosamente a atualização , mas ainda assim reportará sucesso . Este é um problema potencial se você estiver espelhando um banco de dados de contatos existente, onde os endereços de e-mail não são necessariamente exclusivos, na Sharpspring. Você precisará verificar novamente suas atualizações para ver se foram bem-sucedidas. (Um exemplo desse código está em SharpspringSyncJob::finish().)
(Eu agradeceria qualquer relato de correção desses bugs. Eles podem; consulte 'aviso'.)
Aparentemente, a Sharpspring às vezes muda o comportamento de sua API sem anúncio ou documentação/changelogs (nenhum dos quais eles fazem, que eu saiba), e mesmo sem aumentar a versão da API mencionada na documentação on-line da API que pode ser encontrada atrás de um login para site do cliente.
A conclusão disso, ao que parece, é que, como desenvolvedor de aplicativos, você deve testar constantemente seu aplicativo porque não pode confiar que a Sharpspring não quebrará o 'contrato implícito' com você. Porque a Sharpspring aparentemente não sente que tenha um “contrato implícito” com desenvolvedores de aplicativos.
(Tive alguns sentimentos suspeitos sobre isso enquanto desenvolvia esta biblioteca ao longo de meio ano, mas estou baseando isso na mudança no comportamento da chamada getLeadsDateRange (com o parâmetro 'timestamp' definido como "update") - que alterou o formato das datas nos parâmetros e na saída, e o conteúdo da saída. Veja o código-fonte. Isso levou a efeitos imediatos -erros relatados- em sistemas de produção que usavam a classe SharpspringSyncJob. para receber correção de emergência. A versão da API publicada ainda é 1.117 e tem sido assim desde pelo menos novembro de 2016.
A mudança de comportamento pode ter sido causada por inconsistências relatadas por mim (tive uma breve troca de e-mails, que terminou com o envio de uma lista de problemas encontrados com sua API), e estou feliz que eles estejam corrigindo as inconsistências, mas a falta de resposta, changelog ou alteração de versão da API ainda leva à conclusão acima. É claro que espero que isso mude no futuro e que este aviso possa ser excluído, mas parece realmente pertinente agora.)
Ah, olhe! https://help.sharpspring.com/hc/en-us/articles/115001069228-Open-API-Overview agora menciona que eles têm uma API 'v1' e uma API 'v1.2'! O segundo aparentemente aceita entrada de data como UTC (que é o que sua API v1 fazia até cerca de 26 de julho de 2017). Não há menção ao formato das datas de saída (que também foram alteradas na v1), portanto isso precisaria de testes. Esta biblioteca atualmente faz apenas API v1 e deve ser estendida. Não está na minha lista, então PRs (ou uma tarefa remunerada;)) são bem-vindos.
Este código foi testado com Leads e ListMembers. Mais chamadas de API estão presentes, mas nem todas foram testadas extensivamente e algumas estão faltando. Esperamos que adicionar novas chamadas não seja muito trabalhoso; solicitações pull são bem-vindas.
Basta enviar um PR ou entrar em contato comigo.
O 'processo de construção' (veja o ícone na parte superior; uma mensagem semelhante de aprovação/falha aparecerá nos PRs) está apenas verificando os padrões de codificação em relação ao PHP5.6/PSR2. Ainda não há testes de unidade, pois esta é apenas uma fina camada de código envolvendo o Sharpspring. Diga-me se você acha que deveria haver testes e quais/por quê. (Obviamente, seria bom ter um conjunto completo de testes na API Sharpspring ativa , mas acho que esse é um problema diferente e/ou pelo menos requer maior coordenação com eles...)
Gosto de contribuir com software de código aberto para o mundo e gosto de abrir sistemas semifechados e pouco documentados. Avise-me se isso for útil ou se você tiver uma contribuição. Entre em contato comigo se precisar de um trabalho de integração. (Tenho experiência com vários outros sistemas.)
Esta biblioteca está licenciada sob a licença MIT - consulte o arquivo LICENSE.md para obter detalhes.