diff --git a/src/Contracts/DefinesFeaturesExternally.php b/src/Contracts/DefinesFeaturesExternally.php new file mode 100644 index 0000000..86e3e18 --- /dev/null +++ b/src/Contracts/DefinesFeaturesExternally.php @@ -0,0 +1,13 @@ + + */ + public function definedFeaturesForScope(mixed $scope): array; +} diff --git a/src/Drivers/Decorator.php b/src/Drivers/Decorator.php index ed0ff6c..363c9dd 100644 --- a/src/Drivers/Decorator.php +++ b/src/Drivers/Decorator.php @@ -10,6 +10,7 @@ use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; use Laravel\Pennant\Contracts\CanListStoredFeatures; +use Laravel\Pennant\Contracts\DefinesFeaturesExternally; use Laravel\Pennant\Contracts\Driver; use Laravel\Pennant\Contracts\FeatureScopeable; use Laravel\Pennant\Events\AllFeaturesPurged; @@ -620,6 +621,12 @@ public function instance($name) */ public function definedFeaturesForScope($scope) { + $scope = $this->resolveScope($scope); + + if ($this->driver instanceof DefinesFeaturesExternally) { + return collect($this->driver->definedFeaturesForScope($scope)); + } + return collect($this->nameMap) ->only($this->defined()) ->filter(function ($resolver) use ($scope) { diff --git a/tests/Feature/DefinesFeaturesExternallyTest.php b/tests/Feature/DefinesFeaturesExternallyTest.php new file mode 100644 index 0000000..011fcae --- /dev/null +++ b/tests/Feature/DefinesFeaturesExternallyTest.php @@ -0,0 +1,73 @@ + + */ + public function definedFeaturesForScope(mixed $scope): array + { + return [ + 'feature-1', + 'feature-2', + ]; + } + }; + Feature::extend('external', fn () => $driver); + Config::set('pennant.stores.external', ['driver' => 'external']); + + $features = Feature::driver('external')->all(); + + $this->assertSame([ + 'feature-1' => false, + 'feature-2' => false, + ], $features); + } + + public function test_when_features_are_defined_externally_that_scope_is_correctly_resolved() + { + $driver = new class(app('events'), []) extends ArrayDriver implements DefinesFeaturesExternally + { + /** + * Retrieve the defined features for the given scope. + * + * @return list + */ + public function definedFeaturesForScope(mixed $scope): array + { + return [ + $scope, + ]; + } + }; + Feature::extend('external', fn () => $driver); + Config::set('pennant.stores.external', ['driver' => 'external']); + + $features = Feature::driver('external')->for(new class implements FeatureScopeable + { + public function toFeatureIdentifier(string $driver): mixed + { + return 'scope-value'; + } + })->all(); + + $this->assertSame([ + 'scope-value' => false, + ], $features); + } +}