omniphx/forrest

一个用于 Salesforce 的 Laravel 库

资助包维护!
omniphx

v2.19.1 2024-09-02 23:35 UTC

README

Laravel Latest Stable Version Total Downloads License Actions Status

Forrest 是 Laravel 和 Lumen 的 Salesforce/Force.com REST API 客户端。

对 Eloquent Salesforce 模型感兴趣?查看 @roblesterjr04EloquentSalesForce 项目,它利用 Forrest 作为其 API 层。

安装

如果您正在升级到 2.0 版本,请确保重新发布您的配置文件。

可以通过 composer 安装 Forrest。打开您的 composer.json 文件,并在 require 键中添加以下内容

"omniphx/forrest": "2.*"

接下来,在命令行中运行 composer update 来安装包。

Laravel 安装

对于 Laravel >=5.5,该包将自动注册服务提供者和 Forrest 别名。对于更早的版本,请将服务提供者和别名添加到您的 config/app.php 文件中

Omniphx\Forrest\Providers\Laravel\ForrestServiceProvider::class
'Forrest' => Omniphx\Forrest\Providers\Laravel\Facades\Forrest::class

对于 Laravel 4,在 app/config/app.php 中添加 Omniphx\Forrest\Providers\Laravel4\ForrestServiceProvider。别名保持不变。

Lumen 安装

class_alias('Omniphx\Forrest\Providers\Laravel\Facades\Forrest', 'Forrest');
$app->register(Omniphx\Forrest\Providers\Lumen\ForrestServiceProvider::class);
$app->configure('forrest');
$app->withFacades();

然后您将利用 Lumen 服务提供者,通过将其注册到 bootstrap/app.php 文件中来使用它。

配置

您需要一个配置文件来添加您的凭证。使用 artisan 命令发布配置文件

php artisan vendor:publish

这将发布一个 config/forrest.php 文件,它可以切换不同的认证类型以及其他设置。

添加配置文件后,更新您的 .env 文件以包含以下值(获取消费者密钥和秘密的详细信息如下)

SF_CONSUMER_KEY=123455
SF_CONSUMER_SECRET=ABCDEF
SF_CALLBACK_URI=https://test.app/callback

SF_LOGIN_URL=https://login.salesforce.com
# For sandbox: SF_LOGIN_URL=https://test.salesforce.com

SF_USERNAME=mattjmitchener@gmail.com
SF_PASSWORD=password123

对于 Lumen,您应从 src/config/config.php 复制配置文件,并将其添加到您的应用程序根目录下的配置目录中的 forrest.php 配置文件中。对于 Laravel 4,运行 php artisan config:publish omniphx/forrest,这将创建 app/config/omniphx/forrest/config.php

入门

设置连接应用

  1. 登录 Salesforce org
  2. 点击右上角的设置
  3. 在快速查找框中搜索 App,并选择 App Manager
  4. 点击“新建连接应用”。
  5. 为远程应用程序输入以下详细信息
    • 连接应用名称
    • API 名称
    • 联系邮箱
    • 在 API 下启用 OAuth 设置
    • 回调 URL
    • 选择访问范围(如果您需要刷新令牌,请在此处指定)
  6. 点击 保存

保存后,您现在将获得消费者密钥和消费者密钥。更新您的配置文件,使用 consumerKeyconsumerSecretloginURLcallbackURI 的值。

设置

创建认证路由

Web 服务器认证流程

Route::get('/authenticate', function()
{
    return Forrest::authenticate();
});

Route::get('/callback', function()
{
    Forrest::callback();

    return Redirect::to('/');
});

用户名-密码认证流程

使用用户名密码流程,您可以直接使用 Forrest::authenticate() 方法进行认证。

要使用此认证,您必须将您的用户名和密码添加到配置文件中。除非您的 IP 地址被列入白名单,否则可能需要修改安全令牌。

Route::get('/authenticate', function()
{
    Forrest::authenticate();
    return Redirect::to('/');
});

客户端凭证认证流程

使用客户端凭证流程,您可以直接使用 Forrest::authenticate() 方法进行认证。

使用此认证方法只需要您的消费者密钥和密钥。您的 Salesforce 连接应用也必须在设置中启用“客户端凭证流程”。

Route::get('/authenticate', function()
{
    Forrest::authenticate();
    return Redirect::to('/');
});

SOAP 认证流程

(当您无法在 Salesforce 中创建连接应用时)

  1. 销售云允许通过SOAP登录进行个人登录
  2. 从SOAP登录返回的Bearer访问令牌可以类似于OAuth密钥使用
  3. 更新您的配置文件并将authentication值设置为UserPasswordSoap
  4. 更新您的配置文件,设置loginURLusernamepassword的值。使用用户名密码SOAP流程,您可以直接使用Forrest::authenticate()方法进行身份验证。

要使用此身份验证,您可以将用户名和密码添加到配置文件中。除非您的IP地址已列入白名单,否则可能需要修改安全令牌。

Route::get('/authenticate', function()
{
    Forrest::authenticate();
    return Redirect::to('/');
});

如果您的应用程序需要以不同用户身份登录到销售云,您还可以将登录URL、用户名和密码传递给Forrest::authenticateUser()方法。

除非您的IP地址已列入白名单,否则可能需要修改安全令牌。

Route::Post('/authenticate', function(Request $request)
{
    Forrest::authenticateUser('https://login.salesforce.com',$request->username, $request->password);
    return Redirect::to('/');
});

JWT身份验证流程

初始设置

  1. config/forrest.php中将authentication设置为OAuthJWT
  2. 生成密钥和证书:openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt
  3. config/forrest.php中配置私钥(例如,file_get_contents('./../server.key'),

设置连接应用

  1. 应用管理器 > 创建连接应用
  2. 启用OAuth设置
  3. 选中“使用数字签名”
  4. 添加server.crt或您选择的任何名称
  5. 作用域必须包括“refresh_token, offline_access”
  6. 点击保存

接下来,您需要预先授权一个配置文件(目前只能在经典版中执行此步骤,但这是重要的)

  1. 管理应用 > 连接应用
  2. 点击您的应用程序旁边的“编辑”
  3. 将“允许用户”设置为“管理员批准的用户已预先授权”
  4. 保存
  5. 转到设置 > 管理用户 > 配置文件并编辑关联用户的配置文件(例如,销售云管理员)
  6. 在“连接应用访问”下检查相应的应用名称

实现方式与用户密码(例如,需要明确指定用户名和密码)完全相同

Route::get('/authenticate', function()
{
    Forrest::authenticate();
    return Redirect::to('/');
});

对于连接到Lightning组织,您需要在forrest.php配置中配置一个instanceUrl

Lightning: https://<YOUR_ORG>.my.salesforce.com
Lightning Sandbox: https://<YOUR_ORG>--<SANDBOX_NAME>.sandbox.my.salesforce.com
Developer Org: https://<DEV_DOMAIN>.develop.my.salesforce.com

自定义登录URL

有时用户需要连接到沙盒或自定义URL。为此,只需将URL作为参数传递给身份验证方法即可

Route::get('/authenticate', function()
{
    $loginURL = 'https://test.salesforce.com';

    return Forrest::authenticate($loginURL);
});

注意:您可以在配置文件中指定默认登录URL。

基本用法

身份验证后,您的应用程序将存储一个加密的身份验证令牌,可用于进行API请求。

查询记录

Forrest::query('SELECT Id FROM Account');

示例结果

(
    [totalSize] => 2
    [done] => 1
    [records] => Array
        (
            [0] => Array
                (
                    [attributes] => Array
                        (
                            [type] => Account
                            [url] => /services/data/v48.0/sobjects/Account/0013I000004zuIXQAY
                        )

                    [Id] => 0013I000004zuIXQAY
                )

            [1] => Array
                (
                    [attributes] => Array
                        (
                            [type] => Account
                            [url] => /services/data/v48.0/sobjects/Account/0013I000004zuIcQAI
                        )
                    [Id] => 0013I000004zuIcQAI
                )
        )
)

如果您查询的记录超过2000条,则您的响应将包括

(
    [nextRecordsUrl] => /services/data/v20.0/query/01gD0000002HU6KIAW-2000
)

简单地说,调用Forrest::next($nextRecordsUrl)来返回下一批2000条记录。

创建新记录

可以使用以下格式创建记录。

Forrest::sobjects('Account',[
    'method' => 'post',
    'body'   => ['Name' => 'Dunder Mifflin']
]);

更新记录

使用PUT方法更新记录。

Forrest::sobjects('Account/001i000000xxx',[
    'method' => 'put',
    'body'   => [
        'Name'  => 'Dunder Mifflin',
        'Phone' => '555-555-5555'
    ]
]);

Upsert记录

使用PATCH方法更新记录,如果外部ID不存在,则会插入新记录。

$externalId = 'XYZ1234';

Forrest::sobjects('Account/External_Id__c/' . $externalId, [
    'method' => 'patch',
    'body'   => [
        'Name'  => 'Dunder Mifflin',
        'Phone' => '555-555-5555'
    ]
]);

删除记录

使用DELETE方法删除记录。

Forrest::sobjects('Account/001i000000xxx', ['method' => 'delete']);

设置头信息

有时您需要设置自定义头信息(例如,使用分配规则创建潜在客户)

Forrest::sobjects('Lead',[
    'method' => 'post',
    'body' => [
        'Company' => 'Dunder Mifflin',
        'LastName' => 'Scott'
    ],
    'headers' => [
        'Sforce-Auto-Assign' => '01Q1N000000yMQZUA2'
    ]
]);

要禁用分配规则,使用'Sforce-Auto-Assign' => 'false'

XML格式

使用format密钥将请求/响应格式更改为XML或使其在配置文件中为默认值。

Forrest::sobjects('Account',['format'=>'xml']);

API请求

除了searchquery资源外,所有资源都是通过方法重载动态请求的。

您可以通过调用资源方法来确定您可以访问哪些资源

Forrest::resources();

本示例输出显示了通过API可以调用的资源

Array
(
    [sobjects] => /services/data/v30.0/sobjects
    [connect] => /services/data/v30.0/connect
    [query] => /services/data/v30.0/query
    [theme] => /services/data/v30.0/theme
    [queryAll] => /services/data/v30.0/queryAll
    [tooling] => /services/data/v30.0/tooling
    [chatter] => /services/data/v30.0/chatter
    [analytics] => /services/data/v30.0/analytics
    [recent] => /services/data/v30.0/recent
    [process] => /services/data/v30.0/process
    [identity] => https://login.salesforce.com/id/00Di0000000XXXXXX/005i0000000aaaaAAA
    [flexiPage] => /services/data/v30.0/flexiPage
    [search] => /services/data/v30.0/search
    [quickActions] => /services/data/v30.0/quickActions
    [appMenu] => /services/data/v30.0/appMenu
)

从上面的列表中,我可以通过指定的键来调用资源。

Forrest::theme();

或者...

Forrest::appMenu();

还可以传递额外的资源URL参数

Forrest::sobjects('Account/describe/approvalLayouts/');

以及新的格式化选项、头信息或其他配置

Forrest::theme(['format'=>'xml']);

批量更新多条记录(批量API 2.0)

批量API请求在您需要快速将大量数据加载到Salesforce组织时特别有用。主要区别在于它至少需要三个单独的请求(创建、添加、关闭),并且要加载的数据以CSV格式发送。

以下示例是三个用于更新联系人记录CSV的请求。

创建

使用POST方法创建批量上传作业,主体包含以下作业属性

  • object是您要加载的对象类型(每个作业中必须都是相同类型)
  • externalIdFieldName是外部ID,如果存在则更新,如果不存在则插入新记录。仅适用于更新操作。
  • contentType是CSV,这是当前唯一的有效值。
  • operation设置为upsert以添加和更新记录。

我们将响应存储在$bulkJob中,以便在下面的添加和关闭请求中引用唯一的作业ID。

有关此处可用的完整选项列表,请参阅创建作业

$bulkJob = Forrest::jobs('ingest', [
    'method' => 'post',
    'body' => [
        "object" => "Contact",
        "externalIdFieldName" => "externalId",
        "contentType" => "CSV",
        "operation" => "upsert"
    ]
]);

添加数据

使用创建POST请求中的作业ID,您然后通过PUT请求发送要处理的CSV数据。这假设您已将CSV内容加载到$csv

有关其格式应如何设置,请参阅准备CSV文件的详细信息。

Forrest::jobs('ingest/' . $bulkJob['id'] . '/batches', [
    'method' => 'put',
    'headers' => [
        'Content-Type' => 'text/csv'
    ],
    'body' => $csv
]);

关闭

您必须关闭作业后才能处理记录,为此您向作业ID发送一个使用PATCH请求的UploadComplete状态。

有关更多选项和有关如何中止作业的详细信息,请参阅关闭或中止作业

$response = Forrest::jobs('ingest/' . $bulkJob['id'] . '/', [
    'method' => 'patch',
    'body' => [
        "state" => "UploadComplete"
    ]
]);

批量API 2.0在API版本41.0及以后可用。有关Salesforce批量API的更多信息,请参阅官方文档本教程,了解如何成功执行批量上传。

其他API请求

刷新

如果设置了刷新令牌,服务器可以代表用户刷新访问令牌。刷新令牌仅适用于Web服务器流程。

Forrest::refresh();

如果您需要刷新令牌,请确保在您的连接应用中指定此内容为access scope。您也可以通过在配置文件中添加'scope' => 'full refresh_token'来指定此内容。在配置文件中设置范围访问是可选的,默认范围访问由您的Salesforce组织确定。

撤销

这将撤销授权令牌。会话将继续存储令牌,但它将变得无效。

Forrest::revoke();

版本

返回所有当前支持的版本。包括版本、标签以及每个版本的根链接。

Forrest::versions();

资源

根据登录用户的权限和API版本返回可用的资源列表。

Forrest::resources();

身份

返回有关登录用户的信息。

Forrest::identity();

基本URL

返回包含API信息的Salesforce实例的URL。

Forrest::getBaseUrl(); // https://my-instance.my.salesforce.com/services/data/v50.0

实例 URL

返回 Salesforce 实例的 URL。

Forrest::getInstanceURL(); // https://my-instance.my.salesforce.com

有关 API 资源完整列表,请参阅Force.com REST API 开发者指南

自定义 Apex 端点

如果您使用 Apex 创建了自定义 API,您可以使用 custom() 方法来使用它们。

Forrest::custom('/myEndpoint');

可以通过这种方式传递额外的选项和参数

Forrest::custom('/myEndpoint', [
    'method' => 'post',
    'body' => ['foo' => 'bar'],
    'parameters' => ['flim' => 'flam']]);

有关更多信息,请阅读使用 Apex REST 创建 REST API

原始请求

如有需要,您可以向您选择的端点发送原始请求。

Forrest::get('/services/data/v20.0/endpoint');
Forrest::head('/services/data/v20.0/endpoint');
Forrest::post('/services/data/v20.0/endpoint', ['my'=>'param']);
Forrest::put('/services/data/v20.0/endpoint', ['my'=>'param']);
Forrest::patch('/services/data/v20.0/endpoint', ['my'=>'param']);
Forrest::delete('/services/data/v20.0/endpoint');

从 ContentVersion 和 Attachment 获取文件内容

您可以使用 Forrest::getContentVersionBody() 和 Forrest::getAttachmentBody() 来检索上传文件的内容。由于它们返回的是流式响应,因此如果现在用于流,可能会有些繁琐。下面是一个检索上传内容版本内容的示例。

# example 
$data = Forrest::getContentVersionBody($version->Id);
$content =  $data->getBody()->getContents();

原始响应输出

默认情况下,此包将返回响应体作为反序列化的 JSON 对象或 SimpleXMLElement 对象。

有时,您可能希望以不同的方式处理。为此,只需使用 'none' 格式,代码将返回整个响应体作为字符串。

$response = Forrest::sobjects($resource, ['format'=> 'none']);
echo $response; // Unformatted string

事件监听器

此包使用 Guzzle 的事件监听器。

Event::listen('forrest.response', function($request, $response) {
    dd((string) $response);
});

创建多个 Forrest 实例

可能存在需要向多个 Salesforce org 发起调用的场景。这只能通过 UserPassword 流来实现。

  1. 在配置文件中将 storage 设置为 object。这将把令牌存储在对象实例中。
'storage'=> [
    'type' => 'object'
],
  1. 使用 Laravel 的 app()->make() 辅助函数创建多个实例
$forrest1 = app()->make('forrest');
$forrest1->setCredentials(['username' => 'user@email.com.org1', 'password'=> '1234']);
$forrest1->authenticate();

$forrest2 = app()->make('forrest');
$forrest2->setCredentials(['username' => 'user@email.com.org2', 'password'=> '1234']);
$forrest2->authenticate();

有关 Guzzle 响应和事件监听器的更多信息,请参阅他们的文档

创建自定义存储

如果您希望使用除 sessioncacheobject 之外的存储,您可以通过在 storage.type 中配置自定义类实例来实现自定义实现。

'storage' => [
    'type' => App\Storage\CustomStorage::class,
],

您可以为类命名任何名称,但必须实现 Omniphx\Forrest\Interfaces\StorageInterface

<?php

namespace App\Storage;

use Session;
use Omniphx\Forrest\Exceptions\MissingKeyException;
use Omniphx\Forrest\Interfaces\StorageInterface;

class CustomStorage implements StorageInterface
{
    public $path;

    public function __construct()
    {
        $this->path = 'app.custom.path';
    }

    /**
     * Store into session.
     *
     * @param $key
     * @param $value
     *
     * @return void
     */
    public function put($key, $value)
    {
        return Session::put($this->path.$key, $value);
    }

    /**
     * Get from session.
     *
     * @param $key
     *
     * @return mixed
     */
    public function get($key)
    {
        if(!$this->has($key)) {
            throw new MissingKeyException(sprintf('No value for requested key: %s', $key));
        }

        return Session::get($this->path.$key);
    }

    /**
     * Check if storage has a key.
     *
     * @param $key
     *
     * @return bool
     */
    public function has($key)
    {
        return Session::has($this->path.$key);
    }
}