<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lotyp</title>
    <description>The latest articles on DEV Community by lotyp (@lotyp).</description>
    <link>https://dev.to/lotyp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1336723%2F823ad9f8-6d4e-4d13-8b63-86d7ce00a9de.png</url>
      <title>DEV Community: lotyp</title>
      <link>https://dev.to/lotyp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lotyp"/>
    <language>en</language>
    <item>
      <title>Laravel Config Problem: Is It Time for a Revolution?</title>
      <dc:creator>lotyp</dc:creator>
      <pubDate>Wed, 17 Jul 2024 10:26:38 +0000</pubDate>
      <link>https://dev.to/lotyp/laravel-config-problem-is-it-time-for-a-revolution-159f</link>
      <guid>https://dev.to/lotyp/laravel-config-problem-is-it-time-for-a-revolution-159f</guid>
      <description>&lt;h2&gt;
  
  
  📄 Introduction
&lt;/h2&gt;

&lt;p&gt;While working on the Bridge package &lt;a href="https://github.com/wayofdev/laravel-symfony-serializer" rel="noopener noreferrer"&gt;Laravel Symfony Serializer&lt;/a&gt;, I ran into an unexpected limitation: &lt;strong&gt;Laravel configurations don't work with objects.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This issue made me look closely at how Laravel is built and rethink how we set up the framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  → In This Article
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Why caching configs matters&lt;/li&gt;
&lt;li&gt;The challenge of using objects in configs&lt;/li&gt;
&lt;li&gt;Existing workarounds and their drawbacks&lt;/li&gt;
&lt;li&gt;How others tackle this issue&lt;/li&gt;
&lt;li&gt;Applying the "Strategy" pattern to our problem&lt;/li&gt;
&lt;li&gt;Spiral Framework's approach and its advantages&lt;/li&gt;
&lt;li&gt;Potential improvements for Laravel configuration&lt;/li&gt;
&lt;li&gt;Impact on Developer Experience (DX)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll start by identifying the problem, then examine existing solutions, and finally propose a new approach.&lt;/p&gt;

&lt;p&gt;This journey won't just solve a specific issue – it'll give us fresh insights into framework development and evolution.&lt;/p&gt;

&lt;p&gt;As developers, we're always pushing our boundaries. That's why we're not just sticking to Laravel – we're exploring solutions from other frameworks to broaden our perspective.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Why Caching Configs Matters?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the object-in-config issue, let's tackle a key question: why does Laravel bother caching configs in the first place?&lt;/p&gt;

&lt;h3&gt;
  
  
  → The Config Challenge
&lt;/h3&gt;

&lt;p&gt;Every time someone visits a Laravel site, here's what happens behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Laravel reads all the config files&lt;/li&gt;
&lt;li&gt;It processes their contents&lt;/li&gt;
&lt;li&gt;Then it merges everything into one big array&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sounds simple, right? But there's a catch.&lt;/p&gt;

&lt;h3&gt;
  
  
  → The Performance Hit
&lt;/h3&gt;

&lt;p&gt;Let's break it down with a real example. Imagine your app has 20 config files. For each request, Laravel has to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open 20 files&lt;/li&gt;
&lt;li&gt;Read 20 files&lt;/li&gt;
&lt;li&gt;Close 20 files&lt;/li&gt;
&lt;li&gt;Process and merge all that data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's a lot of work, especially when your site gets busy. Each config file needs its own Input/Output (I/O) operation, and in traditional PHP, every new HTTP request kicks off this whole process again.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Caching to the Rescue
&lt;/h3&gt;

&lt;p&gt;Here's how Laravel's config caching solves this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It combines all configs into one array&lt;/li&gt;
&lt;li&gt;Saves this array as a single PHP file&lt;/li&gt;
&lt;li&gt;On future requests, it reads just this one file&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  → The Payoff
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed boost&lt;/strong&gt;: Significantly cuts down load times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fewer I/O operations&lt;/strong&gt;: Less strain on your file system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory efficiency&lt;/strong&gt;: Configs load once and stay loaded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better scalability&lt;/strong&gt;: Your app can handle more requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For live websites, these improvements make a big difference in performance and how well your app can scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  → The Object Dilemma
&lt;/h3&gt;

&lt;p&gt;Now, using objects in configs can be great. They offer perks like type safety and auto-completion in your code editor. But here's the rub: they don't play nice with Laravel's caching system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This clash between speeding things up and making configs more powerful is exactly what we're going to tackle in this article.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤯 The Object Caching Issue in Laravel Configs
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;php artisan config:cache&lt;/code&gt; command fails to cache objects in configuration files.&lt;/p&gt;

&lt;p&gt;For example, let's place several objects in the &lt;code&gt;config/serializer.php&lt;/code&gt; config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="s1"&gt;'encoders'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Encoder\JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Encoder\CsvEncoder&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Encoder\XmlEncoder&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attempting to cache this configuration results in an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;php&lt;/span&gt; &lt;span class="n"&gt;artisan&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;

   &lt;span class="nc"&gt;LogicException&lt;/span&gt;

  &lt;span class="nc"&gt;Your&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;serializable&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;

  &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;laravel&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;framework&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;Illuminate&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;Foundation&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;Console&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;ConfigCacheCommand&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;
     &lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;             &lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="nv"&gt;$configPath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;         &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;             &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$configPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;
  &lt;span class="err"&gt;➜&lt;/span&gt;  &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;             &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LogicException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Your configuration files are not serializable.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;         &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;
     &lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;         &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Configuration cached successfully.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="err"&gt;▕&lt;/span&gt;     &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="mi"&gt;1&lt;/span&gt;   &lt;span class="n"&gt;bootstrap&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;807&lt;/span&gt;
      &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Call to undefined method Symfony\Component\Serializer\Encoder\JsonEncoder::__set_state()"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="n"&gt;vendor&lt;/span&gt; &lt;span class="n"&gt;frames&lt;/span&gt;

  &lt;span class="mi"&gt;15&lt;/span&gt;  &lt;span class="n"&gt;artisan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;
      &lt;span class="nc"&gt;Illuminate\Foundation\Console\Kernel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Symfony\Component\Console\Input\ArgvInput&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Symfony\Component\Console\Output\ConsoleOutput&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unexpected, right? Let's unpack what's happening.&lt;/p&gt;

&lt;h3&gt;
  
  
  → How the Config Module Works in Laravel
&lt;/h3&gt;

&lt;p&gt;All configuration files are located in the &lt;code&gt;/config&lt;/code&gt; folder in the root directory of the project.&lt;/p&gt;

&lt;p&gt;These files contain settings for various aspects of the framework, such as the database, caching, sessions, and other components.&lt;/p&gt;

&lt;p&gt;When the application is initialized, Laravel loads all configuration files from this directory and combines them into a single configuration array.&lt;/p&gt;

&lt;p&gt;This array is made available through the global variable &lt;code&gt;$app['config']&lt;/code&gt; in the application container.&lt;/p&gt;

&lt;p&gt;Developers can access configurations from anywhere in the application in three ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using `Facade`&lt;/span&gt;
&lt;span class="nv"&gt;$timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.timezone'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Using helper function&lt;/span&gt;
&lt;span class="nv"&gt;$timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.timezone'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Directly over Container&lt;/span&gt;
&lt;span class="nv"&gt;$timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.timezone'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For package developers, configurations can be published to the application's &lt;code&gt;/config&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;php artisan vendor:publish &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"WayOfDev&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s2"&gt;erializer&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s2"&gt;ridge&lt;/span&gt;&lt;span class="se"&gt;\L&lt;/span&gt;&lt;span class="s2"&gt;aravel&lt;/span&gt;&lt;span class="se"&gt;\P&lt;/span&gt;&lt;span class="s2"&gt;roviders&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s2"&gt;erializerServiceProvider"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"config"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows users of package to easily customize its behavior in their applications.&lt;/p&gt;

&lt;p&gt;This system provides flexibility in managing application settings, but, as we will see later, it can create problems when trying to cache configurations with objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Configuration Caching Process
&lt;/h3&gt;

&lt;p&gt;By default, Laravel reads all configuration files from the &lt;code&gt;/config&lt;/code&gt; directory on every request.&lt;/p&gt;

&lt;p&gt;To optimize this process and improve performance, the framework provides a configuration caching mechanism.&lt;/p&gt;

&lt;p&gt;To create a cached version of all configurations, use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan config:cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads all files from the &lt;code&gt;/config&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Combines their contents into one large array&lt;/li&gt;
&lt;li&gt;Saves this array as a PHP file in &lt;code&gt;/bootstrap/cache/config.php&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting cache file looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;?php

&lt;span class="k"&gt;return &lt;/span&gt;array&lt;span class="o"&gt;(&lt;/span&gt;
    0 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'hashing'&lt;/span&gt;,
    9 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'broadcasting'&lt;/span&gt;,
    10 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'view'&lt;/span&gt;,
    &lt;span class="s1"&gt;'app'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; array&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;,
        &lt;span class="s1"&gt;'env'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'local'&lt;/span&gt;,
        &lt;span class="s1"&gt;'debug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;,
    // ...
        &lt;span class="s1"&gt;'maintenance'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; array&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'file'&lt;/span&gt;,
        &lt;span class="o"&gt;)&lt;/span&gt;,
        &lt;span class="s1"&gt;'providers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; array&lt;span class="o"&gt;(&lt;/span&gt;
            0 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Illuminate\\Auth\\AuthServiceProvider'&lt;/span&gt;,
            1 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Illuminate\\Broadcasting\\BroadcastServiceProvider'&lt;/span&gt;,
            // ...
        &lt;span class="o"&gt;)&lt;/span&gt;,
    &lt;span class="o"&gt;)&lt;/span&gt;,
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Once this cached file is created, Laravel will use it instead of reading separate configuration files on every request.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This results in significant performance improvements due to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Significant reduction in the number of I/O operations:&lt;/strong&gt; instead of reading many files, only one is read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced processing time:&lt;/strong&gt; There is no need to parse and merge separate configuration files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reducing the load on the file system:&lt;/strong&gt; especially noticeable with high traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's important to note that the benefits of configuration caching are most noticeable in traditional PHP applications, where each request starts a new PHP process.&lt;/p&gt;

&lt;p&gt;Long-running applications (such as those using &lt;a href="https://roadrunner.dev" rel="noopener noreferrer"&gt;RoadRunner&lt;/a&gt;) may not get such a significant performance boost from configuration caching, since they already keep the configuration in memory between requests.&lt;/p&gt;

&lt;p&gt;However, even for long-running applications, configuration caching can be useful during initial boot or process restart, allowing for faster initialization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Interesting fact:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cached config should not be stored in the repository, since it contains values from the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After caching, the &lt;code&gt;env()&lt;/code&gt; function becomes useless.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  → Technical Aspect of the Problem
&lt;/h3&gt;

&lt;p&gt;Now that we understand how configuration caching works in Laravel, let's look at why the problem occurs when trying to use objects instead of the usual arrays.&lt;/p&gt;

&lt;h4&gt;
  
  
  Root of the Problem: &lt;code&gt;var_export()&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Laravel serializes configurations using the PHP &lt;code&gt;var_export()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;In the context of Laravel, the absence of a &lt;code&gt;__set_state()&lt;/code&gt; method on objects used in configurations (in my case, &lt;code&gt;Symfony\Component\Serializer\Encoder\JsonEncoder&lt;/code&gt;) results in an error when attempting to cache.&lt;/p&gt;

&lt;p&gt;Here is the key code snippet from &lt;a href="https://github.com/laravel/framework/blob/11.x/src/Illuminate/Foundation/Console/ConfigCacheCommand.php#L62-L69" rel="noopener noreferrer"&gt;&lt;strong&gt;src/Illuminate/Foundation/Console/ConfigCacheCommand.php&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="nv"&gt;$configPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;laravel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCachedConfigPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$configPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;?php return '&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;var_export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;var_export()&lt;/code&gt; function works great with arrays and primitive data types, which are used in Laravel configurations by default.&lt;/p&gt;

&lt;p&gt;However, there are difficulties in processing objects.&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;var_export()&lt;/code&gt; function encounters an object, it attempts to call the static method &lt;code&gt;__set_state()&lt;/code&gt; on that object's class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Tries to call Config::__set_state()&lt;/span&gt;
&lt;span class="c1"&gt;// and throws an error if the method is not defined&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;var_export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;__set_state()&lt;/code&gt; method is not implemented in the object class (which is often the case), an error occurs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Error Breakdown
&lt;/h4&gt;

&lt;p&gt;For example, when trying to cache the configuration of my &lt;a href="https://github.com/wayofdev/laravel-symfony-serializer" rel="noopener noreferrer"&gt;Laravel Symfony Serializer&lt;/a&gt; package, which uses Symfony objects for serialization, the following error occurred:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Call to undefined method Symfony\Component\Serializer\Encoder\JsonEncoder::__set_state()"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error can be confusing because the &lt;code&gt;Your configuration files are not serializable&lt;/code&gt; message does not directly indicate a problem with the objects.&lt;/p&gt;

&lt;p&gt;It is important to note that this problem often does not appear at the development stage, but during deployment to production or staging environments, where the &lt;code&gt;php artisan config:cache&lt;/code&gt; command is typically used to optimize performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why is This Important to Solve
&lt;/h4&gt;

&lt;p&gt;While arrays work well for basic configurations, using objects in configurations could provide a number of benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type safety&lt;/li&gt;
&lt;li&gt;Improved IDE support (autocompletion, tooltips)&lt;/li&gt;
&lt;li&gt;More structured and object-oriented approach to configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solving these complexities would allow developers to create more flexible and expressive configurations while still maintaining the benefits of Laravel caching.&lt;/p&gt;

&lt;p&gt;In the following sections, we'll look at different approaches to solving this problem, which will allow objects to be used in configurations without losing caching capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  💭 Hint from Elliot Derhay
&lt;/h2&gt;

&lt;p&gt;While googling for solutions, I found an article by &lt;a href="https://elliotderhay.com/blog/caching-laravel-configs-that-use-objects" rel="noopener noreferrer"&gt;Elliot Derhay&lt;/a&gt; where he encountered a similar problem in the package &lt;a href="https://github.com/spatie/laravel-markdown" rel="noopener noreferrer"&gt;spatie/laravel-markdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He proposed a solution by adding Trait &lt;code&gt;Resumeable&lt;/code&gt; to classes used as objects in configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;Resumeable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__set_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$state_array&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;static&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;static&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$state_array&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$prop&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;property_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  → Why is this not a Solution?
&lt;/h3&gt;

&lt;p&gt;While this solution may work in some cases, it has a number of problems in the context of my Symfony Bridge package:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Increasing support complexity:&lt;/strong&gt; Adding Trait to each class would require changing a lot of code, making future support difficult.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Violation of clean code:&lt;/strong&gt; Classes will contain logic that is not related to their main purpose, which is contrary to the principles of clean code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility Issues:&lt;/strong&gt; Making changes to a third-party library such as Symfony may cause problems with updates to that library.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main reason is that we cannot modify other people's packages, and this solution is local, not scalable.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Working Around the Problem Instead of Solving It
&lt;/h3&gt;

&lt;p&gt;What surprised me was that Spatie, having a lot of influence in the Laravel Community, decided to simply work around the problem instead of solving it in the Laravel core itself.&lt;/p&gt;

&lt;p&gt;Their solution was to use only &lt;code&gt;FQCN&lt;/code&gt; (Fully Qualified Class Names) instead of objects in configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&amp;lt;?php
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;return [
&lt;/span&gt;    // ...
&lt;span class="err"&gt;
&lt;/span&gt;    'block_renderers' =&amp;gt; [
&lt;span class="gd"&gt;-       // ['renderer' =&amp;gt; new MyCustomCodeRenderer(), 'priority' =&amp;gt; 0]
&lt;/span&gt;&lt;span class="gi"&gt;+       // ['renderer' =&amp;gt; MyCustomCodeRenderer::class, 'priority' =&amp;gt; 0]
&lt;/span&gt;    ],
&lt;span class="err"&gt;
&lt;/span&gt;    // ...
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is not flexible and does not provide for supplying optional parameters to the constructor if such are needed. It only works around the problem, not solves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Reflections on the Spatie Approach
&lt;/h3&gt;

&lt;p&gt;Given Spatie's many contributions to the Laravel community, their decision leaves room for constructive discussion and perhaps re-evaluation of approaches to solving similar problems in the future.&lt;/p&gt;

&lt;p&gt;It would be interesting to hear from Spatie and other leading community members on this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  👀 Hexium got Around This Problem in Their Own Way
&lt;/h2&gt;

&lt;p&gt;When I was working on upgrading my package &lt;a href="https://github.com/wayofdev/laravel-symfony-serializer" rel="noopener noreferrer"&gt;Laravel Symfony Serializer&lt;/a&gt; from Laravel 10.x to laravel 11.x, I decided to see what new things others had come up with.&lt;/p&gt;

&lt;p&gt;My search led me to another package &lt;a href="https://github.com/hexium-agency/symfony-serializer-for-laravel" rel="noopener noreferrer"&gt;Hexium Agency's Symfony Serializer for Laravel&lt;/a&gt; which also adds Symfony Serializer support to Laravel.&lt;/p&gt;

&lt;p&gt;Let's take a look at how they approached the problem with objects in configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Analysis of the Hexium Approach
&lt;/h3&gt;

&lt;p&gt;In config file &lt;a href="https://github.com/hexium-agency/symfony-serializer-for-laravel/blob/main/config/symfony-serializer.php" rel="noopener noreferrer"&gt;config/symfony-serializer.php&lt;/a&gt; of the package we see that they use string aliases instead of objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'normalizers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'serializer.normalizer.datetimezone'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;915&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'serializer.normalizer.dateinterval'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;915&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'serializer.normalizer.datetime'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;910&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'serializer.normalizer.json_serializable'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;950&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'encoders'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'serializer.encoder.xml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'serializer.encoder.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When looking at the Service Provider, it becomes clear that they strictly prescribe the creation of these services:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hexium-agency/symfony-serializer-for-laravel/blob/main/src/SymfonySerializerForLaravelServiceProvider.php" rel="noopener noreferrer"&gt;SymfonySerializerForLaravelServiceProvider.php&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SymfonySerializerForLaravelServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PackageServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;registeringPackage&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;

        &lt;span class="c1"&gt;// Encoders&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.xml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XmlEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.xml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.yaml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;YamlEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.yaml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.csv'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CsvEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.csv'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  → Workaround Instead of Solution
&lt;/h3&gt;

&lt;p&gt;Having considered the approach of this package, we can highlight the following features:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ability to override settings values using those aliases that are already defined in the package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To add new objects to the config, you need to create a binding in the Service Provider&lt;/li&gt;
&lt;li&gt;The approach violates the principles of configuration flexibility and expandability&lt;/li&gt;
&lt;li&gt;The configuration becomes rigid, requiring changes to the Service Provider to add new settings&lt;/li&gt;
&lt;li&gt;Using aliases instead of objects deprives configuration of benefits such as IDE autocompletion and type safety&lt;/li&gt;
&lt;li&gt;Package customization involves making changes directly to the code of the Hexium package itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach was most likely caused by the inability to use objects directly in Laravel configuration files.&lt;/p&gt;

&lt;p&gt;The authors of the package decided to work around this problem, but at the same time sacrificed flexibility, convenience and extensibility of the configuration.&lt;/p&gt;

&lt;p&gt;Thus, this solution cannot be called a complete one, but rather a workaround that has significant limitations.&lt;/p&gt;

&lt;h2&gt;
  
  
  😬 My intermediate attempt to solve the problem
&lt;/h2&gt;

&lt;p&gt;After analyzing existing solutions such as the Spatie and Hexium approaches, which were essentially workarounds for the problem, I decided to implement my own approach based on the Strategy pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Solution Strategy in The Strategy Pattern!
&lt;/h3&gt;

&lt;p&gt;I created interfaces for Registration Strategies encoders and normalizers.&lt;/p&gt;

&lt;p&gt;This allowed for flexibility and expandability of the configuration without violating the SOLID principles.&lt;/p&gt;

&lt;p&gt;For example, here is the interface for the Encoders Registration Strategy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;WayOfDev\Serializer\Contracts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder\DecoderInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder\EncoderInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EncoderRegistrationStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @return iterable&amp;lt;array{encoder: EncoderInterface|DecoderInterface}&amp;gt;
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;encoders&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;iterable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And its implementation to register default encoders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;WayOfDev\Serializer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder\DecoderInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder\EncoderInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DefaultEncoderRegistrationStrategy&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Contracts\EncoderRegistrationStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @return iterable&amp;lt;array{encoder: EncoderInterface|DecoderInterface}&amp;gt;
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;encoders&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;iterable&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'encoder'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Encoder\JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'encoder'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Encoder\CsvEncoder&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'encoder'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Encoder\XmlEncoder&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config file for my laravel-symfony-serializer package &lt;code&gt;config/serializer.php&lt;/code&gt; now looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;WayOfDev\Serializer\DefaultEncoderRegistrationStrategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;WayOfDev\Serializer\DefaultNormalizerRegistrationStrategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="cm"&gt;/*
     * Allows you to specify the strategy class for registering your normalizers.
     * Default is 'WayOfDev\Serializer\DefaultNormalizerRegistrationStrategy'.
     */&lt;/span&gt;
    &lt;span class="s1"&gt;'normalizerRegistrationStrategy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;DefaultNormalizerRegistrationStrategy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="cm"&gt;/*
     * Allows you to register your custom encoders.
     * Default encoders are registered in src/DefaultEncoderRegistrationStrategy.php.
     *
     * Default encoders include:
     *      JsonEncoder,
     *      CsvEncoder,
     *      XmlEncoder,
     *      YamlEncoder.
     *
     * You can replace the default encoders with your custom ones by implementing
     * your own registration strategy and defining it here.
     */&lt;/span&gt;
    &lt;span class="s1"&gt;'encoderRegistrationStrategy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;DefaultEncoderRegistrationStrategy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registration happens here: &lt;a href="https://github.com/wayofdev/laravel-symfony-serializer/blob/master/src/Bridge/Laravel/Providers/SerializerServiceProvider.php" rel="noopener noreferrer"&gt;SerializerServiceProvider.php&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;WayOfDev\Serializer\Contracts\EncoderRegistrationStrategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;WayOfDev\Serializer\Contracts\EncoderRegistryInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;WayOfDev\Serializer\Contracts\ConfigRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SerializerServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;registerEncoderRegistry&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EncoderRegistrationStrategy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Application&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;EncoderRegistrationStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cd"&gt;/** @var Config $config */&lt;/span&gt;
            &lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConfigRepository&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nv"&gt;$strategyFQCN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;encoderRegistrationStrategy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$strategyFQCN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EncoderRegistryInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Application&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;EncoderRegistryInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cd"&gt;/** @var EncoderRegistrationStrategy $strategy */&lt;/span&gt;
            &lt;span class="nv"&gt;$strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EncoderRegistrationStrategy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EncoderRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$strategy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  → What's The Difference?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;This approach has a number of advantages&lt;/strong&gt; compared to the previously discussed options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility:&lt;/strong&gt; Users can easily replace the standard strategy with their own without changing the core code of the package.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility:&lt;/strong&gt; Adding new encoders or normalizers does not require changing the core code of the laravel-symfony-serializer package.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation:&lt;/strong&gt; The logic for creating and configuring encoders and normalizers is encapsulated in separate classes, which improves code organization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adherence to SOLID principles:&lt;/strong&gt; This approach better adheres to the open/closed principle, allowing functionality to be extended without changing existing code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;However, this approach also has some disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Challenge for the user:&lt;/strong&gt; To make changes, the user needs to create their registration strategy as a separate class and store it in their project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More Code:&lt;/strong&gt; This approach requires writing more code than simply defining the array in a config file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Potential DX Complication:&lt;/strong&gt; From a Developer Experience (DX) perspective, this approach may seem more complex to new users of the package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although this intermediate approach is not a perfect solution, it provides a more flexible and extensible solution than previous options and better aligns with object-oriented programming principles. However, as we will see later, there is a more complete solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 What's Wrong With All These Approaches?
&lt;/h2&gt;

&lt;p&gt;After looking at the various work-arounds around the object problem in Laravel configurations, it becomes clear that each approach has its limitations and does not solve core problem. Let's analyze them in more detail:&lt;/p&gt;

&lt;h3&gt;
  
  
  → Using FQCN (Fully Qualified Class Names)
&lt;/h3&gt;

&lt;p&gt;The approach proposed by Spatie and Elliot Derhay in package &lt;a href="https://github.com/spatie/laravel-markdown" rel="noopener noreferrer"&gt;spatie/laravel-markdown&lt;/a&gt;, although it solves the problem of configuration serialization, significantly limits configuration flexibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'block_renderers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'renderer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;MyCustomCodeRenderer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach does not allow parameters to be passed to the class constructor, which can be critical for complex objects with custom behavior.&lt;/p&gt;

&lt;p&gt;Developers have to find workarounds to initialize objects with the desired parameters, which complicates the code and reduces its readability.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Hardcoding of Dependencies in The Service Provider
&lt;/h3&gt;

&lt;p&gt;The Hexium approach, where dependencies are hardcoded in the Service Provider, violates the SOLID (Open/Closed) principle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serializer.encoder.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach makes it difficult to extend and modify behavior without changing the package source code.&lt;/p&gt;

&lt;p&gt;If a user needs to change the &lt;code&gt;JsonEncoder&lt;/code&gt; configuration, they will have to redefine the entire Service Provider, which can lead to code duplication and become more difficult to maintain with package updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Lack of Dependency Injection Support
&lt;/h3&gt;

&lt;p&gt;All considered approaches do not take into account the possibility of using Dependency Injection in object constructors.&lt;/p&gt;

&lt;p&gt;For example, if we have a class with dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCustomCodeRenderer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;LoggerInterface&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of the approaches discussed make it easy to pass &lt;code&gt;LoggerInterface&lt;/code&gt; when creating an object via configuration. This forces developers to find workarounds or complicate the application architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Lack of a Unified Approach in The Laravel Ecosystem
&lt;/h3&gt;

&lt;p&gt;The lack of a standard solution for working with objects in Laravel configurations results in different packages taking different approaches.&lt;/p&gt;

&lt;p&gt;This makes it difficult to understand and integrate different packages in one project.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡️ How It Is Implemented in The Spiral Framework
&lt;/h2&gt;

&lt;h3&gt;
  
  
  → About The Spiral Framework
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://spiral.dev" rel="noopener noreferrer"&gt;Spiral Framework&lt;/a&gt; is a modern PHP framework for developing enterprise applications that supports: high-performance request processing using RoadRunner, an efficient queuing system, Temporal workflows, WebSockets, gRPC and microservice architecture.&lt;/p&gt;

&lt;p&gt;It is designed with an emphasis on intuitiveness and ease of use, offering a Developer Experience similar to Laravel and Symfony.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Container Auto-Wiring
&lt;/h3&gt;

&lt;p&gt;Spiral attempts to hide container implementation and configuration from your application's domain layer by providing rich auto-wiring functionality that allows you to delegate object creation to the container.&lt;/p&gt;

&lt;p&gt;This makes managing dependencies in your application much easier.&lt;/p&gt;

&lt;p&gt;When the container attempts to resolve &lt;code&gt;Autowire&lt;/code&gt;, it automatically instantiates the class specified in the first argument and passes additional parameters if specified in the second argument.&lt;/p&gt;

&lt;p&gt;The key element of this approach is the &lt;code&gt;Spiral\Core\Container\Autowire&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Let's look at the implementation of the class in more detail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Spiral\Core\Container&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Autowire&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;?object&lt;/span&gt; &lt;span class="nv"&gt;$target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__set_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$anArray&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;static&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$anArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'alias'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$anArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'parameters'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FactoryInterface&lt;/span&gt; &lt;span class="nv"&gt;$factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$factory&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;\array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This class allows:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Specify the class or alias of the service that needs to be created (&lt;code&gt;$alias&lt;/code&gt;), for Laravel developers this will be equivalent to the &lt;code&gt;$abstract&lt;/code&gt; parameter in the &lt;code&gt;app()→make()&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;Pass parameters to the constructor of this class (&lt;code&gt;$parameters&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Postpone the creation of the object until the moment when it is really needed (&lt;code&gt;resolve&lt;/code&gt; method).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pay attention to the &lt;code&gt;__set_state&lt;/code&gt; method. It solves the problem we had previously when using &lt;code&gt;var_export()&lt;/code&gt; to cache configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Using Autowire in Configurations
&lt;/h3&gt;

&lt;p&gt;Now let's look at how this can help us solve the problem with objects in Laravel configurations.&lt;/p&gt;

&lt;p&gt;Let's remember the example of a class from Spatie, where the constructor had an external dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCustomCodeRenderer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;LoggerInterface&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the Spiral approach with &lt;code&gt;Autowire&lt;/code&gt;, we could configure this class in our config as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="s1"&gt;'block_renderers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Autowire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyCustomCodeRenderer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach has several advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can use objects in configurations without worrying about serialization issues.&lt;/li&gt;
&lt;li&gt;External dependencies (for example, &lt;code&gt;LoggerInterface&lt;/code&gt;) will be automatically resolved by the container.&lt;/li&gt;
&lt;li&gt;We can override only those parameters that we need (in this case, &lt;code&gt;priority&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The creation of an object is postponed until the moment when it is actually needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach allows us to achieve a balance between configuration flexibility and performance, solving the problems we encountered previously.&lt;/p&gt;

&lt;p&gt;In the next section, we'll look at how we could adapt this approach for use in Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 How can this be solved in Laravel Framework
&lt;/h2&gt;

&lt;p&gt;After analyzing existing approaches and studying the solution described above, we can propose a more elegant solution for Laravel that will allow objects to be used in configurations while maintaining caching capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Making the AutoWire as a Wrapper Class
&lt;/h3&gt;

&lt;p&gt;Inspired by the Spiral Framework solution, we can create an &lt;code&gt;AutoWire&lt;/code&gt; class that will serve as a wrapper for objects in configurations. This class will implement the magic method &lt;code&gt;__set_state()&lt;/code&gt;, allowing it to be used with &lt;code&gt;var_export()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the concept for implementing the &lt;code&gt;AutoWire&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Support&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Container\BindingResolutionException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AutoWire&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Create a new AutoWire instance.
     *
     * @param array&amp;lt;string, mixed&amp;gt; $parameters
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$abstract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Magic method for var_export().
     *
     * @param array{abstract: string, parameters: array&amp;lt;string, mixed&amp;gt;} $properties
     *
     * @return static
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__set_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$properties&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'abstract'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'parameters'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Resolve the AutoWire instance using the container.
     *
     * @throws BindingResolutionException
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;abstract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class performs the following functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stores the name of the class (&lt;code&gt;$abstract&lt;/code&gt;) and the parameters for creating it (&lt;code&gt;$parameters&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Implements the &lt;code&gt;__set_state()&lt;/code&gt; method, which allows you to recreate the object after serialization.&lt;/li&gt;
&lt;li&gt;Provides the &lt;code&gt;resolve()&lt;/code&gt; method, which uses the Laravel container to lazily load an object.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  → Using AutoWire in Our Config Files
&lt;/h3&gt;

&lt;p&gt;Now we can change our configuration file &lt;code&gt;config/serializer.php&lt;/code&gt; using &lt;code&gt;AutoWire&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Support\AutoWire&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Serializer\Encoder\JsonDecode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="s1"&gt;'encoders'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AutoWire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;abstract&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Encoder\JsonEncoder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'defaultContext'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="nc"&gt;JsonDecode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ASSOCIATIVE&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AutoWire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Encoder\CsvEncoder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;Encoder\XmlEncoder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Encoder\YamlEncoder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Service Provider will now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SerializerServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;registerEncoderRegistry&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EncoderRegistryInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Application&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;EncoderRegistryInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cd"&gt;/** @var Config $config */&lt;/span&gt;
            &lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConfigRepository&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EncoderRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;encoders&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;AutoWire&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;EncoderInterface&lt;/span&gt; &lt;span class="nv"&gt;$encoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$encoder&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;EncoderInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$encoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$encoder&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;AutoWire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$encoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$encoder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach allows us to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use objects in configurations&lt;/li&gt;
&lt;li&gt;Pass parameters to object constructors.&lt;/li&gt;
&lt;li&gt;Maintain the ability to cache configurations.&lt;/li&gt;
&lt;li&gt;Use standard FQCN strings if there is no need for objects&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  → Running
&lt;/h3&gt;

&lt;p&gt;After making the changes, we can try running the configuration caching command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/app &lt;span class="nv"&gt;$ &lt;/span&gt;php artisan config:cache

   INFO  Configuration cached successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the command is executed successfully, without errors.&lt;/p&gt;

&lt;p&gt;If we look at the contents of the cached configuration file &lt;code&gt;bootstrap/cache/config.php&lt;/code&gt;, we see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;?php

&lt;span class="nb"&gt;declare&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    // ...
    &lt;span class="s1"&gt;'serializer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
        // ...
        &lt;span class="s1"&gt;'encoders'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
            0 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Support&lt;span class="se"&gt;\A&lt;/span&gt;utoWire::__set_state&lt;span class="o"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'abstract'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Symfony\\Component\\Serializer\\Encoder\\JsonEncoder'&lt;/span&gt;,
                &lt;span class="s1"&gt;'parameters'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'defaultContext'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                        &lt;span class="s1"&gt;'json_decode_associative'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;,
                    &lt;span class="o"&gt;]&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="o"&gt;])&lt;/span&gt;,
            1 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Support&lt;span class="se"&gt;\A&lt;/span&gt;utoWire::__set_state&lt;span class="o"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'abstract'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Symfony\\Component\\Serializer\\Encoder\\CsvEncoder'&lt;/span&gt;,
                &lt;span class="s1"&gt;'parameters'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="o"&gt;])&lt;/span&gt;,
            2 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Support&lt;span class="se"&gt;\A&lt;/span&gt;utoWire::__set_state&lt;span class="o"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'abstract'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Symfony\\Component\\Serializer\\Encoder\\XmlEncoder'&lt;/span&gt;,
                &lt;span class="s1"&gt;'parameters'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="o"&gt;])&lt;/span&gt;,
        &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="o"&gt;]&lt;/span&gt;,
&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  → How Does It Work?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;When caching configurations, Laravel uses &lt;code&gt;var_export()&lt;/code&gt; to serialize the configuration array.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;AutoWire&lt;/code&gt; objects, the &lt;code&gt;__set_state()&lt;/code&gt; method is called, which saves information about the class and its parameters.&lt;/li&gt;
&lt;li&gt;When loading a cached configuration, &lt;code&gt;AutoWire&lt;/code&gt; objects are restored using &lt;code&gt;__set_state()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When a real object is required, the &lt;code&gt;resolve()&lt;/code&gt; method is called, which uses the Laravel container to create an object with the required parameters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When does &lt;code&gt;resolve()&lt;/code&gt; run?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;resolve()&lt;/code&gt; method is called when the dependency container attempts to instantiate an object. This happens "lazyly", that is, only when the object is actually needed.&lt;/p&gt;

&lt;p&gt;This approach allows us to use objects in configurations while still being cacheable. It also provides flexibility in configuring objects by allowing parameters to be passed to their constructors.&lt;/p&gt;

&lt;h2&gt;
  
  
  🗝️ Going even further, what if we use DTO in the configuration?
&lt;/h2&gt;

&lt;p&gt;Everything we've covered so far could be solved without making any changes to the Laravel core. But what if we go further and think about more radical changes?&lt;/p&gt;

&lt;h3&gt;
  
  
  → Simple Example: Authentication Configuration
&lt;/h3&gt;

&lt;p&gt;Have you ever experienced difficulty configuring Laravel? How often do you have to look at the documentation when you need, for example, to configure an authentication driver?&lt;/p&gt;

&lt;p&gt;Let's do a thought experiment: look at this piece of the config and try to remember what keys should be in the &lt;code&gt;auth.php&lt;/code&gt; config without looking at the documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="s1"&gt;'passwords'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'users'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'???'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'???'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AUTH_PASSWORD_RESET_TOKEN_TABLE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'password_reset_tokens'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'???'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'???'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am sure many of you will not be able to remember even the first of the keys. The problem is that arrays do not support autocompletion. This is only possible when using additional paid plugins for the IDE.&lt;/p&gt;

&lt;p&gt;Now let's imagine that instead of an array we use an object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Support\Auth\Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;ReflectionClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;ReflectionException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;array_key_exists&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PasswordConfig&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @param non-empty-string $provider
     * @param non-empty-string $table
     * @param int&amp;lt;0, max&amp;gt; $expire
     * @param int&amp;lt;0, max&amp;gt; $throttle
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$expire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$throttle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param array&amp;lt;string, mixed&amp;gt; $properties
     *
     * @throws ReflectionException
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__set_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$properties&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReflectionClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ref&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getConstructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getParameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$parameter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$parameter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nv"&gt;$arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_key_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$parameter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getDefaultValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's change the &lt;code&gt;auth.php&lt;/code&gt; config itself in empty Laravel Application and see how it will be now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Support\Auth\Config\PasswordConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="s1"&gt;'passwords'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'users'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PasswordConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'password_reset_tokens'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expire&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;throttle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are using PHP 8.0 Named Arguments, but even without using them everything has become much simpler and clearer: we just need to look at the parameters of the &lt;code&gt;PasswordConfig&lt;/code&gt; constructor. And if we use PHPStorm or similar IDEs, then the tooltips will be automatically available out of the box.&lt;/p&gt;

&lt;p&gt;Also, such a class already contains the &lt;code&gt;__set_state&lt;/code&gt; function which will allow us to use the existing Laravel &lt;code&gt;php artisan config:cache&lt;/code&gt; mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Complex Example: Database Configuration
&lt;/h3&gt;

&lt;p&gt;But let's dig deeper and look at a more complex example - database configuration.&lt;/p&gt;

&lt;p&gt;I use this approach in my &lt;a href="https://github.com/wayofdev/laravel-cycle-orm-adapter" rel="noopener noreferrer"&gt;wayofdev/laravel-cycle-orm-adapter&lt;/a&gt; package. The configuration file can be viewed here: &lt;a href="https://github.com/wayofdev/laravel-cycle-orm-adapter/blob/master/config/cycle.php" rel="noopener noreferrer"&gt;config/cycle.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a look at the default &lt;code&gt;database.php&lt;/code&gt; configuration file in Laravel.&lt;/p&gt;

&lt;p&gt;What it looks like now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="s1"&gt;'connections'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'memory'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sqlite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_URL'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'database'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;':memory:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'foreign_key_constraints'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_FOREIGN_KEYS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="s1"&gt;'sqlite'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sqlite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_URL'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'database'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_DATABASE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;database_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'database.sqlite'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'foreign_key_constraints'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_FOREIGN_KEYS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="s1"&gt;'mysql'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'mysql'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_URL'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'port'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'3306'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'database'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_DATABASE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_USERNAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'unix_socket'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_SOCKET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'charset'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_CHARSET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'utf8mb4'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'collation'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_COLLATION'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'utf8mb4_unicode_ci'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'prefix_indexes'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'strict'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'engine'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'options'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;extension_loaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pdo_mysql'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MYSQL_ATTR_SSL_CA&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MYSQL_ATTR_SSL_CA'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="c1"&gt;// ...    &lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this might look like if we use a &lt;code&gt;DTO&lt;/code&gt; for configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Support\Database\Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="s1"&gt;'connections'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'memory'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config\SQLiteDriverConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config\SQLite\MemoryConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'sqlite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;

          &lt;span class="s1"&gt;'sqlite'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config\SQLiteDriverConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config\SQLite\FileConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_URL'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_DATABASE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;database_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'database.sqlite'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'sqlite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;foreign_key_constraints&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_FOREIGN_KEYS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="s1"&gt;'mysql'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config\MySQLDriverConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config\MySQL\TcpConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_URL'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_DATABASE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_USERNAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;unix_socket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_SOCKET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'mysql'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;extension_loaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pdo_mysql'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MYSQL_ATTR_SSL_CA&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MYSQL_ATTR_SSL_CA'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
            &lt;span class="n"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_CHARSET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'utf8mb4'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;collation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_COLLATION'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'utf8mb4_unicode_ci'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;prefix_indexes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="c1"&gt;// ...    &lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach provides all the benefits of using DTOs and PHP 8.0 Named Arguments that we touched on earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  → What Conclusions Can We Make?
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;AutoWire&lt;/code&gt; class together with configuration DTOs provides a number of significant advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved structure and typing:&lt;/strong&gt; DTOs provide clear configuration structure, and strong typing helps prevent compile-time errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usability:&lt;/strong&gt; Named Arguments in PHP 8.0 make configuration more readable and less prone to typing errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE Support:&lt;/strong&gt; Object-oriented approach provides better support for autocompletion and tooltips in modern IDEs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure-level validation:&lt;/strong&gt; DTOs allow you to build basic validation directly into the object structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Compatibility:&lt;/strong&gt; The &lt;code&gt;__set_state()&lt;/code&gt; method in DTO and &lt;code&gt;AutoWire&lt;/code&gt; provides compatibility with Laravel's configuration caching mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Documentation&lt;/strong&gt;: The DTO structure serves as self-documenting code, making configuration easier to understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polymorphism capability:&lt;/strong&gt; You can use inheritance and interfaces to create more complex configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combination with&lt;/strong&gt; &lt;code&gt;AutoWire&lt;/code&gt;: Using AutoWire allows you to defer object creation and dependency injection while still maintaining the benefits of DTO.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach significantly improves the Developer Experience, making configuration work more intuitive and less error-prone.&lt;/p&gt;

&lt;h2&gt;
  
  
  😌 Conclusion: New Horizons
&lt;/h2&gt;

&lt;p&gt;Our journey through the labyrinths of Laravel has come to an end, but this is only the beginning. We've gone from discovering a problem with caching configurations with objects to creating a potential solution that could change the way we work with Laravel configuration.&lt;/p&gt;

&lt;p&gt;Now I encourage you to join this journey:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🤔 What do you think of the proposed solution using AutoWire and configuration DTOs? Do you see potential problems or improvements?&lt;/li&gt;
&lt;li&gt;💡 Do you have experience in solving similar problems in your projects? How have you dealt with the limitations of Laravel configurations?&lt;/li&gt;
&lt;li&gt;🔮 Do you think Laravel should evolve in this direction? What other aspects of the framework do you think need improvement?&lt;/li&gt;
&lt;li&gt;🌈 What other ideas do you have for improving the Developer Experience in Laravel or other frameworks?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's continue this conversation! Share your thoughts in the comments. Your experience and ideas could be the key to the next big thing in the development world.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Let's continue the journey together!
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🐙 Follow my GitHub account: &lt;a href="https://github.com/lotyp" rel="noopener noreferrer"&gt;github.com/lotyp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐦 Follow me on X: &lt;a href="https://x.com/wlotyp" rel="noopener noreferrer"&gt;х.com/wlotyp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💼 I'm open to new opportunities! Connect with me on LinkedIn: &lt;a href="https://www.linkedin.com/in/wayofdev/" rel="noopener noreferrer"&gt;in/wayofdev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>dx</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Simplifying Local Development with Docker, mkcert, DNSMasq and Traefik.</title>
      <dc:creator>lotyp</dc:creator>
      <pubDate>Mon, 10 Jun 2024 22:10:55 +0000</pubDate>
      <link>https://dev.to/lotyp/simplifying-local-development-with-docker-mkcert-dnsmasq-and-traefik-3k57</link>
      <guid>https://dev.to/lotyp/simplifying-local-development-with-docker-mkcert-dnsmasq-and-traefik-3k57</guid>
      <description>&lt;p&gt;Hello dev.to community!&lt;/p&gt;

&lt;p&gt;I've created a project called &lt;a href="https://github.com/wayofdev/docker-shared-services" rel="noopener noreferrer"&gt;wayofdev/docker-shared-services&lt;/a&gt; that my team and I use to streamline our local development.&lt;/p&gt;

&lt;p&gt;It simplifies the setup for Dockerized projects on macOS and Linux, and I’m excited to share it with the community. Let's dive into how it can help you and your team.&lt;/p&gt;



&lt;h2&gt;
  
  
  🗂️ Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Key Features&lt;/li&gt;
&lt;li&gt;Problem&lt;/li&gt;
&lt;li&gt;The Solution: Docker Shared Services&lt;/li&gt;
&lt;li&gt;Requirements&lt;/li&gt;
&lt;li&gt;Quick Start Guide (macOS)&lt;/li&gt;
&lt;li&gt;Connecting Your Projects to Shared Services&lt;/li&gt;
&lt;li&gt;Example: Spin-up Laravel Sail Project&lt;/li&gt;
&lt;li&gt;Example: Want to See a Ready-Made Template?&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;



&lt;h2&gt;
  
  
  Key Features 🌟
&lt;/h2&gt;

&lt;p&gt;By implementing this approach, you will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automated Local DNS and SSL Setup&lt;/strong&gt;: No more manual edits to &lt;code&gt;/etc/hosts&lt;/code&gt; or dealing with self-signed certificate warnings. You'll have an automated solution for DNS and SSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Development Environment&lt;/strong&gt;: All team members will have the same setup, reducing environment-related bugs and making onboarding new developers faster and easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elimination of Port Conflicts&lt;/strong&gt;: Using Traefik, you can avoid port conflicts entirely, allowing multiple dockerized projects to run concurrently without issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-Friendly Local URLs&lt;/strong&gt;: Access your projects via custom local domains like &lt;code&gt;project.docker&lt;/code&gt; instead of &lt;code&gt;localhost:8000&lt;/code&gt;, improving the overall development experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified CORS and Cookie Management&lt;/strong&gt;: With SSL in place for local domains, configuring CORS and Cookies will mirror production settings, reducing debugging time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Testing Environment&lt;/strong&gt;: Test OAuth, Secure Cookies, and HTTPS APIs locally, ensuring they work exactly as they will in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Service Discovery and Routing&lt;/strong&gt;: Traefik provides automatic service discovery and routing, making it easier to manage and access various services within your Docker network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Integration with Existing Projects&lt;/strong&gt;: Quickly connect your existing Docker projects to the &lt;code&gt;docker-shared-services&lt;/code&gt; setup, leveraging DNSMasq and Traefik for better service management.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2&gt;
  
  
  Problem 🤔
&lt;/h2&gt;

&lt;h3&gt;
  
  
  → Common Challenges in Local Development
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual DNS Configuration&lt;/strong&gt;:&lt;br&gt;
Developers often need to update their &lt;code&gt;/etc/hosts&lt;/code&gt; file to direct traffic for local domains like &lt;code&gt;yourproject.local&lt;/code&gt; or &lt;code&gt;yourproject.domain.local&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt;. This is tedious and requires admin access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of SSL Support&lt;/strong&gt;:&lt;br&gt;
Local domains often lack SSL, making it hard to test secure connections and leading to issues with OAuth providers, CORS, and cookies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Port Conflicts&lt;/strong&gt;:&lt;br&gt;
Forwarding Docker service ports to the host machine can cause conflicts, especially when multiple services use common ports like 80 or 443.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cumbersome Hostnames&lt;/strong&gt;:&lt;br&gt;
Using hostnames like &lt;code&gt;localhost:8000&lt;/code&gt; for local projects is not user-friendly and complicates development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complex CORS and Cookie Configuration&lt;/strong&gt;:&lt;br&gt;
Without SSL and proper DNS setup, configuring CORS and Cookies becomes more challenging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency Across Team Members&lt;/strong&gt;:&lt;br&gt;
When a team is working on the same project, maintaining consistency in the local development environment across different machines is challenging. Differences in setup can lead to issues that are hard to debug and resolve, slowing down the development process.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  → The Hard Way
&lt;/h3&gt;

&lt;p&gt;Traditionally, setting up a local development environment with SSL involves a series of manual steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating and trusting self-signed certificates in the system&lt;/li&gt;
&lt;li&gt;Editing the &lt;code&gt;/etc/hosts&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Setting up and configuring Dockerized projects with custom ports&lt;/li&gt;
&lt;li&gt;Solving CORS and SSL related problems between local services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps are time-consuming and prone to errors, leading to a frustrating development experience.&lt;/p&gt;



&lt;h2&gt;
  
  
  The Solution: Docker Shared Services 🐳
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/wayofdev/docker-shared-services" rel="noopener noreferrer"&gt;wayofdev/docker-shared-services&lt;/a&gt; simplifies local development by providing a Docker-powered environment that integrates Traefik, mkcert, and DNSMasq. This setup offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated DNS and SSL support&lt;/li&gt;
&lt;li&gt;A consistent development environment across different machines&lt;/li&gt;
&lt;li&gt;Simplified service discovery and routing using Traefik&lt;/li&gt;
&lt;li&gt;Elimination of port conflicts on the host network&lt;/li&gt;
&lt;li&gt;The ability to test your Dockerized projects in an environment that closely mimics real-world scenarios, ensuring that your applications behave as expected before deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  → Key Components
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Traefik&lt;/strong&gt;:&lt;br&gt;
Traefik acts as a reverse proxy and load balancer, providing routing for your Docker services. It integrates with Docker via docker-compose labeling functionality to automatically discover and route traffic to your services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;mkcert&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://github.com/FiloSottile/mkcert" rel="noopener noreferrer"&gt;mkcert&lt;/a&gt; generates locally-trusted development certificates, enabling SSL support for your local domains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DNSMasq&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://thekelleys.org.uk/dnsmasq/doc.html" rel="noopener noreferrer"&gt;Dnsmasq&lt;/a&gt; provides lightweight DNS and DHCP services, allowing you to use custom local domains without editing the &lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  → Going Further
&lt;/h3&gt;

&lt;p&gt;You can go further and pack this project into an Ansible setup. This way, you can automate the provisioning of macOS or Linux hosts, ensuring that all necessary tools and configurations are in place. This not only saves time but also ensures consistency across different environments.&lt;/p&gt;



&lt;h2&gt;
  
  
  Requirements 🚩
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt; Monterey+ or &lt;strong&gt;Linux&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Tested on Ubuntu 22.04, but should also work on Debian or Arch variants&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Docker&lt;/strong&gt; 26.0 or newer

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04" rel="noopener noreferrer"&gt;How To Install and Use Docker on Ubuntu 22.04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/install/mac-install/" rel="noopener noreferrer"&gt;How To Install Docker Desktop on Mac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Homebrew&lt;/strong&gt; (macOS only): Install via &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;brew.sh&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Installed &lt;a href="https://github.com/FiloSottile/mkcert" rel="noopener noreferrer"&gt;mkcert&lt;/a&gt; binary in system

&lt;ul&gt;
&lt;li&gt;See full installation instructions in their official &lt;a href="https://github.com/FiloSottile/mkcert" rel="noopener noreferrer"&gt;README.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Quick installation on macOS: &lt;code&gt;brew install mkcert nss&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;



&lt;h2&gt;
  
  
  Quick Start Guide (macOS) 🚀
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install Homebrew&lt;/strong&gt; (if not installed):&lt;br&gt;
If &lt;a href="https://brew.sh" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt; is not already installed, run the following command:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install Docker&lt;/strong&gt; (if not installed):&lt;br&gt;
Set up Docker Desktop via Homebrew:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; docker

&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install &lt;code&gt;mkcert&lt;/code&gt; and &lt;code&gt;nss&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;mkcert&lt;/code&gt; is a tool that creates locally-trusted development certificates, and &lt;code&gt;nss&lt;/code&gt; provides support for mkcert certificates in Firefox.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;mkcert nss
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create shared project directory:&lt;/strong&gt;&lt;br&gt;
This repository should be run once per machine, so let's create a shared directory for this project:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/projects/infra &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/infra
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clone this repository:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="se"&gt;\&lt;/span&gt;
  git@github.com:wayofdev/docker-shared-services.git &lt;span class="se"&gt;\&lt;/span&gt;
  ~/projects/infra/docker-shared-services &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/infra/docker-shared-services
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create &lt;code&gt;.env&lt;/code&gt; file:&lt;/strong&gt;&lt;br&gt;
Generate a default &lt;code&gt;.env&lt;/code&gt; file, which contains configuration settings for the project.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nb"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open this file and read the notes inside to make any necessary changes to fit your setup.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install root certificate&lt;/strong&gt; and generate default project certs:&lt;br&gt;
This step installs the root certificate into your system's trust store and generates default SSL certificates for your local domains, which are listed in the &lt;code&gt;.env&lt;/code&gt; file, under the variable &lt;code&gt;TLS_DOMAINS&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make cert-install
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Currently, on macOS, you may need to enter your password several times to allow &lt;code&gt;mkcert&lt;/code&gt; to install the root certificate.&lt;br&gt;
&lt;strong&gt;This is a one-time operation&lt;/strong&gt; and details can be found in this upstream GitHub &lt;a href="https://github.com/FiloSottile/mkcert/issues/415" rel="noopener noreferrer"&gt;issue&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run this project:&lt;/strong&gt;&lt;br&gt;
Start the Docker services defined in the repository.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make up
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check that all Docker services are running:&lt;/strong&gt;&lt;br&gt;
Ensure Docker is running and services are up by using the &lt;code&gt;make ps&lt;/code&gt; and &lt;code&gt;make logs&lt;/code&gt; commands.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make ps
make logs
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add custom DNS resolver to your system:&lt;/strong&gt;&lt;br&gt;
This allows macOS to understand that &lt;code&gt;*.docker&lt;/code&gt; domains should be resolved by a custom resolver via &lt;code&gt;127.0.0.1&lt;/code&gt;, where our DNSMasq, which runs inside Docker, will handle all DNS requests.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "nameserver 127.0.0.1" &amp;gt; /etc/resolver/docker'&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscacheutil &lt;span class="nt"&gt;-flushcache&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;killall &lt;span class="nt"&gt;-HUP&lt;/span&gt; mDNSResponder
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can check that DNS was added by running:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scutil &lt;span class="nt"&gt;--dns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;resolver &lt;span class="c"&gt;#8&lt;/span&gt;
  domain   : docker
  nameserver[0] : 127.0.0.1
  flags    : Request A records, Request AAAA records
  reach    : 0x00030002 &lt;span class="o"&gt;(&lt;/span&gt;Reachable,Local Address,Directly Reachable Address&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of creating the &lt;code&gt;/etc/resolver/docker&lt;/code&gt; file, you can add &lt;code&gt;127.0.0.1&lt;/code&gt; to your macOS DNS Servers in your Ethernet or Wi-Fi settings.&lt;/p&gt;

&lt;p&gt;Go to System Preferences → Network → Wi-Fi → Details → DNS and add &lt;code&gt;127.0.0.1&lt;/code&gt; as the first DNS server.&lt;/p&gt;

&lt;p&gt;This allows you to do it one time, and if you need to create a new local domain, for example &lt;code&gt;*.mac&lt;/code&gt;, in the future, it will be automatically resolved without creating a separate &lt;code&gt;/etc/resolver/mac&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ping &lt;code&gt;router.docker&lt;/code&gt; to check if DNS is working:&lt;/strong&gt;&lt;br&gt;
Ensure that the DNS setup is functioning correctly.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping router.docker &lt;span class="nt"&gt;-c&lt;/span&gt; 3
ping any-domain.docker &lt;span class="nt"&gt;-c&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Access Traefik dashboard:&lt;/strong&gt;&lt;br&gt;
Open &lt;a href="https://router.docker" rel="noopener noreferrer"&gt;https://router.docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should see the Traefik Dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwayofdev%2Fdocker-shared-services%2Fmaster%2F.github%2Fassets%2Ftraefik.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwayofdev%2Fdocker-shared-services%2Fmaster%2F.github%2Fassets%2Ftraefik.png%3Fraw%3Dtrue" title="Traefik dashboard example" alt="Traefik dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  → Outcome
&lt;/h3&gt;

&lt;p&gt;At this point, you should have a working local development environment with DNS and SSL support ready to be used with your projects.&lt;/p&gt;

&lt;p&gt;Services will be running under a shared Docker network called &lt;code&gt;network.ss&lt;/code&gt;, and all projects or microservices that will share the same &lt;a href="https://docs.docker.com/network/" rel="noopener noreferrer"&gt;Docker network&lt;/a&gt; will be visible to Traefik. The local DNS, served by DNSMasq, will be available on &lt;code&gt;*.docker&lt;/code&gt; domains.&lt;/p&gt;



&lt;h2&gt;
  
  
  Connecting Your Projects to Shared Services 🔌
&lt;/h2&gt;

&lt;p&gt;To connect your projects to the shared services, configure your project's &lt;code&gt;docker-compose.yaml&lt;/code&gt; file to connect to the shared network and Traefik.&lt;/p&gt;

&lt;p&gt;This project comes with an example Portainer service, which also starts by default with the &lt;code&gt;make up&lt;/code&gt; command. You can check the &lt;a href="https://github.com/wayofdev/docker-shared-services/blob/master/docker-compose.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;/a&gt; to see how Traefik labels and the shared network are used to spin up Portainer on the &lt;a href="https://ui.docker" rel="noopener noreferrer"&gt;https://ui.docker&lt;/a&gt; host, which supports SSL by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  → Sample Configuration
&lt;/h3&gt;

&lt;p&gt;Your project should use the shared Docker network &lt;code&gt;network.ss&lt;/code&gt; and Traefik labels to expose services to the outside world.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Change your project's &lt;code&gt;docker-compose.yaml&lt;/code&gt; file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;---
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;services:
&lt;/span&gt;  web:
    image: wayofdev/nginx:k8s-alpine-latest
    restart: on-failure
&lt;span class="gi"&gt;+   networks:
+     - default
+     - shared
&lt;/span&gt;    volumes:
      - ./app:/app:rw,cached
&lt;span class="gi"&gt;+   labels:
+     - traefik.enable=true
+     - traefik.http.routers.api-my-project-secure.rule=Host(`api.my-project.docker`)
+     - traefik.http.routers.api-my-project-secure.entrypoints=websecure
+     - traefik.http.routers.api-my-project-secure.tls=true
+     - traefik.http.services.api-my-project-secure.loadbalancer.server.port=8880
+     - traefik.docker.network=network.ss
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;networks:
&lt;/span&gt;&lt;span class="gi"&gt;+ shared:
+   external: true
+   name: network.ss
+ default:
+   name: project.my-project
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this configuration, we added the shared network and Traefik labels to the web service. These labels help Traefik route the traffic to the service based on the specified rules.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generate SSL certs for your project:&lt;/strong&gt;&lt;br&gt;
Go to the &lt;code&gt;docker-shared-services&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/infra/docker-shared-services
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Edit the &lt;code&gt;.env&lt;/code&gt; file to add your custom domain:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano .env
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It should look something like this:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TLS_DOMAINS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ui.docker router.docker *.my-project.docker"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generate SSL certificates:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make cert-install restart
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;



&lt;h2&gt;
  
  
  Example: Spin-up Laravel Sail Project 🚀
&lt;/h2&gt;

&lt;p&gt;Let's walk through an example of setting up a Laravel project using Sail and integrating it with the &lt;code&gt;docker-shared-services&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create an example Laravel project based on Sail:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://laravel.build/example-app"&lt;/span&gt; | bash
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file of the &lt;code&gt;example-app&lt;/code&gt; project and make adjustments:&lt;/p&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;services:
&lt;/span&gt;  laravel.test:
    build:
      context: ./vendor/laravel/sail/runtimes/8.3
      dockerfile: Dockerfile
      args:
        WWWGROUP: '${WWWGROUP}'
    image: sail-8.3/app
&lt;span class="gd"&gt;-   extra_hosts:
-     - 'host.docker.internal:host-gateway'
&lt;/span&gt;    ports:
&lt;span class="gd"&gt;-     - '${APP_PORT:-80}:80'
-     - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
&lt;/span&gt;    networks:
      - sail
&lt;span class="gi"&gt;+     - shared
&lt;/span&gt;    depends_on:
      - ...
&lt;span class="gi"&gt;+   labels:
+     - traefik.enable=true
+     - traefik.http.routers.test-laravel-app-secure.rule=Host(`api.laravel-app.docker`)
+     - traefik.http.routers.test-laravel-app-secure.entrypoints=websecure
+     - traefik.http.routers.test-laravel-app-secure.tls=true
+     - traefik.http.services.test-laravel-app-secure.loadbalancer.server.port=80
+     - traefik.docker.network=network.ss
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  mailpit:
    image: 'axllent/mailpit:latest'
    networks:
      - sail
&lt;span class="gi"&gt;+     - shared
&lt;/span&gt;    ports:
&lt;span class="gd"&gt;-     - '${FORWARD_MAILPIT_PORT:-1025}:1025'
-     - '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025'
&lt;/span&gt;&lt;span class="gi"&gt;+   labels:
+     - traefik.enable=true
+     - traefik.http.routers.mail-laravel-app-secure.rule=Host(`mail.laravel-app.docker`)
+     - traefik.http.routers.mail-laravel-app-secure.entrypoints=websecure
+     - traefik.http.routers.mail-laravel-app-secure.tls=true
+     - traefik.http.services.mail-laravel-app-secure.loadbalancer.server.port=8025
+     - traefik.docker.network=network.ss
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;networks:
&lt;/span&gt;  sail:
    driver: bridge
&lt;span class="gi"&gt;+ shared:
+   external: true
+   name: network.ss
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These changes connect the Laravel app and Mailpit docker services to the shared network and expose them via Traefik.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run the Laravel project:&lt;/strong&gt;&lt;br&gt;
Navigate to the &lt;code&gt;example-app&lt;/code&gt; directory and start the services using Sail.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vendor/bin/sail up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check Traefik routers&lt;/strong&gt;:&lt;br&gt;
Open &lt;a href="https://router.docker/dashboard/#/http/routers" rel="noopener noreferrer"&gt;https://router.docker/dashboard/#/http/routers&lt;/a&gt; and check that there are two routers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host(api.laravel-app.docker)  → test-laravel-app-secure@docker&lt;/li&gt;
&lt;li&gt;Host(mail.laravel-app.docker)` → mail-laravel-app-secure@docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwayofdev%2Fdocker-shared-services%2Fmaster%2F.github%2Fassets%2Ftraefik-routers.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwayofdev%2Fdocker-shared-services%2Fmaster%2F.github%2Fassets%2Ftraefik-routers.png%3Fraw%3Dtrue" title="Traefik Routers Example" alt="Traefik Routers"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check the setup&lt;/strong&gt;:&lt;br&gt;
Ensure that your Laravel application and Mailpit services are running correctly by accessing their respective domains:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  * **Laravel app** — https://api.laravel-app.docker
  * **Mailpit** — https://mail.laravel-app.docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At this point, your Laravel project is integrated with the &lt;code&gt;wayofdev/docker-shared-services&lt;/code&gt;, utilizing DNS and SSL support for local development.&lt;/p&gt;



&lt;h2&gt;
  
  
  Example: Want to See a Ready-Made Template? 👀
&lt;/h2&gt;

&lt;p&gt;If you come from the PHP or Laravel world, or if you want to see how a complete project can be integrated with &lt;code&gt;docker-shared-services&lt;/code&gt;, check out &lt;a href="https://github.com/wayofdev/laravel-starter-tpl" rel="noopener noreferrer"&gt;wayofdev/laravel-starter-tpl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This Dockerized Laravel starter template works seamlessly with &lt;a href="https://github.com/wayofdev/docker-shared-services" rel="noopener noreferrer"&gt;wayofdev/docker-shared-services&lt;/a&gt;, providing a foundation with integrated DNS and SSL support, and can show you &lt;code&gt;the way&lt;/code&gt; to implement patterns, stated in this article, in your projects.&lt;/p&gt;



&lt;h2&gt;
  
  
  Linux 🐧
&lt;/h2&gt;

&lt;p&gt;Linux Users, you are also covered!&lt;/p&gt;

&lt;p&gt;If you're using Ubuntu or another Linux distribution, I've included a &lt;a href="https://github.com/wayofdev/docker-shared-services?tab=readme-ov-file#-quick-start-guide-linux" rel="noopener noreferrer"&gt;Linux Quick Start Guide&lt;/a&gt; in the README.md of the &lt;code&gt;wayofdev/docker-shared-services&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;This guide provides step-by-step instructions to help you set up the environment on a Linux system, ensuring you can have the same streamlined development experience as described in the macOS Quick Start Guide provided in this article.&lt;/p&gt;



&lt;h2&gt;
  
  
  Conclusion ✨
&lt;/h2&gt;

&lt;p&gt;In conclusion, the &lt;a href="https://github.com/wayofdev/docker-shared-services" rel="noopener noreferrer"&gt;wayofdev/docker-shared-services&lt;/a&gt; project offers a streamlined and automated solution for managing local development environments using Docker, mkcert, DNSMasq, and Traefik.&lt;/p&gt;

&lt;p&gt;By integrating these tools into your workflow, you can ensure that your local environment closely mirrors production, leading to more reliable and predictable outcomes when deploying applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  → What do you think? Would you approach this differently?
&lt;/h3&gt;

&lt;p&gt;I hope this guide has been helpful in demonstrating the potential benefits of using this approach for your local development needs. If you have any suggestions or a better way of achieving this setup, please feel free to share your thoughts.&lt;/p&gt;

&lt;p&gt;Feel free to fork this repository and adjust it to your needs, or use it as a template.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and I hope you find this setup as beneficial as I have.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;



</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>ssl</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
