Skip to content

Commit 206afe5

Browse files
Added variants support with branch
1 parent e0803fe commit 206afe5

8 files changed

Lines changed: 472 additions & 217 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11

22
## CHANGELOG
33
------------------------------------------------
4+
## Version 2.4.3
5+
###### Date: 19-May-2026
6+
### Enhancement
7+
- Added entry variants support with optional branch scoping via `variants()` on Entry and Query
8+
------------------------------------------------
49
## Version 2.4.2
510
###### Date: 05-January-2026
611
### Security fix

composer.lock

Lines changed: 337 additions & 215 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Error/ErrorMessages.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class ErrorMessages
1818
const TAGS_ARRAY = 'Tags must be an array. Convert the value to an array and try again.';
1919
const VALUE_ARRAY = 'Value must be an array. Convert the value to an array and try again.';
2020
const INVALID_QUERY = 'Invalid query. Update the query and try again.';
21+
const VARIANT_UIDS_REQUIRED = 'Variant UID(s) are required. Provide a variant UID string or an array of variant UID strings and try again.';
22+
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.';
2123

2224
// helper.php error messages
2325
const INVALID_STRING_INPUT = 'Invalid input for "%s". Use a string value and try again.';

src/Stack/BaseQuery.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,51 @@ public function includeFallback()
415415
return $this->queryObject;
416416
}
417417

418+
/**
419+
* Scope entry requests to one or more entry variants.
420+
*
421+
* Pass a single variant UID (string) or multiple variant UIDs (array).
422+
* Optionally pass a branch name to scope the request to that branch;
423+
* when omitted, the stack-level branch (if set) is used.
424+
*
425+
* @param string|array $variantUidOrUids - Variant UID or list of variant UIDs
426+
* @param string $branchName - Optional branch name for the request
427+
*
428+
* @example
429+
* use Contentstack\Contentstack;
430+
* $stack = Contentstack::Stack("API_KEY", "DELIVERY_TOKEN", "ENVIRONMENT");
431+
* $result = $stack->ContentType('product')->Entry('ENTRY_UID')->variants('VARIANT_UID', 'branch_name')->toJSON()->fetch();
432+
* $entries = $stack->ContentType('product')->Entry()->variants(array('variant1', 'variant2'), 'branch_name')->toJSON()->find();
433+
*
434+
* @return Query|Entry
435+
*/
436+
public function variants($variantUidOrUids = '', $branchName = '')
437+
{
438+
if (Utility::isEmpty($variantUidOrUids)) {
439+
throw contentstackCreateError(ErrorMessages::VARIANT_UIDS_REQUIRED);
440+
}
441+
if (is_array($variantUidOrUids)) {
442+
if (count($variantUidOrUids) === 0) {
443+
throw contentstackCreateError(ErrorMessages::VARIANT_UIDS_REQUIRED);
444+
}
445+
foreach ($variantUidOrUids as $variantUid) {
446+
if (Utility::isEmpty($variantUid) || !is_string($variantUid)) {
447+
throw contentstackCreateError(ErrorMessages::INVALID_VARIANT_UIDS);
448+
}
449+
}
450+
} elseif (!is_string($variantUidOrUids)) {
451+
throw contentstackCreateError(ErrorMessages::INVALID_VARIANT_UIDS);
452+
}
453+
if (!Utility::isEmpty($branchName) && !is_string($branchName)) {
454+
throw contentstackCreateError(ErrorMessages::INVALID_VARIANT_UIDS);
455+
}
456+
$this->queryObject->variantUid = $variantUidOrUids;
457+
if (!Utility::isEmpty($branchName)) {
458+
$this->queryObject->variantBranch = $branchName;
459+
}
460+
return $this->queryObject;
461+
}
462+
418463
/**
419464
* To include branch of publish content.
420465
*

src/Stack/ContentType/Entry.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ public function __construct($entryUid = '', $contentType = '')
5353
}
5454
}
5555

56+
/**
57+
* Get all entries for the content type (used without an entry UID).
58+
*
59+
* @return Request
60+
*/
61+
public function find()
62+
{
63+
$this->operation = __FUNCTION__;
64+
return Utility::contentstackRequest($this->contentType->stack, $this);
65+
}
66+
5667
/**
5768
* Fetch the specified entry
5869
*

src/Support/Utility.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,21 @@ public static function headers($query = '')
205205
return $headers;
206206
}
207207

208+
/**
209+
* Format variant UID(s) for the x-cs-variant-uid request header.
210+
*
211+
* @param string|array $variantUidOrUids - Variant UID or list of variant UIDs
212+
*
213+
* @return string
214+
*/
215+
public static function formatVariantUids($variantUidOrUids = '')
216+
{
217+
if (is_array($variantUidOrUids)) {
218+
return implode(', ', $variantUidOrUids);
219+
}
220+
return $variantUidOrUids;
221+
}
222+
208223
/**
209224
* POST formatted query for the API server
210225
*
@@ -402,8 +417,15 @@ public static function contentstackRequest($stack, $queryObject = '', $type = ''
402417
}else {
403418
$request_headers[] = 'access_token: '.$Headers["access_token"];
404419
}
405-
if ($Headers["branch"] !== '' && $Headers["branch"] !== "undefined") {
406-
$request_headers[] = 'branch: '.$Headers["branch"];
420+
if (isset($queryObject->variantUid) && !Utility::isEmpty($queryObject->variantUid)) {
421+
$request_headers[] = 'x-cs-variant-uid: '.Utility::formatVariantUids($queryObject->variantUid);
422+
}
423+
$branch = $Headers["branch"] ?? '';
424+
if (isset($queryObject->variantBranch) && !Utility::isEmpty($queryObject->variantBranch)) {
425+
$branch = $queryObject->variantBranch;
426+
}
427+
if ($branch !== '' && $branch !== "undefined") {
428+
$request_headers[] = 'branch: '.$branch;
407429
}
408430

409431
$proxy_details = $stack->proxy;

test/EntriesTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,4 +509,5 @@ public function testFindSearch() {
509509
}
510510
$this->assertTrue($flag);
511511
}
512+
512513
}

test/VariantsTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
use Contentstack\Support\Utility;
5+
use Contentstack\Stack\ContentType;
6+
use Contentstack\Stack\ContentType\Entry;
7+
use Contentstack\Stack\ContentType\Query;
8+
9+
class VariantsTest extends TestCase
10+
{
11+
private function contentType()
12+
{
13+
return new ContentType('ct_uid', '');
14+
}
15+
16+
public function testFormatVariantUids()
17+
{
18+
$this->assertEquals('uid1, uid2', Utility::formatVariantUids(array('uid1', 'uid2')));
19+
$this->assertEquals('uid1', Utility::formatVariantUids('uid1'));
20+
}
21+
22+
public function testEntryVariantsRequiresVariantUids()
23+
{
24+
$this->expectException(\Exception::class);
25+
$this->contentType()->Entry()->variants('');
26+
}
27+
28+
public function testQueryVariantsRejectsEmptyArray()
29+
{
30+
$this->expectException(\Exception::class);
31+
$this->contentType()->Query()->variants(array());
32+
}
33+
34+
public function testVariantsStoresBranchOnQueryObject()
35+
{
36+
$query = $this->contentType()->Query()->variants('variant_uid', 'main');
37+
$this->assertEquals('variant_uid', $query->variantUid);
38+
$this->assertEquals('main', $query->variantBranch);
39+
}
40+
41+
public function testEntryVariantsChainsToEntry()
42+
{
43+
$entry = $this->contentType()->Entry('entry_uid')->variants(array('v1', 'v2'));
44+
$this->assertInstanceOf(Entry::class, $entry);
45+
$this->assertEquals(array('v1', 'v2'), $entry->variantUid);
46+
}
47+
}

0 commit comments

Comments
 (0)