hosmelq/laravel-model-preferences

A Laravel package for managing model preferences with type-safe caching.

dev-main 2025-07-21 22:41 UTC

This package is auto-updated.

Last update: 2025-07-21 22:41:17 UTC


README

A Laravel package for managing model preferences with validation.

Features

  • Model preferences - Any Eloquent model can have preferences.
  • Validation support - Define Laravel validation rules for preference values.
  • Default values - Set fallback values for undefined preferences.
  • Batch operations - Set or delete multiple preferences at once.

Requirements

  • PHP 8.2+
  • Laravel 11.0+

Installation

composer require hosmelq/laravel-model-preferences

Configuration

The service provider will be automatically registered. You can use the install command for quick setup:

php artisan model-preferences:install

This command will publish the migrations, config file, and ask to run migrations automatically.

Alternatively, you can publish and run manually:

php artisan vendor:publish --tag="model-preferences-migrations"

php artisan migrate

Optionally, publish the config file:

php artisan vendor:publish --tag="model-preferences-config"

Basic Usage

Implement the interface and add the trait to any Eloquent model:

<?php

use HosmelQ\ModelPreferences\Contracts\HasPreferences;
use HosmelQ\ModelPreferences\Models\Concerns\InteractsWithPreferences;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements HasPreferences
{
    use InteractsWithPreferences;
    
    public function preferenceDefaults(): array
    {
        return [
            'notifications' => true,
            'theme' => 'light',
            'timezone' => 'UTC',
        ];
    }
}

Set and get preferences:

$user = User::find(1);

// Set a preference
$user->setPreference('theme', 'system');

// Get a preference
$theme = $user->preference('theme'); // 'system'

// Get with custom fallback
$timezone = $user->preference('timezone', 'UTC');

Usage

Setting Up Models

Implement the HasPreferences interface, use the InteractsWithPreferences trait, and optionally define default preferences and validation rules:

<?php

use HosmelQ\ModelPreferences\Contracts\HasPreferences;
use HosmelQ\ModelPreferences\Models\Concerns\InteractsWithPreferences;
use Illuminate\Database\Eloquent\Model;

class Team extends Model implements HasPreferences
{
    use InteractsWithPreferences;
    
    public function preferenceDefaults(): array
    {
        return [
            'allow_invites' => false,
            'max_members' => 10,
            'visibility' => 'private',
        ];
    }
}

Setting Preferences

Set individual preferences:

$team = Team::find(1);

$team->setPreference('max_members', 50);
$team->setPreference('visibility', 'public');

Set multiple preferences at once:

$team->setPreferences([
    'allow_invites' => true,
    'max_members' => 100,
    'visibility' => 'public',
]);

Getting Preferences

Get individual preferences:

$notifications = $user->preference('notifications');  // Returns stored value or default
$theme = $user->preference('theme', 'system'); // Custom fallback

Default Value Priority:

  1. Stored preference value
  2. Value from preferenceDefaults() method (if reference exists)
  3. Method parameter default (only used if reference not in preferenceDefaults())

Note that $user->preference('timezone', 'UTC') will return the value from preferenceDefaults()['timezone'] if defined, ignoring the UTC parameter.

Get all preferences merged with defaults:

$preferences = $user->getAllPreferences();

Check if a preference exists:

if ($user->hasPreference('notifications')) {
}

Deleting Preferences

Delete individual preferences:

$user->deletePreference('notifications');

Delete multiple preferences:

$user->deletePreferences(['notifications', 'theme', 'timezone']);

Eager Loading Preferences

Eager load preferences into the model's relationship cache to avoid N+1 database queries. When you call preference() later, Laravel can optimize these calls using the cached relationship data instead of hitting the database each time:

// Load specific preferences
$user->loadPreferences(['theme', 'notifications']);

// Load all preferences
$user->loadPreferences();

// Now accessing these preferences won't trigger additional queries
$theme = $user->preference('theme');
$notifications = $user->preference('notifications');

Validation

The package uses Laravel's validation system to validate preference values. Validation occurs automatically when calling setPreference() or setPreferences() using the rules defined in your preferenceRules() method.

When validation fails, a PreferenceValidationException is thrown, which has an errors() method to access validation messages.

Define Laravel validation rules for preferences:

<?php

use HosmelQ\ModelPreferences\Contracts\HasPreferences;
use HosmelQ\ModelPreferences\Models\Concerns\InteractsWithPreferences;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rule;

class User extends Model implements HasPreferences
{
    use InteractsWithPreferences;
    
    public function preferenceRules(): array
    {
        return [
            'notifications' => ['boolean'],
            'theme' => ['required', Rule::in(['dark', 'light', 'system'])],
            'timezone' => ['string', 'timezone'],
        ];
    }
}

Handle validation errors when setting preferences:

<?php

use HosmelQ\ModelPreferences\Exceptions\PreferenceValidationException;

try {
    $user->setPreference('theme', 'invalid-theme');
} catch (PreferenceValidationException $e) {
    $errors = $e->errors();
}

Testing

composer test

Changelog

See CHANGELOG.md for a list of changes.

Credits

License

The MIT License (MIT). Please see License File for more information.