wyz/sharpspring-restapi

一个用于与 Sharpspring REST API 通信的 PHP 库。

1.2 2017-08-05 19:08 UTC

This package is not auto-updated.

Last update: 2024-09-17 09:26:06 UTC


README

此 PHP 库包含

  • 一个简单的客户端类,用于执行 REST API 调用(但不包含 Sharpspring 特定逻辑,除了 URL 和认证参数);
  • 一个 '连接' 对象,它
    • 包含许多 API 方法的包装函数,对任何发现的不明确之处都有详尽的注释;
    • 对 API 响应进行严格检查,并尝试抽象出可能令人困惑的部分;
    • 有助于处理自定义字段;
  • ValueObject / Lead 类,有助于处理自定义字段。

它还包含几个示例/部分类,用于实现将源系统中的联系人数据同步到 Sharpspring 的联系人/潜在客户数据库的过程。这通过 Sharpspring 潜在客户的本地缓存来实现,以最小化对 Sharpspring REST API 的更新调用。

代码原则

客户端类可以独立使用,尽管这个库不是为了这个目的而编写的。如果您想自己处理参数构建和解码结果,请继续操作。实例化它;调用 call() 方法。您不需要库的其余部分。

连接类的目标是 帮助您不要对与 Sharpspring REST API 的通信感到困惑。它试图以下列方式提供帮助

  • 它试图接近 Sharpspring REST API 的文档,这是您的主要信息来源。因此,其中记录的所有方法在连接对象上都有相等的方法。
  • 它不会包装不需要包装的东西
    • 调用返回的值与 API 响应中的 'result' 数组相等,因为您通常只需要这个值。除非这个结果是一个元素数组,那么这个数组将被解包。
    • 有时使用包含结果的对象/类是有益的,例如处理潜在客户时。(特别是如果您正在使用自定义字段。)已包含类来执行此操作,但您不是 被迫 使用它们。如果您愿意,您可以使用 Sharpspring API 端点返回的结果数组。
  • 对响应格式进行了广泛的检查。因此,如果您调用返回一个结果,您可以确信它已过审查。(在任何问题迹象下都会抛出异常。)
  • 在观察到的 API '非明显' 行为周围添加了广泛的注释。

(LocalLeadCache 类在此处未讨论。)

使用方法

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');

代码对它遇到的任何奇怪事物都会抛出异常... 除了一个:在响应中看到的不在您调用的特定 API/连接方法期望的数组值中的额外属性。默认情况下,这些将被忽略;它们永远不会被期望遇到。如果您想记录这些,请将 PSR-3 兼容的日志记录对象作为连接构造函数的第二个参数传递。

自定义字段

在 Sharpspring REST API '对象'(数组)中,自定义字段通过它们的系统名称引用,该名称每个账户都不同。为了使代码更通用,连接对象有一个从自定义属性到字段系统名称的映射。当此映射设置(使用您自己的属性名称选择)时,REST API 调用中的任何 '对象' 参数都将自动将自定义属性名称转换为相应的字段系统名称。

假设您有鞋店的潜在客户信息,其中包含一个您通过Sharpspring UI创建的定制字段,用于鞋码,系统名称为shoe_size_384c1e3eacbb3。以下两个例子是等效的

$api->createLead([
    'firstName' => 'Roderik',
    'emailAddress' => 'rm@wyz.biz',
    'shoe_size_384c1e3eacbb3' => 12,
]);

$api->setCustomProperties('lead', ['shoeSize' => 'shoe_size_384c1e3eacbb3']);
$api->createLead([
    'firstName' => 'Roderik',
    'emailAddress' => 'rm@wyz.biz',
    '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.

自动转换仅适用于API调用参数中的'对象'。API调用返回的结果不会被篡改。如果您希望将API结果中的自定义字段系统名称转换回您的自定义属性名称,您需要显式进行此操作

$api->setCustomProperties('lead', ['shoeSize' => 'shoe_size_384c1e3eacbb3']);

$leads = $api->getLeads(['emailAddress' => 'rm@wyz.biz']);
$lead = reset($leads);
$my_lead = $api->convertSystemNames('lead', $lead);

值对象

使用数组来表示API '对象'是可以的。但您可能更喜欢使用对象/类。这会提供IDE自动完成功能,同时最大限度地减少由REST API无法处理的误大写属性名称的机会。

基本类是ValueObject,目前有一个Lead类实现了所有已知字段(并注释了Sharpspring API文档过时的地方)。

以下示例与上面相同

/**
 * 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 = rm@wyz.biz';
$lead->shoeSize = 12;
$api->createLead($lead);

// And this is the 'get' call which puts the result into a new object:
$leads = $api->getLeads(['emailAddress' => 'rm@wyz.biz']);
$lead = reset($leads);
$my_lead = $api->convertSystemNames('lead', $lead);
$my_lead_obj = new ShoeStoreLead($my_lead);

显然,如果您没有任何自定义字段,则此示例会变得非常简单(因为您不需要子类化Lead或使用setCustomProperties() / convertSystemNames())。

在上面的示例中,ValueObject不知道其属性与字段系统名称之间的映射;Connection对象负责处理创建/更新操作,而在'get'操作之后,您需要显式地将它们转换回自定义属性名称,然后再构建对象。

还有另一种方法:您可以在ValueObject中设置映射,而不是在Connection中。

$mapping = ['shoeSize' => 'shoe_size_384c1e3eacbb3'];
// $api->setCustomProperties('lead', $mapping) is not called here.

// For create:
$lead = new ShoeStoreLead([], $mapping);
$lead->firstName = 'Roderik';
$lead->emailAddress = rm@wyz.biz';
$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' => 'rm@wyz.biz']);
$lead = reset($leads);
$my_lead_obj = new ShoeStoreLead($my_lead, $mapping);

因此:对于具有自定义字段的ValueObject,可以选择在连接中设置映射或将其设置在ValueObject中。后者的优点是自动将REST API检索的数据在构造函数中转换,但缺点是每次构建对象时都需要设置映射。

还有另一种方法:要么在对象内部硬编码映射,如下所示

// Override the parent's (empty) property mapping variable:
protected $_customProperties = ['shoeSize' => 'shoe_size_384c1e3eacbb3'];

...或者让您的自定义ValueObject子类的构造函数设置它(或从中派生它)。这很可能是针对您自己情况特定的代码。

选择您自己的首选方法。

API错误

Sharpspring REST API的大部分奇怪行为都已由本库记录或部分缓解/隐藏。但是,如果您打算基于API进行严肃的工作,您至少应该知道一些事情,并决定是否需要考虑这些问题。

  1. 包含非标准字符(大致上是htmlspecialchars()会编码的字符)的值在Sharpspring中的存储方式不同,具体取决于是通过REST API插入还是通过UI输入。(对于UI,标准字段和自定义字段之间也有所不同。)'<'尤其奇怪:它有时会双编码。详细信息请参阅encoding.md。本库能够缓解这种行为的方式是CurlClient始终对任何字段进行HTML解码,无论是否必要。由于HTML解码是透明的,您可能看不到这种行为,但严肃的应用程序仍然应该考虑这是否是一个问题。

  2. 更新联系人调用可以通过提交(至少)现有 'id' 值以及更改后的电子邮件地址来更改现有联系人的电子邮件地址。然而,如果更改后的电子邮件地址已经被另一个现有联系人使用,API 将默默地丢弃更新但仍然报告成功。如果您正在将现有联系人数据库(电子邮件地址不一定唯一)镜像到 Sharpspring,这是一个潜在问题。您需要仔细检查更新是否成功。(SharpspringSyncJob::finish() 中的一个示例代码就是这样。)

(我欢迎关于这些错误已被修复的报告。它们可能已经修复了;请见 '警告'。)

警告

Sharpspring 似乎有时会不通知或提供文档/变更日志(据我所知,他们都没有这样做),甚至不会增加他们客户网站登录后可找到的在线 API 文档中提到的 API 版本。

从这个角度来看,似乎作为应用程序开发者,您应该不断地测试您的应用程序,因为您不能相信 Sharpspring 不会破坏他们与您的 '隐含合同'。因为据 Sharpspring 看来,他们认为与应用程序开发者没有 '隐含合同'。

(在我花半年的时间开发这个库的过程中,我对此有一些怀疑,但我依据的是他们 getLeadsDateRange 调用(设置 'timestamp' 参数为 "update")的行为变化 - 这改变了参数和输出的日期格式,以及输出的内容。请参阅源代码。这导致了生产系统中立即出现错误 - 报告错误 - 使用 SharpspringSyncJob 类的系统必须进行紧急修复。发布的 API 版本仍然是 1.117,自 2016 年 11 月以来至少如此。)

这种行为变化可能是由于我报告的不一致造成的(我与他们有简短的电子邮件交流,最后以我发送有关他们 API 遇到的问题清单结束),我很高兴他们正在修复不一致之处,但缺乏响应、变更日志或 API 版本更改仍然导致了上述结论。我当然希望将来会改变,这个警告现在似乎很相关。)

更新

哦,看!现在https://help.sharpspring.com/hc/en-us/articles/115001069228-Open-API-Overview 提到他们有一个 'v1' API 和一个 'v1.2' API!第二个显然接受 UTC 日期输入(这是他们的 v1 API 直到 2017 年 7 月 26 日左右所做的事情)。没有提及输出日期的格式(在 v1 中也发生了变化),因此需要测试。这个库目前只支持 API v1,应该进行扩展。这不在我的短期清单上,因此欢迎 PR(或付费任务);)。

完整性

此代码已与联系人 ListMembers 进行测试。存在更多的 API 调用,但并非所有都经过充分测试,一些缺失。添加新调用可能是工作量不大;欢迎 PR。

贡献 / 构建

只需提交 PR 或其他方式与我联系。

“构建过程”(请参阅顶部的图标;PR 上将出现类似的通过/失败消息)仅检查与 PHP5.6 / PSR2 的编码标准。由于这只是一个围绕 Sharpspring 的薄层代码,因此尚未进行单元测试。请告诉我您是否认为应该进行测试以及哪些/为什么。显然,有一个针对 实际 Sharpspring API 的完整测试套件将很好,但我猜这是一个不同的问题,或者至少至少需要与他们进一步协调...

作者

  • Roderik Muit - Wyz

我喜欢向世界贡献开源软件,我喜欢打开半封闭的文档不充分的系统。如果这有用或有贡献,请给我留言。如果您需要集成工作,请与我联系。(我有在其他系统上的经验。)

许可

本库采用MIT许可协议 - 详细信息请参阅LICENSE.md文件。

致谢

  • 部分由Yellowgrape赞助,他们是电子商务策略/营销/设计领域的专业人士。(同步过程由他们支付费用;编写可开源的代码以及仔细测试和记录事物的额外工作是在我自己的非付费时间内完成的。)