# 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',
],
......
]
]
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
],
],
];
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');
}
}
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,
],
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()
{
//
}
}
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);
});
2
3
4
5
6
7
8
9
10
11
12
13
完成綁定後,就可以透過下列方式進行呼叫了
$cart = app('cart')->driver()->cart();
$cookieCart = app('cart')->driver('cookie)->cart();
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);
}
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_driver 用 Str::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.");
}
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.