【Laravel】パッケージ開発の第一歩 〜helloページを出力する〜

Laravelのパッケージ開発に必要な手順をまとめました。
なお、この記事ではローカルでの動作確認のみとしてpackagistへの登録等は行いません。

# 動作環境
- Laravel 8.12
- PHP 7.4.1
- Composer 2.0.4

どちらもローカルで動作させます

# ざっくりとした仕様
ブラウザで`http://localhost:8000/hello`にアクセスすると「Hello from SayHello Package!」と表示できるようになるパッケージの開発

# 大まかな流れ
- 必要なディレクトリを作成する
- composer.jsonを作成する
- パッケージ開発をする
- パッケージを利用したい側のcomposer.jsonにrepositoriesをキーに使用したいパッケージへのパスを記述する
- composer require [vendor]/[package_name]でパッケージをインストールする

# ディレクトリ構成
[laravelpackage.com](https://laravelpackage.com/02-development-environment.html#package-skeleton)によるとLaravelのプロジェクトとパッケージのディレクトリは分けた方がよい（highly recommend）らしいのですが、今回はごく簡単なパッケージなのでLaravelプロジェクトの中にpackages/というディレクトリを切ってその中で開発します。

```
pac-dev/　<-　ライブラリを使用するLaravelアプリのルートディレクトリ
　├ app/
　├ packages/
　│　└ nasteng/
　│　　└ say-hello/　<-　今回作成するパッケージ
　│　　　├ routes/
　│　　　│　　└ routes.php
　│　　　├ resources/
　│　　　│　　└ views/
　│　　　│　　　└ hello.blade.php
　│　　　├ src/
　│　　　│　　├ Http/
　│　　　│　　│　└ Controllers/
　│　　　│　　│　　└ HelloController.php
　│　　　│　　└ HelloServiceProvider.php 
　│　　　└ composer.json
　└ ・・・
```

それでは実装していきます。

# 実装
## composer.jsonの作成
Laravel new [project名(今回はpac-dev)]でプロジェクトが準備できたらpackages/[vendor_name]/[package_name]ディレクトリを作成します。[vendor_name]は省略してpackages/[package_name]としても(のちの名前空間の指定を正確に記述できれば)問題ないかと思います。

```
$ mkdir packages && cd packages
$ mkdir nasteng && cd nasteng 
$ mkdir say-hello && cd say-hello
```

ターミナルでcomposer initをすると対話形式でcomposer.jsonを作成することができますが、今回は手打ちしました。
重要なのは**autoload**と**extra**です。
名前空間を使用する場合はautoloadで指定、Laravelプロジェクトに読み込ませるServiceProviderがある場合はprovidersに追記します。

また、今回はLaravel8で新しくなったルーティングの書き方をしているのでrequireでlaravel8以上に依存するように書いておきます。

```json:composer.json
{
    "name": "nasteng/say-hello",
    "description": "Add hello world automatically",
    "authors": [{
        "name": "nasteng"
    }],
    "require": {
        "laravel/framework": "8.*"
    },
    "license": "MIT",
    "autoload": {
        "psr-4": {
            "Nasteng\\SayHello\\": "src/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Nasteng\\SayHello\\HelloServiceProvider"
            ]
        }
    }
}

```

## ServiceProviderの作成
`php artisan make:provider HelloServiceProvider`で作成ができますが、出力先がapp/Providersディレクトリなのでパッケージのディレクトリに移動させてnamespace等を編集する必要があります。

```PHP:say-hello/src/HelloServiceProvider.php
<?php

namespace Nasteng\SayHello;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;

class HelloServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRoutes();
        $this->configureViews();
    }

    private function configureRoutes()
    {
        $this->loadRoutesFrom(__DIR__ . '/../routes/routes.php');
    }

    private function configureViews()
    {
        $this->loadViewsFrom(__DIR__ . '/../resources/views', 'nasteng');
    }
}
```

$this->loadRoutesFrom()メソッドでパッケージ内で定義するルーティングファイルを読み込みます。これによって自動的にLaravelアプリケーションのルーティングにroutes.phpの内容が追加されます。

$this->loadViewsFrom()メソッドではパッケージ内で使用するViewの場所を第一引数に渡し、第二引数はパッケージ名を指定します。詳しくは[こちら](https://readouble.com/laravel/8.x/ja/packages.html)のビューの項目を参考ください。こうすることでパッケージ内で`view('nasteng::hello')`のようにヘルパメソッドのview()が使えるようになります。

## Controllerの作成
localhost:8000/helloで受けたリクエストを処理するControllerを作成します。

```PHP:say-hello/src/Http/Controllers/HelloController.php
<?php

namespace Nasteng\SayHello\Http\Controllers;

use Illuminate\Routing\Controller;

class HelloController extends Controller
{
    public function index()
    {
        // パッケージ内のviewを指定
        return view('nasteng::hello');
    }
}

```

## routesファイルの作成
helloページを表示するためのルーティングを定義します。
/helloへのアクセスは先ほど作成したHelloControllerのindexメソッドが処理します。

```PHP:say-hello/routes/routes.php
<?php

use Illuminate\Support\Facades\Route;
use Nasteng\SayHello\Http\Controllers\HelloController;

Route::get('hello', [HelloController::class, 'index']);
```

## ビューの作成
今回は単にHello from SaHello Package!というH1タグを表示するBladeをsrc/resources/viewsディレクトリに作成します。

```PHP:say-hello/resources/views/hello.blade.php
<h1>Hello from SayHello Package!</h1>
```

## Laravelアプリケーション側のcomposer.jsonを編集
ローカルで作成しているパッケージを使うときは`repositories`というキーに追記していきます。場所はどこでも構いません。urlは絶対パスでも相対パスでもよいですが、相対パスの場合はLaravelアプリのcomposer.jsonからのパスを指定します。optionsのsymlinkはtrueにしておくとシンボリックリンクを貼ってくれるので、パッケージ開発中にソースコードを変更しても`composer update`不要でリアルタイムに反映してくれます。

```json:pack-dev/composer.json
    "repositories": [{
        "type": "path",
        "url": "./packages/nasteng/say-hello",
        "options": {
            "symlink": true
        }
    }],
    "require": {
・・・
```

## Laravelアプリケーション側でrequireする
ここまで書いたら後は使いたいLaravelアプリ側で`composer require nasteng/say-hello`するとパッケージをインストールしてくれます。

```
$ composer require nasteng/say-hello
Using version dev-master for nasteng/say-hello
./composer.json has been updated
Running composer update nasteng/say-hello
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking nasteng/say-hello (dev-master)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing nasteng/say-hello (dev-master): Symlinking from ./packages/nasteng/say-hello
Generating optimized autoload files
・・・
```
これでパッケージのインストールは完了です。

`php artisan serve`でビルトインサーバーを起動してlocalhost:8000/helloにアクセスすると・・・

![スクリーンショット 2020-11-02 22.37.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115590/89a087b3-e574-492e-d05d-6cfba8a38ee0.png)

表示できました！ 

機会があればもう少し実用的な機能を追加してみたいと思います。

以上、HappyなLaravelライフを🎉