marktic/settings

Multi-tenant settings management package

Installs: 11

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/marktic/settings

dev-main 2026-02-25 20:13 UTC

This package is auto-updated.

Last update: 2026-02-25 20:13:30 UTC


README

Latest Version on Packagist Tests License

A multi-tenant settings management package for PHP 8.2+ applications. Supports typed values (string, JSON, integer, float, boolean), grouped settings, tenant-scoped configuration, and multiple storage adapters (database, cache file).

Features

  • Typed settings: PHP property types (string, bool, int, float, array) automatically determine the cast — no manual configuration needed
  • Class-based settings: Define settings as plain PHP classes extending AbstractSettings; properties with default values are automatically used as fallbacks
  • Grouped settings: Each settings class declares its group via group()
  • Multi-tenant support: Scope settings to any tenant by passing a tenant record to SettingsManager::get()
  • Instance caching: SettingsManager::get() always returns the same object for the same class+tenant combination
  • Multiple storages: Persist settings to a relational database (DatabaseStorage) or a JSON cache file (FileStorage)
  • Mapper: SettingMapper handles low-level conversion between SettingDto and database/array representations
  • Trait-based integration: Use HasSettingsRecordTrait to add per-model setting capabilities
  • Timestamps: Every stored setting entry tracks created_at and updated_at

Installation

composer require marktic/settings

Register the service provider (if not using auto-discovery):

// config/app.php (Laravel)
'providers' => [
    Marktic\Settings\MktSettingsServiceProvider::class,
],

Run migrations:

php artisan migrate

Usage

Defining a Settings Class

use Marktic\Settings\AbstractSettings;

class GeneralSettings extends AbstractSettings
{
    // PHP property types determine the storage cast automatically:
    // string → SettingType::String
    // bool   → SettingType::Boolean
    // int    → SettingType::Integer
    // float  → SettingType::Float
    // array  → SettingType::Json (stored as JSON)

    public string $site_name = 'My App';

    public bool $site_active = true;

    public int $max_items = 20;

    public float $tax_rate = 0.2;

    public array $supported_locales = ['en'];

    // Determines the group used in storage
    public static function group(): string
    {
        return 'general';
    }

    // Returns the named storage to use, or null for the package default
    public static function repository(): ?string
    {
        return null;
    }
}

Setting up the SettingsManager

use Marktic\Settings\Hydrator\SettingsHydrator;use Marktic\Settings\Mapper\SettingMapper;use Marktic\Settings\MktSettingsManager;use Marktic\Settings\Storages\FileStorage;use Marktic\Settings\Utility\MktSettingsModels;

// Database storage (requires bytic/orm set up)
$storage = MktSettingsModels::createDatabaseStorage();

// File storage (no database required)
$storage = new FileStorage('/path/to/settings.json', new SettingMapper());

$manager = new MktSettingsManager($storage, new SettingsHydrator());

// Register optional named storages referenced by repository()
$manager->addStorage('file', new FileStorage('/path/to/file.json', new SettingMapper()));

Retrieving and Saving Settings

// Get hydrated settings (default values are used if nothing is stored yet)
$settings = $manager->get(GeneralSettings::class);
echo $settings->site_name; // "My App"

// Repeated calls return the SAME object instance
$settings1 = $manager->get(GeneralSettings::class);
$settings2 = $manager->get(GeneralSettings::class);
var_dump($settings1 === $settings2); // true

// Modify and save
$settings->site_name = 'New Name';
$settings->max_items = 50;
$manager->save($settings);

Tenant-Scoped Settings

use Marktic\Settings\SettingsTenantInterface;

// Your model can implement SettingsTenantInterface
class Organization implements SettingsTenantInterface
{
    public function getSettingTenantType(): string
    {
        return static::class;
    }

    public function getSettingTenantId(): string|int|null
    {
        return $this->id;
    }
}

$org = Organization::find(42);

// Each tenant gets its own isolated instance and storage scope
$tenantSettings = $manager->get(GeneralSettings::class, $org);
$tenantSettings->site_name = 'Org Site';

$manager->save($tenantSettings);

// Another tenant is isolated
$otherOrg = Organization::find(99);
$otherSettings = $manager->get(GeneralSettings::class, $otherOrg);
echo $otherSettings->site_name; // "My App" (its own defaults)

Low-level: Using Storages Directly

use Marktic\Settings\Mapper\SettingMapper;use Marktic\Settings\Settings\Dto\SettingDto;use Marktic\Settings\Settings\Enums\SettingType;use Marktic\Settings\Storages\FileStorage;

$storage = new FileStorage('/path/to/settings.json', new SettingMapper());

$dto = new SettingDto();
$dto->name = 'site.title';
$dto->group = 'general';
$dto->type = SettingType::String;
$dto->setValue('My Application');

$storage->save($dto);

$found = $storage->find('site.title', 'general');
echo $found->getCastValue(); // "My Application"

Database Schema

Table: mkt_settings

Column Type Description
id BIGINT UNSIGNED Primary key
name VARCHAR(191) Setting name/key
group VARCHAR(100) Logical group, default: "default"
value TEXT Raw stored value
type VARCHAR(20) Value type: string, json, integer, float, boolean
tenant_type VARCHAR(191) Tenant class/type (nullable)
tenant_id BIGINT UNSIGNED Tenant identifier (nullable)
created_at DATETIME Creation timestamp
updated_at DATETIME Last update timestamp

A unique index on (name, group, tenant_type, tenant_id) ensures no duplicate settings per scope.

Architecture

src/
├── AbstractSettings.php     # Base class for user-defined settings (extend this)
├── SettingsManager.php      # Manager: get(class, tenant?) with caching + save()
├── SettingsTenantInterface.php  # Interface for tenant objects
├── Settings/
│   ├── Models/              # ORM Record (Setting) + RecordManager (Settings)
│   ├── Dto/                 # SettingDto — low-level value object
│   ├── Enums/               # SettingType — typed enum with cast/encode
│   ├── Mapper/              # SettingMapper — DTO ↔ DB / array conversion
│   ├── Hydrator/            # SettingsHydrator — reflection-based property mapping
│   └── Storages/            # SettingStorageInterface, DatabaseStorage, FileStorage
├── AbstractBase/            # Base Record and Repository classes
├── ModelsRelated/           # Cross-cutting HasSettings traits
├── Utility/                 # SettingsModels (ModelFinder), PackageConfig
└── SettingsServiceProvider.php

Testing

composer test

Changelog

See CHANGELOG.md for recent changes.

License

The MIT License (MIT). See LICENSE.

Inspiration

This package was inspired by and takes ideas from the following open-source projects: