Laravel 5 パッケージのつくり方

本記事は [Qiita Advent Calendar 2016 - Laravel](http://qiita.com/advent-calendar/2016/laravel) の 15 日目の記事です。

Laravel 5.3 製のアプリケーションで [ConoHa オブジェクトストレージ](https://www.conoha.jp/guide/objectstorage.php)とファイルをやりとりするヘルパークラスを作ったのですが、
こういったものをパッケージとして公開する方法に興味があったので手順をまとめました。

ちなみに、できあがったものがこちらです。
[okashoi/laravel5-conoha-object-handler](https://packagist.org/packages/okashoi/laravel5-conoha-object-handler)

（この記事では公開用のリポジトリとして [GitHub](https://github.com/) を使うという想定で書いているので、必要に応じて読み替えてください）


公式ドキュメント：[Package Development - Laravel](https://laravel.com/docs/5.3/packages)

## とりあえず空のパッケージを登録

![image.png](https://qiita-image-store.s3.amazonaws.com/0/49690/daf2d9e0-fb9d-bda2-da1c-22c4b71ded2a.png)

Laravel のパッケージを公開するということは [Packagist](https://packagist.org/) にパッケージを登録することと同義です。

というわけでまずは Packagist に空のパッケージを登録してみます。

### 空のパッケージをつくる

開発環境上で composer.json ファイルをつくります。

`composer init` コマンドを実行すれば対話形式で作成することができますし、直接 composer.json を記述しても良いです。
当然、後から編集することもできます。

作成の際には以下のようなことを気をつける・知っておくと良いです。

* name
    * そのまま Packagist に登録されるパッケージ名になります
    * 一度登録してしまった後にパッケージ名の変更はできないので注意しましょう
* license
    * どうすればいいかわからない！という場合は MIT ライセンスにしておけば良いのでは、と個人的には思います （ライセンスを理由なく選ぶのもどうかと思いますが）
    * 主要なライセンスの種類と内容については[tukiyo3](http://qiita.com/tukiyo3)さんが[わかりやすくまとめている](http://qiita.com/tukiyo3/items/58b8b3f51e9dc8e96886)ので、目を通しておくと良いでしょう
* autoload
    * これを書かないとクラスを読み込めません（私はこれに気づかずに1日くらいハマりました :innocent:）
    * PSR-4 の名前空間に合わせて次のように書きました
        * 無駄に深い階層にしていますが、ここで `src/` とだけ書いて src/ 以下にソースを置くのでよかったみたいです

```json:composer.json
{
    // ...（略）
    "autoload": {
        "psr-4": {
            "Okashoi\\Laravel5ConohaObjectHandler\\": "src/Okashoi/Laravel5ConohaObjectHandler"
        }
    }
}
```

composer.json を作成したら GitHub に リポジトリを作成し commit & push してしまいましょう。
（ついでに .gitignore くらいは追加しておくと良いと思います）


### Packagist アカウントを作成

リポジトリに push したら Packagist に登録しましょう。

そのためにまずは Packagist アカウントを作成します。

普通にアカウントを作成する際には Email・ユーザー名・パスワードを設定するのですが、GitHub アカウントでもログインすることができるのでそれで充分でしょう。

ついでに GitHub 上で Packagist との連携を許可しておきます。

### Packagist にパッケージを登録

画面右上の「Submit」から新規パッケージを登録できます。

リポジトリのURLを入力するだけでパッケージ名の重複などがチェックされた後に登録が完了します。

以降は GitHub 上のリポジトリを更新すれば、Packagist 側の情報も更新されるようになります。

## パッケージ本体の実装

パッケージを登録してしまえば、後はコーディングをしていくだけです。
ガンガン書いてどんどん push していきましょう！

当然、パッケージ内で他のパッケージを使いたければ　`composer require` コマンドで追加することができます。

今回私が作成したのは、 [Guzzle](http://docs.guzzlephp.org/) を使って REST API へのアクセスをラップしたものです。
これくらいの内容なら簡単につくれるので練習に最適ですね！

また、phpDocumentor やコメントは利用者のためにしっかり書いておくと良いでしょう。

## ServiceProvider を作成

作ったパッケージを Laravel 上で利用する際には、ServiceProvider として アプリケーションに登録される必要があります。
アプリケーション起動時に config/app.php 内の `providers` に記載されているものが登録される、という仕組みになっています。

ServiceProvider は`Illuminate\Support\ServiceProvider` を継承し、最低限 `register` メソッドを実装する必要があります。

（公式ドキュメント：[Service Providers - Laravel](https://laravel.com/docs/5.3/providers)）

以下、[私の実装](https://github.com/okashoi/laravel5-conoha-object-handler/blob/master/src/Okashoi/Laravel5ConohaObjectHandler/ConohaObjectServiceProvider.php)を例示しながら説明します。
`register` メソッドは次のように実装されています。

```php:ConohaObjectServiceProvider.php（一部抜粋）
    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton(ObjectHandler::class, function ($app) {
            return new ObjectHandler();
        });
    }
```

内容を説明するには [ServiceContainer](https://laravel.com/docs/5.3/container) についての理解が必要なのですが、
私は他のパッケージを参考に見よう見まねで作ったので詳細に説明はできません……

とりあえず、作成したクラスは Singleton としてアプリケーションに登録される、ということです。


また「実際に必要とされるタイミングまで登録が行われない」というようなことも可能で、
その場合は `$defer` プロパティと`provides` メソッドを利用します。

```php:ConohaObjectServiceProvider.php（一部抜粋）
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = true;

    // ...（略）

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return [ObjectHandler::class];
    }
```

`$defer` プロパティに `true` をセットし、 `provides` メソッドは提供するクラスを返すように実装します。


### パッケージ用の設定ファイル

実装を進めていく中で、認証情報などの値を設定ファイルに切り出したくなることもあるでしょう。

Laravel　ではそういったファイルをアプリケーションの所定の位置に追加するために、 `php artisan vendor:publish` コマンドを利用できます。
（設定ファイルだけでなく、ビューやマイグレーション等も追加できます）

コマンド実行時の挙動は、ServiceProvider の `boot` メソッドに記述します。

例えばパッケージ内に config/conoha.php という設定ファイルを作成し、ServiceProvider の `boot` メソッドを次のように実装します。

```php:ConohaObjectServiceProvider.php（一部抜粋）
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->publishes([
            __DIR__.'/config/conoha.php' => config_path('conoha.php'),
        ]);
    }
```

すると、`php artisan vendor:publish` を実行した際に、アプリケーションに　config/conoha.php が追加されるようになります。

## READMEを書く

もちろん、README を書いてあげるのもお忘れなく。
パッケージを利用しようとする人が最初に目にするのはここです。

Laravel パッケージの README は大体次のような構成になっていることが多いです。

* `composer require`　でインストールできるよ
* config/app.php に ServiceProvider を追加してね
* （必要に応じて）`php artisan vendor:publish` を実行したあと設定を .env に記述してね
* 実際にはこうやって使うよ

英語で書けるとより良いので、できれば頑張りましょう。


ここまで来れば完成です！

あとは　GitHub に push し、バージョンを示すタグを付けてあげましょう！
（タグ付けしなくても copmoser 上でバージョンに `dev-<ブランチ名>` を指定すればインストールすることはできます）

## 振り返ってみて

テスト書いてなかったり、なんだか長いパッケージだったり、"ConoHa" ではなく "Conoha" になっていたりといろいろツッコミどころはありますが、自分で作ったパッケージが composer 経由でインストールされるのを見るとなんだか感動します。

今回は単なるヘルパークラス的なものでしたが、せっかくなので Storage ファサードから利用できるようにすると良いかなーなどと思いました。


## 余談
タイトルは「Laravel 5 パッケージのつくり方」となっていますが、Laravel 4 とどれだけ互換性があるかは調べていません。
Larvel 4 用と 5 用で別パッケージになっているケースを多く見かけるので、互換性はないんじゃないかなぁ、というのが予想です。