17 reasons, each a before and an after. Select the problems you have and see how many match.
public string $mode accepts
'live',
'Live',
'liv' and every other string in existence.
Type it as an enum and the valid set becomes part of the signature β a typo stops being a runtime surprise
and starts being a parse error.
public string $mode;
// all of these compile
$mode = 'live';
$mode = 'Live'; // silently wrong
$mode = 'sandbx'; // silently wrong
public PaymentMode $mode;
// only these two exist
$mode = PaymentMode::Live;
$mode = PaymentMode::Sandbox;
$mode = PaymentMode::Liv; // parse error
Pick a flavour and watch the console, the JSON and your own code follow.
Its cases are the complete set of legal values.
// app/Enums/PaymentMode.php
namespace App\Enums;
enum PaymentMode: string
{
case Live = 'live';
case Sandbox = 'sandbox';
}
The constructor signature now documents the valid set.
use App\Enums\PaymentMode;
public function __construct(
public PaymentMode $mode,
public int $retry_attempts,
) {}
The enum says what is possible; the factories say what each environment uses.
public static function development(): static
{
return new static(PaymentMode::Sandbox, 1);
}
public static function production(): static
{
return new static(PaymentMode::Live, 5);
}
An enum has no environment awareness. Configuration placed inside one is invisible to
env-settings:show,
env-settings:diff, masking and local overrides.
Keep per-environment values in the factories; let the enum define only which values are legal.
env-settings:make takes the type name as written, so the property
is typed correctly β but it only knows defaults for scalar types. It seeds both factories with
'' and does not import the enum, so you finish the two
// TODO values yourself.
php artisan env-settings:make PaymentSettings \
--properties="mode:PaymentMode,retry_attempts:int"