Laravel開発でパッケージ開発を取り入れてみよう

# はじめに

今回はComposerを使ってLaravelの独自パッケージ開発をしてみました。

# 前提
- Laravelプロジェクト（パッケージ導入先となるプロジェクト：以下、親プロジェクトと記載します）
- Composer

# 本編

今回は`sleeplovers`という睡眠を愛してやまない方が
`offton`パッケージを作った想定でパッケージの実装をしていきます。

## パッケージのローカル開発準備

親プロジェクトのインストールまでは既に行っているものとします。

まずはインストール済みの親プロジェクト内に`packages`ディレクトリを用意します。
こちらはローカル環境でのLaravel向けパッケージの配置場所となります。

```
Laravel
  |- /app
  |- /packages    配置先
  |- /bootstarp
  ...
  |-composer.json
  |-composer.lock
```


親プロジェクトがgit管理対象となっている場合は差分検知されてしまいますので、
`.gitignore`に除外するように記載しておきましょう。

```.gitignore
/packages
```

それでは、下記のコマンドを実行します。

```bash
# プロジェクトのルートディレクトリで
mkdir -p packages/sleeplovers/offton
cd packages/sleeplovers/offton
```

移動後、git管理ができるように別のリポジトリを設定しておきます。

```
git init
git branch -m main
git remote add ...
```

こちらで作成したリポジトリにプッシュすることで
最終的に`composer install`でインストールできるようになります。


## パッケージの初期設定を行う

```bash
composer init
```

対話形式で聞かれるので、こんな感じで答えていきます：
- Vendor名：`sleeplovers`
- パッケージ名：`offton`
- それ以外は未記入で「Enter」します


```
/src/packages/sleeplovers/offton# composer init

                                            
  Welcome to the Composer config generator  
                                            


This command will guide you through creating your composer.json config.

Package name (<vendor>/<name>) [user/offton]: sleeplovers/offton
Description []: 
Author [n to skip]: n
Minimum Stability []: 
Package Type (e.g. library, project, metapackage, composer-plugin) []: library
License []: 

Define your dependencies.

Would you like to define your dependencies (require) interactively [yes]? 
Search for a package: 
Would you like to define your dev dependencies (require-dev) interactively [yes]? 
Search for a package: 
Add PSR-4 autoload mapping? Maps namespace "Sleeplovers\Offton" to the entered relative path. [src/, n to skip]: 

{
    "name": "sleeplovers/offton",
    "type": "libraly",
    "autoload": {
        "psr-4": {
            "Sleeplovers\\Offton\\": "src/"
        }
    },
    "require": {}
}

Do you confirm generation [yes]? yes
Generating autoload files
Generated autoload files
PSR-4 autoloading configured. Use "namespace Sleeplovers\Offton;" in src/
Include the Composer autoloader with: require 'vendor/autoload.php';

```

### composer.jsonをカスタマイズ
生成されたcomposer.jsonを、こんな感じに編集します：

```json
{
    "name": "sleeplovers/offton",
    "description": "Laravel用オフタイム管理パッケージ",
    "type": "library",
    "require": {
        "php": "^8.2",
        "laravel/framework": "^12.0"
    },
    "autoload": {
        "psr-4": {
            "Sleeplovers\\Offton\\": "src/"
        }
    },
    "extra": { 
        "laravel": { ✨こちらの記載でLaravelから認識できるようになります
            "providers": [
                "Sleeplovers\\Offton\\OfftonServiceProvider" 
            ]
        }
    }
}
```


## パッケージ内容を実装する

### サービスプロバイダーを作成

```php:./packages/sleeplovers/offton/src/OfftonServiceProvider.php
<?php

namespace Sleeplovers\Offton;

use Illuminate\Support\ServiceProvider;

class OfftonServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // ルーティングの登録
        $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
        // ビューの登録
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'offton');
    }

    public function register()
    {
        // サービスの登録
    }
}
```

### ルーティングとコントローラーの追加

```php:./packages/sleeplovers/offton/src/Http/Controllers/OfftonController.php
<?php

namespace Sleeplovers\Offton\Http\Controllers;

use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;

class OfftonController extends Controller
{
    public function index()
    {
        return View::make('offton::index');
    }
}
```

### ビューの作成

```php:./packages/sleeplovers/offton/src/resources/views/index.blade.php
<!DOCTYPE html>
<html>
<head>
    <title>Offton - 布団が吹っ飛んだ</title>
    <style>
        body { 
            font-family: Arial, sans-serif; 
            display: flex; 
            justify-content: center; 
            align-items: center; 
            height: 100vh; 
            margin: 0; 
            background-color: #f0f0f0; 
            font-size: 2rem; 
            text-align: center;
        }
    </style>
</head>
<body>
    <div>
        布団が吹っ飛んだ💥
    </div>
</body>
</html>
```

### ルーティングファイルの作成

```php:./packages/sleeplovers/offton/src/routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use Sleeplovers\Offton\Http\Controllers\OfftonController;

Route::get('/offton', [OfftonController::class, 'index']);
```

## 反映と確認

### 親プロジェクトのcomposer.jsonを編集
下記の設定を追加します。こちらの設定を行うことでローカル開発しているライブラリを`vender`ディレクトリにインストールした扱い（シンボリックリンクを貼ってくれている）ようになります。

```json
{
    "repositories": [
        {
            "type": "path",
            "url": "packages/sleeplovers/offton"
            "options": {
                "symlink": true
            }
        }
    ],
    "require": {
        "sleeplovers/offton": "@dev"
    }
}
```

### ライブラリのインストール状況を更新する
こちらのコマンドで上記の設定が反映されるようになります。
これでリアルタイムに親プロジェクトを使ったパッケージ開発をすることができます。

```bash
composer update sleeplovers/offton
```

### URLにアクセス

パッケージをインストール後は`/offton`にルーティングが追加されているので
アクセスすると「布団が吹っ飛んだ💥」が表示されるようになります。

# まとめ
パッケージ開発って難しそう😢と思っていましたが、
やってみると意外と簡単に環境は作成することができました！

公開についてもどこかで記事にしたいなと思います。