Laravel Casts: Best Practices for Eloquent Model Attributes

Prioritize casting date fields, boolean flags, and sensitive data using primitive types, while reserving custom class casts for complex domain objects like money or coordinates.

Laravel casts provide automatic type conversion between your database and Eloquent models, ensuring data integrity and reducing boilerplate. When working with the laravel/framework repository, understanding the internal casting mechanism in the HasAttributes trait helps you implement efficient, secure attribute handling. This guide covers which attribute types to prioritize and how to leverage primitive, enum, and custom casts effectively.

How Laravel Casts Work Internally

The casting system resides in Illuminate\Database\Eloquent\Concerns\HasAttributes. During model initialization, the initializeHasAttributes method merges casts from the $casts property and any casts() method return value (lines 84‑87). All cast definitions are normalized to strings via ensureCastsAreStringValues.

When retrieving attributes, castAttribute() (lines 332‑390) checks against the $primitiveCastTypes array (lines 105‑133) to determine whether to apply built-in conversion logic or delegate to custom cast classes.

Primitive vs. Custom Cast Types

Laravel distinguishes between lightweight primitive casts and heavyweight custom implementations:

Type Example Use Case
Primitive 'is_active' => 'boolean' Built-in types (bool, int, float, decimal, string, date, datetime, timestamp, array, json, collection, encrypted, hashed, immutable variants). No class loading overhead.
Enum 'status' => OrderStatus::class PHP 8.1+ BackedEnum for type-safe domain values.
Custom Class 'total' => Money::class Complex objects requiring transformation logic; must implement CastsAttributes.
Attribute Object price(): Attribute Modern syntax combining get/set with caching support.

Prioritizing Attribute Types for Laravel Casts

Based on the internal implementation and performance characteristics of HasAttributes, prioritize casting in this order:

1. Date and DateTime Fields

Always cast temporal columns. The isDateCastable method (lines 1715‑1717) identifies these fields for automatic Carbon conversion.

protected $casts = [
    'published_at' => 'datetime',
    'archived_at' => 'immutable_datetime',
    'birth_date' => 'date:Y-m-d',
];

2. Boolean Flags

Prevent "truthy" string bugs by casting tinyint/boolean columns:

protected $casts = [
    'is_active' => 'boolean',
    'is_admin' => 'bool',
];

3. Numeric Types

Use specific numeric casts for mathematical integrity:

protected $casts = [
    'login_count' => 'integer',
    'rating' => 'float',
    'price' => 'decimal:2',
];

4. JSON and Array Structures

For columns storing structured data, use array, json, or collection. The castAttributeAsJson method (lines 616‑631) handles encoding:

protected $casts = [
    'preferences' => 'array',
    'metadata' => 'json',
    'tags' => 'collection',
];

5. Encrypted and Hashed Values

Secure sensitive data automatically via castAttributeAsEncryptedString (lines 384‑390):

protected $casts = [
    'api_key' => 'encrypted',
    'secrets' => 'encrypted:array',
    'password' => 'hashed',
];

6. PHP Enums

Type-safe domain constraints via getEnumCastableAttributeValue (lines 636‑642):

use App\Enums\OrderStatus;

protected $casts = [
    'status' => OrderStatus::class,
];

7. Custom Value Objects

For complex domain logic, implement CastsAttributes and reference the class:

use App\Casts\Money;

protected $casts = [
    'total' => Money::class,
];

The getClassCastableAttributeValue (lines 904‑926) and setClassCastableAttribute (lines 1249‑1269) methods invoke your custom logic.

Modern Attribute Syntax

Laravel also provides the Attribute class for inline get/set definitions with caching support (stored in $attributeCastCache, lines 88‑92):

use Illuminate\Database\Eloquent\Casts\Attribute;

protected function price(): Attribute
{
    return Attribute::make(
        get: fn ($value) => new Money($value / 100, 'USD'),
        set: fn (Money $money) => $money->getAmount() * 100,
    );
}

Performance and Security Considerations

Consideration Implementation Detail
Avoid over-casting large JSON Casting forces full decode via fromJson on every model load. Use json only when necessary.
Prefer primitives Primitive casts in castAttribute (lines 332‑390) avoid class instantiation overhead.
Use immutable dates immutable_datetime returns CarbonImmutable, preventing accidental mutation.
Validate cast strings ensureCastsAreStringValues normalizes definitions; always use strings or class names.
Custom encryption Override Json::encodeUsing/decodeUsing or implement CastsAttributes for custom algorithms.

Summary

  • Start with primitive casts for dates, booleans, integers, and JSON to leverage lightweight conversion in HasAttributes::castAttribute.
  • Secure sensitive data using encrypted and hashed casts, which automatically invoke encryption logic before persistence.
  • Enforce domain constraints with PHP 8.1+ BackedEnum casts via getEnumCastableAttributeValue.
  • Reserve custom class casts for complex value objects requiring transformation logic, utilizing CastsAttributes implementations.
  • Consider Attribute objects when you need combined get/set logic with built-in caching support.

Frequently Asked Questions

What is the difference between array and json casts in Laravel?

Both casts store data as JSON in the database, but array automatically decodes to a PHP array on retrieval, while json can return a stdClass object depending on the JSON decoder settings. Use array when you need associative array access and json when working with objects or raw JSON strings.

How do I cast encrypted attributes in Laravel?

Use the encrypted or encrypted:array cast types in your $casts property. Laravel automatically encrypts values before saving and decrypts them when retrieving via the castAttributeAsEncryptedString method in HasAttributes. Ensure your application has a properly configured encryption key in the environment.

Should I use custom cast classes or Attribute objects?

Use Attribute objects (introduced in Laravel 9) for simple get/set transformations that benefit from caching, as they store values in $attributeCastCache. Use custom cast classes implementing CastsAttributes when you need reusable transformation logic across multiple models or when handling complex value objects like Money or coordinates that require specific serialization formats.

What are immutable date casts and when should I use them?

immutable_date and immutable_datetime casts return CarbonImmutable instances instead of mutable Carbon objects. Use these when you want to prevent accidental modification of date values after retrieval, ensuring that any changes to the date object don't affect other parts of your application holding references to the same model instance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →