laravel-options stores application settings in a table and reaches them through option()
Global key-value store in the database
At a glance
- What is it?
- appstract/laravel-options is a small MIT licensed Laravel package that adds an options table and a global helper for reading and writing key-value settings, with a facade, Blade directives and an artisan command. It solves a narrow problem, and the interesting questions are about where the data lives and when the cache goes stale.
- Who is it for?
- Adopt laravel-options when your Laravel application needs a handful of global settings that many parts of the code read, and you would rather not add Redis or a config file to the deployment. Do not adopt it for high write volume, for per-user or per-tenant values, or as a cache: it is a table, and the README does not document any caching or invalidation behaviour.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 171 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One table, one helper, and no extra service
Laravel applications accumulate settings. A feature flag, a maintenance notice, the name of the current theme, a webhook URL that support keeps changing. Each of these has a natural home in config, but config is a file, and a value that changes at run time has nowhere obvious to live in a framework built around files and environment variables.
appstract/laravel-options answers that with the smallest possible piece of infrastructure. The package description is a global key-value store in the database, and the implementation is a single migration that creates an `options` table, plus a set of helpers and a facade.
The appeal is that there is no new service to run. There is no Redis to configure, no cache store to wire into the environment, no extra container in the deployment. The values live in the same database the application already uses, so a backup, a replica or a read replica behaves the way the rest of the data does, and a transaction that writes an option and a record is one transaction.
The audience is Laravel developers who have one of those values and do not want to introduce infrastructure for it. The package is published on Packagist under appstract/laravel-options, is MIT licensed, and is written in PHP with the conventional layout of a Laravel package: a src directory for the code, a database directory for migrations, and a tests directory.
It is a small package with a small job, and reading it as a general purpose configuration system would be a mistake.
The option helper covers reads, writes, defaults and deletes
The whole API is documented in one block, and it is worth reading closely because the shape of the setter is unusual:
// Get option
option('someKey');
// Get option, with a default fallback value if the key doesn't exist
option('someKey', 'Some default value if the key is not found');
// Set option
option(['someKey' => 'someValue']);
// Remove option
option()->remove('someKey');
// Check the option exists
option_exists('someKey');Reading takes a key and optionally a default, so a missing key is not an exception you have to catch but a value you supply at the call site. Removal is the only part that reads differently: you call option() with no arguments to get the underlying object and then call remove on it, which is a slightly awkward shape compared with the rest of the API.
The setter takes an array rather than a key and a value. That is a deliberate choice rather than a stylistic accident, and it means the natural way to write a value is as a key-value pair inside an array, which leaves the door open to writing several options in one call. The README does not document a bulk set, so treat that as an implication of the signature rather than a documented feature.
Two separate existence checks exist, and they are not the same thing. The global function option_exists('someKey') tells you whether a key is present, and the facade method Option::exists('someKey') does the same job through the class. That duplication is a small design smell, and it is one more reason to pick one style and stick to it in a given codebase.
The facade and the Blade directives reach the same store
The package also exposes a facade, and the README shows the import and the call:
use Option;
$check = Option::exists('someKey');The facade is the object-oriented entry point, useful when you want to type-hint or mock the store in tests, and the helper is the terse one. Both read the same table.
Then there are Blade directives, which is the part that will decide whether you like the package. The README gives three:
@option('someKey')
@option('someKey', 'Default value')
@optionExists('someKey')
// Code
@endifThe first two mirror the helper, and the third is an existence check wrapped as a block that pairs with the @endif directive. In a template, that means you can write the condition inline rather than reaching for a controller, which keeps a view readable for a simple conditional. It also means your Blade files contain calls that look like template syntax but are doing database reads, and the framework will execute them on every render.
That last point is the one to weigh. A template that reads a global option on each render turns a cheap lookup into a query per view, and this package is a table, not a cache. Whether that matters depends entirely on how often the view is rendered and how heavy your pages are.
Setting a value from the console
There is one artisan command, and it exists for the case where a setting has to change on a running server without a deploy:
php artisan option:set {someKey} {someValue}The two placeholders are a key and a value, so a run would set one option to one value. This is the practical escape hatch for the global settings case: a support team can flip a maintenance notice or change a feature flag on production without a deployment, which is exactly the gap that makes people add a key-value store in the first place.
The README does not describe a matching command to read, list or delete options from the console. The documented commands are option:set, and the removal path is the option() helper with its remove method. That asymmetry is worth knowing about before you build a runbook around the console, because a script that sets values but cannot read them back is only half a tool.
For development, the package also gives you the standard test entry point, since the repository has a phpunit.xml and a tests directory:
$ composer testPublishing the migration is a required first step
Installation is a composer command:
composer require appstract/laravel-optionsAfter that the package needs its migration published, and the README is specific that for this package the only published file is a migration. The publish command names the service provider explicitly, `php artisan vendor:publish --provider="Appstract\Options\OptionsServiceProvider"`, and the migration is then run like any other with `php artisan migrate`. After that there is an `options` table in your database.
The distinction between the service provider class and the package name is worth noting, because the provider class lives in the Appstract namespace while the composer package is appstract/laravel-options. Copying the provider string from the README rather than guessing is the safe move, and the same applies to the artisan command name, which is singular, option:set, rather than a plural or a hyphenated form.
Because the table is created by a migration in your project rather than shipped with the package, it also becomes part of your own schema history, which is the right arrangement for a table your application will read and write.
The release history shows a package that survived several major versions
The version history on Packagist tells you what kind of package this is. The first release, 1.0.0, dates from 2018-01-22. Version 6.0.0 followed on 2024-06-03, and 8.0.0 was published on 2026-05-06, which is also the date of the last push to the repository.
Three major versions over eight years, with a gap of six years between the first two majors, is a pattern that usually means one of two things: either the package is stable and rarely needs to break, or its maintainers move in large jumps when a Laravel major forces a change. The repository is not archived, and the last push was on 2026-05-06.
For an adopter the practical question is which constraint of Laravel each major bumps. A package that stores options in a table has to follow the framework's support policy for PHP and Laravel versions, and each major bump is where that policy is expressed. The README does not document upgrade notes between majors, so the release pages on Packagist and the repository history are where you would look before moving from one major to the next.
The licence is MIT, with the text in LICENSE.md, and the package comes from Appstract, described in the README as a small team from The Netherlands that creates open source tools for web development.
One small thing the README does not cover: the package does not describe caching, so a value read on every request is a database read on every request unless you add your own caching layer.
Where laravel-options is the wrong tool
The first wrong case is a high write volume or a hot path. This is a table, and the README documents no caching, no invalidation and no batching. A value read on every page render, or written on every request, will show up as database load, and the fix is to add a cache layer, at which point you should ask whether you need a table at all.
The second wrong case is per-user or per-tenant data. A global key-value store has no notion of scope. If your real requirement is a setting per user, per organisation or per environment, this table is the wrong shape and you want an Eloquent model with the relevant columns and indexes.
The third is configuration that must be readable without a database. During a deploy, or in a health check, or in a queue worker boot, you may need a value before the application can reach the database. A config file or an environment variable answers that question; a table cannot, because the answer lives in the thing you were trying to reach.
The alternative approach, for the same need, is to keep the value in config and treat runtime changes as the exception rather than the rule. Laravel's own configuration cache already exists to make config cheap to read. The difference in approach is that laravel-options is mutable at run time through a console command, while config is only changed by a deploy, and that is the trade you are making when you choose a table over a file: convenience of live changes in exchange for a database round trip on every read.
Editorial conclusion
Adopt laravel-options when your Laravel application needs a handful of global settings that many parts of the code read, and you would rather not add Redis or a config file to the deployment. Do not adopt it for high write volume, for per-user or per-tenant values, or as a cache: it is a table, and the README does not document any caching or invalidation behaviour. Before you rely on it, run php artisan option:set from the console and read the value back, check the migration in the database directory for the columns and index it actually creates, and note that the package is MIT licensed with 8.0.0 published on 2026-05-06 as the most recent release.
Frequently asked questions
What does the appstract/laravel-options package do?
It adds a global key-value store to a Laravel application by publishing a migration that creates an options table, then reading and writing values through the option helper, the Option facade, Blade directives or an artisan command.
How do I install laravel-options?
Run composer require appstract/laravel-options, then publish the package files with php artisan vendor:publish --provider="Appstract\Options\OptionsServiceProvider" and run php artisan migrate, after which an options table exists in your database.
How do I set an option value in laravel-options?
The option helper sets values with an array, for example option(['someKey' => 'someValue']), and from the console there is php artisan option:set {someKey} {someValue}. The facade exposes the same store for class based access.
Can I read options inside a Blade template?
Yes. The README documents the @option and @optionExists directives, so a template can read a value with a default and wrap conditional code in @optionExists, which pairs with @endif.
Does laravel-options cache values?
The README does not document any caching or invalidation behaviour, so a value read from Blade or from the helper is a database read each time. If you need cached reads you have to add that layer yourself.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/appstract-laravel-options)Was this useful?