2016年9月2日 星期五

如何開發自己的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/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()55關於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

from : http://oomusou.io/laravel/laravel-package-hello-world/

沒有留言:

wibiya widget