Scout APM is PHP application performance monitoring designed for developers. With tracing logic that ties issues back to the line of code causing them, you can pinpoint n+1 queries, memory leaks, and other abnormalities in real time so you can knock them out and get back to building a great product. Start your free 14-day trial today and get the performance insight you need in less than 4 minutes.

Store strongly typed settings in a Laravel app

Original – by Freek Van der Herten – 4 minute read

We have released a new package, called spatie/laravel-settings, that allows you to strongly type settings in a Laravel app. In this blog post, I'd like to introduce the package to you.

Why we created this package

At Spatie, we're currently building a huge application. This application will be multi-tenant, and each tenant has literally hundreds of settings that can be configured to tweak behavior.

The app's admins can update the settings, so it's only logical to store these settings in the database. We could have created a table called settings with a name and a value column and call it a day. But we want to store different types of settings: strings, integers, booleans, and objects such as dates. And it would also be nice if we could easily fetch these settings from anywhere in our app and get them in the right type.

Because we think our solution might help others, we decided to package it up.

Introducing spatie/laravel-settings

The package is built around settings classes. They are responsible for storing the settings. They all have public properties that can be used to fetch settings values. Each settings class extends from Spatie\LaravelSettings\Settings. They have a static method group that should return a string uniquely grouping settings.

use Spatie\LaravelSettings\Settings;

class GeneralSettings extends Settings
{
    public string $site_name;
    
    public bool $site_active;
    
    public static function group(): string
    {
        return 'general';
    }
}

To set up a class's initial values, you can use a SettingMigration to ensure that settings are set up correctly.

use Spatie\LaravelSettings\SettingsMigration;

class CreateGeneralSettings extends SettingsMigration
{
    public function up(): void
    {
       $this->migrator->add('general.site_name', 'Spatie');
       $this->migrator->add('general.site_active', true);
    }
}

To retrieve settings, you can resolve your settings class from the container. Here's an example where we use method injection.

class IndexController
{
    public function __invoke(GeneralSettings $settings){
        return view('index', [
            'site_name' => $settings->site_name,
        ]);
    }
}

Updating the settings can be done by setting the public properties and calling save on the settings class.

class SettingsController
{
    public function __invoke(GeneralSettings $settings, GeneralSettingsRequest $request){
        $settings->site_name = $request->input('site_name');
        $settings->site_active = $request->boolean('site_active');
        
        $settings->save();
        
        return redirect()->back();
    }
}

Of course, you could also just instanciate the class using the container:

function getName(): string{
	return app(GeneralSettings::class)->site_name
}

Using casts

Let's take a look at how objects can be used as settings. Here's an example where we are going to use a date object in a setting. Notice that there is now a casts function that returns the casters for a setting's property.

class DateSettings extends Settings
{
    public DateTime $birth_date;
    
    public static function group(): string
    {
        return 'date';
    }
    
    public static function casts(): array
    {
        return [
            'birth_date' => DateTimeInterfaceCast::class
        ];
    }
}

The DateTimeInterfaceCast ships with the package and can be used for properties with types like DateTime, DateTimeImmutable, Carbon and CarbonImmutable.

Writing your own casters is pretty easy. You just need to create a class that implement that Spatie\LaravelSettings\SettingsCasts\SettingsCast interface.

namespace Spatie\LaravelSettings\SettingsCasts;

interface SettingsCast
{
    /**
     * Will be used to when retrieving a value from the repository, and
     * inserting it into the settings class.
     */
    public function get($payload);

    /**
     * Will be used to when retrieving a value from the settings class, and
     * inserting it into the repository.
     */
    public function set($payload);
}

Here is an implementation of a caster that can be used with DTOs from the spatie/data-transfer-object package.

class DtoCast implements SettingsCast
{
    private string $type;

    public function __construct(?string $type)
    {
        $this->type = $type;
    }

    public function get($payload): DataTransferObject
    {
        return new $this->type($payload);
    }

    public function set($payload): array
    {
        return $payload->toArray();
    }
}

In closing

Using this package, we can safely use all of the configuration settings throughout our entire codebase. It's pretty nice that we also get autocompletion on the properties when a settings class is injected.

The package has many other features not mentioned in this blogpost: using Redis, encrypt properties,global casts, locking properties, ... and much more. To learn more about these features, head over to the readme of spatie/laravel-settings on GitHub.

This package has been coded up by my colleague Ruben, who, as always, did an excellent job.

Be sure also to take a look at our other packages and paid products as well. Pretty sure we got something for your next project.

Stay up to date with all things Laravel, PHP, and JavaScript.

Follow me on Twitter. I regularly tweet out programming tips, and what I myself have learned in ongoing projects.

Every two weeks I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

Webmentions

Manojkiran liked on 12th November 2020
Laravel 360 liked on 11th November 2020
Barnabas Kecskes liked on 11th November 2020
mojtabaa hn liked on 11th November 2020
Volkan Baskın liked on 10th November 2020
Bikash Khatri liked on 10th November 2020
Danijel K. liked on 10th November 2020
Hirusha Cooray liked on 10th November 2020
Fajar Rizky Hidayat liked on 10th November 2020
Jason Kenyon liked on 10th November 2020
Simpledev liked on 10th November 2020
Hardik Shah liked on 10th November 2020
Abhishek Jain liked on 10th November 2020
Amir liked on 10th November 2020
Yassen Sayed liked on 10th November 2020
Zaknafein liked on 10th November 2020
Roman Pronskiy liked on 10th November 2020
Oilmone liked on 10th November 2020
Ronald Rotteveel liked on 10th November 2020
Loki liked on 10th November 2020
yaslam liked on 10th November 2020
شريف مراد liked on 10th November 2020
Gaël Reyrol liked on 10th November 2020
Daniele Rosario liked on 10th November 2020
Yann Haefliger liked on 10th November 2020
Oluwatobi Samuel Omisakin liked on 10th November 2020
Shadow liked on 10th November 2020
Roberto B 🚀 liked on 10th November 2020
Christopher Pearse liked on 10th November 2020
Tauseef shah liked on 10th November 2020
Chew liked on 10th November 2020
Enzo Notario liked on 10th November 2020
Paco Orozco liked on 10th November 2020
Mohan Raj liked on 10th November 2020
newwave liked on 10th November 2020
Ernest Chiang liked on 10th November 2020
Le Huu Phuc liked on 10th November 2020
Paulo Freitas 🇧🇷 liked on 10th November 2020
Leonardo Prabangkoro liked on 10th November 2020
Luigi Cruz liked on 10th November 2020
Jānis Lācis liked on 10th November 2020
Julio Serpone liked on 10th November 2020
Jess Archer liked on 10th November 2020
TheSteed liked on 10th November 2020
Avenger liked on 10th November 2020
π liked on 10th November 2020
Daniel Ramirez liked on 10th November 2020
kanamastaka liked on 9th November 2020
Stephen Shead liked on 9th November 2020
Mahdi Jedari liked on 9th November 2020
Salman Zafar liked on 9th November 2020
#SARSMUSTEND ! Josiah Yahaya liked on 9th November 2020
Lennart Fischer liked on 9th November 2020
Web Developer MF liked on 9th November 2020
Vaggelis Yfantis liked on 9th November 2020
Alex Goller liked on 9th November 2020
Sadegh PM liked on 9th November 2020
Mu96afa liked on 9th November 2020
Nathan Snyder 🦇 liked on 9th November 2020
Abrar Ahmad liked on 9th November 2020
Omar Andrés Barbosa Ortiz liked on 9th November 2020
José Cage liked on 9th November 2020
Sid 🏄 retweeted on 9th November 2020
Sid 🏄 liked on 9th November 2020
apo ⚡️ liked on 9th November 2020
Dep Infect retweeted on 9th November 2020
BinaryKitten (Kat) retweeted on 9th November 2020
Faruk Nasir liked on 9th November 2020
BinaryKitten (Kat) liked on 9th November 2020
Sebastiaan Luca 🤖 liked on 9th November 2020
JOHN DOE liked on 9th November 2020
Dalton Sutton liked on 9th November 2020
Dep Infect liked on 9th November 2020
. liked on 9th November 2020
Songhua Hu liked on 9th November 2020
Ruben Van Assche liked on 9th November 2020
thao liked on 9th November 2020
Rafael Delgado liked on 9th November 2020
Owen Voke liked on 9th November 2020
Willan Correia liked on 9th November 2020
Tiagosimoes liked on 9th November 2020
Tiago Rodrigues liked on 9th November 2020
Robin Dirksen liked on 9th November 2020