# Laravel Config, Drivers and Managers

# Date: 2021/November/29


Laravel有相當多功能是透過在 env/config內設定,用以切換底層的程式。以 mail.php (opens new window) 來說好了,

return [
    'default' => env('MAIL_MAILER', 'smtp'),

    /*
     |------------------------
     | Driver Samples
     |------------------------
     */
    'mailers' => [
        'smtp' => [
            'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
            'port' => env('MAIL_PORT', 587),
        ],

        'ses' => [
            'transport' => 'ses',
        ],

        'mailgun' => [
            'transport' => 'mailgun',
        ],

        ......
    ]
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

當需要切換發送 email 的驅動時(比如說從 smtp 切換到 AWS 的 ses),只要到 env 檔案裡變更 MAIL_MAILER 的值為 ses 就好。

若是要讓自己寫的程式碼可以支援切換功能的話,可以參考以下作法:

# 60 seconds

首先先設定好本次範例需要的設定檔 app/cart.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Video Uploader Driver
    |--------------------------------------------------------------------------
    |
    */
    'default' => env('DEFAULT_CART', 'cookie'),

    'cart' => [
        'cookie' => [
            // some configurations
        ],

        'session' => [
            // some configurations
        ],

        'database' => [
            // some configurations
        ],
    ],
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

根據 pineco.de/drivers-and-managers-in-laravel (opens new window) 的介紹,可以透過繼承 Illuminate\Support\Manager (opens new window) 的方式實踐。

<?php

namespace App\Services;

use Illuminate\Support\Manager;

class CartManager extends Manager
{
    /**
     * Get the default driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->config->get('cart.default');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

記得要到 config/app.php 與 Service Provider內進行綁定:

# config/app.php

    'providers' => [
        /*
        * Application Service Providers...
        */
        App\Providers\CartServiceProvider::class,
    ],
1
2
3
4
5
6

# Add new Service Provider

<?php

namespace App\Providers;

use App\Services\CartManager;
use Illuminate\Support\ServiceProvider;

class CartServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('cart', function ($app) {
            return new CartManager($app);
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

當程式碼需要支援多個驅動時,除了在 Manager 內指定以外,也能透過 Illuminate\Support\Manager 提供的 extend 方法進行擴充。

$cart = app('cart');

$cart->extend('session', function ($app) {
    return new SessionDriver($app);
});

$cart->extend('cookie', function ($app) {
    return new CookieDriver($app);
});

$cart->extend('null', function ($app) {
    return new NullDriver($app);
});
1
2
3
4
5
6
7
8
9
10
11
12
13

完成綁定後,就可以透過下列方式進行呼叫了

$cart = app('cart')->driver()->cart();

$cookieCart = app('cart')->driver('cookie)->cart();
1
2
3

# 60 minutes

這邊先以 Illuminate/Broadcasting (opens new window) 作為範例,可以看到 Laravel 是怎麼實踐 driver() 方法的

    /**
     * Get a driver instance.
     *
     * @param  string|null  $name
     * @return mixed
     */
    public function driver($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->drivers[$name] = $this->get($name);
    }
1
2
3
4
5
6
7
8
9
10
11
12

Laravel中有些功能有提供其他名稱的方式 (如 mailer (opens new window) ),但基本上還是會呼叫 driver() 以取出正確的 driver 執行功能。

Laravel 提供的 Illuminate\Support\Manager 更為簡潔易用。
可以看到它能把 app('my_manager')->driver('my_driver') 裡指定的 my_driverStr::studly($driver) 自動轉換為 createMyDriverDriver() ,接下來要做的事情就是實踐這個方法,或是如上面寫的透過 extend() 方法註冊 MyDriver 即可。

    /**
     * Get a driver instance.
     *
     * @param  string|null  $driver
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    public function driver($driver = null)
    {
        $driver = $driver ?: $this->getDefaultDriver();

        if (is_null($driver)) {
            throw new InvalidArgumentException(sprintf(
                'Unable to resolve NULL driver for [%s].', static::class
            ));
        }

        // If the given driver has not been created before, we will create the instances
        // here and cache it so we can return it next time very quickly. If there is
        // already a driver created by this name, we'll just return that instance.
        if (! isset($this->drivers[$driver])) {
            $this->drivers[$driver] = $this->createDriver($driver);
        }

        return $this->drivers[$driver];
    }

    /**
     * Create a new driver instance.
     *
     * @param  string  $driver
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    protected function createDriver($driver)
    {
        // First, we will determine if a custom driver creator exists for the given driver and
        // if it does not we will check for a creator method for the driver. Custom creator
        // callbacks allow developers to build their own "drivers" easily using Closures.
        if (isset($this->customCreators[$driver])) {
            return $this->callCustomCreator($driver);
        } else {
            $method = 'create'.Str::studly($driver).'Driver';

            if (method_exists($this, $method)) {
                return $this->$method();
            }
        }

        throw new InvalidArgumentException("Driver [$driver] not supported.");
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

之後一些細節,例如資料庫的連線資訊、連線至外部API的金鑰等等設定,在 createMyDriverDriver() 內實作就好,對呼叫者來說隱藏了不必要的資訊,把建立實例的工作都打包起來了,挺好的。



# References


# 其他參考資料

Gergő D. Nagy (2020/JAN/28). Drivers and Managers in Laravel (opens new window). pineco.de.

Laravel (2021/NOV/29). laravel/framework (opens new window). GitHub.

Lavavel (2021/NOV/29). Service Container (opens new window). laravel.com.

Lavavel (2021/NOV/29). Service Providers (opens new window). laravel.com.

Last Updated: 2022/7/7 上午8:34:36