如何開發自己的Package?
Service provider最主要的功能就是讓我們寫package,本文一步一步的介紹最簡單的package,僅有Hello World的功能,並包含route、controller與view,然後打包成package送上Github與Packagist,最後另外開一個專案,使用composer require下載,執行我們自己上傳的package。
實際寫過package之後,會讓我們對service provider與composer有更深刻的了解。
Version
Laravel 5.1
建立Laravel專案
1 | oomusou@mac:~$ composer create-project laravel/laravel MyPackage --prefer-dist |
建立開發package用的專案。
建立目錄
在專案根目錄下建立packages目錄,我們所有的package source code都會放這裡。
在packages目錄下建立vendor/package/src子目錄,本範例會建立oomusou/helloworld/src。1 1vendor與package名稱建議都小寫,且可以相同,如laravel/laravel,也可以不同,如Jeffery Way的Generators是way/generators。
建立composer.json
每個package都必須有composer.json,用來描述其使用的相依套件與namespace。進入packages/oomusou/helloworld目錄 :1
oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ composer init
所有問題皆按Enter即可,會產生一個預設的composer.json。1
2
3
4
5
6
7
8
9
10{
"name": "oomusou/helloworld",
"authors": [
{
"name": "oomusou",
"email": "[email protected]"
}
],
"require": {}
}
設定PSR-4命名空間
由於目前Laravel預設的root namespace是在app目錄下,並無法得知新加的package/oomusou/helloworld/src目錄,必須在MyPackage專案的composer.json加入新的root namespace。2 2注意是MyPackage專案的composer.json,不是剛剛在packages/oomusou/helloworld目錄下建立的composer.json。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
(略)
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/",
"Oomusou\\HelloWorld\\": "packages/oomusou/helloworld/src/"
}
},
(略)
}
12行1
"Oomusou\\Helloworld\\": "packages/oomusou/helloworld/src/"
加入新的namespace : Oomusou\HelloWorld。
產生新的autoload檔案3 3注意是MyPackage專案目錄下產生autoload檔案,不是剛剛的packages/oomusou/helloworld目錄下。1
oomusou@mac:~/MyPackage$ composer dump-autoload
建立Service Provider
1 | oomusou@mac:~/MyPackage$ php artisan make:provider HelloWorldServiceProvider |
產生HelloWorldServiceProvider.php在app/Providers目錄下,因為我們是要寫package,所以將此檔案移到packages/oomusou/helloworld/src目錄下。
因為目錄已經移動,需要重新修改namespace。4 4Namespace建議遵循PSR-2規範,使用CamelCase,詳細請參考PSR-1 & PSR-2 PHP Coding Style1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26namespace Oomusou\HelloWorld;
use Illuminate\Support\ServiceProvider;
class HelloWorldServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
預設會建立boot()與register()。5 5關於boot()與register()的意義與差異,詳細請參考深入探討Service Provider
在config/app.php中註冊HelloWorldServiceProvider。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25return [
(略)
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
(略)
Illuminate\View\ViewServiceProvider::class,
Oomusou\HelloWorld\HelloWorldServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
(略)
];
12行1
oomusou\helloworld\HelloWorldServiceProvider::class,
註冊剛剛建立的HelloWorldServiceProvider。
建立Controller
1 | oomusou@mac:~/MyPackage$ php artisan make:controller HelloWorldController |
產生HelloWorldController在app/Http/Controllers目錄下,因為我們是要寫package,所以將此檔案移到packages/oomusou/helloworld/src目錄下。
因為目錄已經移動,須重新修改namespace,並且只留下index()。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18namespace Oomusou\HelloWorld;
use App\Http\Controllers\Controller;
use App\Http\Requests;
class HelloWorldController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$message = 'Hello World';
return view('HelloWorld::welcome', compact('message'));
}
}
建立$message變數,並顯示welcome.blade.php。HelloWorld::welcome表示welcome在HelloWorld的namespace下,稍後會指定。
建立Route
將app/Http/routes.php複製到packages/oomusou/helloworld/src。1
Route::get('helloworld', 'Oomusou\HelloWorld\HelloWorldController@index');
將URI為helloworld時,使用HelloWorldController的index。
建立View
在此不用特別建立view,先在packages/oomusou/helloworld/src建立views目錄,並將Laravel 5.1預設的resources/views/welcome.blade.php複製到package/oomusou/helloworld/src/views目錄下。

將Laravel 5改成{{$message}}。
修改Service Provider
之前都沒使用到service provider的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
30namespace Oomusou\HelloWorld;
use Illuminate\Support\ServiceProvider;
class HelloWorldServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
$this->app->make(HelloWorldController::class);
include(__DIR__ . '/routes.php');
$this->loadViewsFrom(__DIR__ . '/views', 'HelloWorld');
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
13行1
2
3
4
5
6
7
8public function boot()
{
$this->app->make(HelloWorldController::class);
include(__DIR__ . '/routes.php');
$this->loadViewsFrom(__DIR__ . '/views', 'HelloWorld');
}
- 由
$this->app->make()建立HelloWorldController。 - 其中
__DIR__會傳回HelloWorldServiceProvider.php的目錄路徑,也就是packages/oomusou/helloworld/src,將routes.phpinclude進來,使專案會用到package的route。 - 使用
loadViewsFrom(),第1個參數傳入package的view的目錄位置,第2個參數為view的namespace。
至於register(),因為這個範例都沒使用到service container,所以不用寫任何code。
測試Package

上傳Github
設定命名空間
目前package由於是搭配MyPackage專案測試,所以將PSR-4的root namespace設定在MyPackage專案的composer.json,但發佈package之後,不可能要user也手動在composer.json加上namespace,所以我們要將namespace設定在package自己的composer.json。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"name": "oomusou/helloworld",
"authors": [
{
"name": "oomusou",
"email": "[email protected]"
}
],
"require": {},
"autoload": {
"psr-4": {
"Oomusou\\HelloWorld\\": "src/"
}
}
}
建立本地git倉儲
1 | oomusou@mac:~/MyPackage$ cd packages/oomusou/helloworld oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git init oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git add . oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git commit -m "Initial commit." |
建立遠端git倉儲

Push至Github
1 | oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git remote add origin [email protected]:oomusou/helloworld.git oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git push -u origin master oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git tag -a 1.0.0 -m "First version" oomusou@mac:~/MyPackage/packages/oomusou/helloworld$ git push --tags |
其中下tag是必須的,否則composer安裝package時,會要求你設定minimum-stability。
上傳Packagist
登入Packagist
登入至Packagist,按右上角Submit上傳package。
提交Github網址
貼上package在Github Repository的網址。
確認提交
因為Packagist已經有很多package名字叫做helloworld,Packagist請你確認是否要上傳。
上傳成功

測試Package
建立測試專案
1 | oomusou@mac:~$ composer create-project laravel/laravel MyTestPackage --prefer-dist |
建立開發測試package用的專案。
安裝Package
1 | oomusou@mac:~$ cd MyTestPackage oomusou@mac:~/MyTestPackage$ composer require oomusou/helloworld |

註冊Service Provider
在config/app.php中註冊HelloWorldServiceProvider。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25return [
(略)
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
(略)
Illuminate\View\ViewServiceProvider::class,
Oomusou\HelloWorld\HelloWorldServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
(略)
];
12行1
oomusou\helloworld\HelloWorldServiceProvider::class,
註冊剛剛建立的HelloWorldServiceProvider。
瀏覽器測試

Conclusion
- Package開發牽涉到對service provider與composer的了解,還要搭配Git與Github操作,只要一個環節出錯,整個開發過程就會失敗。
- 與商業邏輯無關,且可以重複使用的部分,可以考慮寫成package,以加速團隊開發流程。
- 寫package與寫testing一樣有趣,一旦迷上了就很難戒掉 XDD
Sample Code
完整的範例可以在我的GitHub上找到。