Weave 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

Gears Laravel Package

konekt/gears

Konekt Gears adds feature flags to Laravel: define gears in code or database, toggle them globally or per model, and schedule time-based activation. Blade directives, middleware and Eloquent scopes let you gate features without redeploying.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up

composer require konekt/gears
php artisan vendor:publish --provider="Konekt\Gears\Providers\GearsServiceProvider"
php artisan migrate

Publishing gives you config/gears.php (backend selection, cache, table name) and the migration that creates the gears table. The table is a single generic store — one row per key per scope — so you do not add a column every time you add a setting.

Where to look first

  1. config/gears.php — confirms your backend (DatabaseBackend by default) and optional cache.
  2. Your AppServiceProvider::boot() — this is where you register every setting and preference your app knows about (see below). Registration is what makes a key "known" and gives it a default; it's the closest thing this package has to a schema.
  3. The HasPreferences trait — drop it onto your User model.

First real use case: a site-wide knob + a per-user toggle

In AppServiceProvider::boot():

use Konekt\Gears\Facades\Settings;
use Konekt\Gears\Facades\Preferences;

Settings::register('site.tagline', 'Built with Laravel');
Preferences::register('notifications.weekly_digest', true);

Then anywhere in the app:

echo Settings::get('site.tagline');                 // falls back to the registered default

Preferences::set('notifications.weekly_digest', false, $user);
$user->preferences->get('notifications.weekly_digest'); // false

That's the whole mental model: registration defines the key and its default; get/set/forget operate on it; preferences are the same but scoped to a model instance.

Implementation Patterns

Centralise registration in a dedicated provider

Don't scatter Settings::register() across feature providers — you'll lose track of what keys exist. A single SettingsServiceProvider (or a config/gears-registry.php file iterated in boot()) that lists every key and default is far easier to audit and is what you'll grep for six months later.

// app/Providers/GearRegistryServiceProvider.php
public function boot(): void
{
    Settings::register('billing.currency', 'USD');
    Settings::register('features.beta_ui', false);
    Settings::register('mail.from', config('mail.from.address'));

    Preferences::register('ui.theme', 'light');
    Preferences::register('ui.rows_per_page', 25);
    Preferences::register('email.marketing_opt_in', true);
}

Settings page pattern

For an admin settings screen, read the whole group, validate, then persist. Because values are loosely typed by design, cast on the way out and normalise on the way in:

// Controller
$currency = Settings::get('billing.currency');
$betaUi   = (bool) Settings::get('features.beta_ui');

// Save
Settings::set('billing.currency', $request->validated('currency'));
Settings::set('features.beta_ui', $request->boolean('beta_ui'));

For preferences, resolve the model once and hit the trait:

$user->preferences->set('ui.rows_per_page', (int) $request->input('rows'));

Reading in Blade / views

Settings are cheap reads (especially with the cache backend on), so calling Settings::get() directly in a view is acceptable. For preferences in a Blade layout, resolve from the authenticated user:

<html data-theme="{{ auth()->user()?->preferences->get('ui.theme') ?? 'light' }}">

Backend swapping for tests

The pluggable backend is the most useful extension point day-to-day. Point the config at the in-memory backend in phpunit.xml or a test service provider so tests stay isolated:

<env name="GEARS_BACKEND" value="memory"/>

Then each test starts with a clean slate and no DB round-trips. Check the config file for the exact env key — the backend is resolved from config('gears.backend').

Prefixing by domain

There's no built-in namespace/group concept beyond what you put in the key string. Adopt a convention early — billing.currency, ui.theme, mail.from — and stick to it. You'll want to bulk-export or clear a domain later, and a prefix is the only handle you'll have.

Caching wrapper

If you enable the cache layer in config, reads hit your cache store and writes invalidate automatically. This is the standard reason to enable it — settings are read far more than written, and Settings::get() in a hot path otherwise means an extra query per request.

Gotchas and Tips

Unregistered keys still work, which is a foot-gun. Settings::get('foo') on a key you never registered will typically return null rather than throwing. That means a typo silently reads as "off" or an empty string. Two defences: keep registration mandatory in code review, and if you need to distinguish "never set" from "set to null", check the raw backend rather than the facade. If the package exposes an existence check (e.g. has), use it in validation rules for any key coming from user input.

Preferences need the trait and a saved model. $user->preferences requires HasPreferences on the model and the model to have an ID — preferences are stored polymorphically against model_type/model_id, so an unsaved model has nowhere to write. Guard against auth()->user() being null in shared layouts.

Values are not strongly typed in storage. Booleans, integers, and nulls make a round trip through the generic store. Always cast explicit on read ((bool), (int)), and be aware (bool) "false" is true if something stringified on the way in. Normalise at write time.

Cache invalidation is per-key, not per-request. If you enabled caching, remember that a Settings::set() in one request invalidates the cached value for subsequent requests, not for the current in-request read (depending on implementation). Don't read a setting you just wrote and assume it's fresh in the same request — use the value you passed to set() instead. In tests, the cache layer is a common source of "flaky" behaviour; disable it or use the memory backend.

The single gears table is global; multi-tenancy is DIY. There is no tenant column built in. If you need per-tenant settings you either prefix keys (tenant.{id}.foo) or extend the backend. The latter is cleaner but means implementing the backend interface yourself — do the prefixing until you genuinely outgrow it.

Extending via a custom backend is the sanctioned extension point: implement the Backend contract (get/set/forget/all) and swap the class in config/gears.php. This is the right move if you want settings in Redis, in an external config service, or tenant-scoped. Prefer this over reaching into the DB table directly.

Debugging: start by inspecting the gears table directly — select * from gears where key = '...' tells you instantly whether the problem is storage or registration. If a value "doesn't stick", it's usually (a) unregistered key, (b) writing to the wrong scope (settings vs preferences), or (c) cache. If a preference is "shared between users", you almost certainly forgot to pass the model on set().

Facades vs injection. The Settings/Preferences facades are convenient but make testing and typing muddier. For application services, inject the underlying Gears service (or bind an interface to it) so you can swap the backend in tests without touching the container config.

Register defaults once, not on every boot with dynamic values. Settings::register('x', now()) bakes a timestamp into the "default" every deploy. Keep defaults static; use the storage value for anything that must change.

Watch the migration. If you already have a gears table from another package or a previous install, the published migration may conflict. Rename via config/gears.php before migrating, not after.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console