feature flag management by admins
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class FeatureFlagController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of feature flags.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Get all feature flag classes from app/Features directory
|
||||
$featurePath = app_path('Features');
|
||||
$features = [];
|
||||
|
||||
if (File::isDirectory($featurePath)) {
|
||||
$files = File::files($featurePath);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$className = 'App\\Features\\' . $file->getFilenameWithoutExtension();
|
||||
|
||||
if (class_exists($className)) {
|
||||
$featureName = $this->getFeatureName($className);
|
||||
|
||||
// Get the current state for null scope (global)
|
||||
$isActive = Feature::for(null)->active($featureName);
|
||||
|
||||
$features[] = [
|
||||
'name' => $featureName,
|
||||
'class' => $className,
|
||||
'is_active' => $isActive,
|
||||
'description' => $this->getFeatureDescription($className),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort features by name
|
||||
usort($features, fn($a, $b) => strcmp($a['name'], $b['name']));
|
||||
|
||||
return view('admin.feature-flags.index', compact('features'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle a feature flag on or off.
|
||||
*/
|
||||
public function toggle(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'feature' => ['required', 'string'],
|
||||
'active' => ['required', 'boolean'],
|
||||
]);
|
||||
|
||||
$featureName = $validated['feature'];
|
||||
|
||||
// Activate or deactivate the feature for null scope (global)
|
||||
if ($validated['active']) {
|
||||
Feature::for(null)->activate($featureName);
|
||||
$message = "Feature '{$featureName}' has been activated.";
|
||||
} else {
|
||||
Feature::for(null)->deactivate($featureName);
|
||||
$message = "Feature '{$featureName}' has been deactivated.";
|
||||
}
|
||||
|
||||
return redirect()->route('admin.feature-flags.index')
|
||||
->with('success', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge a feature flag (remove all stored values).
|
||||
*/
|
||||
public function purge(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'feature' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
$featureName = $validated['feature'];
|
||||
|
||||
Feature::purge($featureName);
|
||||
|
||||
return redirect()->route('admin.feature-flags.index')
|
||||
->with('success', "Feature '{$featureName}' has been purged from storage.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the feature name from the class.
|
||||
*/
|
||||
private function getFeatureName(string $className): string
|
||||
{
|
||||
// Check if class has Name attribute
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$attributes = $reflection->getAttributes(\Laravel\Pennant\Attributes\Name::class);
|
||||
|
||||
if (!empty($attributes)) {
|
||||
$nameAttribute = $attributes[0]->newInstance();
|
||||
return $nameAttribute->name;
|
||||
}
|
||||
|
||||
// Default to class basename
|
||||
return class_basename($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the feature description from class docblock.
|
||||
*/
|
||||
private function getFeatureDescription(string $className): ?string
|
||||
{
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$docComment = $reflection->getDocComment();
|
||||
|
||||
if ($docComment) {
|
||||
// Extract the first line of the docblock as description
|
||||
preg_match('/\/\*\*\s*\n\s*\*\s*(.+?)\n/s', $docComment, $matches);
|
||||
if (isset($matches[1])) {
|
||||
return trim($matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user