Kevin Foster
Kevin Foster

Reputation: 179

How do I update an eloquent json column from validation data without over writing what's stored in the column?

I'm hoping for a straight-forward solution to do this, but so far I've been coming up empty...

I have a front end vue app/form that sends data back to my laravel backend - I have a controller that validates and saves the request (not looking for feedback on this architecture at the moment unless it actually solves the problem - that's a task for another day...)

I've added a json column called "custom_redeem_fields" For context, it's to support more flexibility and accepts key/val pairs to use in another field called "custom_redeem_instructions" that has text with delimiters for each of the keys from "custom_redeem_fields", although, I'd prefer to keep from defining these keys statically because the whole point is to be able to add new keys at will. So custom_redeem_instructions will read something like "please visit {•URL•} and enter code {•CODE•}..." and those values will come from the custom_redeem_fields json field.

In the model, I have "custom_redeem_fields" in the fillable array, as well as set as castable to json.

protected $fillable = ['custom_redeem_fields'];
protected $casts = ['custom_redeem_fields' => 'json']; 

In the controller, I have ~20 additional columns (not really relevant here, so I've only included two) so I'm trying not to call them out individually beyond their validation rules. The request typically sends one field at a time, so the user can update and save each field as they go. This was working appropriately for all the other fields I had before I added the "custom_redeem_fields.xxxx" to the mix.

$validatedData = $request->validate([
      'title' => 'sometimes|required|max:255',
      'text' => 'sometimes|required_unless:redeem_type,9|max:255',
      'custom_redeem_fields.email' => 'sometimes|email',
      'custom_redeem_fields.phone' => ['sometimes', new ValidPhone],
      'custom_redeem_fields.code' => 'sometimes',
      'custom_redeem_fields.url' => 'sometimes|url'
]);

$ticket = Ticket::find($id)
$ticket->update($validatedData);

Now, with the "custom_redeem_fields.xxxxx" this falls apart - the entire json object stored in "custom_redeem_fields" is overwritten with the most recent update, rather than just updating the key included in the validatedData array. So if I save:

[
  "title" => "Monty Pythons Flying Circus"
  "text" => "Monty Pythons Flying Circus is a British surreal sketch comedy series created by and starring the comedy group Monty Python, consisting of Graham Chapman, ..."
  "custom_redeem_fields" => [
    "email" => "[email protected]",
    "phone" => "503.555.5555",
    "code" => "1xoicvjq",
    "url" => "https://example.com/"
  ]
]

and then I send: "custom_redeem_fields" => ["email" => "[email protected]"]

the custom redeem fields returns: "custom_redeem_fields" => ["email" => "[email protected]"]

rather than: "custom_redeem_fields" => ["email" => "[email protected]", "phone" => "503.555.5555", "code" => "1xoicvjq", "url" => "https://example.com/"]

It seems that validation rules need json keys to be notated with dot syntax (custom_redeem_fields.url), and eloquent needs arrow syntax (custom_redeem_fields->url), but I'm not sure what's the most straightforward way to transition between the two, which seems very not-laravel, and the documentation is certainly lacking in this department...

Any help would be appreciated.

Thanks!

Upvotes: 2

Views: 454

Answers (1)

mrhn
mrhn

Reputation: 18976

Wouldn't array_merge() solve your problem, it would overwrite values you provide with the second parameter. If you give it the already existing ones as the first, it would combine the two as you want.

$customRedeemInput = [...];

$model->custom_redeem_fields = array_merge($model->custom_redeem_fields, $customRedeemInput);
$model->save();

Upvotes: 1

Related Questions