Installation:
composer require cviebrock/eloquent-typecast
Add the trait to your Eloquent model:
use Cviebrock\EloquentTypecast\Typecastable;
class User extends Model
{
use Typecastable;
}
First Use Case: Define typecasts in your model:
protected $typecasts = [
'is_active' => 'boolean',
'age' => 'integer',
'price' => 'float',
'created_at' => 'datetime',
];
Now, when retrieving a User model, the attributes will automatically be cast to their specified types.
Basic Typecasting:
protected $typecasts = [
'active' => 'boolean',
'user_id' => 'integer',
'weight' => 'float',
];
Custom Casting Logic:
Override the getTypecastedAttribute method for custom logic:
public function getTypecastedAttribute($key)
{
if ($key === 'custom_field') {
return strtoupper($this->attributes[$key]);
}
return parent::getTypecastedAttribute($key);
}
Dynamic Typecasting: Use a closure for dynamic casting:
protected $typecasts = [
'discount' => function ($value) {
return $value / 100; // Convert percentage to decimal
},
];
API Development: Ensure API responses return correct types without manual casting:
return User::find(1); // Returns JSON with proper types (e.g., `is_active: true` instead of `"1"`)
Database Migrations: Define typecasts alongside migrations to maintain consistency:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->boolean('is_admin')->default(false);
$table->integer('score');
$table->timestamps();
});
protected $typecasts = [
'is_admin' => 'boolean',
'score' => 'integer',
];
Query Builder Integration: Use with query builder to ensure results are typecast:
$users = User::where('age', '>', 18)->get();
// All `age` attributes are integers, not strings.
Laravel Scout: If using Scout for search, ensure typecasts are applied before indexing:
public function toSearchableArray()
{
return $this->typecasts; // Ensure proper types for search
}
Laravel Nova: Nova automatically respects Eloquent typecasts, so no additional setup is needed for proper field rendering.
Validation: Combine with Laravel validation for seamless type handling:
$validated = $request->validate([
'age' => 'required|integer',
]);
MySQL Driver Quirk:
mysqlnd, typecasting may not be necessary (as noted in the README). Test thoroughly to avoid redundant casting.Closure Performance:
Null Handling:
null values to avoid errors:
'discount' => function ($value) {
return $value ? $value / 100 : null;
},
Attribute Overrides:
attributes or casts in the same model, typecasts take precedence. Ensure no conflicts:
protected $casts = ['is_active' => 'boolean']; // Overridden by typecasts
protected $typecasts = ['is_active' => 'integer']; // Forces integer
Database vs. PHP Types:
boolean as t/f strings. Ensure your typecasts handle these edge cases:
'is_active' => function ($value) {
return $value === 't';
},
Check Typecasts: Dump the model to verify casting:
dd(User::find(1)->toArray());
Override for Testing: Temporarily disable typecasts in tests:
public function getTypecastedAttribute($key)
{
if (app()->environment('testing')) {
return $this->attributes[$key];
}
return parent::getTypecastedAttribute($key);
}
Log Casted Values: Add debug logging to trace casting behavior:
public function getTypecastedAttribute($key)
{
\Log::debug("Typecasting {$key}: " . $this->attributes[$key]);
return parent::getTypecastedAttribute($key);
}
Consistent Naming:
Use consistent naming for typecasts (e.g., is_active instead of active_flag) to align with Laravel conventions.
Document Typecasts: Add PHPDoc comments to clarify expected types:
/**
* @var boolean
*/
protected $typecasts = ['is_active' => 'boolean'];
Leverage for APIs: Use this package to ensure API responses are type-safe, reducing client-side type juggling.
Extend for Custom Types:
Add support for custom types (e.g., uuid, json) by extending the trait:
use Cviebrock\EloquentTypecast\Typecastable as BaseTypecastable;
trait ExtendedTypecastable
{
use BaseTypecastable;
protected $typecasts = [
...,
'metadata' => 'json',
];
protected function castJson($value)
{
return json_decode($value, true);
}
}
Performance Optimization: For large datasets, cache typecast results if logic is expensive:
protected $typecastCache = [];
public function getTypecastedAttribute($key)
{
if (!isset($this->typecastCache[$key])) {
$this->typecastCache[$key] = parent::getTypecastedAttribute($key);
}
return $this->typecastCache[$key];
}
How can I help you explore Laravel packages today?