Key-value Field with dot in key

Maybe I am blind/stupid, but why isn't it possible to save a key value in a KeyValuefield with a dot in the name? I have just this simple field:
public static function form(Form $form): Form
{
return $form
->schema([
KeyValue::make('data'),
]);
}
public static function form(Form $form): Form
{
return $form
->schema([
KeyValue::make('data'),
]);
}
and of course I cast it in the model to array:
protected function casts(): array
{
return [
'data' => 'array',
];
}
protected function casts(): array
{
return [
'data' => 'array',
];
}
So keys like "Test" or "Test 2asdas" or "Test 3!" are working and I can save them without errors. But as soon as there is a dot inside like "Test." or "Test Value. XY" then I get: "No synthesizer found for key: "1"" Context: I use this field to create a mapping of values to IDs. For example, I want to map certain brand names like "Apple" or "Google" to IDs. However, my source dataset also contains brands with a "." in their name, such as "O.B.":
{
"Apple": 1,
"Google": 2,
"O.B.": 3
}
{
"Apple": 1,
"Google": 2,
"O.B.": 3
}
No description
31 Replies
toeknee
toeknee6d ago
I think these are used you see
bernhard
bernhardOP6d ago
@toeknee Sorry, I don't understand what you mean?
awcodes
awcodes6d ago
The dots are likely interfering with the statePath which also uses dots to denote the components in the form. It’s an internal mapping.
toeknee
toeknee6d ago
As ;er awcodes and depending if we are using dataget // Arr:get that treats . as a nested array. I'd recommend stripping out . and replacing with
bernhard
bernhardOP6d ago
Well, I was already thinking into this direction, but beforeStateDehydrated isn't working I tried
KeyValue::make('data')
->beforeStateDehydrated(fn ($state) => dd(collect($state)->mapWithKeys(fn ($value, $key) => [
str_replace('.', '__', $key) => $value
])->toArray()))
KeyValue::make('data')
->beforeStateDehydrated(fn ($state) => dd(collect($state)->mapWithKeys(fn ($value, $key) => [
str_replace('.', '__', $key) => $value
])->toArray()))
toeknee
toeknee6d ago
Why not just add it in the model as the mutator?
bernhard
bernhardOP6d ago
Well this is just a simplified example. In my real usecase, I am using the keyvalue field inside a repeater and I get an error when saving when using beforeStateDehydrated. So the problem occurse before
toeknee
toeknee6d ago
So use ->formatStateUsing(fn($state, $record) => )
bernhard
bernhardOP6d ago
Thats the worng position in lifecycle.
toeknee
toeknee6d ago
But on the model you could use:
public function getDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
public function getDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
Which would return data with the .'s replaced.
bernhard
bernhardOP6d ago
Maybe we are speaking of different problems. The user enters the name like "O.B." into the KeyValue Field. So formatStateUsing or mutators are too late / too early. The error occurs on saving
toeknee
toeknee6d ago
Ok so do it on the set
public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
That is assuming the values save to the DB ok which I suspect they should
bernhard
bernhardOP6d ago
This is again too late. The error isn't from the model, but from the field. But my first try on beforeStateDehydrated is wrong. I thought I get the string in beforeStateDehydrated, but I get already the wrong data:
{
"Test1" => "1"
"Test2" => "2"
"Test3" => "3"
"Test2_" => "3"
"Test" => array:1 [
"" => "444"
]
}
{
"Test1" => "1"
"Test2" => "2"
"Test3" => "3"
"Test2_" => "3"
"Test" => array:1 [
"" => "444"
]
}
toeknee
toeknee6d ago
If you want to do it on the key value field: mutateDehydratedStateUsing() I did jsut test the saving though through a KEyvalue field and it tried to save to the db as expected set test = { "test.test": "testvalue" }, At which point you could mutate it on the setDataAttribute on the saving to the DB.
bernhard
bernhardOP6d ago
Well thats funny.
bernhard
bernhardOP6d ago
This is without any extra code, just KeyValue::make('data') So when creating a new record, setting the value to test.test it works and is saved without problems. Then editing and adding something without a dot, it works as well. Then creating a new record without a dot, saving and adding the same test.test again, I get the error
bernhard
bernhardOP6d ago
No description
bernhard
bernhardOP6d ago
Thats how its saved
toeknee
toeknee6d ago
So yes that's how it's saved... but did you add the attribute above? Which will adjust it from . to _ on saving?
public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
to your model?
bernhard
bernhardOP6d ago
Where should I add this? To the model?
toeknee
toeknee6d ago
Yes It is a Standard laravel Attribute caster
bernhard
bernhardOP6d ago
No description
bernhard
bernhardOP6d ago
Thats now the whole model:
class Test extends Model
{
protected function casts()
{
return [
'data' => 'array',
];
}


public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
}
class Test extends Model
{
protected function casts()
{
return [
'data' => 'array',
];
}


public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}
return $transformedData;
}
}
toeknee
toeknee6d ago
Are you on laravel 11
bernhard
bernhardOP6d ago
this is the latest fork of the demo app laravel/framework v11.31.0 The Laravel Framework. But once again, I dont think this is a problem on the model part of laravel, but on the filament part
Route::get("test", function() {
$test = \App\Models\Test::create([
"data" => [
"test.test" => "Test"
]
]);
$test->save();

$test->data = array_merge($test->data, [
"test2.test" => "test2",
]);
$test->save();
});
Route::get("test", function() {
$test = \App\Models\Test::create([
"data" => [
"test.test" => "Test"
]
]);
$test->save();

$test->data = array_merge($test->data, [
"test2.test" => "test2",
]);
$test->save();
});
Works. without any setDataAttribute
toeknee
toeknee6d ago
That will, just doing it on the model is easier for future proofing This is it working
public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}

$this->attributes['data'] = json_encode($transformedData);
}
public function setDataAttribute($value)
{
$transformedData = [];
foreach ($value as $key => $val) {
$newKey = str_replace('.', '_', $key);
$transformedData[$newKey] = $val;
}

$this->attributes['data'] = json_encode($transformedData);
}
bernhard
bernhardOP6d ago
Not sure if modifing data just to make it work for a specific form isn't they way to go imho. Because on alll other places, the Dot is just fine. In the import Command, on the output, the API's where I have to use the data. So just the form is the problem, not the data, the storage or the formating But anyway, thanks for your help and time. Maybe I will sort it out otherwise
toeknee
toeknee6d ago
Well then you need to just make sure the dots are put back in on save, and replaced on get
bernhard
bernhardOP6d ago
Yeah. The idea is to fix this with some kind of "Using" method. But none of them are triggered early enought. I get the error before beforeStateDehydrated
toeknee
toeknee6d ago
You will, because that's filling it we use php to get the values likely, so you would need to use the getStateUsing method because the state is what is filled I beleive.
bernhard
bernhardOP6d ago
Method Filament\Forms\Components\KeyValue::getStateUsing does not exist. None is triggered before the error: KeyValue::make('data') ->mutateDehydratedStateUsing(fn ($state) => dd($state)) ->afterStateUpdated(fn ($state) => dd($state)) ->beforeStateDehydrated(fn ($state) => dd($state)) ->dehydrateStateUsing(fn ($state) => dd($state)) even ->afterStateUpdated(fn ($state) => dd($state)) ->live(onBlur: true) throws the error, when the field loses the focus. So as you can see, it has nothing to do with the save / model but only on edit, not on create. weird bug

Did you find this page helpful?