-
Notifications
You must be signed in to change notification settings - Fork 60
Description
Feature Idea: Settings Casting
This document outlines the idea and implementation plan for a new "Settings Casting" feature.
1. Feature Summary
This feature would introduce a $settingsCasts
property on models, similar to Eloquent's native $casts
. It would allow developers to define expected data types for their settings, and the package would automatically handle the casting of values when they are retrieved or stored. This ensures data integrity and convenience (e.g., always getting a Carbon
object for a date, or a boolean
instead of a string '1'
).
2. User-Facing API
The developer would define a $settingsCasts
property on their model.
// In a model like App\Models\User.php
use Glorand\Model\Settings\Traits\HasSettings;
class User extends Model
{
use HasSettings;
protected $settingsCasts = [
'notifications_enabled' => 'boolean',
'login_attempts' => 'integer',
'last_seen' => 'datetime',
'preferences' => 'array',
'api_token' => 'encrypted',
];
}
3. Implementation Plan
The implementation would primarily involve modifying the HasSettings
trait and the AbstractSettingsManager
.
Step 3.1: Add getSettingsCasts()
to HasSettings
Trait
To safely access the $settingsCasts
property from the manager, a helper method will be added to the src/Traits/HasSettings.php
file.
trait HasSettings
{
// ... existing methods
public function getSettingsCasts(): array
{
if (property_exists($this, 'settingsCasts') && is_array($this->settingsCasts)) {
return $this->settingsCasts;
}
return [];
}
}
Step 3.2: Modify AbstractSettingsManager
The core logic will reside in src/Managers/AbstractSettingsManager.php
.
A. Update the get()
method:
The get()
method will be modified to pass the retrieved value through a new casting method before returning it.
public function get(string $path = null, $default = null)
{
if (is_null($path)) {
return $this->all();
}
$value = Arr::get($this->all(), $path, $default);
return $this->castAttribute($path, $value); // New casting step
}
B. Update the set()
method:
The set()
method will be updated to handle two-way casting, such as encryption, before the value is stored.
public function set(string $path, $value): SettingsManagerContract
{
$settings = $this->all();
// Apply cast before setting the value in the array
$value = $this->castAttributeForSet($path, $value); // New step
Arr::set($settings, $path, $value);
return $this->apply($settings);
}
C. Add the Caster Methods:
Two new private methods will be added to handle the actual casting logic.
/**
* Cast an attribute to a native PHP type on get.
*/
private function castAttribute(string $key, $value)
{
$casts = $this->model->getSettingsCasts();
if (!isset($casts[$key])) {
return $value;
}
return match ($casts[$key]) {
'int', 'integer' => (int) $value,
'real', 'float', 'double' => (float) $value,
'string' => (string) $value,
'bool', 'boolean' => (bool) $value,
'array', 'json' => is_string($value) ? json_decode($value, true) : $value,
'object' => is_string($value) ? json_decode($value, false) : $value,
'date' => \Illuminate\Support\Carbon::parse($value),
'datetime' => \Illuminate\Support\Carbon::parse($value),
'encrypted' => \Illuminate\Support\Facades\Crypt::decryptString($value),
default => $value,
};
}
/**
* Cast an attribute for storage.
*/
private function castAttributeForSet(string $key, $value)
{
$casts = $this->model->getSettingsCasts();
if (!isset($casts[$key])) {
return $value;
}
return match ($casts[$key]) {
'encrypted' => \Illuminate\Support\Facades\Crypt::encryptString($value),
// Other set-side casts (e.g., date to string) could be added here.
default => $value,
};
}