diff --git a/CHANGELOG.md b/CHANGELOG.md index 91bb6a81..75df3dd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ## CHANGELOG ------------------------------------------------ +## Version 2.6.0 +###### Date: 27-July-2026 +### Enhancement + - Added entry variants support with optional branch scoping via `variants()` on Entry and Query +------------------------------------------------ ## Version 2.5.0 ###### Date: 15-June-2026 ### Enhancement diff --git a/src/Error/ErrorMessages.php b/src/Error/ErrorMessages.php index 27f5b71d..01487a48 100644 --- a/src/Error/ErrorMessages.php +++ b/src/Error/ErrorMessages.php @@ -18,6 +18,8 @@ class ErrorMessages const TAGS_ARRAY = 'Tags must be an array. Convert the value to an array and try again.'; const VALUE_ARRAY = 'Value must be an array. Convert the value to an array and try again.'; const INVALID_QUERY = 'Invalid query. Update the query and try again.'; + const VARIANT_UIDS_REQUIRED = 'Variant UID(s) are required. Provide a variant UID string or an array of variant UID strings and try again.'; + const INVALID_VARIANT_UIDS = 'Invalid variant UID(s). Use a string for a single variant UID or an array of variant UID strings and try again.'; // helper.php error messages const INVALID_STRING_INPUT = 'Invalid input for "%s". Use a string value and try again.'; diff --git a/src/Stack/BaseQuery.php b/src/Stack/BaseQuery.php index 9dce5aad..b74a9066 100755 --- a/src/Stack/BaseQuery.php +++ b/src/Stack/BaseQuery.php @@ -415,6 +415,51 @@ public function includeFallback() return $this->queryObject; } + /** + * Scope entry requests to one or more entry variants. + * + * Pass a single variant UID (string) or multiple variant UIDs (array). + * Optionally pass a branch name to scope the request to that branch; + * when omitted, the stack-level branch (if set) is used. + * + * @param string|array $variantUidOrUids - Variant UID or list of variant UIDs + * @param string $branchName - Optional branch name for the request + * + * @example + * use Contentstack\Contentstack; + * $stack = Contentstack::Stack("API_KEY", "DELIVERY_TOKEN", "ENVIRONMENT"); + * $result = $stack->ContentType('product')->Entry('ENTRY_UID')->variants('VARIANT_UID', 'branch_name')->toJSON()->fetch(); + * $entries = $stack->ContentType('product')->Entry()->variants(array('variant1', 'variant2'), 'branch_name')->toJSON()->find(); + * + * @return Query|Entry + */ + public function variants($variantUidOrUids = '', $branchName = '') + { + if (Utility::isEmpty($variantUidOrUids)) { + throw contentstackCreateError(ErrorMessages::VARIANT_UIDS_REQUIRED); + } + if (is_array($variantUidOrUids)) { + if (count($variantUidOrUids) === 0) { + throw contentstackCreateError(ErrorMessages::VARIANT_UIDS_REQUIRED); + } + foreach ($variantUidOrUids as $variantUid) { + if (Utility::isEmpty($variantUid) || !is_string($variantUid)) { + throw contentstackCreateError(ErrorMessages::INVALID_VARIANT_UIDS); + } + } + } elseif (!is_string($variantUidOrUids)) { + throw contentstackCreateError(ErrorMessages::INVALID_VARIANT_UIDS); + } + if (!Utility::isEmpty($branchName) && !is_string($branchName)) { + throw contentstackCreateError(ErrorMessages::INVALID_VARIANT_UIDS); + } + $this->queryObject->variantUid = $variantUidOrUids; + if (!Utility::isEmpty($branchName)) { + $this->queryObject->variantBranch = $branchName; + } + return $this->queryObject; + } + /** * To include branch of publish content. * diff --git a/src/Stack/ContentType/Entry.php b/src/Stack/ContentType/Entry.php index d83bc6e9..b52adc71 100755 --- a/src/Stack/ContentType/Entry.php +++ b/src/Stack/ContentType/Entry.php @@ -53,6 +53,17 @@ public function __construct($entryUid = '', $contentType = '') } } + /** + * Get all entries for the content type (used without an entry UID). + * + * @return Request + */ + public function find() + { + $this->operation = __FUNCTION__; + return Utility::contentstackRequest($this->contentType->stack, $this); + } + /** * Fetch the specified entry * diff --git a/src/Support/Utility.php b/src/Support/Utility.php index 448bcb15..483da02b 100755 --- a/src/Support/Utility.php +++ b/src/Support/Utility.php @@ -205,6 +205,21 @@ public static function headers($query = '') return $headers; } + /** + * Format variant UID(s) for the x-cs-variant-uid request header. + * + * @param string|array $variantUidOrUids - Variant UID or list of variant UIDs + * + * @return string + */ + public static function formatVariantUids($variantUidOrUids = '') + { + if (is_array($variantUidOrUids)) { + return implode(', ', $variantUidOrUids); + } + return $variantUidOrUids; + } + /** * POST formatted query for the API server * @@ -402,8 +417,15 @@ public static function contentstackRequest($stack, $queryObject = '', $type = '' }else { $request_headers[] = 'access_token: '.$Headers["access_token"]; } - if ($Headers["branch"] !== '' && $Headers["branch"] !== "undefined") { - $request_headers[] = 'branch: '.$Headers["branch"]; + if (isset($queryObject->variantUid) && !Utility::isEmpty($queryObject->variantUid)) { + $request_headers[] = 'x-cs-variant-uid: '.Utility::formatVariantUids($queryObject->variantUid); + } + $branch = $Headers["branch"] ?? ''; + if (isset($queryObject->variantBranch) && !Utility::isEmpty($queryObject->variantBranch)) { + $branch = $queryObject->variantBranch; + } + if ($branch !== '' && $branch !== "undefined") { + $request_headers[] = 'branch: '.$branch; } $proxy_details = $stack->proxy; diff --git a/test/EntriesTest.php b/test/EntriesTest.php index a5ab7490..8110dce2 100755 --- a/test/EntriesTest.php +++ b/test/EntriesTest.php @@ -509,4 +509,5 @@ public function testFindSearch() { } $this->assertTrue($flag); } + } \ No newline at end of file diff --git a/test/VariantsTest.php b/test/VariantsTest.php new file mode 100644 index 00000000..7b6519af --- /dev/null +++ b/test/VariantsTest.php @@ -0,0 +1,47 @@ +assertEquals('uid1, uid2', Utility::formatVariantUids(array('uid1', 'uid2'))); + $this->assertEquals('uid1', Utility::formatVariantUids('uid1')); + } + + public function testEntryVariantsRequiresVariantUids() + { + $this->expectException(\Exception::class); + $this->contentType()->Entry()->variants(''); + } + + public function testQueryVariantsRejectsEmptyArray() + { + $this->expectException(\Exception::class); + $this->contentType()->Query()->variants(array()); + } + + public function testVariantsStoresBranchOnQueryObject() + { + $query = $this->contentType()->Query()->variants('variant_uid', 'main'); + $this->assertEquals('variant_uid', $query->variantUid); + $this->assertEquals('main', $query->variantBranch); + } + + public function testEntryVariantsChainsToEntry() + { + $entry = $this->contentType()->Entry('entry_uid')->variants(array('v1', 'v2')); + $this->assertInstanceOf(Entry::class, $entry); + $this->assertEquals(array('v1', 'v2'), $entry->variantUid); + } +}