Get "PHP 8 in a Nuthshell" (Soon includes PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Dynamically build Mail, DB, and Cache configurations in Laravel

Usually, when working with things that require configuration, such as databases, caches, and mailers, you have to define them in their respective configuration files beforehand.

So, for instance, you need to define all the mailers for your application in the config/mail.php file like so.

return [
    'mailers' => [
        'smtp' => [
            'transport' => 'smtp',
            'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
            'port' => env('MAIL_PORT', 587),
            'encryption' => env('MAIL_ENCRYPTION', 'tls'),
            'username' => env('MAIL_USERNAME'),
            'password' => env('MAIL_PASSWORD'),
            'timeout' => null,
            'local_domain' => env('MAIL_EHLO_DOMAIN'),
        ],
        'log' => [
            'transport' => 'log',
            'channel' => env('MAIL_LOG_CHANNEL'),
        ],
    ],
];

When you send your mailable, you will either use the default mailer or a specific one.

// using the default mailer
Mail::send(new OrderShipped($order));

// specifying the mailer explicitly 
Mail::mailer('smtp')->send(new OrderShipped($order));

But what if you want to dynamically build the mailers for instances where you have the configurations stored in a database or somewhere else?

This is where a new build() method contributed by Steve Bauman comes in handy.

The Mail::build() method

The new build() method allows you to dynamically build your application’s mailers. This means you can define the configuration for your mailers on the fly and use them to send mailable.

Here’s how you can use it.

use Illuminate\Support\Facades\Mail;

$mailer = Mail::build([
    'transport' => 'smtp',
    'host' => '127.0.0.1',
    'port' => 587,
    'encryption' => 'tls',
    'username' => 'usr',
    'password' => 'pwd',
    'timeout' => 5,
]);

$mailer->send($mailable);

As you can tell, you can pass an array of configurations to the build() method. This configuration can come, like I said previously, from a database or wherever you want. So, you don’t need to rely upon the hard-coded ones stored in config/mail.php.

There’s a little catch to the queued mailables. Like he explains, the queued job doesn’t have access to the config used to create the mailer. So, instead, you’ll need to queue a job that contains the Mail::build to send the mailable immediately inside.

namespace App\Jobs;
use App\Models \Mailbox;
use Illuminate\Support\Facades \Mail;
use Illuminate\Contracts\Mail\Mailable;

class SendMailboxEmail implements ShouldQueue {
{
    public function _construct(
        public Mailbox $mailbox,
        public Mailable $email,
    ) {}

    public function handle(): void
    {
        Mail:: build(
            'transport' -> 'smtp',
            'host' = $this->mailbox->host,
            'port' => $this->mailbox->port,
            'encryption' => $this->mailbox->encryption,
            'username' => $this->mailbox->username,
            'password' = $this-mailbox->password,
            'timeout' => 5,
        ]) ->send ($this->email);
    }
}

The DB::build() method

Similar to the Mail::build() method, the DB::build() method allows you to dynamically build your application’s database connection.

use Illuminate\Support\Facades\DB;

$sqlite = DB::build([
    'driver' => 'sqlite',
    'database' => ':memory:',
]);

$mysql = DB::build([
    'driver' => 'mysql',
    'database' => 'forge',
    'username' => 'root',
    'password' => 'secret',
]);

Here’s an example usage of this method where you can import an SQLite database dynamically.

namespace App\Http\Controller;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class ImportController 
{
    public function _invoke(Request $request)
    {
        $path = $request->file('database')->store();

        $sqlite = DB::build({
            'driver' => 'sqlite',
            'database' => $path,
        });

        $sqlite
            ->table('...')
            ->select([...])
            ->each(function (object $record) {
                // Perform import...
            });

        return back()->banner(
            'Successfully imported records.'
        );
    }
}

The Cache::build() method

Lastly, there’s a new Cache::build() method that allows you to create new cache repositories that are not defined in your configuration (config/cache.php) file.

use Illuminate\Support\Facades\Cache;

$apc = Cache::build([
    'driver' => 'apc',
]);

$array = Cache::build([
    'driver' => 'array',
    'serialize' => false,
]);

$database = Cache::build([
    'driver' => 'database',
    'table' => 'cache',
    'connection' => null,
    'lock_connection' => null,
]);

$file = Cache::build([
    'driver' => 'file',
    'path' => storage_path('framework/cache/data'),
]); 
Learn the fundamentals of PHP 8 (including 8.1, 8.2, and 8.3), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

Like this article?

Buy me a coffee

👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.

Comments?