Weave Code
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

Laravel Mutate Laravel Package

weebly/laravel-mutate

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require weebly/laravel-mutate
    

    Ensure Weebly\Mutate\LaravelMutatorServiceProvider is registered in config/app.php (or use auto-discovery).

  2. Publish Config:

    php artisan vendor:publish --provider="Weebly\Mutate\LaravelMutatorServiceProvider"
    

    This generates config/mutators.php with default settings.

  3. Extend Your Model: Replace your App\Models\Model with Weebly\Mutate\Database\Model in your base model or individual models.

  4. Define Mutations: Add a $mutate property to your model:

    protected $mutate = [
        'ip_address' => [
            'get' => function ($value) {
                return $value ? inet_ntop($value) : null;
            },
            'set' => function ($value) {
                return $value ? inet_pton($value) : null;
            },
        ],
        'encrypted_data' => [
            'get' => fn($value) => decrypt($value),
            'set' => fn($value) => encrypt($value),
        ],
    ];
    
  5. First Use Case:

    $user = new User();
    $user->ip_address = '192.168.1.1'; // Automatically converted to BINARY(16) on save
    $user->save();
    
    $storedIp = $user->fresh()->ip_address; // Returns readable string
    

Implementation Patterns

Core Workflows

  1. Attribute Mapping: Use $mutate to define bidirectional transformations for attributes:

    $mutate = [
        'attribute' => [
            'get' => fn($value) => /* transform for retrieval */,
            'set' => fn($value) => /* transform for storage */,
        ],
    ];
    
    • Chaining: Combine with Laravel’s built-in accessors/mutators for layered logic.
  2. Database-Specific Optimizations:

    • Store large strings as BINARY or JSON in DB, decode in PHP:
      $mutate = [
          'metadata' => [
              'get' => fn($value) => json_decode($value, true),
              'set' => fn($value) => json_encode($value),
          ],
      ];
      
  3. Encryption/Decryption:

    $mutate = [
        'ssn' => [
            'get' => fn($value) => decrypt($value),
            'set' => fn($value) => encrypt($value),
        ],
    ];
    
  4. Conditional Mutations:

    $mutate = [
        'status' => [
            'get' => fn($value) => $value === 'active' ? 'Active' : 'Inactive',
            'set' => fn($value) => strtolower($value),
        ],
    ];
    
  5. Mass Assignment Safety: Use $guarded or $fillable alongside $mutate to control which attributes can be mutated.


Integration Tips

  1. Query Scopes: Mutations apply to retrieved data, so use them in scopes:

    public function scopeActive($query) {
        return $query->where('status', 'active'); // 'active' is stored as lowercase
    }
    
  2. API Resources: Mutations ensure consistent data shapes in responses:

    // UserResource.php
    public function toArray($request) {
        return [
            'ip' => $this->resource->ip_address, // Automatically decoded
        ];
    }
    
  3. Testing: Mock mutations in tests:

    $model = new User();
    $model->shouldReceive('mutateAttribute')
          ->with('ip_address', $value)
          ->andReturnTransformedValue();
    
  4. Performance:

    • Avoid complex mutations in loops (e.g., bulk updates).
    • Cache transformed values if mutations are expensive.
  5. Relationships: Mutations work recursively for loaded relationships:

    $user = User::with('posts')->find(1);
    // $user->posts[0]->content is automatically mutated if defined.
    

Gotchas and Tips

Pitfalls

  1. Double Transformation:

    • Issue: Forgetting mutations apply both on get and set.
    • Fix: Test both directions:
      $model->attribute = 'raw'; // set → stored
      $model->fresh()->attribute; // get → retrieved
      
  2. Database Schema Mismatch:

    • Issue: Mutations won’t auto-create columns. Ensure your DB schema matches the stored type (e.g., BINARY(16) for IPs).
    • Fix: Run migrations separately.
  3. Circular Dependencies:

    • Issue: Mutations referencing other mutated attributes can cause infinite loops.
    • Fix: Use static methods or avoid inter-attribute dependencies.
  4. Mass Assignment Risks:

    • Issue: Mutations don’t protect against mass assignment vulnerabilities.
    • Fix: Always use $fillable or $guarded.
  5. Lazy Loading:

    • Issue: Mutations aren’t applied to lazy-loaded relationships until accessed.
    • Fix: Eager-load relationships when mutations are needed.

Debugging

  1. Log Mutations: Temporarily add logging to $mutate callbacks:

    'get' => function ($value) {
        \Log::debug("Mutating to get: $value");
        return /* ... */;
    },
    
  2. Check Stored Values: Use DB::table('users')->get() to verify raw DB values differ from model attributes.

  3. Disable Mutations: Override shouldMutateAttribute() in your model to debug:

    protected function shouldMutateAttribute($attribute) {
        return false; // Disable all mutations
    }
    

Extension Points

  1. Custom Mutator Classes: Extract complex mutations to standalone classes:

    class IpMutator {
        public static function encode($ip) { /* ... */ }
        public static function decode($binary) { /* ... */ }
    }
    
    $mutate = [
        'ip_address' => [
            'get' => [IpMutator::class, 'decode'],
            'set' => [IpMutator::class, 'encode'],
        ],
    ];
    
  2. Global Mutations: Define mutations in a trait or base model to reuse across models:

    trait Encryptable {
        protected $mutate = [
            'secret' => [
                'get' => fn($value) => decrypt($value),
                'set' => fn($value) => encrypt($value),
            ],
        ];
    }
    
  3. Dynamic Mutations: Use closures to conditionally apply mutations:

    $mutate = [
        'data' => [
            'get' => function ($value) {
                return request()->has('decrypt') ? decrypt($value) : $value;
            },
            'set' => fn($value) => encrypt($value),
        ],
    ];
    
  4. Event-Based Mutations: Trigger mutations via model events (e.g., retrieved):

    protected static function booted() {
        static::retrieved(function ($model) {
            $model->mutateAttribute('dynamic_field', $model->dynamic_field);
        });
    }
    
  5. Config Overrides: Override default behavior in config/mutators.php:

    'default_mutator' => \App\CustomMutator::class,
    'skip_attributes' => ['timestamps'], // Exclude from mutation
    
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui