如何開發自己的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.php
include進來,使專案會用到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上找到。