The Death of Property Arrays: Mastering PHP Attributes in Laravel 13
In previous versions of Laravel, our Eloquent models were often cluttered with massive arrays. We had $fillable, $hidden, $casts, and $appends sitting at the top of every class. While functional, it felt like "config-driven development" rather than true Object-Oriented programming.
As applications scaled, these arrays became harder to maintain, easy to forget, and disconnected from the actual properties they controlled.
With Laravel 13, this functionality shifts completely.
By embracing PHP 8 Attributes, Laravel allows you to define model behavior directly on properties and methods — bringing clarity, structure, and true OOP principles into your models.
In this article we will see how to use PHP Attributes in Laravel 13.
🚀 What Are PHP Attributes?
PHP Attributes (introduced in PHP 8) provide a way to attach metadata directly to code elements like classes, properties, and methods.
Instead of external configuration, you can write:
#[Cast('datetime')]
public $created_at;
🧠 What We'll Cover in This Blog
- The difference between the old array-based approach and the new attribute-based approach.
- Why attributes improve IDE support and static analysis
- Real-world use cases like security, casting, and API responses
- Performance considerations
- Best practices for migrating existing code
- A full working example
📌 The "Old Way" vs. The "Attribute Way"
❌ Old Way (Array-Based Configuration)
class User extends Model
{
protected $fillable = ['name', 'email'];
protected $hidden = ['password'];
protected $casts = [
'email_verified_at' => 'datetime',
];
}
✅ Attribute Way (Modern Laravel 13)
Instead of maintaining centralized arrays, behavior is now co-located with the data it affects.
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Attributes\Cast;
class User extends Model
{
#[Fillable]
public string $name;
#[Fillable]
public string $email;
#[Hidden]
public string $password;
#[Cast('datetime')]
public $email_verified_at;
}
📌 Why this is better for IDEs and Static Analysis
This change is not just about cleaner syntax — it has real tooling benefits.
🔍 Stronger Type Awareness
Lets say you have a strict typed property as below.
public string $email;
IDEs like PhpStorm can:
- Detect invalid assignments
- Provide better autocomplete
- Catch bugs earlier
Improved Static Analysis
Tools like PHPStan and Psalm can now:
- Understand model structure more accurately
- Detect unused or incorrect attributes
- Validate casts and property usage
No More "Magic Arrays"
Previously:
- A typo in $fillable → silently ignored
- Wrong cast → runtime bug
Now:
- Everything is explicit and can be analyzed
📌 Real-life Use Cases (Security, Casting, and API Visibility)
🔐 Security (Mass Assignment)
#[Fillable]
public string $email;
Only explicitly marked properties can be mass assigned — reducing unwanted vulnerabilities.
🙈 API Visibility (Hiding Sensitive Data)
#[Hidden]
public string $password;
No risk of accidentally exposing sensitive fields in API responses.
🔄 Data Casting
#[Cast('datetime')]
public $email_verified_at;
#[Cast('array')]
public $preferences;
➕ Computed Fields (Appended Attributes)
use Illuminate\Database\Eloquent\Attributes\Appends;
#[Appends]
public function fullName(): string
{
return "{$this->first_name} {$this->last_name}";
}
📌 Performance Implications
Are attributes slower than arrays?
⚡ The Reality
- PHP attributes are parsed once and cached internally
- Laravel optimizes attribute resolution
- The difference is negligible in real-world apps
📊 Practical Insight
- Slight overhead during reflection (first load)
- No noticeable impact in request lifecycle
- Clean code benefits outweigh micro-performance concerns
📌 Best Practices for Refactoring
If you're upgrading an existing project:
- Don’t rewrite everything at once. Start with new models or small modules.
- Avoid Mixing Approaches Either choose Arrays format or attributes
- Leverage Typed Properties ( Define attribute type like string or int etc )
- Keep Methods Clean ( Use attributes for metadata not for business logic )
- Follow Naming Consistency - Keep property names aligned with database columns to avoid unnecessary complexity.
🧠 Why This Matters
- This is more than a syntax upgrade — it's a design evolution.
- Behavior lives with the property it affects.
- No more scanning large arrays.
- Everything is visible and predictable.
- Models become self-documenting.
Full Example
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Attributes\Cast;
use Illuminate\Database\Eloquent\Attributes\Appends;
class User extends Model
{
#[Fillable]
public string $first_name;
#[Fillable]
public string $last_name;
#[Fillable]
public string $email;
#[Hidden]
public string $password;
#[Cast('datetime')]
public $email_verified_at;
#[Appends]
public function fullName(): string
{
return "{$this->first_name} {$this->last_name}";
}
}
🧭 When Should You Use This?
Use Attributes When:
- Starting a new Laravel 13 project
- You want cleaner, maintainable models
- Your team prefers OOP principles
Be Careful When:
- Working with legacy applications
- Team is unfamiliar with PHP attributes
- Tooling is not fully aligned yet
🏁 Final Thoughts
Laravel 13’s adoption of PHP Attributes marks a major shift in how we design models.
Centralized configuration arrays to Decentralized, property-driven design
- Better readability
- Stronger tooling support
- More maintainable code
For developers who value clean architecture and modern PHP practices, this isn’t just a feature — it’s a new standard.
-compressed.jpg)