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

Configurable Laravel Package

jwderoos/configurable

A Laravel package that adds configurable options to Eloquent models. Define options and values, then build variants with their own attributes and relations—ideal for configurable products, plans, or any model needing selectable configurations. Ships with traits, migrations, and relationships.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its migration/config:

composer require jwderoos/configurable
php artisan vendor:publish --tag="configurable-config"
php artisan vendor:publish --tag="configurable-migrations"
php artisan migrate

The first place to look is the Configurable trait (or InteractsWithConfiguration) applied to the model you want to make configurable. In 1.3.0 the recommended path is to declare configuration keys via PHP attributes on the model class:

use Jwderoos\Configurable\Attributes\Configurable;
use Jwderoos\Configurable\Attributes\Config;

#[Configurable]
class Team extends Model
{
    use \Jwderoos\Configurable\Concerns\Configurable;

    #[Config(key: 'display_name', default: 'My Team', cast: 'string')]
    #[Config(key: 'max_users', default: 5, cast: 'int')]
    #[Config(key: 'features', default: [], cast: 'array')]
    protected array $configurable = [];
}

First use case: read and write config values on the model instance.

$team->config('max_users');          // 5
$team->setConfig('max_users', 25);   // persisted
$team->config('features');           // cast to array

Run the published migration before touching config, otherwise reads fall through to defaults and writes throw a "table not found" error.

Implementation Patterns

Attribute-driven schema (new in 1.3.0). Attributes let you define keys, defaults, casts, and validation alongside the model instead of scattering them across a service provider or config array. Keep attribute declarations in one place per model — the package resolves them via reflection at boot, so they behave like a declarative schema.

Fallback chain. Config resolution typically goes: stored DB value → attribute default → package config default. Use defaults in the attribute for sensible bootstrap values and avoid null checks in callers.

Casting. cast: reuses Laravel's cast conventions (int, bool, array, json, datetime, custom casters). This keeps your model clean — no manual json_decode on $model->config('features').

Admin / settings screens. Because keys are declared, you can reflect over them to build a settings form dynamically:

$schema = Configurable::getSchema(Team::class);
// [['key' => 'max_users', 'default' => 5, 'cast' => 'int'], ...]

This is the biggest day-to-day win: new config options only require an attribute, not a new column or config file edit.

Environment overrides. Prefer keeping truly environment-specific values in config/configurable.php and only persisting user-editable settings in the DB. Mixing the two leads to confusion about which wins.

Testing. In tests, use the package's fresh-migration setup and reset cached config between tests (see gotchas). Assert via the public accessor rather than querying the config table directly — the storage layout is an implementation detail.

Gotchas and Tips

  • Caching. Configuration is typically cached per model instance / per request. After setConfig() in a loop or job, call the package's refresh/reset method (usually $model->refreshConfig() or $model->unsetConfigCache()) if you read the value again in the same request. Stale reads after a write are the #1 source of "my setting didn't save" bug reports.
  • Attribute autoloading. If new attributes don't seem to be picked up, confirm the model class is autoloaded (composer dump-autoload) and that the #[Configurable] class-level attribute is present — some versions only scan classes marked configurable.
  • Migration naming. The published migration may already exist from an earlier install (1.1.x). Diff the new migration against your existing one before running; don't blindly migrate in an existing project.
  • Type drift on existing rows. Changing a cast in an attribute doesn't rewrite stored values. If you switch string → array, backfill or you'll get cast exceptions on read.
  • Key hygiene. Keys are stored as strings; renaming a key in an attribute orphans the old row. Keep a rename/migration strategy for production data.
  • Package config vs app config. The package config file usually only controls table name, cache TTL, and model resolution — actual values live in the DB. Don't expect config('configurable') to hold model settings.
  • Extension points. Custom casts (implement Laravel's CastsAttributes) and a custom repository/storage driver are the intended hooks. Overriding the config table name is supported via the published config; overriding the storage backend is possible but brittle across minor versions.
  • Upgrading from 1.1.x. 1.3.0 is additive (attributes), so existing trait/property-based declarations keep working. Still run php artisan vendor:publish --tag="configurable-config" --force only after diffing — forced publish will overwrite table-name customizations.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle