Transform JSON to Typed Collections with Laravel's AsCollection::of()

Transform JSON to Typed Collections with Laravel's AsCollection::of()

When working with complex data in Laravel applications, you often need to store structured information in JSON columns. But working with raw arrays can quickly lead to messy, hard-to-maintain code. That's where Laravel's powerful AsCollection::of() method comes in - transforming your JSON data into strongly-typed collections of value objects with minimal effort.

What is AsCollection::of()?

The AsCollection::of() method is part of Laravel's robust casting system that allows you to automatically map an Eloquent attribute into a collection of specific class instances. Instead of dealing with raw arrays or generic collections, you can work with properly typed objects that encapsulate both data and behavior.

Here's how you define it in your Eloquent model:

use App\ValueObjects\Option;
use Illuminate\Database\Eloquent\Casts\AsCollection;
 
protected function casts(): array
{
    return [
        'options' => AsCollection::of(Option::class)
    ];
}

Behind the scenes, this is equivalent to calling:

Collection::make($this->attributes['options'])->mapInto(Option::class);

The magic happens when Laravel hydrates your model from the database. Your JSON column is automatically transformed into a collection of your value objects, giving you type safety and encapsulation.

Real-World Example

Let's say you're building an e-commerce platform where products have multiple customization options. Each option might have a name, price adjustment, and availability flag.

First, create a value object to represent each option:

namespace App\ValueObjects;

use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;

class ProductOption implements Arrayable, JsonSerializable
{
    public function __construct(
        public readonly string $name,
        public readonly float $priceAdjustment,
        public readonly bool $isAvailable = true
    ) {}
    
    public function toArray()
    {
        return [
            'name' => $this->name,
            'price_adjustment' => $this->priceAdjustment,
            'is_available' => $this->isAvailable,
        ];
    }
    
    public function jsonSerialize(): mixed
    {
        return $this->toArray();
    }
    
    public function isValidForPurchase(): bool
    {
        return $this->isAvailable && $this->priceAdjustment >= 0;
    }
}

Next, in your Product model, use the AsCollection::of() cast:

namespace App\Models;

use App\ValueObjects\ProductOption;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected function casts(): array
    {
        return [
            'options' => AsCollection::of(ProductOption::class)
        ];
    }
    
    public function getAvailableOptionsAttribute()
    {
        return $this->options->filter(fn ($option) => $option->isAvailable);
    }
}

Now you can work with your product options as first-class citizens in your codebase:

$product = Product::find(1);

// Get all options as ProductOption objects
$allOptions = $product->options;

// Filter using methods on your value object
$validOptions = $product->options->filter(fn ($option) => $option->isValidForPurchase());

// Add a new option (automatically serialized back to JSON)
$product->options->push(new ProductOption('Extra Large', 10.00, true));
$product->save();

It's perfect for scenarios like product configurations, user preferences, complex form responses, feature flags with metadata, or any situation where you store semi-structured data that follows a consistent pattern.

Ready to bring order to your JSON columns and level up your Laravel models? Give AsCollection::of() a try on your next project!

Stay Updated with More Laravel Content

Enjoyed this article? There's plenty more where that came from! Subscribe to our channels to stay updated with the latest Laravel tips, tricks, and best practices:

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe