Skip to content

Commit 5ca4435

Browse files
committed
docs: enhance README with additional usage examples and installation instructions; update config defaults for clarity
refactor: improve cache handling in DatabaseSettingsRepository and ClearCacheCommand
1 parent a65b605 commit 5ca4435

File tree

5 files changed

+173
-73
lines changed

5 files changed

+173
-73
lines changed

README.md

Lines changed: 92 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/ahs12/laravel-setanjo/Fix%20PHP%20code%20style%20issues?label=code%20style)](https://github.com/ahs12/laravel-setanjo/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
66
[![Total Downloads](https://img.shields.io/packagist/dt/ahs12/laravel-setanjo.svg?style=flat-square)](https://packagist.org/packages/ahs12/laravel-setanjo)
77

8-
A powerful Laravel package for managing application settings with multi-tenant support. Store global settings or tenant-specific configurations with automatic type casting, caching, and a clean API.
8+
A powerful Laravel package for managing application settings with multi-tenant support. Store global settings or tenant-specific configurations with automatic type casting, caching, and a clean API. Perfect for A/B testing, feature flags, and user preferences.
99

1010
## Features
1111

@@ -20,19 +20,65 @@ A powerful Laravel package for managing application settings with multi-tenant s
2020

2121
## Installation
2222

23+
1. **Install the package:**
24+
2325
```bash
2426
composer require ahs12/laravel-setanjo
25-
php artisan vendor:publish --tag="laravel-setanjo-migrations"
27+
```
28+
29+
2. **Publish and run migrations:**
30+
31+
```bash
32+
php artisan vendor:publish --tag="setanjo-migrations"
2633
php artisan migrate
2734
```
2835

29-
Optionally, publish the configuration file:
36+
3. **Configure tenancy mode (optional):**
37+
38+
By default, the package runs in **strict mode** (single tenant model type).
39+
40+
For basic setup with User model, no configuration needed. For other models or multiple tenant types:
3041

3142
```bash
32-
php artisan vendor:publish --tag="laravel-setanjo-config"
43+
php artisan vendor:publish --tag="setanjo-config"
44+
```
45+
46+
## Quick Setup
47+
48+
### Option 1: Strict Mode (Default - Single Tenant Type)
49+
50+
Perfect for simple user preferences or single-model tenancy:
51+
52+
```php
53+
// Uses App\Models\User by default
54+
// No configuration needed
3355
```
3456

35-
## Quick Start
57+
### Option 2: Custom Strict Mode
58+
59+
For using a different model as the single tenant type:
60+
61+
```php
62+
// config/setanjo.php
63+
'tenancy_mode' => 'strict',
64+
'strict_tenant_model' => App\Models\Company::class,
65+
```
66+
67+
### Option 3: Polymorphic Mode
68+
69+
For multiple tenant model types (SaaS apps, complex multi-tenancy):
70+
71+
```php
72+
// config/setanjo.php
73+
'tenancy_mode' => 'polymorphic',
74+
'allowed_tenant_models' => [
75+
App\Models\User::class,
76+
App\Models\Company::class,
77+
App\Models\Organization::class,
78+
],
79+
```
80+
81+
## Basic Usage
3682

3783
### Global Settings
3884

@@ -47,30 +93,35 @@ Settings::set('maintenance_mode', false);
4793
$appName = Settings::get('app_name', 'Default Name');
4894
```
4995

50-
### Tenant-Specific Settings
96+
### Tenant Settings (Strict Mode)
5197

5298
```php
53-
// Company-specific settings
54-
$company = Company::find(1);
55-
Settings::for($company)->set('company_name', 'Acme Corp');
56-
Settings::for($company)->set('timezone', 'America/New_York');
57-
58-
// For tenant by ID (useful in polymorphic mode)
59-
Settings::forTenantId(1, Company::class)->set('setting', 'value');
60-
// For tenant by ID in strict mode (model class is optional)
61-
Settings::forTenantId(1)->set('setting', 'value');
62-
63-
// User preferences
99+
// User-specific settings (default tenant model)
64100
$user = User::find(1);
65101
Settings::for($user)->set('theme', 'dark');
66102
Settings::for($user)->set('language', 'en');
67103

68-
// Each tenant has isolated settings
69-
echo Settings::for($company)->get('theme'); // null
104+
// Or by tenant ID
105+
Settings::forTenantId(1)->set('notifications', true);
106+
70107
echo Settings::for($user)->get('theme'); // 'dark'
71108
```
72109

73-
### Automatic Type Casting
110+
### Tenant Settings (Polymorphic Mode)
111+
112+
```php
113+
// Different model types as tenants
114+
$user = User::find(1);
115+
$company = Company::find(1);
116+
117+
Settings::for($user)->set('theme', 'dark');
118+
Settings::for($company)->set('timezone', 'UTC');
119+
120+
// Or by tenant ID with model class
121+
Settings::forTenantId(1, Company::class)->set('currency', 'USD');
122+
```
123+
124+
### Type Casting
74125

75126
```php
76127
Settings::set('is_active', true); // Boolean
@@ -80,67 +131,47 @@ Settings::set('features', ['api', 'sms']); // Array
80131

81132
// Values are returned with correct types
82133
$isActive = Settings::get('is_active'); // Returns boolean true
83-
$features = Settings::get('features'); // Returns array
84134
```
85135

86-
## Use Cases
136+
## Common Use Cases
87137

88-
**SaaS Applications**
138+
### User Preferences
89139

90140
```php
91-
// Per-tenant feature flags and limits
92-
Settings::for($tenant)->set('feature_api_enabled', true);
93-
Settings::for($tenant)->set('user_limit', 50);
94-
```
95-
96-
**E-commerce Platforms**
97-
98-
```php
99-
// Store-specific configurations
100-
Settings::for($store)->set('currency', 'USD');
101-
Settings::for($store)->set('tax_rate', 8.5);
102-
```
103-
104-
**User Preferences**
105-
106-
```php
107-
// Individual user settings
141+
$user = auth()->user();
108142
Settings::for($user)->set('notification_email', true);
109143
Settings::for($user)->set('dashboard_layout', 'grid');
144+
Settings::for($user)->set('timezone', 'America/New_York');
110145
```
111146

112-
**Application Configuration**
147+
### Application Configuration
113148

114149
```php
115-
// Global app settings
116150
Settings::set('maintenance_mode', false);
117151
Settings::set('registration_enabled', true);
152+
Settings::set('api_rate_limit', 1000);
118153
```
119154

120-
## Configuration
155+
### Multi-Tenant SaaS (Polymorphic Mode)
121156

122-
The package works out of the box, but you can customize it by publishing the configuration file:
157+
```php
158+
// Company-level settings
159+
Settings::for($company)->set('feature_api_enabled', true);
160+
Settings::for($company)->set('user_limit', 50);
123161

124-
```bash
125-
php artisan vendor:publish --tag="laravel-setanjo-config"
162+
// User preferences within company
163+
Settings::for($user)->set('email_notifications', false);
126164
```
127165

128-
Then modify `config/setanjo.php` to configure tenant models and caching:
166+
## Environment Variables
129167

130-
```php
131-
// config/setanjo.php
132-
return [
133-
'tenancy_mode' => 'polymorphic', // or 'strict'
134-
'allowed_tenant_models' => [
135-
App\Models\User::class,
136-
App\Models\Company::class,
137-
],
138-
'cache' => [
139-
'enabled' => true,
140-
'ttl' => 3600,
141-
'store' => null, // Use default cache store
142-
],
143-
];
168+
You can configure the package using environment variables:
169+
170+
```env
171+
SETANJO_TENANCY_MODE=strict
172+
SETANJO_STRICT_TENANT_MODEL="App\Models\User"
173+
SETANJO_CACHE_ENABLED=true
174+
SETANJO_CACHE_TTL=3600
144175
```
145176

146177
## Documentation

config/setanjo.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
| Define the single model class that can be used as tenant
5454
|
5555
*/
56-
'strict_tenant_model' => env('SETANJO_STRICT_TENANT_MODEL', true),
56+
'strict_tenant_model' => env('SETANJO_STRICT_TENANT_MODEL', 'App\\Models\\User'), // App\Models\User::class - common default Laravel user model.
5757

5858
/*
5959
|--------------------------------------------------------------------------
@@ -79,7 +79,7 @@
7979
|
8080
*/
8181
'cache' => [
82-
'enabled' => env('SETANJO_CACHE_ENABLED', true),
82+
'enabled' => env('SETANJO_CACHE_ENABLED', false),
8383
'store' => env('SETANJO_CACHE_STORE', null), // null = use Laravel's default
8484
'prefix' => env('SETANJO_CACHE_PREFIX', 'setanjo'),
8585
'ttl' => env('SETANJO_CACHE_TTL', 3600), // 1 hour
@@ -104,6 +104,7 @@
104104
|--------------------------------------------------------------------------
105105
|
106106
| Default settings that will be created when running artisan commands
107+
| php artisan setanjo:install-defaults
107108
|
108109
*/
109110
'defaults' => [

database/migrations/create_setanjo_settings_table.php.stub

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ return new class extends Migration
1010
{
1111
Schema::create($this->getTableName(), function (Blueprint $table) {
1212
$table->id();
13-
$table->string('key');
13+
$table->string('key', 191);
1414
$table->longText('value')->nullable();
1515
$table->text('description')->nullable();
16-
$table->string('type')->default('string');
16+
$table->string('type', 10)->default('string');
1717

1818
// Polymorphic tenantable columns
1919
$table->nullableMorphs($this->getTenantableColumn());
2020

2121
$table->timestamps();
2222

2323
// Indexes for performance
24-
$table->index(['key']);
24+
$table->index(['key'])->whereNull($this->getTenantableColumn() . '_type');
2525
$table->index([
2626
$this->getTenantableColumn() . '_type',
27-
$this->getTenantableColumn() . '_id'
28-
]);
27+
$this->getTenantableColumn() . '_id',
28+
'key'
29+
], 'idx_settings_tenant_key');
2930

3031
// Unique constraint for key + tenant combination
3132
$table->unique([

src/Commands/ClearCacheCommand.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class ClearCacheCommand extends Command
1010
{
1111
protected $signature = 'setanjo:clear-cache
12-
{--tenant= : Clear cache for specific tenant (format: ModelClass:ID)}
12+
{--tenant= : Clear cache for specific tenant (format: App\\Models\\User:ID)}
1313
{--all : Clear all setanjo cache}';
1414

1515
protected $description = 'Clear setanjo settings cache';
@@ -31,6 +31,7 @@ public function handle(): int
3131
$all = $this->option('all');
3232

3333
if ($tenant) {
34+
$tenant = $this->normalizeTenantKey($tenant);
3435
$this->clearTenantCache($tenant);
3536
} elseif ($all) {
3637
$this->clearAllCache();
@@ -41,6 +42,41 @@ public function handle(): int
4142
return self::SUCCESS;
4243
}
4344

45+
/**
46+
* Normalize tenant key to handle escaped backslashes
47+
*/
48+
protected function normalizeTenantKey(string $tenantKey): string
49+
{
50+
// Try to reconstruct the proper namespace if backslashes are missing
51+
if (! str_contains($tenantKey, '\\') && str_contains($tenantKey, 'ModelsUser:')) {
52+
// Handle common Laravel pattern: AppModelsUser:ID -> App\Models\User:ID
53+
$tenantKey = preg_replace('/^App([A-Z][a-z]+)+User:/', 'App\\Models\\User:', $tenantKey);
54+
} elseif (! str_contains($tenantKey, '\\') && preg_match('/^([A-Z][a-z]+)+([A-Z][a-z]+):(\d+)$/', $tenantKey, $matches)) {
55+
// General pattern reconstruction for missing backslashes
56+
$fullClass = $matches[0];
57+
$parts = preg_split('/(?=[A-Z])/', $fullClass, -1, PREG_SPLIT_NO_EMPTY);
58+
59+
if (count($parts) >= 3) {
60+
// Assume App\Models\ModelName pattern
61+
$reconstructed = $parts[0].'\\'.$parts[1].'\\'.implode('', array_slice($parts, 2));
62+
$this->line("Auto-reconstructed: '{$reconstructed}'");
63+
$tenantKey = $reconstructed;
64+
}
65+
}
66+
67+
// Handle double-escaped backslashes that might occur in command line
68+
$tenantKey = str_replace('\\\\', '\\', $tenantKey);
69+
70+
// Validate format: ModelClass:ID
71+
if (! preg_match('/^[A-Za-z\\\\]+:\d+$/', $tenantKey)) {
72+
$this->error('Invalid tenant format. Use: ModelClass:ID (e.g., App\\Models\\User:1)');
73+
$this->error('On Windows, try using quotes: --tenant="App\\Models\\User:1"');
74+
exit(1);
75+
}
76+
77+
return $tenantKey;
78+
}
79+
4480
/**
4581
* Clear cache for specific tenant
4682
*/
@@ -51,6 +87,7 @@ protected function clearTenantCache(string $tenantKey): void
5187
Cache::store(config('setanjo.cache.store'))->forget($cacheKey);
5288

5389
$this->info("Cleared cache for tenant: {$tenantKey}");
90+
$this->line("Cache key used: {$cacheKey}");
5491
}
5592

5693
/**

0 commit comments

Comments
 (0)