Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save sushantaryal/eb19479c4c83b24fe36761607000a72b to your computer and use it in GitHub Desktop.

Select an option

Save sushantaryal/eb19479c4c83b24fe36761607000a72b to your computer and use it in GitHub Desktop.
Laravel Settings
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->text('value')->nullable();
$table->string('type')->default('string');
$table->string('group')->default('general');
$table->text('description')->nullable();
$table->timestamps();
$table->index('group');
});
}
public function down(): void
{
Schema::dropIfExists('settings');
}
};
<?php
use App\Http\Requests\SaveSettingRequest;
use App\Services\SettingService;
use Illuminate\Support\Facades\Route;
Route::get('settings', function () {
$settings = app(SettingService::class)->all();
return $settings;
});
Route::put('settings', function (SaveSettingRequest $request) {
app(SettingService::class)->updateSettings($request->validated());
return app(SettingService::class)->all();
});
Route::get('settings/{key}', function (string $key) {
return app(SettingService::class)->get($key);
});
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SaveSettingRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'site_name' => 'required|string|max:255',
'site_description' => 'nullable|string|max:500',
'site_keywords' => 'nullable|string|max:255',
'contact_email' => 'required|email|max:255',
'contact_phone' => 'nullable|string|max:20',
'contact_address' => 'nullable|string|max:500',
'facebook_url' => 'nullable|url|max:255',
'twitter_url' => 'nullable|url|max:255',
'linkedin_url' => 'nullable|url|max:255',
'instagram_url' => 'nullable|url|max:255',
'youtube_url' => 'nullable|url|max:255',
'logo' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
'favicon' => 'nullable|image|mimes:ico,png|max:1024',
'conference_date_start' => 'nullable|date',
'conference_date_end' => 'nullable|date|after_or_equal:conference_date_start',
'conference_venue' => 'nullable|string|max:255',
'conference_address' => 'nullable|string|max:500',
'registration_enabled' => 'boolean',
'registration_deadline' => 'nullable|date',
'early_bird_deadline' => 'nullable|date',
'google_analytics_id' => 'nullable|string|max:255',
'google_maps_api_key' => 'nullable|string|max:255',
];
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = ['key', 'value'];
public function getLabel(): string
{
return ucwords(str_replace('_', ' ', $this->key));
}
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Settings</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body class="dark:bg-gray-900 dark:text-white">
<div class="flex flex-col items-center justify-center min-h-screen px-4 py-12 sm:px-6 lg:px-8">
<div class="w-full max-w-7xl space-y-8">
<h1 class="text-3xl font-bold text-gray-900 dark:text-gray-100">Settings</h1>
<form action="{{ route('settings.update') }}" method="post" enctype="multipart/form-data">
@csrf
@method('put')
@foreach($settings as $group => $settings)
<input type="hidden" name="group" value="{{ $group }}">
<div class="w-full bg-white border border-gray-200 mb-8 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
<div class="p-4">
<h5 class="text-xl font-medium text-gray-900 dark:text-white">{{ ucfirst($group) }}</h5>
</div>
<div class="p-4 border-t border-gray-200 dark:border-gray-700 grid md:grid-cols-2 gap-4">
@foreach($settings as $setting)
<div>
<label for="{{ $setting->key }}" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ $setting->getLabel() }}</label>
@if($setting->type == 'string')
<input type="text" id="{{ $setting->key }}" name="{{ $setting->key }}" value="{{ old($setting->key, $setting->value) }}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
@elseif($setting->type == 'date')
<input type="date" id="{{ $setting->key }}" name="{{ $setting->key }}" value="{{ old($setting->key, $setting->value) }}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
@elseif($setting->type == 'boolean')
<label class="inline-flex items-center cursor-pointer">
<input type="checkbox" id="{{ $setting->key }}" name="{{ $setting->key }}" value="1" class="sr-only peer" {{ $setting->value ? 'checked' : '' }}>
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 dark:peer-checked:bg-blue-600"></div>
</label>
@elseif($setting->type == 'file')
<label for="{{ $setting->key }}" class="flex flex-col items-center justify-center w-full h-42 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/>
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Click to upload</span> or drag and drop</p>
@if($setting->key == 'logo')
<p class="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 1MB)</p>
@elseif($setting->key == 'favicon')
<p class="text-xs text-gray-500 dark:text-gray-400">ICO, PNG (MAX. 1MB)</p>
@endif
</div>
<input id="{{ $setting->key }}" name="{{ $setting->key }}" type="file" class="hidden" accept="image/svg+xml, image/png, image/jpeg, image/gif">
</label>
@endif
@error($setting->key)
<p class="mt-2 text-sm text-red-600 dark:text-red-500">{{ $message }}</p>
@enderror
@if($setting->type == 'file' && $setting->value)
<img src="{{ Storage::url($setting->value) }}" alt="{{ $setting->getLabel() }}" class="w-auto h-64 rounded-lg mt-4">
@endif
</div>
@endforeach
</div>
</div>
@endforeach
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center cursor-pointer dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Submit</button>
</form>
</div>
</div>
</body>
</html>
<?php
namespace App\Services;
use App\Models\Setting;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
class SettingService
{
protected string $cacheKey = 'app-settings';
public function all(): array
{
return Cache::rememberForever($this->cacheKey, function () {
return Setting::pluck('value', 'key')->toArray();
});
}
public function allGrouped(): Collection
{
return Cache::rememberForever($this->cacheKey . '-grouped', function () {
return Setting::select('key', 'value', 'type', 'group')->get()->groupBy('group');
});
}
public function get(string $key, $default = null)
{
return $this->all()[$key] ?? $default;
}
public function set(string $key, $value): void
{
$this->setRecord($key, $value);
$this->clearCache();
}
public function updateSettings(array $data): void
{
foreach ($data as $key => $value) {
$this->setRecord($key, $value);
}
$this->clearCache();
}
public function forget(string $key): void
{
Setting::where('key', $key)->update(['value' => null]);
$this->clearCache();
}
private function clearCache(): void
{
Cache::forget($this->cacheKey);
Cache::forget($this->cacheKey . '-grouped');
}
private function setRecord(string $key, $value): void
{
if ($value instanceof UploadedFile) {
$this->removeFileIfExists($key);
$value = $this->uploadFile($value);
}
Setting::updateOrCreate(['key' => $key], ['value' => $value]);
}
private function uploadFile(UploadedFile $file): string
{
$file->store('settings', 'public');
return 'settings/' . $file->hashName();
}
private function removeFileIfExists(string $key): void
{
$setting = Setting::where('key', $key)->first();
if ($setting && $setting->value && Storage::exists($setting->value)) {
Storage::delete($setting->value);
}
}
}
<?php
use App\Http\Requests\SaveSettingRequest;
use App\Services\SettingService;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('settings', function () {
$settings = app(SettingService::class)->allGrouped();
return view('settings', compact('settings'));
});
Route::put('settings', function (SaveSettingRequest $request) {
app(SettingService::class)->updateSettings($request->validated());
return back()->with('success', 'Settings updated successfully.');
})->name('settings.update');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment