建立一個Hello World Package並送上Packagist

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/src1 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

packges/oomusou/helloworld/composer.json
1
2
3
4
5
6
7
8
9
10
{
"name": "oomusou/helloworld",
"authors": [
{
"name": "oomusou",
"email": "oomusou@gmail.com"
}
],
"require": {}
}

設定PSR-4命名空間


由於目前Laravel預設的root namespace是在app目錄下,並無法得知新加的package/oomusou/helloworld/src目錄,必須在MyPackage專案的composer.json加入新的root namespace2 2注意是MyPackage專案的composer.json不是剛剛在packages/oomusou/helloworld目錄下建立的composer.json

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.phpapp/Providers目錄下,因為我們是要寫package,所以將此檔案移到packages/oomusou/helloworld/src目錄下。

因為目錄已經移動,需要重新修改namespace。4 4Namespace建議遵循PSR-2規範,使用CamelCase,詳細請參考PSR-1 & PSR-2 PHP Coding Style

packages/oomusou/helloworld/HelloWorldServiceProvider.php
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
namespace 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

config/app.php
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
return [
(略)

'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

產生HelloWorldControllerapp/Http/Controllers目錄下,因為我們是要寫package,所以將此檔案移到packages/oomusou/helloworld/src目錄下。

因為目錄已經移動,須重新修改namespace,並且只留下index()

packages/oomusou/helloworld/src/HelloWorldController.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace 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表示welcomeHelloWorld的namespace下,稍後會指定。

建立Route


app/Http/routes.php複製到packages/oomusou/helloworld/src

packages/oomusou/helloworld/src/routes.php
1
Route::get('helloworld', 'Oomusou\HelloWorld\HelloWorldController@index');

將URI為helloworld時,使用HelloWorldControllerindex

建立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(),現在要正式使用了。

packages/oomusou/helloworld/src/HelloWorldServiceProvider.php
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
namespace 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
8
public 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

packges/oomusou/helloworld/composer.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name": "oomusou/helloworld",
"authors": [
{
"name": "oomusou",
"email": "oomusou@gmail.com"
}
],
"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 git@github.com: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

config/app.php
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
return [
(略)

'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上找到。

  1. MyPackage
  2. MyTestPackage
2015-11-11