128 lines
3.8 KiB
PHP
128 lines
3.8 KiB
PHP
<?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;
|
||
|
|
}
|
||
|
|
}
|