Skip to content

Laravel 12 Store JSON Format Data in Database Tutorial

Learn to store JSON data in Laravel 12 databases with complete examples. Master JSON columns, Eloquent casting, queries, and API integration.

Dasun Kalupahana 2 min read 1K views 0 comments

Introduction

In modern web development, storing JSON data in databases has become increasingly common, especially when dealing with flexible, schema-less data structures. Laravel 12 provides excellent support for JSON data types, making it easier than ever to store, retrieve, and manipulate JSON data in your applications.

This comprehensive tutorial will guide you through everything you need to know about storing JSON format data in Laravel 12, including database setup, model configuration, CRUD operations, and best practices.


Why Store JSON Data in Databases?

JSON (JavaScript Object Notation) storage offers several advantages:

  • Flexibility: Store varying data structures without rigid schema constraints
  • Performance: Reduce the need for complex JOIN operations
  • Scalability: Easily accommodate changing data requirements
  • Modern Compatibility: Native support in modern databases like MySQL 5.7+, PostgreSQL, and SQLite

Setting Up the Database Migration

First, let’s create a migration that includes JSON columns. We’ll use a practical example of storing user preferences and product attributes.

<?php
// database/migrations/2025_10_10_000000_create_users_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->json('preferences')->nullable(); // JSON column for user preferences
            $table->json('metadata')->nullable();    // Additional JSON data
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('users');
    }
};

For a more complex example, let’s create a products table:

<?php
// database/migrations/2025_10_10_000001_create_products_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->decimal('price', 10, 2);
            $table->json('attributes'); // Store product attributes as JSON
            $table->json('tags')->nullable(); // Store tags as JSON array
            $table->json('specifications')->nullable(); // Technical specifications
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

Run the migrations:

php artisan migrate

Configuring Eloquent Models

Now, let’s configure our Eloquent models to properly handle JSON data:

User Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'preferences',
        'metadata'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'preferences' => 'array',
            'metadata' => 'array',
        ];
    }

    /**
     * Accessor for formatted preferences
     */
    protected function preferences(): Attribute
    {
        return Attribute::make(
            get: fn($value) => json_decode($value, true),
            set: fn($value) => json_encode($value)
        );
    }

    /**
     * Scope for users with specific preference
     */
    public function scopeWithPreference($query, $key, $value)
    {
        return $query->whereJsonContains("preferences->{$key}", $value);
    }
}

Product Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = [
        'name',
        'price',
        'attributes',
        'tags',
        'specifications'
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'attributes' => 'array',
            'tags' => 'array',
            'specifications' => 'array',
            'price' => 'decimal:2'
        ];
    }

    /**
     * Scope for products with specific attribute
     */
    public function scopeWithAttribute($query, $attribute, $value)
    {
        return $query->whereJsonContains("attributes->{$attribute}", $value);
    }

    /**
     * Scope for products with specific tag
     */
    public function scopeWithTag($query, $tag)
    {
        return $query->whereJsonContains('tags', $tag);
    }
}

CRUD Operations with JSON Data

Creating Records with JSON Data

Here’s how to create records with JSON data:

<?php
// In a controller or service class

use App\Models\User;
use App\Models\Product;

// Creating a user with preferences
$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'preferences' => [
        'theme' => 'dark',
        'language' => 'en',
        'notifications' => [
            'email' => true,
            'push' => false,
            'sms' => true
        ],
        'dashboard' => [
            'layout' => 'grid',
            'items_per_page' => 20
        ]
    ],
    'metadata' => [
        'registration_source' => 'web',
        'utm_campaign' => 'spring_sale',
        'referrer' => 'google.com'
    ]
]);

// Creating a product with attributes
$product = Product::create([
    'name' => 'Wireless Headphones',
    'price' => 199.99,
    'attributes' => [
        'color' => 'black',
        'brand' => 'TechBrand',
        'model' => 'TB-WH-001',
        'features' => [
            'noise_cancellation' => true,
            'wireless' => true,
            'battery_life' => '30 hours'
        ]
    ],
    'tags' => ['electronics', 'audio', 'wireless', 'premium'],
    'specifications' => [
        'weight' => '250g',
        'dimensions' => '18x15x7 cm',
        'connectivity' => ['Bluetooth 5.0', 'USB-C'],
        'compatibility' => ['iOS', 'Android', 'Windows']
    ]
]);

Reading and Querying JSON Data

Laravel provides powerful methods to query JSON data:

<?php

// Basic JSON queries
$users = User::where('preferences->theme', 'dark')->get();

// Query nested JSON data
$users = User::where('preferences->notifications->email', true)->get();

// Using JSON contains
$products = Product::whereJsonContains('tags', 'electronics')->get();

// Query array within JSON
$products = Product::whereJsonContains('attributes->features->wireless', true)->get();

// Multiple JSON conditions
$products = Product::where('attributes->brand', 'TechBrand')
                  ->whereJsonContains('tags', 'premium')
                  ->get();

// Using scopes we defined earlier
$darkThemeUsers = User::withPreference('theme', 'dark')->get();
$wirelessProducts = Product::withTag('wireless')->get();

// JSON length queries
$usersWithMultipleTags = User::whereJsonLength('preferences->dashboard', '>', 1)->get();

Updating JSON Data

Laravel provides several ways to update JSON data:

<?php

// Update entire JSON column
$user = User::find(1);
$user->preferences = [
    'theme' => 'light',
    'language' => 'es',
    'notifications' => [
        'email' => false,
        'push' => true,
        'sms' => false
    ]
];
$user->save();

// Update specific JSON key using update method
User::where('id', 1)->update([
    'preferences->theme' => 'light',
    'preferences->language' => 'es'
]);

// Update nested JSON values
Product::where('id', 1)->update([
    'attributes->features->battery_life' => '35 hours',
    'specifications->weight' => '240g'
]);

// Append to JSON array
$product = Product::find(1);
$currentTags = $product->tags ?? [];
$currentTags[] = 'bestseller';
$product->tags = $currentTags;
$product->save();

Deleting JSON Data

<?php

// Remove specific JSON keys
User::where('id', 1)->update([
    'preferences->old_setting' => null
]);

// Clear entire JSON column
User::where('id', 1)->update([
    'preferences' => null
]);

Best Practices and Performance Tips

1. Data Structure Design

  • Keep JSON structures as flat as possible for better query performance
  • Use consistent naming conventions across JSON fields
  • Avoid deeply nested structures when possible

2. Indexing Strategy

  • Index frequently queried JSON paths
  • Use partial indexes for large datasets
  • Consider database-specific JSON index types (GIN for PostgreSQL, functional indexes for MySQL)

3. Validation and Security

  • Always validate JSON data before storing
  • Sanitize user input to prevent JSON injection
  • Use type casting to ensure data consistency

4. Performance Optimization

  • Use database-level JSON functions when possible
  • Avoid loading large JSON objects unnecessarily
  • Consider caching frequently accessed JSON data

Conclusion

Laravel 12 provides robust support for JSON data storage and manipulation, making it easier to build flexible, modern web applications. By following the patterns and best practices outlined in this tutorial, you can effectively leverage JSON columns in your Laravel applications while maintaining good performance and data integrity.

The key takeaways from this tutorial include:

  • Proper migration setup for JSON columns
  • Correct model configuration with casts and accessors
  • Efficient querying techniques for JSON data
  • Performance optimization through indexing
  • Security considerations and validation strategies

With these tools and techniques, you’re well-equipped to handle complex data structures in your Laravel 12 applications using JSON storage.

If you found this helpful, feel free to share or drop a comment. Happy coding with Laravel! 🧱✨

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Free download: Laravel interview questions (PDF)

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.