Help us understand the problem. What is going on with this article?

Laravel でいつも最初から仕込むようなファイルたちはパッケージにしてしまおう

はじめに

以前、Laravel の Validation を正しく拡張する という記事を書きましたが、こういったバリデーションの拡張などは最初から仕込みで動いていて欲しいわけです。

なので composer でインストール後、コマンドを叩けばある程度の設定が最初から入ってしまう状態にしたい、という欲求が湧いてきます。

これに答えてくれるのがパッケージ化です。

FuelPHP を使ってた頃は作り方が簡単だったんですが、Laravel のパッケージは定型があまりないので自由度が高い分、ちとやり方がめんどくさいですが整理のためにも記録として残しておきます。

Laravel におけるパッケージとは簡単に言えば vendor ディレクトリ以下にインストールされるファイル群たちの構成要素を自分で一つ増やす感じの配置になりますね。

開発の準備

ディレクトリは任意です。
ただ動作確認的には自分自身の Laravel と組み合わせながら作るのが一番手っ取り早いとは思いますので、開発中は Laravel をインストール後に app たちと同一階層とかにパッケージ専用ディレクトリを作るのが自分としてはわかりやすかったです。

$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.4.3 — cli) by Justin Hileman
>>> base_path();
=> "/FULL/PATH/Parent/PackageDir"

この base_path() と同じ所にいれるのがよかったです。このディレクトリで下記を実行。

$ mkdir packages
$ cd packages
$ composer init

いろいろ聞かれると思いますが、あとでどうせ編集するので適当でよいです。
この composer.json を最終的に下記のように調整しました。一部情報はダミーです。

packages/composer.json
{
    "name": "funaffect/packages",
    "description": "Funaffect Laravel Packages",
    "authors": [
        {
            "name": "moobay9",
            "email": "dummy@mail.dummy"
        }
    ],
    "require": {
        "php": "^7.0",
        "laravel/framework": "5.8.*"
    },
    "autoload": {
        "psr-4": {
            "Funaffect\\LaravelPackages\\": "app/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Funaffect\\LaravelPackages\\Providers\\StartupServiceProvider"
            ]
        }
    }
}

ここでは

  • 依存
  • 名前空間とオートロード
  • Laravel 本体から呼ばれる ServiceProvider

を合わせて設定しています。

名前空間とオートロード

composer.json の autoload 部分ですが、今回は app/ にしていますが、これは任意です。
ただディレクトリは具体的にどこだよ、って感じになりますが、

base_path().'/packages/app/'

です。app と同じ階層に今回は config と resources を置きました。
ディレクトリ構造を Laravel がインストールされているものと合わせたかったからです。

名前空間は任意ですきなものをつかたら良いと思いますが、今後ついてまわるのでちゃんと考えて名前をつけましょう。
今回は psr-4 の下の Funaffect\\LaravelPackages の部分ですね。

サービスプロバイダの準備

今回は Funaffect\\LaravelPackages\\Providers\\StartupServiceProvider という名前にしたサービスプロバイダーでこのパッケージのインストールから利用まで全てをカバーさせます。
設置パスは

base_path().'/packages/app/Providers/StartupServiceProvider.php'

になりますので、ここにファイルを作りました。

サービスプロバイダでできること

最初にファイルを展開することと、アプリケーションが動作中にもサービスプロバイダーを通過して起動するようになります。
このサービスプロバイダで自分がやりたかったのは、下記の数ありました。

  • バリデーションの拡張ファイルの設置
  • バリデーションルールの追加
  • config のファイルを設置
  • 言語ファイルの設置
  • middleware の追加

これは後ほど細部を説明します。

動作させてみる

親のディレクトリ(Laravel の Root)にある composer.json で、一個下のパッケージ開発中の composer.json を認識させる必要があります。

composer.json
"repositories": [
    {
        "type": "path",
        "url": "/packages",
        "symlink": true
    }
],

このあと

composer require funaffect/packages 

でインストールできます。require は packages/composer.jsonname と一致させてください。
場合によってはそのあとで

php artisan vendor:publish --tag=packages

が必要です。

サービスプロバイダに設定していく

サービスプロバイダー内は基本的に register()boot() で設定していきますが、register はドキュメントなどにも記載がありますが、まだロードが終わってなかったりインスタンス化されてないクラスがあったりしますので大抵は boot 側に記載していくことになると思います。

configファイル を app/config へ設置する

packages/app/Providers/StartupServiceProvider.php
    public function boot() {
        $this->publishes([
            __DIR__.'/../../config/packages.php' => config_path('packages.php'),
        ], 'packages');
    }

publishes() で設置するべきディレクトリに設置するべきファイルをおくだけですね。今回はパッケージ内にある packages/config/packages.php をいつも使う config のディレクトリの中へコピーできるようにしました。

ミドルウェアを追加する

ミドルウェアは通常、Route から始まるクラスが出てくるあたりで設定しますので、一般的には routes/web.php の中でやっていることが多いと思います。
ですので、web というミドルウェアグループがありますんで、これに追加してあげる形にしました。

packages/app/Providers/StartupServiceProvider.php
use Funaffect\LaravelPackages\Http\Middleware\ConvertDot;
// (略)

    public function boot() {
        $this->app['router']->pushMiddlewareToGroup('web', ConvertDot::class);
    }

バリデーションを拡張する

バリデーションの拡張はクラスにまとめる場合と一個一個追加する場合があります。
例では大まかに ExtensionValidator でクラスごとバリデーションを拡張、 max_lengthmin_length を個別に追加しています。
それとは別にルールを一つ追加するために publishes で先ほどの config と同じようにファイルを設置しました。
(設置先はパスでわかると思います)

packages/app/Providers/StartupServiceProvider.php
use Funaffect\LaravelPackages\Http\Validators\ExtensionValidator;
    public function boot() {
        // Validation Extension
        $this->app['validator']->resolver(function($translator, $data, $rules, $messages, $attribute) {
            return new ExtensionValidator($translator, $data, $rules, $messages, $attribute);
        });

        $this->app['validator']->replacer('max_length', function ($message, $attribute, $rule, $parameters) {
            return str_replace(':max', $parameters[0], $message);
        });

        $this->app['validator']->replacer('min_length', function ($message, $attribute, $rule, $parameters) {
            return str_replace(':min', $parameters[0], $message);
        });

        $this->publishes([
            __DIR__.'/../Rules/CurrentPassword.php' => app_path('Http/Rules/CurrentPassword.php'),
        ], 'packages');
    }

初期設定を上書きする

config 配下の app.config を毎度毎度書き直していますが、これもめんどくせえとなったときには最初から変えておく手段が欲しいなと思った時に、 mergeConfigFrom() という真逆の動きをする関数がサービスプロバイダ内部にあります。なのでこれの src と dst を入れ替えた mergeConfigFor() という関数を作り、register で実行するようにしました。

packages/app/Providers/StartupServiceProvider.php
    public function register()
    {
        // Config
        $this->mergeConfigFor(__DIR__.'/../../config/app.php', 'app');
    }

    protected function mergeConfigFor($path, $key)
    {
        if (! ($this->app instanceof CachesConfiguration && $this->app->configurationIsCached())) {
            $this->app['config']->set($key, array_merge(
                $this->app['config']->get($key, []), require $path
            ));
        }
    }

ここまでで、大体やりたいことは初期構築時に組み込めるようになりました。

実際に運用する場合

こちらで公開してますので、README にも同じことを書いていますが、Laravel をインストール後、composer.json に以下を追加してください。

composer.json
"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/moobay9/laravelpackages.git",
        "symlink": true
    }
],
"require": {
    "funaffect/packages": "dev-master"
},

composer install 実行後、php artisan vendor:publish --tag=packages をお忘れなく。

なお、packagist には登録していません。

まとめ

駆け足で振り返ってきましたが、とりあえず備忘録の代わりなのでミスが見つかったら随時修正していきます。
ミスを発見した方はお手数ですがコメントでお知らせください。
git のほうは pull req していただいても構いません。

moobay9
普段はインフラをつついたりネットワークを設計構築したりしてるせいでプログラム力がまるでない。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした