【第4回】LaravelのORM「Eloquent」を単体で使ってスクラッチ開発する

【第4回】Composerを使ってお手軽アプリケーション開発

本連載ではComposerで公開されているパッケージの中から、フレームワークを問わず汎用的に使えるライブラリをサンプルコードと共に紹介します。

今回はDBを扱う「Eloquent(Illuminate/Database)」というORMを紹介します。

ORMとEloquent

ORMとは?

ORMは「Object Relational Mapping (オブジェクト関係マッピング)」と呼ばれている、オプジェクト指向で書かれているプログラムから、オブジェクト指向ではないデータベースなどのデータをオプジェクト指向のように扱う手法のことを言います。

かみ砕いて説明すると、データベースをオブジェクト指向っぽく扱うためのクラス構造ということです。

例えば、

SELECT 'id', 'name', 'age' FROM users WHERE age >= 20

のようなSQL文があったときに、ORMを使用すると下記のようなオブジェクト指向で書くことができます。

DB::table('users')->where('age', '>=', 20)->get(['id', 'name', 'age']);

Eloquentとは?

EloquentとはPHP製フレームワークLaravelのデータベース関連のライブラリIlluminate/Databaseに含まれているORMです。

LaravelやLumenを利用しないと使えないと思われがちですが、リポジトリのreadmeにも記載されているように、Illuminate/Databaseだけを単体で使用することもできます。

Illuminate/Databaseの導入方法

インストールは他のライブラリ同様

composer require illuminate/database

を実行するか、composer.jsonに下記を記載して、composer updateを実行します。

{
    "require": {
        "illuminate/database": "^5.7"
     }
}
2018/12/07時点の最新バージョンは5.7.15です。

Illuminate/DatabaseからDBに接続する

まずは、Eloquentを使用するためにもIlluminate/Databaseからデータベースに接続する設定例を見てみましょう

<?php

require_once __DIR__ . '/vendor/autoload.php';

class_alias(Illuminate\Database\Capsule\Manager::class, 'DB');

# DB設定
$database = new DB();

// 接続情報
$config = [
    'driver'    => 'mysql',              // mysql, pgsql, sqlite, sqlsrv から選択可
    'host'      => 'localhost',          // ホスト名
    'database'  => 'database',           // データベース名
    'username'  => 'user',               // ユーザー名
    'password'  => 'password',           // パスワード
    'charset'   => 'utf8mb4',            // 文字セット
    'collation' => 'utf8mb4_general_ci', // コレーション
];
// コネクションを追加
$database->addConnection($config);

// グローバルで(staticで)利用できるようにする宣言
$database->setAsGlobal();

// Eloquentを有効にする
$database->bootEloquent();

基本的には公式のreadmeと同じですが、

class_alias(Illuminate\Database\Capsule\Manager::class, 'DB');

でクラスのエイリアスを指定していることが1つのポイントです。

これをすることによって、LaravelのDBファサードと同じようにDB::table()のようなメソッドを使うことができます。

接続情報は前回紹介したdotenvを使って、管理すると良いでしょう。

接続ができたところで、Illuminate/DatabaseのクエリビルダやEloquentを使ってみたいと思います。

クエリビルダを使用する

クエリビルダを使用した簡単なCRUD操作のサンプルを紹介します。詳細な使い方はreadoubleを参考にしてください。

<?php
# データ取得 (SELECT文)
DB::table('users')
    ->where('age', '>=', 20)
    ->get(['id', 'name', 'age']);

# データ挿入 (INSERT文)
$values = [
    'name'  => 'ソルダー',
    'email' => 'solder@technoledge.net',
    'age'   => '28',
];
DB::table('users')->insert($values);

# データ更新 (UPDATE文)
DB::table('users')
    ->where('id', '=', 1)
    ->update(['email' => 'tak-solder@technoledge.net']);

# データ削除 (DELETE文)
DB::table('users')
    ->where('id', '=', 1)
    ->delete();
データ挿入の処理はDB::table('users')->insertGetId($values)にすると挿入した行のIDが返り値として取得できます。

Eloquentモデルを使用する

Eloquentを利用するときもLaravelから使うときとほとんど相違なく使用することができます。詳細な使い方はreadoubleを参考にしてください。

モデル定義

<?php
use Illuminate\Database\Eloquent\Model;

/**
 * Class User
 * @property int $id
 * @property string $name
 * @property string $email
 * @property int $age
 * @mixin \Illuminate\Database\Eloquent\Builder
 */
class User extends Model {

}
phpdocにDBの各カラムをproperty属性に書いたり、 \Illuminate\Database\Eloquent\Builderをmixin属性として記載することで、PHPStormなどのIDEで補完されやすくなります。

クエリビルダ風のデータ操作

<?php
# SELECT文
User::where('age', '>=', 20)
    ->get(['id', 'name', 'age']);

## データ挿入 (INSERT文)
$values = [
    'name'  => 'ソルダー',
    'email' => 'solder@technoledge.net',
    'age'   => 28,
];
$user = User::create($values);
// Eloquentのcreateメソッドは作成されたレコードのインスタンスが返る
echo $user->id;    // 1
echo $user->name;  // ソルダー
echo $user->email; // solder@technoledge.net
echo $user->age;   // 28

## データ更新 (UPDATE文)
User::where('id', '=', 1)
    ->update(['email' => 'tak-solder@technoledge.net']);

## データ削除 (DELETE文)
User::where('id', '=', 1)->delete();

インスタンスを軸としたデータ操作

<?php
# データ挿入 (INSERT文)
$user = new User();
$user->name  = 'ソルダー';
$user->email = 'solder@technoledge.net';
$user->age   = 28;
$user->save();
// こちらのやり方でも保存(save実行後)はレコードのIDが取得できる
echo $user->id;    // 1

# データ更新 (UPDATE文)
$user = User::find(1);
$user->email = 'tak-solder@technoledge.net';
$user->save();

# データ削除 (DELETE文)
$user = User::find(1);
$user->delete();

複数のデータベースを使用するときの設定

アプリケーション内で複数のデータベースを使用するときの実装例を紹介します。Illuminate/Databaseでは1つのデータベースに接続するのに1つのコネクションを生成する必要があるので、異なるサーバや異なる種類のDBに接続する場合はもちろん、同じサーバの別のDBに接続するときにも有効です。

<?php

require_once __DIR__ . '/vendor/autoload.php';

class_alias(Illuminate\Database\Capsule\Manager::class, 'DB');

$database = new DB();

// 接続情報 その1
$default_config = [
    'driver'    => 'mysql',              // mysql, pgsql, sqlite, sqlsrv から選択
    'host'      => 'localhost',          // ホスト名
    'database'  => 'default',            // データベース名
    'username'  => 'user',               // ユーザー名
    'password'  => 'password',           // パスワード
    'charset'   => 'utf8mb4',            // 文字セット
    'collation' => 'utf8mb4_general_ci', // コレーション
];
$database->addConnection($config); // 第2引数を指定しなければ、コネクション名が"default"になる

// 接続情報 その2
$extend_config = [
    'driver'    => 'mysql',              // mysql, pgsql, sqlite, sqlsrv から選択
    'host'      => 'localhost',          // ホスト名
    'database'  => 'extend',          // データベース名
    'username'  => 'user',               // ユーザー名
    'password'  => 'password',           // パスワード
    'charset'   => 'utf8mb4',            // 文字セット
    'collation' => 'utf8mb4_general_ci', // コレーション
];
$database->addConnection($extend_config, 'extend'); // 第2引数を指定しているので、コネクション名が"extend"になる

// コネクションの指定がない時は、"default"のコネクションを使用してDBにアクセスする (default.usersテーブルから取得)
DB::table('users')->where('id', '=', 1)->get();

// "extend"のコネクションを使用してDBにアクセスする (extend.usersテーブルから取得)
DB::connection('extend')->table('users')->where('id', '=', 1)->get();

また、クラス作成時にコネクション名をプロパティとして格納することで、Eloquentを使用する際でも複数データベースを使い分けることができます。

<?php
/**
 * Class User
 * @property int $id
 * @property string $name
 * @property string $email
 * @property int $age
 * @mixin \Illuminate\Database\Eloquent\Builder
 */
class User extends Model {
    // このモデルでは"extend"のコネクションを使用する
    protected $connection = 'extend';
}

まとめ

Laravelを使ったことがある方はもちろん、使ったことがない方でもEloquentを使用すると少ない学習コストでDBを扱いやすくなります。もちろん、JOINやUNIONといった複雑なクエリも生成することができるので、幅広いアプリケーションで使用することができます。