Giter VIP home page Giter VIP logo

sanity-php's Introduction

sanity-php

PackagistBuild Status

PHP library for the Sanity API

Requirements

sanity-php requires PHP >= 5.6, with the json module installed.

Composer

You can install the library via Composer. Run the following command:

composer require sanity/sanity-php

To use the library, use Composer's autoload:

require_once 'vendor/autoload.php';

Usage

Instantiating a new client

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

Using an authorization token

$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',
]);

Specifying API version

Sanity uses ISO dates (YYYY-MM-DD) in UTC timezone for versioning. The explanation for this can be found in the documentation

In general, unless you know what API version you want to use, you'll want to set it to todays UTC date. By doing this, you'll get all the latest bugfixes and features, while preventing any timezone confusion and locking the API to prevent breaking changes.

Note: Do not be tempted to use a dynamic value for the apiVersion. The whole reason for setting a static value is to prevent unexpected, breaking changes.

In future versions, specifying an API version will be required. For now, to maintain backwards compatiblity, not specifying a version will trigger a deprecation warning and fall back to using v1.

Fetch a single document by ID

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

Performing queries

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

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

See the query documentation for more information on how to write queries.

Using perspectives

The perspective option can be used to specify special filtering behavior for queries. The default value is raw, which means no special filtering is applied, while published and previewDrafts can be used to optimize for specific use cases.

published

Useful for when you want to be sure that draft documents are not returned in production. Pairs well with private datasets.

With a dataset that looks like this:

[
  {
    "_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"
  }
]

And a query like this:

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

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

Then $authors will only contain documents that don't have a drafts. prefix in their _id, in this case just "George Martin":

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

previewDrafts

Designed to help answer the question "What is our app going to look like after all the draft documents are published?".

Given a dataset like this:

[
  {
    "_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"
  }
]

And a query like this:

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

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

Then authors will look like this. Note that the result dedupes documents with a preference for the draft version:

[
  {
    "_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"
  }
]

Since the query simulates what the result will be after publishing the drafts, the _id doesn't contain the drafts. prefix. If you want to check if a document is a draft or not you can use the _originalId field, which is only available when using the previewDrafts perspective.

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

Which changes the result to be:

[
  {
    "_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"
  }
]

Creating documents

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

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

This creates a new document with the given properties. It must contain a _type attribute, and may contain a _id attribute. If an ID is specified and a document with that ID already exist, the mutation will fail. If an ID is not specified, it will be auto-generated and is included in the returned document.

Creating a document (if it does not exist)

As noted above, if you include an _id property when calling create() and a document with this ID already exists, it will fail. If you instead want to ignore the create operation if it exists, you can use createIfNotExists(). It takes the same arguments as create(), the only difference being that it requires an _id attribute.

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

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

Replacing a document

If you don't care whether or not a document exists already and just want to replace it, you can use the createOrReplace() method.

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

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

Patch/update a document

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

Todo: Document all patch operations

Delete a document

use Sanity\Exception\BaseException;

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

Multiple mutations in a transaction

$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);
}

Clientless patches & transactions

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

An important note on this approach is that you cannot call commit() on transactions or patches instantiated this way, instead you have to pass them to client.mutate().

Upload an image asset (from local file)

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

Upload an image asset (from a string)

$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'];

Upload image, extract exif and palette data

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

var_dump($asset['metadata']);

Upload a file asset (from local file)

$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'];

Upload a file asset (from a string)

$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'];

Referencing an uploaded image/file

// 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();

Upload image and append to array

$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();

Get client configuration

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

Set client configuration

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

The new configuration will be merged with the existing, so you only need to pass the options you want to modify.

Rendering block content

When you use the block editor in Sanity, it produces a structured array structure that you can use to render the content on any platform you might want. In PHP, a common output format is HTML. To make the transformation from the array structure to HTML simpler, we include a helper class for this within the library.

If your content only contains the basic, built-in block types, you can get rendered HTML like this:

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

If you have some custom types, or would like to customize the rendering, you may pass an associative array of serializers:

$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>';
    }
  ]
]);

Contributing

sanity-php follows the PSR-2 Coding Style Guide. Contributions are welcome, but must conform to this standard.

License

MIT-licensed. See LICENSE

sanity-php's People

Contributors

christeredvartsen avatar jasontolliver avatar malinowskip avatar renovate[bot] avatar rexxars avatar sjelfull avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

sanity-php's Issues

HTML to Portable Text

What's the suggested way to import an HTML, or any text for that matter, in a portable text field?

Perpectives support

Hello there ๐Ÿ‘‹

First of all, really appreciate your work with this library! We are just starting to use Sanity on the company I work for, and having a PHP library to start with is definitely helping a lot, as most our projects are PHP based.

I see Perspectives is something relatively new to Sanity and found a working branch on this repository with changes to implement it, so currently I am using this working branch on my project and wondering are there still active plans to merge it into main?

While using it on my project I saw a notice on error.log, undefined index clientConfig on line 70 of sanity-php/lib/Client.php:
https://github.com/sanity-io/sanity-php/blob/7520615550075eab491152dbb3c7fc6e81d75632/lib/Client.php#L70C79-L70C91

So I've created a pull request with what I believe would be the fix for that: #33

Many thanks!

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.


Using a curated preset maintained by


Sanity: The Composable Content Cloud

Pending Approval

These branches will be created by Renovate only once you click their checkbox below.

  • chore(deps): pin shivammathur/setup-php action to c665c7a
  • chore(deps): lock file maintenance
  • ๐Ÿ” Create all pending approval PRs at once ๐Ÿ”

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

composer
composer.json
  • php >=5.6
  • guzzlehttp/guzzle ^6.2|^7.0
  • phpunit/phpunit ^5.7
  • squizlabs/php_codesniffer ^2.8
github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • shivammathur/setup-php v2
  • actions/cache v2
  • ubuntu 20.04

  • Check this box to trigger a request for Renovate to run again on this repository

No examples on how to actually render images inline

As good as Sanity.IO seems to be, it's frustrating to use, at least with PHP, because the documentation seems lacking. While I can make a new type and have a serializer for it work as intended for text based entries, it seems very difficult to get an image type to actually render out.

Can we get some examples on how to use this php helper project to render out an image from an attribute within a document?

Feature request: HTML to portable text

Is there any way this lib can have html to block ability like sanity tool does?
I'm trying to import legacy db into sanity with php ( since all utilities are already built there ) and not able to find a way to convert html into block in php.

Not possible to pass extra options to Guzzle

There are a number of use cases where it is necessary to pass extra options to the HTTP Client that the SDK uses, e.g. configuring a proxy, or even allowing the developer to pass their own instance of Guzzle\Client to allow for custom middlewear to be used.

Due to the fact that Sanity\Client uses private properties it isn't possible to easily extend the class to override this default behaviour.

Could the ability to do the above be added?

Unclear as to how to patch an array

So I've tried a couple of things but can't figure out how to perform a patch on an array field.

Here's my latest attempt, with all variables definitely setup the way they should be:

$response = $client
    ->patch( $group_id )
    ->append( 'members', [
        [
            '_type'  => 'reference',
            '_ref'  => $user['_id']
        ]
    ] )
    ->commit();

This is basically not updating anything in Sanity. I've also tried different combinations using the set() method. What am I missing?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.