【Eloquent×Docker】Eloquent単体を最小限のDocker環境で使う

Eloquent単体のDocker環境を作成する手順です。
Dockerの設定は可能な限り減らしました。

:::note warn
Dockerは最小限の設定で構成されています。実務で使用するとエラーが発生する可能性がありますので、学習用や簡単な動作確認にのみお使いください。
:::

# 動作環境
- MacBook Air M1 2020
- macOS Ventura 13.2.1
- Docker Desktop 4.17.0

# 設定
## ゴール
Eloquentを利用してデータの取得を行う

## 要件
- PHPは``PHP8.2``、DBは``MySQL8.0.32``のDockerイメージを利用する
- コンテナ起動時に``データベース作成〜テーブル作成〜テストデータ投入``まで実行する
- Eloquentのパッケージ[illuminate/database](https://github.com/illuminate/database)を``composer``でインストールする

## ファイル・ディレクトリ構成
```shell
.
├── Dockerfile
├── app
│   ├── Models
│   │   └── User.php
│   ├── connection.php
│   └── main.php
├── composer.json
├── docker-compose.yml
└── mysql
    └── init
        └── init.sql
```

## ディレクトリ作成
必要なディレクトリを作成します。
```shell
% mkdir eloquent_docker
% cd eloquent_docker
% mkdir -p app/Models
% mkdir -p mysql/init
```
## Dockerの設定
### docker-compose.yml
各コンテナの設定を記述します。
コンテナはPHP用に``web``、MySQL用に``db``を用意します。今回composer専用のコンテナは用意していないので、パッケージのインストールもwebコンテナ内で行います。
```docker-compose.yml
version: '3'

services:
  web:
    # カレントディレクトリでイメージを構築
    build: .
    # /bin/bashでプロセスを継続させてコンテナの正常終了を防ぐ
    command: bash -c "composer install && /bin/bash"
    # カレントディレクトリをコンテナへマウント
    volumes:
      - .:/usr/src/web
    # プロセスを継続させてコンテナの正常終了を防ぐ
    stdin_open: true

  db:
    image: mysql:8.0.32
    # コンテナが停止しても自動で再起動する
    restart: always
    # ホストの13306番ポートにコンテナの3306番ポートをフォワード
    ports:
      - "13306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
    # コンテナ起動時に実行するSQL群をマウント
    volumes:
      - ./mysql/init:/docker-entrypoint-initdb.d
```
### Dockerfile
PHPイメージ構築の設定を記述します。
composerのインストール設定は[公式サイト](https://hub.docker.com/_/composer)からそのまま引用しました。
```Dockerfile
FROM php:8.2-cli
# パッケージリスト更新後にDockerイメージの構築に必要なパッケージをインストール
RUN apt-get update && apt-get install -y git zip unzip
# PDOを使用するための拡張モジュールをインストール
RUN docker-php-ext-install pdo_mysql
# composerのインストール
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY . /usr/src/web
WORKDIR /usr/src/web
```

## composer.jsonの設定
Eloquentを利用するため、``illuminate/database``のrequireとアプリケーション用の名前空間``App``を設定します。
```composer.json
{
    "require": {
        "illuminate/database": "^10.4"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    }
}
```

## コンテナ起動時に実行されるSQL群を設定
コンテナ起動時に実行されるSQLを記述します。
データベース``eloquent_test``とテーブル``users``を作成し、テストデータを投入します。
```mysql/init/init.sql
CREATE DATABASE eloquent_test;
-- 使用するデータベースを設定
USE eloquent_test;
-- テーブル作成
CREATE TABLE users
(
    id INT(10) AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30),
    email VARCHAR(50)
);
-- テストデータ投入
INSERT INTO users (name, email)
VALUES('Taro', 'taro@example.com'), ('Jiro', 'jiro@example.com'), ('Hanako', 'hanako@example.com');
```

## DB接続の設定
DBの接続設定を記述します。
設定は[公式サイト](https://github.com/illuminate/database)を参考にしました。

```app/connection.php
use Illuminate\Database\Capsule\Manager as Capsule;

require_once 'vendor/autoload.php';

$capsule = new Capsule;

$capsule->addConnection([
    'driver' => 'mysql',              // mysqlを指定
    'host' => 'db',                   // コンテナ名
    'database' => 'eloquent_test',    // データベース名
    'username' => 'root',             // ユーザー名
    'password' => 'password',         // パスワード
    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix' => '',
]);

// Capsuleインスタンスをグローバル化
$capsule->setAsGlobal();

// Eloquent起動
$capsule->bootEloquent();
```

## Model作成
Userモデルを作成します。
今回は``Illuminate\Database\Eloquent\Model``のメソッドを利用するので、クラス定義のみでOKです。
```app/models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

require_once 'app/connection.php';

class User extends Model
{
}
```

## メイン処理作成
Userモデル経由でusersのデータを取得する処理を記述します。
```app/main.php
use App\Models\User;

require_once 'app/connection.php';

$users = User::all();

foreach ($users as $user) {
    echo "id: {$user->id} name: {$user->name} email: {$user->email}\n";
}
```

# コンテナの起動〜動作確認
コンテナを起動します。
```shell
% docker-compose up -d
```
main.phpを実行するとデータの取得が確認できます。
```shell
% docker-compose exec web php app/main.php
id: 1 name: Taro email: taro@example.com
id: 2 name: Jiro email: jiro@example.com
id: 3 name: Hanako email: hanako@example.com
```

# まとめ
EloquentはLaravel経由で利用されることがほとんどです。ですが、Eloquent単体の動作を知ることによってエラーが発生した時に原因の切り分けがしやすくなります。Laravelのありがたみも分かります。

また、Docker環境を最小限の設定にすることにより、個々の設定の理解も深まると思います。

参考になれば幸いです。

# 参考記事
https://qiita.com/igayamaguchi/items/16bad23bd4e4cbed337a

https://re-engines.com/2018/10/04/%E5%8D%98%E4%BD%93%E3%81%AEorm%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%A8%E3%81%97%E3%81%A6eloquent%E3%82%92%E4%BD%BF%E3%81%86/

https://www.wakuwakubank.com/posts/596-mysql-8-with-docker/

https://qiita.com/nasuB7373/items/523f1392d87dffb5521d

https://ja.stackoverflow.com/questions/52132/docker-compose-up-%E3%81%A7-tty-true%E3%82%92%E4%BB%98%E3%81%91%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%AB-command%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E3%81%A8%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%8C%E7%B5%82%E3%82%8F%E3%81%A3%E3%81%A6%E3%81%97%E3%81%BE%E3%81%86

https://qiita.com/tarch710/items/1236a23f7ffde4c512f2

# 採用PR
弊社で一緒に働く仲間を募集しています。

Devトークやってます。お気軽にご応募ください(^^)

https://plus.jobs.qiita.com/dev-talk-company-pickup-vivion/

募集要項はこちらをご覧ください！

https://hrmos.co/pages/vivion/jobs?category=1687770395095928832