{"id":290,"date":"2025-08-27T19:18:35","date_gmt":"2025-08-27T19:18:35","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=290"},"modified":"2025-08-27T19:18:37","modified_gmt":"2025-08-27T19:18:37","slug":"using-json-columns-in-laravel-eloquent-practical-guide","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/using-json-columns-in-laravel-eloquent-practical-guide\/","title":{"rendered":"Using JSON Columns in Laravel Eloquent (Practical Guide)"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Using JSON Columns in Laravel Eloquent (Practical Guide)<\/strong><\/h2>\n\n\n\n<p>JSON columns help you store flexible, semi-structured data without exploding your schema. In this practical guide, you\u2019ll create JSON columns, cast them in Eloquent, query nested paths efficiently, validate JSON payloads, update nested keys, and build a small UI to edit JSON-based user settings.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 &#8211; Creating JSON Columns<\/strong><\/h2>\n\n\n\n<p>Define JSON columns with the schema builder. On MySQL 5.7+\/8 and PostgreSQL, JSON is a native type; on SQLite it\u2019s stored as text but works fine for most cases.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/migrations\/2025_08_27_000000_add_profile_json_to_users_table.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Migrations<\/span>\\<span class=\"hljs-title\">Migration<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Schema<\/span>\\<span class=\"hljs-title\">Blueprint<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Schema<\/span>;\n\n<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Migration<\/span> <\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">up<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        Schema::table(<span class=\"hljs-string\">'users'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n            $table-&gt;json(<span class=\"hljs-string\">'profile'<\/span>)-&gt;nullable();   <span class=\"hljs-comment\">\/\/ e.g. {\"country\":\"TR\",\"preferences\":{\"theme\":\"dark\"}}<\/span>\n        });\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">down<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        Schema::table(<span class=\"hljs-string\">'users'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n            $table-&gt;dropColumn(<span class=\"hljs-string\">'profile'<\/span>);\n        });\n    }\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This migration adds a nullable <code>profile<\/code> column intended to hold a JSON object with free-form keys such as <code>country<\/code> and <code>preferences<\/code>. Removing it in <code>down()<\/code> keeps the migration reversible.<\/p>\n\n\n\n<p><strong>Indexing tip:<\/strong> If you frequently filter by a nested property, consider indexing via a generated column (MySQL) or a GIN index (PostgreSQL JSONB).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql\"><span class=\"hljs-comment\">-- MySQL: generated (virtual) column + index for profile-&gt;country<\/span>\n<span class=\"hljs-keyword\">ALTER<\/span> <span class=\"hljs-keyword\">TABLE<\/span> <span class=\"hljs-keyword\">users<\/span>\n  <span class=\"hljs-keyword\">ADD<\/span> <span class=\"hljs-keyword\">COLUMN<\/span> country_code <span class=\"hljs-built_in\">VARCHAR<\/span>(<span class=\"hljs-number\">2<\/span>)\n    <span class=\"hljs-keyword\">GENERATED<\/span> <span class=\"hljs-keyword\">ALWAYS<\/span> <span class=\"hljs-keyword\">AS<\/span> (JSON_UNQUOTE(JSON_EXTRACT(profile, <span class=\"hljs-string\">'$.country'<\/span>))) <span class=\"hljs-keyword\">VIRTUAL<\/span>,\n  <span class=\"hljs-keyword\">ADD<\/span> <span class=\"hljs-keyword\">INDEX<\/span> idx_users_country_code (country_code);\n\n<span class=\"hljs-comment\">-- PostgreSQL: convert to JSONB and index<\/span>\n<span class=\"hljs-comment\">-- ALTER TABLE users ADD COLUMN profile JSONB;<\/span>\n<span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">INDEX<\/span> idx_users_profile_gin <span class=\"hljs-keyword\">ON<\/span> <span class=\"hljs-keyword\">users<\/span> <span class=\"hljs-keyword\">USING<\/span> GIN ((profile));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>MySQL example projects <code>$.country<\/code> into a virtual column to index it. PostgreSQL\u2019s GIN index accelerates containment and path queries on JSONB.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 &#8211; Casting JSON to Arrays\/Objects in Eloquent<\/strong><\/h2>\n\n\n\n<p>Use attribute casting so you can read\/write JSON naturally as arrays. Eloquent will automatically JSON-encode\/decode on save\/fetch.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Models\/User.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Foundation<\/span>\\<span class=\"hljs-title\">Auth<\/span>\\<span class=\"hljs-title\">User<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">Authenticatable<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">User<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Authenticatable<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;<span class=\"hljs-string\">'name'<\/span>,<span class=\"hljs-string\">'email'<\/span>,<span class=\"hljs-string\">'password'<\/span>,<span class=\"hljs-string\">'profile'<\/span>];\n\n    <span class=\"hljs-keyword\">protected<\/span> $casts = &#91;\n        <span class=\"hljs-string\">'profile'<\/span> =&gt; <span class=\"hljs-string\">'array'<\/span>, <span class=\"hljs-comment\">\/\/ or 'collection' if you prefer Illuminate\\Support\\Collection<\/span>\n    ];\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Setting <code>'profile' =&gt; 'array'<\/code> means <code>$user-&gt;profile<\/code> is an array in PHP. Assigning an array automatically serializes back to JSON when the model is saved.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 &#8211; Querying JSON Columns<\/strong><\/h2>\n\n\n\n<p>Laravel supports vendor-agnostic JSON path syntax like <code>where('profile-&gt;country','TR')<\/code>, plus helpers such as <code>whereJsonContains<\/code> for arrays\/objects.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Path operator (country == 'TR')<\/span>\n$turkish = User::where(<span class=\"hljs-string\">'profile-&gt;country'<\/span>, <span class=\"hljs-string\">'TR'<\/span>)-&gt;get();\n\n<span class=\"hljs-comment\">\/\/ Array contains (roles includes 'admin')<\/span>\n$admins = User::whereJsonContains(<span class=\"hljs-string\">'profile-&gt;roles'<\/span>, <span class=\"hljs-string\">'admin'<\/span>)-&gt;get();\n\n<span class=\"hljs-comment\">\/\/ Object contains (preferences has {\"theme\":\"dark\"})<\/span>\n$darkTheme = User::whereJsonContains(<span class=\"hljs-string\">'profile-&gt;preferences'<\/span>, &#91;<span class=\"hljs-string\">'theme'<\/span> =&gt; <span class=\"hljs-string\">'dark'<\/span>])-&gt;get();\n\n<span class=\"hljs-comment\">\/\/ Null checks inside JSON<\/span>\n$missingPhone = User::whereNull(<span class=\"hljs-string\">'profile-&gt;phone'<\/span>)-&gt;get();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>where('profile-&gt;country','TR')<\/code> targets a nested key under <code>profile<\/code>. <code>whereJsonContains<\/code> is ideal when filtering arrays (<code>roles<\/code>) or matching object fragments (<code>preferences<\/code> contains <code>theme: dark<\/code>).<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 &#8211; Validating and Persisting JSON Input<\/strong><\/h2>\n\n\n\n<p>Validate incoming JSON as an array and validate nested keys with dot notation. Then assign the validated array to the casted attribute.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/ProfileController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Auth<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ProfileController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">update<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $data = $request-&gt;validate(&#91;\n            <span class=\"hljs-string\">'profile'<\/span> =&gt; &#91;<span class=\"hljs-string\">'required'<\/span>,<span class=\"hljs-string\">'array'<\/span>],\n            <span class=\"hljs-string\">'profile.country'<\/span> =&gt; &#91;<span class=\"hljs-string\">'required'<\/span>,<span class=\"hljs-string\">'string'<\/span>,<span class=\"hljs-string\">'size:2'<\/span>],\n            <span class=\"hljs-string\">'profile.phone'<\/span> =&gt; &#91;<span class=\"hljs-string\">'nullable'<\/span>,<span class=\"hljs-string\">'string'<\/span>,<span class=\"hljs-string\">'max:30'<\/span>],\n            <span class=\"hljs-string\">'profile.roles'<\/span> =&gt; &#91;<span class=\"hljs-string\">'nullable'<\/span>,<span class=\"hljs-string\">'array'<\/span>],\n            <span class=\"hljs-string\">'profile.roles.*'<\/span> =&gt; &#91;<span class=\"hljs-string\">'string'<\/span>],\n            <span class=\"hljs-string\">'profile.preferences'<\/span> =&gt; &#91;<span class=\"hljs-string\">'nullable'<\/span>,<span class=\"hljs-string\">'array'<\/span>],\n            <span class=\"hljs-string\">'profile.preferences.theme'<\/span> =&gt; &#91;<span class=\"hljs-string\">'nullable'<\/span>,<span class=\"hljs-string\">'in:light,dark'<\/span>],\n            <span class=\"hljs-string\">'profile.preferences.notifications'<\/span> =&gt; &#91;<span class=\"hljs-string\">'nullable'<\/span>,<span class=\"hljs-string\">'array'<\/span>],\n            <span class=\"hljs-string\">'profile.preferences.notifications.email'<\/span> =&gt; &#91;<span class=\"hljs-string\">'boolean'<\/span>],\n            <span class=\"hljs-string\">'profile.preferences.notifications.sms'<\/span> =&gt; &#91;<span class=\"hljs-string\">'boolean'<\/span>],\n        ]);\n\n        $user = Auth::user();\n        $user-&gt;profile = $data&#91;<span class=\"hljs-string\">'profile'<\/span>]; <span class=\"hljs-comment\">\/\/ cast handles JSON<\/span>\n        $user-&gt;save();\n\n        <span class=\"hljs-keyword\">return<\/span> back()-&gt;with(<span class=\"hljs-string\">'status'<\/span>,<span class=\"hljs-string\">'Profile saved.'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The validator ensures <code>profile<\/code> is an array and that expected nested keys conform to your rules. Assigning the array to <code>$user-&gt;profile<\/code> triggers casting and JSON persistence.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Updating Individual Nested Keys<\/strong><\/h2>\n\n\n\n<p>You can update a single nested path using the JSON path key syntax. This avoids fetching and rewriting the whole object for simple toggles.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Toggle email notifications ON for a user<\/span>\nUser::whereKey($id)-&gt;update(&#91;\n    <span class=\"hljs-string\">\"profile-&gt;preferences-&gt;notifications-&gt;email\"<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n]);\n\n<span class=\"hljs-comment\">\/\/ Append item into a JSON array (DB-specific alternative)<\/span>\nDB::table(<span class=\"hljs-string\">'users'<\/span>)\n  -&gt;where(<span class=\"hljs-string\">'id'<\/span>, $id)\n  -&gt;update(&#91;\n      <span class=\"hljs-string\">'profile'<\/span> =&gt; DB::raw(<span class=\"hljs-string\">\"JSON_SET(profile, '$.roles', JSON_ARRAY_APPEND(JSON_EXTRACT(profile, '$.roles'), '$', 'editor'))\"<\/span>)\n  ]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The first example uses Laravel\u2019s dotted JSON path to set a boolean inside nested objects. The second shows a DB-level array append using raw SQL (syntax varies by engine).<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 &#8211; UI: Editing JSON Settings in a Form<\/strong><\/h2>\n\n\n\n<p>Here\u2019s a small UI: a controller method to show the form and a Blade template that binds to the JSON fields. Submissions hit <code>ProfileController@update<\/code> from above.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/ProfileController.php (add show() action)<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">show<\/span><span class=\"hljs-params\">()<\/span>\n<\/span>{\n    $user = auth()-&gt;user();\n    <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'profile.edit'<\/span>, &#91;<span class=\"hljs-string\">'user'<\/span> =&gt; $user]);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>show()<\/code> action passes the current user to the view so you can pre-populate the form from the JSON column.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/profile\/edit.blade.php --&gt;\n@extends(<span class=\"hljs-string\">'layouts.app'<\/span>)\n\n@section(<span class=\"hljs-string\">'content'<\/span>)\n&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">container<\/span>\"&gt;\n  &lt;<span class=\"hljs-title\">h1<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">mb<\/span>-4\"&gt;<span class=\"hljs-title\">Profile<\/span> <span class=\"hljs-title\">Settings<\/span>&lt;\/<span class=\"hljs-title\">h1<\/span>&gt;\n\n  @<span class=\"hljs-title\">if<\/span>(<span class=\"hljs-title\">session<\/span>('<span class=\"hljs-title\">status<\/span>'))\n    &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">alert<\/span> <span class=\"hljs-title\">alert<\/span>-<span class=\"hljs-title\">success<\/span>\"&gt;<\/span>{{ session(<span class=\"hljs-string\">'status'<\/span>) }}&lt;\/div&gt;\n  @<span class=\"hljs-keyword\">endif<\/span>\n\n  &lt;form method=<span class=\"hljs-string\">\"POST\"<\/span> action=<span class=\"hljs-string\">\"{{ route('profile.update') }}\"<\/span>&gt;\n    @csrf\n    @method(<span class=\"hljs-string\">'PUT'<\/span>)\n\n    &lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">row<\/span> <span class=\"hljs-title\">g<\/span>-3\"&gt;\n      &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">col<\/span>-<span class=\"hljs-title\">md<\/span>-3\"&gt;\n        &lt;<span class=\"hljs-title\">label<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">form<\/span>-<span class=\"hljs-title\">label<\/span>\"&gt;<span class=\"hljs-title\">Country<\/span> <span class=\"hljs-title\">Code<\/span> (<span class=\"hljs-title\">ISO2<\/span>)&lt;\/<span class=\"hljs-title\">label<\/span>&gt;\n        &lt;<span class=\"hljs-title\">input<\/span> <span class=\"hljs-title\">name<\/span>=\"<span class=\"hljs-title\">profile<\/span>&#91;<span class=\"hljs-title\">country<\/span>]\" <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">form<\/span>-<span class=\"hljs-title\">control<\/span>\"\n               <span class=\"hljs-title\">value<\/span>=\"<\/span>{{ data_get($user-&gt;profile, <span class=\"hljs-string\">'country'<\/span>) }}<span class=\"hljs-string\">\" maxlength=\"<\/span><span class=\"hljs-number\">2<\/span><span class=\"hljs-string\">\"\/&gt;\n      &lt;\/div&gt;\n\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-6<\/span><span class=\"hljs-string\">\"&gt;\n        &lt;label class=\"<\/span>form-label<span class=\"hljs-string\">\"&gt;Phone&lt;\/label&gt;\n        &lt;input name=\"<\/span>profile&#91;phone]<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\"\n               value=\"<\/span>{{ data_get($user-&gt;profile, <span class=\"hljs-string\">'phone'<\/span>) }}<span class=\"hljs-string\">\"\/&gt;\n      &lt;\/div&gt;\n\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-12<\/span><span class=\"hljs-string\">\"&gt;\n        &lt;label class=\"<\/span>form-label<span class=\"hljs-string\">\"&gt;Roles (comma separated)&lt;\/label&gt;\n        &lt;input name=\"<\/span>roles_csv<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\"\n               value=\"<\/span>{{ implode(<span class=\"hljs-string\">','<\/span>, (<span class=\"hljs-keyword\">array<\/span>) data_get($user-&gt;profile, <span class=\"hljs-string\">'roles'<\/span>, &#91;])) }}<span class=\"hljs-string\">\"\/&gt;\n      &lt;\/div&gt;\n\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-4<\/span><span class=\"hljs-string\">\"&gt;\n        &lt;label class=\"<\/span>form-label<span class=\"hljs-string\">\"&gt;Theme&lt;\/label&gt;\n        &lt;select name=\"<\/span>profile&#91;preferences]&#91;theme]<span class=\"hljs-string\">\" class=\"<\/span>form-select<span class=\"hljs-string\">\"&gt;\n          @php $theme = data_get($user-&gt;profile,'preferences.theme'); @endphp\n          &lt;option value=\"<\/span><span class=\"hljs-string\">\" {{ $theme ? '' : 'selected' }}&gt;System&lt;\/option&gt;\n          &lt;option value=\"<\/span>light<span class=\"hljs-string\">\" {{ $theme==='light' ? 'selected' : '' }}&gt;Light&lt;\/option&gt;\n          &lt;option value=\"<\/span>dark<span class=\"hljs-string\">\"  {{ $theme==='dark'  ? 'selected' : '' }}&gt;Dark&lt;\/option&gt;\n        &lt;\/select&gt;\n      &lt;\/div&gt;\n\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-4<\/span> form-check mt<span class=\"hljs-number\">-4<\/span><span class=\"hljs-string\">\"&gt;\n        @php $emailOn = (bool) data_get($user-&gt;profile,'preferences.notifications.email'); @endphp\n        &lt;input id=\"<\/span>notif_email<span class=\"hljs-string\">\" type=\"<\/span>checkbox<span class=\"hljs-string\">\" class=\"<\/span>form-check-input<span class=\"hljs-string\">\"\n               name=\"<\/span>profile&#91;preferences]&#91;notifications]&#91;email]<span class=\"hljs-string\">\" value=\"<\/span><span class=\"hljs-number\">1<\/span><span class=\"hljs-string\">\"\n               {{ $emailOn ? 'checked' : '' }} \/&gt;\n        &lt;label for=\"<\/span>notif_email<span class=\"hljs-string\">\" class=\"<\/span>form-check-label<span class=\"hljs-string\">\"&gt;Email Notifications&lt;\/label&gt;\n      &lt;\/div&gt;\n\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-4<\/span> form-check mt<span class=\"hljs-number\">-4<\/span><span class=\"hljs-string\">\"&gt;\n        @php $smsOn = (bool) data_get($user-&gt;profile,'preferences.notifications.sms'); @endphp\n        &lt;input id=\"<\/span>notif_sms<span class=\"hljs-string\">\" type=\"<\/span>checkbox<span class=\"hljs-string\">\" class=\"<\/span>form-check-input<span class=\"hljs-string\">\"\n               name=\"<\/span>profile&#91;preferences]&#91;notifications]&#91;sms]<span class=\"hljs-string\">\" value=\"<\/span><span class=\"hljs-number\">1<\/span><span class=\"hljs-string\">\"\n               {{ $smsOn ? 'checked' : '' }} \/&gt;\n        &lt;label for=\"<\/span>notif_sms<span class=\"hljs-string\">\" class=\"<\/span>form-check-label<span class=\"hljs-string\">\"&gt;SMS Notifications&lt;\/label&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n\n    &lt;button class=\"<\/span>btn btn-theme mt<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;Save Settings&lt;\/button&gt;\n  &lt;\/form&gt;\n&lt;\/div&gt;\n@endsection<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This form posts nested arrays using PHP\u2019s bracket notation (e.g., <code>profile[preferences][theme]<\/code>). In the controller, the validator receives a structured array, which you assign to <code>$user-&gt;profile<\/code>. The optional <code>roles_csv<\/code> field is a UX shortcut\u2014if you want to support it, convert to an array before validation or within a form request.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7 &#8211; Performance &amp; Gotchas<\/strong><\/h2>\n\n\n\n<p>JSON columns are flexible but keep these in mind: index frequently filtered paths, avoid storing huge blobs that you rewrite often, and prefer stable columns for high-cardinality filters. When schema hardens, consider migrating hot properties into first-class columns.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>You created JSON columns, cast them in Eloquent, queried nested paths, validated and saved structured input, and built a UI for editing JSON settings. With careful indexing and path updates, JSON gives you agility without sacrificing query power.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/how-to-use-eloquent-api-resources-for-clean-apis\">How to Use Eloquent API Resources for Clean APIs<\/a><\/li>\n<li><a href=\"\/blog\/soft-deletes-in-laravel-restore-force-delete-and-prune-data\">Soft Deletes: Restore, Force Delete, and Prune Data<\/a><\/li>\n<li><a href=\"\/blog\/filtering-and-searching-with-laravel-eloquent-query-builder\">Filtering and Searching with Eloquent Query Builder<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Using JSON Columns in Laravel Eloquent (Practical Guide) JSON columns help you store flexible, semi-structured data without exploding your schema. In this practical guide, you\u2019ll create JSON columns, cast them in Eloquent, query nested paths efficiently, validate JSON payloads, update nested keys, and build a small UI to edit JSON-based user settings. 1 &#8211; Creating [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":294,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[38,36,37],"class_list":["post-290","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-database","tag-eloquent","tag-relationships"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/290","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=290"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/290\/revisions"}],"predecessor-version":[{"id":293,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/290\/revisions\/293"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/294"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=290"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=290"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=290"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}