sanity/sanity-php

Sanity API 的 PHP 库


README

PackagistBuild Status

Sanity API 的 PHP 库 Sanity API

要求

sanity-php 需要 PHP >= 5.6,并安装了 json 模块。

Composer

您可以通过 Composer 安装此库。运行以下命令

composer require sanity/sanity-php

要使用此库,请使用 Composer 的 自动加载

require_once 'vendor/autoload.php';

使用方法

实例化新的客户端

use Sanity\Client as SanityClient;

$client = new SanityClient([
  'projectId' => 'your-project-id',
  'dataset' => 'your-dataset-name',
  // Whether or not to use the API CDN for queries. Default is false.
  'useCdn' => true,
  // If you are starting a new project, using the current UTC date is usually
  // a good idea. See "Specifying API version" section for more details
  'apiVersion' => '2019-01-29',
]);

使用授权令牌

$client = new SanityClient([
  'projectId' => 'your-project-id',
  'dataset' => 'your-dataset-name',
  'useCdn' => false,
  'apiVersion' => '2019-01-29',
  // Note that you cannot combine a token with the `useCdn` option set to true,
  // as authenticated requests cannot be cached
  'token' => 'sanity-auth-token',
]);

指定 API 版本

Sanity 使用 UTC 时区的 ISO 日期(YYYY-MM-DD)进行版本控制。关于此的解释可以在文档中找到

通常,除非您知道要使用哪个 API 版本,否则您希望将其设置为今天的 UTC 日期。这样做可以获得所有最新的错误修复和功能,同时防止任何时区混淆并锁定 API 以防止破坏性更改。

注意:不要被诱惑使用 apiVersion 的动态值。设置静态值的原因是为了防止意外的破坏性更改。

在未来的版本中,将需要指定 API 版本。目前,为了保持向后兼容性,不指定版本将触发弃用警告并回退到使用 v1

通过 ID 获取单个文档

$document = $client->getDocument('someDocumentId');

执行查询

$results = $client->fetch(
  '*[_type == $type][0...3]', // Query
  ['type' => 'product'] // Params (optional)
);

foreach ($product in $results) {
  echo $product['title'] . '\n';
}

有关如何编写查询的更多信息,请参阅 查询文档

使用视角

可以使用 perspective 选项为查询指定特殊的过滤行为。默认值是 raw,这意味着不应用任何特殊过滤,而 publishedpreviewDrafts 可以用于针对特定用例进行优化。

published

当您想确保在生产中不返回草稿文档时很有用。与私有数据集配合良好。

对于一个看起来像这样的数据集

[
  {
    "_type": "author",
    "_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George Martin"
  },
  {
    "_type": "author",
    "_id": "drafts.ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George R.R. Martin"
  },
  {
    "_type": "author",
    "_id": "drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
    "name": "Stephen King"
  }
]

和一个像这样的查询

$client = new SanityClient([
  // ...config...
  'useCdn' => true,
  'perspective' => 'published',
]);

$authors = $client->fetch('*[_type == "author"]');

那么 $authors 将只包含没有在它们的 _id 中具有 drafts. 前缀的文档,在这种情况下只是 "George Martin"。

[
  {
    "_type": "author",
    "_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George Martin"
  }
]

previewDrafts

旨在帮助回答“在所有草稿文档发布后,我们的应用程序将是什么样子?”的问题。

给定一个像这样的数据集

[
  {
    "_type": "author",
    "_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George Martin"
  },
  {
    "_type": "author",
    "_id": "drafts.ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George R.R. Martin"
  },
  {
    "_type": "author",
    "_id": "drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
    "name": "Stephen King"
  },
  {
    "_type": "author",
    "_id": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
    "name": "Terry Pratchett"
  }
]

和一个像这样的查询

$client = new SanityClient([
  // ...config...
  'useCdn' => false, // the `previewDrafts` perspective requires this to be `false`
  'perspective' => 'previewDrafts',
]);

$authors = $client->fetch('*[_type == "author"]');

然后 authors 将看起来像这样。请注意,结果去除了具有偏好的草稿版本的文档

[
  {
    "_type": "author",
    "_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
    "_originalId": "drafts.ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George R.R. Martin"
  },
  {
    "_type": "author",
    "_id": "f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
    "_originalId": "drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
    "name": "Stephen King"
  },
  {
    "_type": "author",
    "_id": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
    "_originalId": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
    "name": "Terry Pratchett"
  }
]

由于查询模拟发布草稿后的结果,因此 _id 不包含 drafts. 前缀。如果您想检查文档是否为草稿,可以使用 _originalId 字段,该字段仅在使用 previewDrafts 视角时可用。

$authors = $client->fetch('*[_type == "author"]{..., "status": select(
  _originalId in path("drafts.**") => "draft",
  "published"
)}');

这将使结果变为

[
  {
    "_type": "author",
    "_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
    "_originalId": "drafts.ecfef291-60f0-4609-bbfc-263d11a48c43",
    "name": "George R.R. Martin",
    "status": "draft"
  },
  {
    "_type": "author",
    "_id": "f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
    "_originalId": "f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
    "name": "Stephen King",
    "status": "published"
  }
]

创建文档

$doc = [
  '_type' => 'bike',
  'name'  => 'Bengler Tandem Extraordinaire',
  'seats' => 2,
];

$newDocument = $client->create($doc);
echo 'Bike was created, document ID is ' . $newDocument['_id'];

这会创建一个新的文档,并具有给定的属性。它必须包含一个 _type 属性,可能 包含一个 _id 属性。如果指定了一个ID并且已经存在具有该ID的文档,则该更改将失败。如果没有指定ID,它将自动生成,并包含在返回的文档中。

创建文档(如果不存在)

如上所述,当调用 create() 并包含一个 _id 属性时,如果该ID的文档已经存在,它将失败。如果您想忽略如果它存在则创建操作,可以使用 createIfNotExists()。它接受与 create() 相同的参数,唯一的区别是它 需要 一个 _id 属性。

$doc = [
  '_id'   => 'my-document-id',
  '_type' => 'bike',
  'name'  => 'Amazing bike',
  'seats' => 3,
];

$newDocument = $client->createIfNotExists($doc);

替换文档

如果您不关心文档是否已经存在,只想替换它,可以使用 createOrReplace() 方法。

$doc = [
  '_id'   => 'my-document-id',
  '_type' => 'bike',
  'name'  => 'Amazing bike',
  'seats' => 3,
];

$newDocument = $client->createOrReplace($doc);

修补/更新文档

use Sanity\Exception\BaseException;

try {
  $updatedBike = $client
    ->patch('bike-123') // Document ID to patch
    ->set(['inStock' => false]) // Shallow merge
    ->inc(['numSold' => 1]) // Increment field by count
    ->commit(); // Perform the patch and return the modified document
} catch (BaseException $error) {
  echo 'Oh no, the update failed: ';
  var_dump($error);
}

待办事项:记录所有修补操作

删除文档

use Sanity\Exception\BaseException;

try {
  $client->delete('bike-123');
} catch (BaseException $error) {
  echo 'Delete failed: ';
  var_dump($error);
}

事务中的多个更改

$namePatch = $client->patch('bike-310')->set(['name' => 'A Bike To Go']);

try {
  $client->transaction()
    ->create(['name' => 'Bengler Tandem Extraordinaire', 'seats' => 2])
    ->delete('bike-123')
    ->patch($namePatch)
    ->commit();

  echo 'A whole lot of stuff just happened!';
} catch (BaseException $error) {
  echo 'Transaction failed:';
  var_dump($error);
}

无客户端修补和事务

use Sanity\Patch;
use Sanity\Transaction;

// Patches:
$patch = new Patch('<documentId>');
$patch->inc(['count' => 1])->unset(['visits']);
$client->mutate($patch);

// Transactions:
$transaction = new Transaction();
$transaction
  ->create(['_id' => '123', 'name' => 'FooBike'])
  ->delete('someDocId');

$client->mutate($transaction);

关于这种方法的一个重要注意事项是,您不能在通过这种方式实例化的事务或修补上调用 commit(),相反,您必须将它们传递给 client.mutate()

上传图像资产(从本地文件)

$asset = $client->uploadAssetFromFile('image', '/some/path/to/image.png');
echo $asset['_id'];

上传图像资产(从字符串)

$image = file_get_contents('/some/path/to/image.png');
$asset = $client->uploadAssetFromString('image', $buffer, [
    // Will be set in the `originalFilename` property on the image asset
    // The filename in the URL will still be a hash
    'filename' => 'magnificent-bridge.png'
]);
echo $asset['_id'];

上传图像,提取EXIF和调色板数据

$asset = $client->uploadAssetFromFile('image', '/some/path/to/image.png', [
    'extract' => ['exif', 'palette']
]);

var_dump($asset['metadata']);

上传文件资产(从本地文件)

$asset = $client->uploadAssetFromFile('file', '/path/to/raspberry-pi-specs.pdf', [
    // Including a mime type is not _required_ but strongly recommended
    'contentType' => 'application/pdf'
]);
echo $asset['_id'];

上传文件资产(从字符串)

$image = file_get_contents('/path/to/app-release.apk');
$asset = $client->uploadAssetFromString('file', $buffer, [
    // Will be set in the `originalFilename` property on the image asset
    // The filename in the URL will still be a hash
    'filename' => 'dog-walker-pro-v1.4.33.apk',
    // Including a mime type is not _required_ but strongly recommended
    'contentType' => 'application/vnd.android.package-archive'
]);
echo $asset['_id'];

引用上传的图像/文件

// Create a new document with the referenced image in the "image" field:
$asset = $client->uploadAssetFromFile('image', '/some/path/to/image.png');
$document = $client->create([
    '_type' => 'blogPost',
    'image' => [
        '_type' => 'image',
        'asset' => ['_ref' => $asset['_id']]
    ]
]);
echo $document['_id'];
// Patch existing document, setting the `heroImage` field
$asset = $client->uploadAssetFromFile('image', '/some/path/to/image.png');
$updatedBike = $client
    ->patch('bike-123') // Document ID to patch
    ->set([
        'heroImage' => [
            '_type' => 'image',
            'asset' => ['_ref' => $asset['_id']]
        ]
    ])
    ->commit();

上传图像并将其追加到数组中

$asset = $client->uploadAssetFromFile('image', '/some/path/to/image.png');
$updatedHotel = $client
    ->patch('hotel-coconut-lounge') // Document ID to patch
    ->setIfMissing(['roomPhotos' => []]) // Ensure we have an array to append to
    ->append('roomPhotos', [
        [
            '_type' => 'image',
            '_key' => bin2hex(random_bytes(5)),
            'asset' => ['_ref' => $image['_id']]
        ]
    ])
    ->commit();

获取客户端配置

$config = $client->config();
echo $config['dataset'];

设置客户端配置

$client->config(['dataset' => 'newDataset']);

新配置将合并到现有配置中,因此您只需传递您想要修改的选项。

渲染块内容

当您在Sanity中使用块编辑器时,它会生成一个结构化数组结构,您可以使用它来在任何您想要的平台渲染内容。在PHP中,一个常见的输出格式是HTML。为了使从数组结构到HTML的转换更简单,我们在库中包含了一个辅助类。

如果您的只包含基本的内置块类型,您可以通过这种方式获取渲染的HTML

use Sanity\BlockContent;

$document = $client->getDocument('some-doc');
$article = $document['article']; // The field that contains your block content

$html = BlockContent::toHtml($article, [
    'projectId'    => 'abc123',
    'dataset'      => 'bikeshop',
    'imageOptions' => ['w' => 320, 'h' => 240]
]);

如果您有一些自定义类型,或者想自定义渲染,您可以传递一个序列化器的关联数组

$html = BlockContent::toHtml($article, [
  'serializers' => [
    'listItem' => function ($item, $parent, $htmlBuilder) {
      return '<li class="my-list-item">' . implode('\n', $item['children']) . '</li>';
    },
    'geopoint' => function ($item) {
      $attrs = $item['attributes']
      $url = 'https://www.google.com/maps/embed/v1/place?key=someApiKey&center='
      $url .= $attrs['lat'] . ',' . $attrs['lng'];
      return '<iframe class="geomap" src="' . $url . '" allowfullscreen></iframe>'
    },
    'pet' => function ($item, $parent, $htmlBuilder) {
      return '<p class="pet">' . $htmlBuilder->escape($item['attributes']['name']) . '</p>';
    }
  ]
]);

贡献

sanity-php 遵循 PSR-2编码风格指南。欢迎贡献,但必须符合此标准。

许可协议

MIT许可。查看LICENSE