{"id":31850498,"url":"https://github.com/sanity-io/sanity-php","last_synced_at":"2025-10-12T11:55:12.531Z","repository":{"id":44740756,"uuid":"87019086","full_name":"sanity-io/sanity-php","owner":"sanity-io","description":"PHP library for retrieving, creating and patching data from Sanity.io","archived":false,"fork":false,"pushed_at":"2025-08-10T15:56:00.000Z","size":138,"stargazers_count":34,"open_issues_count":19,"forks_count":13,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-09-30T04:48:54.180Z","etag":null,"topics":["client-library","php","sanity","sanity-io"],"latest_commit_sha":null,"homepage":"https://www.sanity.io/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sanity-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-04-02T22:01:00.000Z","updated_at":"2025-09-28T11:18:10.000Z","dependencies_parsed_at":"2025-07-28T21:12:42.118Z","dependency_job_id":"cdb1a66f-724a-419c-b263-6a0b6413639e","html_url":"https://github.com/sanity-io/sanity-php","commit_stats":{"total_commits":72,"total_committers":3,"mean_commits":24.0,"dds":0.02777777777777779,"last_synced_commit":"e7446468b0b6f078328dc1bc17f58e4690592423"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/sanity-io/sanity-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanity-io%2Fsanity-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanity-io%2Fsanity-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanity-io%2Fsanity-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanity-io%2Fsanity-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sanity-io","download_url":"https://codeload.github.com/sanity-io/sanity-php/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanity-io%2Fsanity-php/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279011192,"owners_count":26084900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client-library","php","sanity","sanity-io"],"created_at":"2025-10-12T11:55:06.955Z","updated_at":"2025-10-12T11:55:12.526Z","avatar_url":"https://github.com/sanity-io.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sanity-php\n\n[![Packagist](https://img.shields.io/packagist/v/sanity/sanity-php.svg?style=flat-square)](https://packagist.org/packages/sanity/sanity-php)\n\nPHP library for the [Sanity API](https://sanity.io/)\n\n## Requirements\n\nsanity-php requires PHP \u003e= 5.6, with the `json` module installed.\n\n## Composer\n\nYou can install the library via [Composer](http://getcomposer.org/). Run the following command:\n\n```bash\ncomposer require sanity/sanity-php\n```\n\nTo use the library, use Composer's [autoload](https://getcomposer.org/doc/00-intro.md#autoloading):\n\n```php\nrequire_once 'vendor/autoload.php';\n```\n\n## Usage\n\n### Instantiating a new client\n\n```php\nuse Sanity\\Client as SanityClient;\n\n$client = new SanityClient([\n  'projectId' =\u003e 'your-project-id',\n  'dataset' =\u003e 'your-dataset-name',\n  // Whether or not to use the API CDN for queries. Default is false.\n  'useCdn' =\u003e true,\n  // If you are starting a new project, using the current UTC date is usually\n  // a good idea. See \"Specifying API version\" section for more details\n  'apiVersion' =\u003e '2019-01-29',\n]);\n```\n\n### Using an authorization token\n\n```php\n$client = new SanityClient([\n  'projectId' =\u003e 'your-project-id',\n  'dataset' =\u003e 'your-dataset-name',\n  'useCdn' =\u003e false,\n  'apiVersion' =\u003e '2019-01-29',\n  // Note that you cannot combine a token with the `useCdn` option set to true,\n  // as authenticated requests cannot be cached\n  'token' =\u003e 'sanity-auth-token',\n]);\n```\n\n### Specifying API version\n\nSanity uses ISO dates (YYYY-MM-DD) in UTC timezone for versioning. The explanation for this can be found [in the documentation](http://sanity.io/help/api-versioning)\n\nIn 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.\n\n**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.\n\nIn 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`.\n\n### Fetch a single document by ID\n\n```php\n$document = $client-\u003egetDocument('someDocumentId');\n```\n\n### Performing queries\n\n```php\n$results = $client-\u003efetch(\n  '*[_type == $type][0...3]', // Query\n  ['type' =\u003e 'product'] // Params (optional)\n);\n\nforeach ($product in $results) {\n  echo $product['title'] . '\\n';\n}\n```\n\nSee the [query documentation](https://www.sanity.io/docs/front-ends/query-cheat-sheet) for more information on how to write queries.\n\n### Using perspectives\n\nThe `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`](#published) and [`previewDrafts`](#previewdrafts) can be used to optimize for specific use cases.\n\n#### `published`\n\nUseful for when you want to be sure that draft documents are not returned in production. Pairs well with private datasets.\n\nWith a dataset that looks like this:\n\n```json\n[\n  {\n    \"_type\": \"author\",\n    \"_id\": \"ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George Martin\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"drafts.ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George R.R. Martin\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2\",\n    \"name\": \"Stephen King\"\n  }\n]\n```\n\nAnd a query like this:\n\n```php\n$client = new SanityClient([\n  // ...config...\n  'useCdn' =\u003e true,\n  'perspective' =\u003e 'published',\n]);\n\n$authors = $client-\u003efetch('*[_type == \"author\"]');\n```\n\nThen `$authors` will only contain documents that don't have a `drafts.` prefix in their `_id`, in this case just \"George Martin\":\n\n```json\n[\n  {\n    \"_type\": \"author\",\n    \"_id\": \"ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George Martin\"\n  }\n]\n```\n\n#### `previewDrafts`\n\nDesigned to help answer the question \"What is our app going to look like after all the draft documents are published?\".\n\nGiven a dataset like this:\n\n```json\n[\n  {\n    \"_type\": \"author\",\n    \"_id\": \"ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George Martin\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"drafts.ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George R.R. Martin\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2\",\n    \"name\": \"Stephen King\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"6b3792d2-a9e8-4c79-9982-c7e89f2d1e75\",\n    \"name\": \"Terry Pratchett\"\n  }\n]\n```\n\nAnd a query like this:\n\n```php\n$client = new SanityClient([\n  // ...config...\n  'useCdn' =\u003e false, // the `previewDrafts` perspective requires this to be `false`\n  'perspective' =\u003e 'previewDrafts',\n]);\n\n$authors = $client-\u003efetch('*[_type == \"author\"]');\n```\n\nThen `authors` will look like this. Note that the result dedupes documents with a preference for the draft version:\n\n```json\n[\n  {\n    \"_type\": \"author\",\n    \"_id\": \"ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"_originalId\": \"drafts.ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George R.R. Martin\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"f4898efe-92c4-4dc0-9c8c-f7480aef17e2\",\n    \"_originalId\": \"drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2\",\n    \"name\": \"Stephen King\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"6b3792d2-a9e8-4c79-9982-c7e89f2d1e75\",\n    \"_originalId\": \"6b3792d2-a9e8-4c79-9982-c7e89f2d1e75\",\n    \"name\": \"Terry Pratchett\"\n  }\n]\n```\n\nSince 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.\n\n```php\n$authors = $client-\u003efetch('*[_type == \"author\"]{..., \"status\": select(\n  _originalId in path(\"drafts.**\") =\u003e \"draft\",\n  \"published\"\n)}');\n```\n\nWhich changes the result to be:\n\n```json\n[\n  {\n    \"_type\": \"author\",\n    \"_id\": \"ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"_originalId\": \"drafts.ecfef291-60f0-4609-bbfc-263d11a48c43\",\n    \"name\": \"George R.R. Martin\",\n    \"status\": \"draft\"\n  },\n  {\n    \"_type\": \"author\",\n    \"_id\": \"f4898efe-92c4-4dc0-9c8c-f7480aef17e2\",\n    \"_originalId\": \"f4898efe-92c4-4dc0-9c8c-f7480aef17e2\",\n    \"name\": \"Stephen King\",\n    \"status\": \"published\"\n  }\n]\n```\n\n### Creating documents\n\n```php\n$doc = [\n  '_type' =\u003e 'bike',\n  'name'  =\u003e 'Bengler Tandem Extraordinaire',\n  'seats' =\u003e 2,\n];\n\n$newDocument = $client-\u003ecreate($doc);\necho 'Bike was created, document ID is ' . $newDocument['_id'];\n```\n\nThis 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.\n\n### Creating a document (if it does not exist)\n\nAs 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.\n\n```php\n$doc = [\n  '_id'   =\u003e 'my-document-id',\n  '_type' =\u003e 'bike',\n  'name'  =\u003e 'Amazing bike',\n  'seats' =\u003e 3,\n];\n\n$newDocument = $client-\u003ecreateIfNotExists($doc);\n```\n\n### Replacing a document\n\nIf you don't care whether or not a document exists already and just want to replace it, you can use the `createOrReplace()` method.\n\n```php\n$doc = [\n  '_id'   =\u003e 'my-document-id',\n  '_type' =\u003e 'bike',\n  'name'  =\u003e 'Amazing bike',\n  'seats' =\u003e 3,\n];\n\n$newDocument = $client-\u003ecreateOrReplace($doc);\n```\n\n### Patch/update a document\n\n```php\nuse Sanity\\Exception\\BaseException;\n\ntry {\n  $updatedBike = $client\n    -\u003epatch('bike-123') // Document ID to patch\n    -\u003eset(['inStock' =\u003e false]) // Shallow merge\n    -\u003einc(['numSold' =\u003e 1]) // Increment field by count\n    -\u003ecommit(); // Perform the patch and return the modified document\n} catch (BaseException $error) {\n  echo 'Oh no, the update failed: ';\n  var_dump($error);\n}\n```\n\nTodo: Document all patch operations\n\n### Delete a document\n\n```php\nuse Sanity\\Exception\\BaseException;\n\ntry {\n  $client-\u003edelete('bike-123');\n} catch (BaseException $error) {\n  echo 'Delete failed: ';\n  var_dump($error);\n}\n```\n\n### Multiple mutations in a transaction\n\n```php\n$namePatch = $client-\u003epatch('bike-310')-\u003eset(['name' =\u003e 'A Bike To Go']);\n\ntry {\n  $client-\u003etransaction()\n    -\u003ecreate(['name' =\u003e 'Bengler Tandem Extraordinaire', 'seats' =\u003e 2])\n    -\u003edelete('bike-123')\n    -\u003epatch($namePatch)\n    -\u003ecommit();\n\n  echo 'A whole lot of stuff just happened!';\n} catch (BaseException $error) {\n  echo 'Transaction failed:';\n  var_dump($error);\n}\n```\n\n### Clientless patches \u0026 transactions\n\n```php\nuse Sanity\\Patch;\nuse Sanity\\Transaction;\n\n// Patches:\n$patch = new Patch('\u003cdocumentId\u003e');\n$patch-\u003einc(['count' =\u003e 1])-\u003eunset(['visits']);\n$client-\u003emutate($patch);\n\n// Transactions:\n$transaction = new Transaction();\n$transaction\n  -\u003ecreate(['_id' =\u003e '123', 'name' =\u003e 'FooBike'])\n  -\u003edelete('someDocId');\n\n$client-\u003emutate($transaction);\n```\n\nAn 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()`.\n\n### Upload an image asset (from local file)\n\n```php\n$asset = $client-\u003euploadAssetFromFile('image', '/some/path/to/image.png');\necho $asset['_id'];\n```\n\n### Upload an image asset (from a string)\n\n```php\n$image = file_get_contents('/some/path/to/image.png');\n$asset = $client-\u003euploadAssetFromString('image', $buffer, [\n    // Will be set in the `originalFilename` property on the image asset\n    // The filename in the URL will still be a hash\n    'filename' =\u003e 'magnificent-bridge.png'\n]);\necho $asset['_id'];\n```\n\n### Upload image, extract exif and palette data\n\n```php\n$asset = $client-\u003euploadAssetFromFile('image', '/some/path/to/image.png', [\n    'extract' =\u003e ['exif', 'palette']\n]);\n\nvar_dump($asset['metadata']);\n```\n\n### Upload a file asset (from local file)\n\n```php\n$asset = $client-\u003euploadAssetFromFile('file', '/path/to/raspberry-pi-specs.pdf', [\n    // Including a mime type is not _required_ but strongly recommended\n    'contentType' =\u003e 'application/pdf'\n]);\necho $asset['_id'];\n```\n\n### Upload a file asset (from a string)\n\n```php\n$image = file_get_contents('/path/to/app-release.apk');\n$asset = $client-\u003euploadAssetFromString('file', $buffer, [\n    // Will be set in the `originalFilename` property on the image asset\n    // The filename in the URL will still be a hash\n    'filename' =\u003e 'dog-walker-pro-v1.4.33.apk',\n    // Including a mime type is not _required_ but strongly recommended\n    'contentType' =\u003e 'application/vnd.android.package-archive'\n]);\necho $asset['_id'];\n```\n\n### Referencing an uploaded image/file\n\n```php\n// Create a new document with the referenced image in the \"image\" field:\n$asset = $client-\u003euploadAssetFromFile('image', '/some/path/to/image.png');\n$document = $client-\u003ecreate([\n    '_type' =\u003e 'blogPost',\n    'image' =\u003e [\n        '_type' =\u003e 'image',\n        'asset' =\u003e ['_ref' =\u003e $asset['_id']]\n    ]\n]);\necho $document['_id'];\n```\n\n```php\n// Patch existing document, setting the `heroImage` field\n$asset = $client-\u003euploadAssetFromFile('image', '/some/path/to/image.png');\n$updatedBike = $client\n    -\u003epatch('bike-123') // Document ID to patch\n    -\u003eset([\n        'heroImage' =\u003e [\n            '_type' =\u003e 'image',\n            'asset' =\u003e ['_ref' =\u003e $asset['_id']]\n        ]\n    ])\n    -\u003ecommit();\n```\n\n### Upload image and append to array\n\n```php\n$asset = $client-\u003euploadAssetFromFile('image', '/some/path/to/image.png');\n$updatedHotel = $client\n    -\u003epatch('hotel-coconut-lounge') // Document ID to patch\n    -\u003esetIfMissing(['roomPhotos' =\u003e []]) // Ensure we have an array to append to\n    -\u003eappend('roomPhotos', [\n        [\n            '_type' =\u003e 'image',\n            '_key' =\u003e bin2hex(random_bytes(5)),\n            'asset' =\u003e ['_ref' =\u003e $image['_id']]\n        ]\n    ])\n    -\u003ecommit();\n```\n\n### Get client configuration\n\n```php\n$config = $client-\u003econfig();\necho $config['dataset'];\n```\n\n### Set client configuration\n\n```php\n$client-\u003econfig(['dataset' =\u003e 'newDataset']);\n```\n\nThe new configuration will be merged with the existing, so you only need to pass the options you want to modify.\n\n### Rendering block content\n\nWhen 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.\n\nIf your content only contains the basic, built-in block types, you can get rendered HTML like this:\n\n```php\nuse Sanity\\BlockContent;\n\n$document = $client-\u003egetDocument('some-doc');\n$article = $document['article']; // The field that contains your block content\n\n$html = BlockContent::toHtml($article, [\n    'projectId'    =\u003e 'abc123',\n    'dataset'      =\u003e 'bikeshop',\n    'imageOptions' =\u003e ['w' =\u003e 320, 'h' =\u003e 240]\n]);\n```\n\nIf you have some custom types, or would like to customize the rendering, you may pass an associative array of serializers:\n\n```php\n$html = BlockContent::toHtml($article, [\n  'serializers' =\u003e [\n    'listItem' =\u003e function ($item, $parent, $htmlBuilder) {\n      return '\u003cli class=\"my-list-item\"\u003e' . implode('\\n', $item['children']) . '\u003c/li\u003e';\n    },\n    'geopoint' =\u003e function ($item) {\n      $attrs = $item['attributes']\n      $url = 'https://www.google.com/maps/embed/v1/place?key=someApiKey\u0026center='\n      $url .= $attrs['lat'] . ',' . $attrs['lng'];\n      return '\u003ciframe class=\"geomap\" src=\"' . $url . '\" allowfullscreen\u003e\u003c/iframe\u003e'\n    },\n    'pet' =\u003e function ($item, $parent, $htmlBuilder) {\n      return '\u003cp class=\"pet\"\u003e' . $htmlBuilder-\u003eescape($item['attributes']['name']) . '\u003c/p\u003e';\n    }\n  ]\n]);\n```\n\n## Contributing\n\n`sanity-php` follows the [PSR-2 Coding Style Guide](http://www.php-fig.org/psr/psr-2/). Contributions are welcome, but must conform to this standard.\n\n## License\n\nMIT-licensed. See LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsanity-io%2Fsanity-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsanity-io%2Fsanity-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsanity-io%2Fsanity-php/lists"}