Install the package:
composer require sagalbot/encryptable
Ensure Laravel encryption is configured:
Run php artisan key:generate if APP_KEY isn’t already set in .env.
Verify config/app.php includes the key under 'key' => env('APP_KEY').
Apply the trait to a model:
use Sagalbot\Encryptable\Encryptable;
class User extends Model
{
use Encryptable;
protected $encryptable = ['ssn', 'credit_card'];
}
Test immediately:
$user = new User;
$user->ssn = '123-45-6789'; // Automatically encrypted on save
$user->save();
echo $user->ssn; // Automatically decrypted on access
Secure PII in a User Model:
class User extends Model
{
use Encryptable;
protected $encryptable = [
'ssn',
'driver_license_number',
'medical_history'
];
protected $fillable = ['name', 'email', 'ssn']; // ssn is encrypted in DB
}
Define Encryptable Fields:
protected $encryptable = ['field1', 'field2'];
$model->field1.Mass Assignment Safety:
$fillable or $guarded as usual. Encryption happens after validation.$user = User::create([
'name' => 'John',
'ssn' => '123-45-6789' // Encrypted before DB insert
]);
Querying Encrypted Fields:
$users = User::where('ssn', 'like', '123%')->get(); // Decrypts during query
DB::select()). Use Eloquent.Serialization/JSON:
$user->toArray(); // ssn is decrypted
API Responses:
hidden() or visible() to control exposure:
protected $hidden = ['ssn']; // Exclude from API responses
Dynamic Encryption:
Dynamically set $encryptable based on logic (e.g., tenant-specific fields):
public function initializeEncryptable()
{
$this->encryptable = array_merge(
$this->encryptable,
['tenant_specific_field']
);
}
Custom Encryption Key: Override the default Laravel key per-model:
use Sagalbot\Encryptable\Encryptable;
class User extends Model
{
use Encryptable;
protected $encryptableKey = 'custom-encryption-key';
}
Partial Updates:
Use update() or fill():
$user->update(['ssn' => '987-65-4321']); // Encrypts only the updated field
Events:
Listen to retrieved (after decryption) or storing (before encryption):
protected static function bootEncryptable()
{
static::retrieved(function ($model) {
// Log decrypted sensitive data
});
}
Relationships: Encrypted fields work seamlessly with relationships:
class Order extends Model
{
use Encryptable;
protected $encryptable = ['billing_address'];
public function user()
{
return $this->belongsTo(User::class);
}
}
Raw Queries Fail:
DB::select() or DB::table() bypasses Eloquent’s decryption.$rawData = DB::table('users')->select('ssn')->first();
$decrypted = decrypt($rawData->ssn);
Caching Issues:
fresh():
$user = User::find(1)->fresh(); // Forces re-fetch and decryption
Migration Conflicts:
nullable and backfill:
Schema::table('users', function (Blueprint $table) {
$table->string('ssn')->nullable()->after('email');
});
User::all()->each->fill(['ssn' => null])->save();
Performance Overhead:
public function getSsnAttribute($value)
{
return $value ?? $this->attributes['ssn'];
}
Key Rotation:
APP_KEY won’t decrypt existing data.config(['app.cipher' => 'AES-256-CBC'])).Verify Encryption: Check raw DB values:
$user->fresh()->getRawOriginal('ssn'); // Returns encrypted string
Log Decryption Errors: Wrap access in a try-catch:
try {
$ssn = $user->ssn;
} catch (\Exception $e) {
Log::error("Decryption failed for user {$user->id}: " . $e->getMessage());
}
Test with encrypt()/decrypt():
Manually test encryption:
$encrypted = encrypt($user->ssn);
$decrypted = decrypt($encrypted);
Custom Encryption Logic:
Override the encryptAttribute() method:
protected function encryptAttribute($value, $key)
{
return bcrypt($value); // Use bcrypt instead of Laravel's encrypt()
}
Conditional Encryption: Dynamically exclude fields from encryption:
public function getEncryptable()
{
if ($this->isAdmin()) {
return array_diff($this->encryptable, ['admin_notes']);
}
return $this->encryptable;
}
Add to Existing Models:
Use use Encryptable; in existing models and add $encryptable incrementally.
Integration with Policies: Restrict access to decrypted fields in policies:
public function viewAny(User $user)
{
return $user->can('view_ssn');
}
Database-Level Encryption:
Combine with Laravel’s encrypt column type for extra security:
Schema::table('users', function (Blueprint $table) {
$table->string('ssn')->encrypt()->nullable();
});
How can I help you explore Laravel packages today?