Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Eloquent Encryptable Laravel Package

kduma/eloquent-encryptable

Transparently encrypt sensitive Eloquent attributes at rest. A simple trait encrypts chosen fields on save and decrypts them on read, so your code keeps using plain values while the database stores ciphertext—no schema changes required.

View on GitHub
Deep Wiki
Context7

Getting Started

Before installing, check whether Laravel’s native encrypted cast already covers the use case. This package is archived, has very low adoption, and overlaps with a first-party feature; use it only for legacy compatibility or a specific behavior you cannot reproduce with the native cast.

Minimal setup:

composer require kduma/eloquent-encryptable

Pin the exact version in composer.json and consider forking/vendoring it, since archived packages receive no Laravel-version or security updates. Ensure APP_KEY is set and backed up securely; losing it means losing all encrypted data.

Add the package’s encryptable trait to the model and declare the attributes to encrypt. Confirm the exact trait FQCN and property name in the installed README/vendor source, as the package is small and version-specific:

use Kduma\Eloquent\Encryptable\Encryptable;

class Customer extends Model
{
    use Encryptable;

    protected $encryptable = [
        'tax_id',
        'phone',
        'address',
    ];
}

Use TEXT/LONGTEXT columns, not VARCHAR(255); ciphertext is much longer than plaintext. For an existing table, plan a backfill rather than expecting existing plaintext rows to decrypt automatically.

First practical use case: encrypt PII or secrets that are only displayed/edited in detail views, never searched or sorted in SQL — for example tax IDs, phone numbers, home addresses, API tokens, or 2FA secrets.

Where to look first: the README, the trait source in vendor/kduma/eloquent-encryptable, the package tests, and the release history. Verify which Laravel versions the pinned release claims to support.

Implementation Patterns

Basic encrypted model attributes. Keep encrypted fields as simple strings, hide them from JSON, and expose them through explicit resources or detail views:

class Customer extends Model
{
    use Encryptable;

    protected $encryptable = ['tax_id', 'phone'];
    protected $hidden = ['tax_id', 'phone'];
}

Searchable fields via a blind index. Encryption is non-deterministic, so where('email', $email) will not work. Store a separate deterministic hash/HMAC column for exact-match lookup:

$customer->email_hash = hash_hmac('sha256', strtolower($email), config('app.key'));
Customer::where('email_hash', $hash)->first();

Use a dedicated HMAC key rather than raw APP_KEY if you need to rotate encryption keys independently.

Backfilling existing plaintext data. Write an artisan command using chunkById, read the plaintext, assign through the model so the trait encrypts it, and save. Do not use DB::table()->update() for the encrypted column.

Key rotation. Plan a dual-read window: decrypt old values with the old key, re-encrypt with the new key, and backfill in batches. This package does not provide key rotation; you must build it.

Combining with accessors and casts. Keep the encrypted attribute as a raw string and layer formatting in accessors/resources. If you need arrays or JSON, verify whether the package serializes values before encryption; otherwise serialize explicitly:

protected $encryptable = ['settings'];

public function setSettingsAttribute($value): void
{
    $this->attributes['settings'] = json_encode($value);
}

Testing. Set APP_KEY in phpunit.xml. Assert plaintext through the model and ciphertext through the query builder:

$this->assertSame('123-45-6789', $customer->tax_id);
$this->assertNotSame('123-45-6789', DB::table('customers')->value('tax_id'));

Admin/UI integration. Treat encrypted columns as non-searchable and non-sortable. In Filament/Nova or custom admin screens, show decrypted values only on detail/edit pages, and avoid global search over those columns.

Gotchas and Tips

  • Archived and low adoption. No upstream fixes, no guaranteed Laravel 12+ support, and only five stars. Audit the trait before trusting it with regulated data. Prefer Laravel’s native encrypted cast where possible.
  • No SQL operations on ciphertext. WHERE, ORDER BY, LIKE, GROUP BY, and unique indexes on encrypted columns are effectively useless. Use blind indexes for exact lookups and accept that fuzzy search requires a separate search store.
  • Column sizing. Base64 encoding, IVs, MACs, and serialization overhead expand values significantly. Use TEXT; VARCHAR(255) will truncate or fail.
  • APP_KEY is a single point of failure. Rotating it without re-encrypting makes all values undecryptable. Back up the key and test restore procedures.
  • Raw queries bypass encryption. DB::table(), Model::insert(), bulk upserts, and raw SQL skip the trait entirely. Enforce model-level writes or add explicit encryption in those paths.
  • Dirty tracking surprises. Reassigning the same plaintext can produce new ciphertext, marking the model dirty and causing unnecessary writes. Compare plaintext before assignment if this matters.
  • JSON leakage. toArray() and API resources may expose decrypted values. Add encrypted attributes to $hidden and use explicit API resources.
  • firstOrCreate/updateOrCreate pitfalls. These query by the plaintext value, which will not match ciphertext. Query by the blind-index hash instead.
  • Null and empty-string behavior. Verify how the package handles null, '', and non-string values in your pinned version; behavior may differ from the native cast.
  • Migration to native casts is not automatically drop-in. Ciphertext formats differ (Crypt::encryptString vs Crypt::encrypt/serialization). Test decryption compatibility before switching.
  • Performance. Decryption happens on attribute access. Avoid appending encrypted fields globally, and avoid decrypting in large loops or list endpoints.
  • Debugging. Inspect getRawOriginal()/getAttributes() to see ciphertext versus accessor output. Catch DecryptException during key or data migrations, and log the record identifier — not the ciphertext or plaintext.
  • Never encrypt primary or foreign keys. Joins, relationships, and route model binding will break.
  • Extension points. If the package exposes overridable encrypt/decrypt methods, wrap them to add key versioning or custom serialization. Otherwise, a custom cast is usually a cleaner long-term abstraction.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style