seka19/basic-shopify-api

一个基本的 Shopify API 包装器,支持 REST 和 GraphQL。

v8.2.0 2020-04-19 22:14 UTC

README

Build Status Coverage Status StyleCI License

一个简单的、经过测试的 Shopify API 包装器,使用 Guzzle 实现。它支持 Shopify 提供的 REST 和 GraphQL API,以及基本的速率限制能力。它包含生成安装 URL、授权 URL(离线和按用户)、HMAC 签名验证、调用限制和 API 请求的有用方法。它与 OAuth 和私有 API 应用程序一起工作。

还支持:通过 Guzzle 的承诺进行异步请求。

此库需要 PHP >= 7。

目录

安装

推荐的安装方式是 通过 composer

$ composer require ohmybrew/basic-shopify-api

用法

use OhMyBrew\BasicShopifyAPI; 添加到您的导入中。

公共 API

这假设您已在合作伙伴仪表板中正确设置了应用程序,并具有正确的密钥和重定向 URI。

REST(同步)

对于 REST 调用,需要商店域名和访问令牌。

$api = new BasicShopifyAPI();
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop('your shop here');
$api->setAccessToken('your token here');

// Now run your requests...
$resul = $api->rest(...);

REST(异步)

对于 REST 调用,需要商店域名和访问令牌。

$api = new BasicShopifyAPI();
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop('your shop here');
$api->setAccessToken('your token here');

// Now run your requests...
$promise = $api->restAsync(...);
$promise->then(function ($result) {
  // ...
});

GraphQL

对于 GraphQL 调用,需要商店域名和访问令牌。

$api = new BasicShopifyAPI();
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop('your shop here');
$api->setAccessToken('your token here');

// Now run your requests...
$api->graph(...);

获取访问权限(离线)

这是默认模式,返回永久令牌。

在获取用户的商店域名后,然后使用 getAuthUrl 将其重定向到授权屏幕,例如(基本的 PHP)

$api = new BasicShopifyAPI();
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop($_SESSION['shop']);
$api->setApiKey(env('SHOPIFY_API_KEY'));
$api->setApiSecret(env('SHOPIFY_API_SECRET'));

$code = $_GET['code'];
if (!$code) {
  /**
   * No code, send user to authorize screen
   * Pass your scopes as an array for the first argument
   * Pass your redirect URI as the second argument
   */
  $redirect = $api->getAuthUrl(env('SHOPIFY_API_SCOPES'), env('SHOPIFY_API_REDIRECT_URI'));
  header("Location: {$redirect}");
  exit;
} else {
  // We now have a code, lets grab the access token
  $api->requestAndSetAccess($code);

  // Above is equiv. to:
  //
  // $access = $api->requestAccess($code);
  // $api->setAccessToken($access->access_token);
  //
  // You can use: $api->getAccessToken() and set it into the database or a cookie, etc

  // You can now make API callsn`
  $request = $api->rest('GET', '/admin/shop.json'); // or GraphQL
}

获取访问权限(按用户)

您还可以将授权模式更改为 per-user,如 Shopify 文档中概述。这将接收 Shopify 商店中应用程序用户的用户信息。收到的令牌将在特定时间到期。

$api = new BasicShopifyAPI();
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop($_SESSION['shop']);
$api->setApiKey(env('SHOPIFY_API_KEY'));
$api->setApiSecret(env('SHOPIFY_API_SECRET'));

$code = $_GET['code'];
if (!$code) {
  /**
   * No code, send user to authorize screen
   * Pass your scopes as an array for the first argument
   * Pass your redirect URI as the second argument
   * Pass your grant mode as the third argument
   */
  $redirect = $api->getAuthUrl(env('SHOPIFY_API_SCOPES'), env('SHOPIFY_API_REDIRECT_URI'), 'per-user');
  header("Location: {$redirect}");
  exit;
} else {
  // We now have a code, lets grab the access object
  $api->requestAndSetAccess($code);

  // Above is equiv. to:
  //
  // $access = $api->requestAccess($code);
  // $api->setAccessToken($access->access_token);
  // $api->setUser($access->associated_user)
  //
  // You can use: $api->getAccessToken() and set it into a cookie, etc
  // You can also get user details with: $api->getUser(), example: $api->getUser()->email

  // You can now make API calls
  $request = $api->rest('GET', '/admin/shop.json'); // or GraphQL
}

验证 HMAC 签名

只需传递一个 GET 参数数组。

// Will return true or false if HMAC signature is good.
$valid = $api->verifyRequest($_GET);

私有 API

这假设您已在合作伙伴仪表板中正确设置了应用程序,并具有正确的密钥和重定向 URI。

REST

对于 REST(同步)调用,需要商店域名、API 密钥和 API 密码。

$api = new BasicShopifyAPI(true); // true sets it to private
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop('example.myshopify.com');
$api->setApiKey('your key here');
$api->setApiPassword('your password here');

// Now run your requests...
$result = $api->rest(...);

GraphQL

对于 GraphQL 调用,需要商店域名和 API 密码。

$api = new BasicShopifyAPI(true); // true sets it to private
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setShop('example.myshopify.com');
$api->setApiPassword('your password here');

// Now run your requests...
$api->graph(...);

发送请求

REST

请求使用 Guzzle 发送。

$api->rest(string $type, string $path, array $params = null, array $headers = [], bool $sync = true);
  • type 指的是 GET、POST、PUT、DELETE 等
  • path 指的是 API 路径,例如:/admin/products/1920902.json
  • params 指的是要传递到路径的参数数组,例如:['handle' => 'cool-coat']
  • headers 指的是要随请求一起发送的自定义头数组,例如:['X-Shopify-Test' => '123']
  • sync 指的是请求是否应该是同步的。

您可以使用别名 restAsync 来跳过将 sync 设置为 false

如果同步为 true(常规 REST 调用)

请求的返回值将是一个包含的对象

  • response 是完整的 Guzzle 响应对象
  • body 是解析后的 JSON 响应体

注意: request() 将别名到 rest()

如果同步为 false(restAsync 调用)

请求的返回值将是一个 Guzzle 承诺,您可以自己处理。

承诺的返回值将是一个包含的对象

  • response 是完整的 Guzzle 响应对象
  • body 是解析后的 JSON 响应体
$promise = $api->restAsync(...);
$promise->then(function ($result) {
  // `response` and `body` available in `$result`.
});

GraphQL

请求使用 Guzzle 发送。

$api->graph(string $query, array $variables = []);
  • query 指的是完整的 GraphQL 查询
  • variables 指的是用于查询的变量(如果有)

请求的返回值将是一个包含的对象

  • response 是完整的 Guzzle 响应对象
  • body 是解析后的 JSON 响应体
  • errors 如果有错误或没有错误

示例查询

$result = $api->graph('{ shop { productz(first: 1) { edges { node { handle, id } } } } }');
echo $result->body->shop->products->edges[0]->node->handle; // test-product

示例突变

$result = $api->graph(
    'mutation collectionCreate($input: CollectionInput!) { collectionCreate(input: $input) { userErrors { field message } collection { id } } }',
    ['input' => ['title' => 'Test Collection']]
);
echo $result->body->collectionCreate->collection->id; // gid://shopify/Collection/63171592234

API 版本控制

这个库支持请求版本控制,例如

$api = new BasicShopifyAPI(true);
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"

// ... your code

您可以在任何时候覆盖特定API请求的版本控制,例如

$api = new BasicShopifyAPI(true);
$api->setVersion('2019-04');

$api->rest('GET', '/admin/api/unstable/shop.json'); // Will ignore "2019-04" version and use "unstable" for this request
// ... your code

检查 API 限制

每次请求后,API调用限制都会更新。要访问它们,只需使用

// Returns an array of left, made, and limit.
// Example: ['left' => 79, 'made' => 1, 'limit' => 80]
$limits = $api->getApiCalls('rest'); // or 'graph'

对于GraphQL,还有以下值:restoreRaterequestedCostactualCost

要快速获取值,您可以在getApiCalls方法中传递一个可选参数

// As example, this will return 79
// You may pass 'left', 'made', or 'limit'
$left = $api->getApiCalls('graph', 'left'); // returns 79
// or
$left = $api->getApiCalls('graph')['left']; // returns 79

速率限制

此库内置了一个基本速率限制器,默认禁用。它会暂停x微秒以确保您不会超过与Shopify的调用限制。在非Plus计划中,每500毫秒有1次调用(每秒2次),对于Plus计划,每500毫秒有2次调用(每秒4次)。

默认周期设置为500毫秒,并额外添加了100毫秒的安全缓冲。

启用速率限制

按正常方式设置API实例,并添加了

$api->enableRateLimiting();

这将在默认500毫秒周期和100毫秒缓冲的情况下开启速率限制。要更改此设置,请按照以下步骤操作

$api->enableRateLimiting(0.25 * 1000, 0);

这会将周期设置为250毫秒和0毫秒缓冲。

禁用速率限制

如果您之前已启用它,您只需运行

$api->disableRateLimiting();

检查速率限制状态

$api->isRateLimitingEnabled();

page_info / 分页支持

2019-07 API版本引入了一个新的Link头,用于分页(在此处解释)。

如果端点支持page_info,您可以使用$response->link获取page_info值并将其传递到下一个请求中。

示例

$response = $api->rest('GET', '/admin/products.json', ['limit' => 5]);
$link = $response->link->next; // eyJsYXN0X2lkIjo0MDkw
$link2 = $response->link->previous; // dkUIsk00wlskWKl
$response = $api->rest('GET', '/admin/products.json', ['limit' => 5, 'page_info' => $link]);

获取时间戳

该库将跟踪前一个和当前(最后一个)调用的时间戳。要查看此信息

$response = $api->rest('POST', '/admin/gift_cards.json', ['gift_cards' => ['initial_value' => 25.00]]);
print_r($response->timestamps);

/* Above will return an array of [previous call, current (last) call], example:
 * [1541119962.965, 1541119963.3121] */

隔离 API 调用

您可以使用API初始化一次,然后用于多个商店。每个实例都将包含在各自的实例中,以避免相互干扰。这对于像后台作业处理这样的任务很有用。

$api->withSession(string $shop, string $accessToken, Closure $closure);
  • shop指代Shopify域名
  • accessToken指代API调用的访问令牌
  • closure指代用于会话的闭包

$this将被绑定到当前API。例如

$api = new BasicShopifyAPI(true);
$api->setVersion('2019-04'); // "YYYY-MM" or "unstable"
$api->setApiKey('your key here');
$api->setApiPassword('your password here');

$api->withSession('some-shop.myshopify.com', 'token from database?', function() {
  $request = $this->rest('GET', '/admin/shop.json');
  echo $request->body->shop->name; // Some Shop
});

$api->withSession('some-shop-two.myshopify.com', 'token from database?', function() {
  $request = $this->rest('GET', '/admin/shop.json');
  echo $request->body->shop->name; // Some Shop Two
});

错误

此库通过Guzzle内部仅捕获400-500状态范围错误。您可以检查此类型的错误并获取其响应状态码和正文。

$call = $api->rest('GET', '/admin/non-existant-route-or-object.json');

if ($call->errors) {
  echo "Oops! {$call->status} error";
  var_dump($call->body);

  // Original exception can be accessed via `$call->exception`
  // Example, if response body was `{"error": "Not found"}`...
  /// then: `$call->body` would return "Not Found"
}

日志记录

此库接受PSR兼容的日志记录器。

$api->setLogger(... your logger instance ...);

文档

代码文档在此处可用:phpDocumentor通过phpdoc -d src -t doc

许可证

该项目根据MIT许可证发布。