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 Custom Properties Laravel Package

latevaweb/laravel-custom-properties

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require latevaweb/laravel-custom-properties
    
  2. Add the trait to your Eloquent model:
    use LaTevaWeb\CustomProperties\HasCustomProperties;
    
    class Customer extends Model
    {
        use HasCustomProperties;
    }
    
  3. Add the custom_properties JSON column to your database table:
    Schema::table('customers', function (Blueprint $table) {
        $table->json('custom_properties')->nullable();
    });
    
  4. Run migrations:
    php artisan migrate
    

First Use Case

Adding and retrieving a dynamic property:

$customer = new Customer();
$customer->setCustomProperty('preferred_language', 'es')
         ->setCustomProperty('loyalty_points', 150)
         ->save();

// Retrieve the property
$language = $customer->getCustomProperty('preferred_language'); // 'es'

// Check if a property exists
$hasPoints = $customer->hasCustomProperty('loyalty_points'); // true

// Remove a property
$customer->forgetCustomProperty('loyalty_points')->save();

Implementation Patterns

Common Workflows

  1. Dynamic Model Attributes: Use setCustomProperty() to add metadata that doesn’t warrant a dedicated database column (e.g., user preferences, temporary flags, or API-specific configurations).

    $user->setCustomProperty('api_token_expiry', now()->addDays(30))->save();
    
  2. Conditional Logic: Leverage hasCustomProperty() to gate behavior without querying the database.

    if ($order->hasCustomProperty('is_rushed')) {
        $order->update(['status' => 'processing']);
    }
    
  3. Bulk Property Management: Use getCustomProperties() to retrieve all dynamic properties as an array.

    $preferences = $user->getCustomProperties(); // ['theme' => 'dark', 'notifications' => false]
    
  4. Serialization/Deserialization: Integrate with API responses or JSON payloads:

    $data = $model->toArray(); // Includes custom_properties as a JSON-encoded string
    

Integration Tips

  • Validation: Validate custom properties before saving:

    $this->validate([
        'custom_properties' => 'required|array',
        'custom_properties.*' => 'string|max:255',
    ]);
    
  • Caching: Cache frequently accessed custom properties:

    $cacheKey = "user_{$user->id}_preferences";
    $preferences = Cache::remember($cacheKey, now()->addHours(1), function () use ($user) {
        return $user->getCustomProperties();
    });
    
  • API Resources: Expose custom properties in API responses:

    public function toArray($request)
    {
        return array_merge(parent::toArray($request), [
            'custom' => $this->getCustomProperties(),
        ]);
    }
    
  • Model Events: Listen for saved events to log or process custom properties:

    $model->saved(function ($model) {
        if ($model->hasCustomProperty('audit_log')) {
            AuditLog::create([
                'action' => 'update_custom_properties',
                'changes' => $model->getCustomProperties(),
            ]);
        }
    });
    
  • Query Scoping: Filter models by custom property values (requires raw SQL or a custom scope):

    public function scopeWithCustomProperty($query, $key, $value)
    {
        return $query->whereRaw("JSON_CONTAINS(custom_properties, ?)", [json_encode($value)]);
    }
    

Gotchas and Tips

Pitfalls

  1. JSON Column Limitations:

    • MySQL’s JSON column has a 64KB limit for values. Avoid storing large blobs (e.g., serialized arrays or base64-encoded files).
    • Workaround: Use a separate table for large custom properties or compress data.
  2. Case Sensitivity:

    • Custom property keys are case-sensitive in PHP. Ensure consistency when setting/retrieving:
      $model->setCustomProperty('UserId', 123); // Key is 'UserId'
      $model->getCustomProperty('userId');     // Returns null (case mismatch)
      
  3. Database Driver Quirks:

    • SQLite: May not support JSON columns in older versions. Use text with manual JSON encoding/decoding.
    • PostgreSQL: Supports advanced JSON operations (e.g., ->> for text extraction), but the package uses generic JSON handling.
    • Workaround: Add a custom accessor for driver-specific optimizations:
      public function getCustomProperty($key)
      {
          if (app()->make('db')->getDriverName() === 'pgsql') {
              return json_decode($this->custom_properties)->{$key} ?? null;
          }
          return parent::getCustomProperty($key);
      }
      
  4. Mass Assignment Risks:

    • Custom properties are not guarded by $fillable. Explicitly whitelist or validate:
      $this->validate([
          'custom_properties' => 'sometimes|array',
          'custom_properties.allowed_key' => 'required_if:custom_properties|string',
      ]);
      
  5. Migration Conflicts:

    • If the custom_properties column already exists, migrations may fail. Use:
      Schema::table('table', function (Blueprint $table) {
          if (!$table->hasColumn('custom_properties')) {
              $table->json('custom_properties')->nullable()->after('updated_at');
          }
      });
      
  6. Serialization Issues:

    • Eloquent’s toArray() converts custom_properties to a string. Override if needed:
      public function toArray()
      {
          $array = parent::toArray();
          $array['custom_properties'] = json_decode($array['custom_properties'], true);
          return $array;
      }
      

Debugging Tips

  1. Inspect Raw JSON: Dump the raw custom_properties column to debug encoding issues:

    dd($model->getRawOriginal('custom_properties'));
    
  2. Check for Null Values: Ensure custom_properties is never null when accessing:

    $properties = json_decode($model->custom_properties ?? '{}', true);
    
  3. Validate JSON Syntax: Use json_last_error() to catch malformed JSON:

    $json = $model->custom_properties;
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new \Exception("Invalid JSON in custom_properties: " . json_last_error_msg());
    }
    

Extension Points

  1. Custom Storage Backend: Override the trait’s storage logic to use Redis or a cache layer:

    public function getCustomProperty($key)
    {
        $cacheKey = "model_{$this->id}_{$key}";
        return Cache::get($cacheKey) ?? parent::getCustomProperty($key);
    }
    
  2. Encryption: Encrypt sensitive custom properties:

    public function setCustomProperty($key, $value)
    {
        if (in_array($key, ['api_token', 'credit_card'])) {
            $value = encrypt($value);
        }
        $this->custom_properties[$key] = $value;
        return $this;
    }
    
  3. Event Triggers: Dispatch events when custom properties change:

    public function setCustomProperty($key, $value)
    {
        $oldValue = $this->getCustomProperty($key);
        parent::setCustomProperty($key, $value);
        event(new CustomPropertyUpdated($this, $key, $oldValue, $value));
    }
    
  4. Default Values: Add defaults for frequently used properties:

    protected static function bootHasCustomProperties()
    {
        static::created(function ($model) {
            $model->setCustomProperty('created_via', 'api')->save();
        });
    }
    
  5. Query Builder Extensions: Add scopes for filtering by custom properties:

    public function scopeWithPreference($query, $key, $value)
    {
        return $query->where(function ($q) use ($key, $value) {
            $q->whereNotNull('custom_properties')
              ->whereRaw("JSON_EXTRACT(custom_properties, '$.{$key}') = ?", [$value]);
        });
    }
    
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky