4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PHPフレームワークのORMを徹底比較!Eloquent(Laravel) vs Model/Entity(CodeIgniter4)

Last updated at Posted at 2022-11-21

はじめに

私は現在(2022/11時点)社内システム開発をメインに担当しています。

執筆時点で社内システムは10個以上存在し、各システムのフレームワークもバラバラです。
その中でも比較的採用数が多いのが Slim4+EloquentCodeIgniter(3 or 4) になります。

Eloquent はもともとLaravelフレームワークのORMとして提供されているサービスであり、多機能であることが特徴です。
一方、CodeIgniter はLaravelに比べて軽量でパフォーマンスが良いのが特徴です。
バージョン4になってからは様々な機能が追加され、3以前でも存在していた Model に加え、取得レコードをエンティティとして扱うことに特化した Entity が新たに導入されました。1
2022年現在も機能が追加されており、バージョンが上がるたびに使いやすくなってきています。

そこで、Eloquent と CodeIgniter(4に限定します)の Model/Entity とを機能ごとに比較し、それぞれの使い勝手を見てみることにしました!
今はLaravel使ってるけど、機能多すぎだしCodeIgniterも少し気になるから使ってみたい!という方の参考になれば幸いです。

モデルの作成

どちらもコマンドで作成可能です。
CodeIgniter4のほうがデフォルトで生成されるコード量は多いです。

Eloquent

空のモデルが作成されます。
ただしそのままだとApp直下に作成されるため、名前空間を付けたい場合はコマンド引数に Entities/User のような形で指定する必要があります。

生成コマンド
$ php artisan make:model User
User.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    //
}

Model/Entity(CodeIgniter4)

各プロパティにデフォルト値がセットされたモデルが作成されます。
名前空間は App\Models となります。

生成コマンド
$ php spark make:model UserModel
$ php spark make:entity User
UserModel.php
<?php

namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $DBGroup          = 'default';
    protected $table            = 'users';
    protected $primaryKey       = 'id';
    protected $useAutoIncrement = true;
    protected $insertID         = 0;
    protected $returnType       = 'array';
    protected $useSoftDeletes   = false;
    protected $protectFields    = true;
    protected $allowedFields    = [];

    // Dates
    protected $useTimestamps = false;
    protected $dateFormat    = 'datetime';
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';
    protected $deletedField  = 'deleted_at';

    // Validation
    protected $validationRules      = [];
    protected $validationMessages   = [];
    protected $skipValidation       = false;
    protected $cleanValidationRules = true;

    // Callbacks
    protected $allowCallbacks = true;
    protected $beforeInsert   = [];
    protected $afterInsert    = [];
    protected $beforeUpdate   = [];
    protected $afterUpdate    = [];
    protected $beforeFind     = [];
    protected $afterFind      = [];
    protected $beforeDelete   = [];
    protected $afterDelete    = [];
}
User.php
<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $datamap = [];
    protected $dates   = ['created_at', 'updated_at', 'deleted_at'];
    protected $casts   = [];
}

モデルにセットできる主なプロパティ

CodeIgniter4でプロパティにセットできるものが全てEloquentでもセットできるわけではなく、トレイトや定数、カスタム関数などでセットするものもあるようです。
イベントはEloquentのほうが豊富です。

Eloquent Model(CodeIgniter4)
DB接続先 ○($connection) ○($DBGroup)
テーブル名 ○($table)
デフォルトはModel名スネークケースの複数形
○($table)
Eloquentと異なり必須
主キー ○($primaryKey) ○($primaryKey)
複合キーは指定できない
主キーのオートインクリメント ○($incrementing) ○($useAutoIncrement)
主キーのデータ型 ○($keyType) ×(intのみ)
結果オブジェクトの型 ×(Modelに固定) ○($returnType)
Entityを指定可
論理削除フラグ △(SoftDeletesトレイトを使用) ○($useSoftDeletes)
更新許可フィールド ○($fillable) ○($allowedFields)
更新禁止フィールド ○($guarded) ×
表示フィールド ○($visible) ×
非表示フィールド ○($hidden) ×
現在日時の自動設定 ○($timestamps) ○($useTimestamps)
DBに入れる日付値の型 ○($dateFormat) ○($dateFormat)
作成日時フィールド △(CREATED_AT定数) ○($createdField)
更新日時フィールド △(UPDATED_AT定数) ○($updatedField)
削除日時フィールド △(DELETED_AT定数) ○($deletedField)
属性値のキャスト ○($casts) ○(Entityの$casts)
デフォルト属性値 ○($attributes) ×
バリデーション ×
$validateRules にルールをセットする
$validationMessages にエラーメッセージをセットする
$skipValidation = trueでバリデーションをスキップ可
イベント ○($dispatchesEvents)
以下のイベントを連想配列にして登録可
retrieved
creating
created
updating
updated
saving
saved
deleting
deleted
trashed
forceDeleted
restoring
restored
replicating
○以下のイベントをプロパティ名にして登録可
beforeInsert
beforeInsert
beforeUpdate
afterUpdate
beforeFind
afterFind
beforeDelete
afterDelete

インスタンス生成

データを取得せず、空のエンティティを生成したい場合の書き方です。
Eloquentだとレコード登録も同時に行なえます。

Eloquent

new 演算子を使用します。

$entity = new User();

インスタンス生成と同時にDB保存も行いたい場合は create() を使用します。

$entity = User::create(['name' => 'セレス 太郎']);

Entity(CodeIgniter4)

new 演算子を使用します。2

$entity = new User();

Eloquentのcreate()にあたる機能はないようです。

findによる取得

引数にIDを指定し、エンティティを1件返します。
Eloquentのほうがバリエーションが多いです。

Eloquent

引数にIDを指定し、該当レコードが見つかればModelで返します。
配列でIDを複数指定するとCollectionで返却されます(内部的にはfindMany()を呼び出している)。

$entity = User::find($id);
$entities = User::find([$id1, $id2, $id3]);

findOr〜の形のメソッドもあり、find()に失敗した場合に所定の動作をします。

// 失敗したら例外
$entity = User::findOrFail($id);

// 失敗したらインスタンスを生成
$entity = User::findOrNew($id);

Model/Entity(CodeIgniter4)

Modelfind()します。IDの配列指定で複数レコード取得可能です(配列で返却されます)。
$returnTypeEntityをセットした場合はEntityで受け取れます。

$entity = model('App\Models\UserModel')->find($id);
$entities = model('App\Models\UserModel')->find([$id1, $id2, $id3]);

findOr系はないようです。

firstによる取得

クエリ結果の最初の1レコード目を返します。
バリエーションとしてはほぼfind()と同様です。

Eloquent

find()と同じくModelで返します。

$entity = User::where(['code' => '1'])->first();
// firstWhereだとさらに簡略化できる
$entity = User::firstWhere(['code' => '1'];

firstOr系のメソッドも定義されています。

// 存在しなければ例外
$entity  = User::where(['code' => '1'])->firstOrFail();

// 存在しなければインスタンスを生成
$entity = User::firstOrNew(['code' => '1']);

// 存在しなければレコード作成
$entity = User::firstOrCreate(['id' => $id], ['code' => '1', 'name' => 'セレス 太郎']);

Model/Entity(CodeIgniter4)

find()と同様、$returnTypeに指定した型で返します。

$entity = model('App\Models\UserModel')->where(['code' => '1'])->first();

firstOr系はないようです。

保存・変更検知

どちらにも保存機能はありますが、Eloquentのほうが変更検知機能は豊富です。

Eloquent

save()で保存します。
こちらもsaveOr系のメソッドが存在します。

// 保存
$entity->save();

// 保存に失敗したら例外
$entity->saveOrFail();

save()はエンティティの内容に変更がある場合のみDBにアクセスします。
変更があるかどうかはisDirty()を呼び出します(⇔isClean())。

// 変更前なのでfalse
var_dump($entity->isDirty());
// 値の変更
$entity->name = 'セレス 次郎';
// 変更したのでtrue
var_dump($entity->isDirty('name'));

save()実行で実際に保存されたかを確認するには、以下のようにします。

// 新規作成
$entity1 = User::create(['name' => 'セレス 次郎'])
// trueを返す
var_dump($this->wasRecentlyCreated);

// 変更
$entity2 = User::find(1)->fill(['name' => 'セレス 三郎'])->save();
// trueを返す
var_dump($this->wasChanged('name'));
// 保存したのでdirtyではなくなっている(false)
var_dump($this->isDirty('name'));

Entity(CodeIgniter4)

Eloquentと同様、save()で保存します。
saveOr系のメソッドはないようです。

$entity->save();

エンティティが変更されたかを確認するにはhasChanged()を使います。

$entity = new User();
// falseを返す
var_dump($entity->hasChanged('name'));

$entity->name = 'セレス 次郎';
// trueを返す
var_dump($entity->hasChanged('name'));

実際に変更が反映されたかどうかを確認するメソッドはないようです。

削除

Eloquent、Model(CodeIgniter4)、いずれもソフトデリート(SoftDeletes)に対応しています。

Eloquent

Modelに対応するレコードを削除するにはdelete()、レコードを直接削除するにはdestroy()を使用します。

// モデルから削除
$user = User::find(3);
$user->delete();

// ID指定で直接削除
User::destroy(3);

また、SoftDeletesトレイトを追加することでソフトデリート(論理削除)も扱えます。

// ソフトデリート
$user->delete();

// 復元
$user->restore();

// 完全削除
$user->forceDelete();

また、ソフトデリートに関するクエリビルダのメソッドとして以下のようなものがあります。

  • withTrashed() : ソフトデリートしたレコードも含む
  • onlyTrashed() : ソフトデリートしたレコードのみ

Model(CodeIgniter4)

レコードを削除するにはModeldelete()を使用します。
Entity側にはdelete()は存在しないようです。

model('App\Models\UserModel')->delete(3);

また、Model$useSoftDeletesプロパティにtrueをセットすることでソフトデリートも扱えます。

// ソフトデリート
model('App\Models\UserModel')->where('id', 3)->delete();

// 完全削除
model('App\Models\UserModel')->where('id', 3)->purgeDeleted();

また、ソフトデリートに関するクエリビルダのメソッドとして以下のようなものがあります。

  • withDeleted() : ソフトデリートしたレコードも含む
  • onlyDeleted() : ソフトデリートしたレコードのみ

リレーション

リレーション定義はEloquentのみ可能です。

Eloquent

リレーションを定義するための専用のメソッドがあります。
また、これらの仕組みを使って Eager Loading を実現できます。

子テーブル(1対1)

hasOne()を使用します。

User.php
public function address()
{
    return $this->hasOne('App\Models\Address');
}
子テーブル(1対多)

hasMany()を使用します。

User.php
public function articles()
{
    return $this->hasMany('App\Models\Article');
}
親テーブル

belongsTo()を使用します。

User.php
public function department()
{
    return $this->belongsTo('App\Models\Department');
}

Model/Entity(CodeIgniter4)

リレーションを定義する仕組みはありません。

アクセサ(getter)、ミューテタ(setter)

アクセサ、ミューテタを使用することで、取得・保存時に追加処理を加えたり、データベースにないフィールド(カスタムフィールド)を定義することができます。
Eloquent、Entity(CodeIgniter4)どちらでも定義ができ、書き方もほとんど変わりません。

Eloquent

アクセサは get + フィールド(パスカルケース) + Attribute 、ミューテタは set + フィールド(パスカルケース) + Attribute というメソッド名で定義します。

User.php
class User extends Model
{
    // 生年月日をもとに年齢を計算する
    public function getAgeAttribute()
    {
        if ($this->birthday === null) {
            return null;
        }
        $diff = date_diff(date_create($this->birthday), date_create());
        return $diff->y;
    }

    // パスワードを暗号化する
    public function setPasswordAttribute($plain)
    {
        $this->attributes['password'] = password_hash($plain, PASSWORD_BCRYPT);
    }
}

なお、日付項目については自動的にCarbon(Laravelにも標準搭載されている日時ライブラリ)インスタンスに変換されるアクセサやミューテタがデフォルトで使われているそうです。

Entity(CodeIgniter4)

アクセサは get + フィールド(パスカルケース)、ミューテタは set + フィールド(パスカルケース) というメソッド名で定義します。

User.php
class User extends Entity
{
    // 生年月日をもとに年齢を計算する
    public function getAge()
    {
        if ($this->birthday === null) {
            return null;
        }
        $diff = date_diff(date_create($this->birthday), date_create());
        return $diff->y;
    }

    // パスワードを暗号化する
    public function setPassword($plain)
    {
        $this->attributes['password'] = password_hash($plain, PASSWORD_BCRYPT);
        return $this;
    }
}

なお、日付項目についてはEntity$dateプロパティに配列形式で対象フィールドをセットしておくとTime型に自動変換してくれるそうです。

クエリビルダ

Eloquent、Model(CodeIgniter4)にはクエリビルダ機能も備わっています。
メソッドの数が多すぎるので、主に使われるメソッドに絞って簡単に比較してみます。

Eloquent Model(CodeIgniter4)
全件取得 条件を指定せずget() 条件を指定せずfindAll()
単一行取得 first()またはfind() first()またはfind()
単一値取得 value() なし
カラム値リスト取得 pluck() findColumn()
集計 count(),max(),min(),avg(),sum() countAllResults():集計値を整数で返す
selectCount(),selectMax(),selectMin(),selectAvg(),selectSum():SELECT項目の追加
レコード存在判定 exists()(否定形はdoesntExist()) なし
SELECT select(),selectRaw()(素のSELECT) select()(素のSELECTも可)
DISTINCT distinct() distinct()
WHERE where(),whereRaw()(素のWHERE)など where()など(素のWHEREも可)
HAVING having(),havingRaw()(素のHAVING)など having()など(素のHAVINGも可)
ORDER BY orderBy(),orderByRaw()(素のORDER BY)など orderBy()など(素のORDER BYも可)
GROUP BY groupBy(),groupByRaw()(素のGROUP BY)など groupBy()など(素のGROUP BYも可)
INNER JOIN join() join()の第三引数にinnerを指定
LEFT JOIN leftJoin() join()の第三引数にleftを指定
RIGHT JOIN rightJoin() join()の第三引数にrightを指定
CROSS JOIN crossJoin() join()の第二引数以降指定なし
複雑なJOIN クロージャーで設定可 全てstringで書く必要あり
サブクエリJOIN joinSub(),leftJoinSub(),rightJoinSub() サブクエリをstring化し、エスケープなしでjoin()の第一引数にセットする
UNION union(),unionAll() union(),unionAll()
サブクエリWHERE where()whereExists()などにクロージャーで指定 サブクエリをstring化し、エスケープなしでwhere()の第一引数にセットする
INSERT insert(),insertIgnore()(エラーの無視) insert()
バルクインサート insert()に二次元配列をセットする insertBatch()
INSERT IGNORE insertOrIgnore() ignore(true)->insert()
INSERT SELECT insertUsing() なし
UPSERT upsert() なし
更新または挿入 updateOrInsert() なし
DELETE delete() delete()
Raw DB::raw() new RawSql()(v4.2以降)

総評

双方のORMの主な機能をだいたい比較してみました。

やはりEloquentのほうがCodeIgniterに比べて断然使える機能は多いです。
ですが、CodeIgniterをバージョン3から使っている筆者としては、だいぶ機能が増えたなぁという感想です。

コレクションと Eager Loading の仕組みが無いのが一番大きいですが、そこに目を瞑ればCodeIgniterでも小〜中規模くらいのサービスなら開発できると思います。

また実際に開発してみて気づきましたが、Laravelのコードは複雑怪奇で読みにくい一方で、CodeIgniterのコードはすごく読みやすいです。
未だにLaravelは人気No.1のフレームワークですが、CodeIgniterももう少し人気が出てもいい気はします。

参考

  1. ModelがDAO寄り、EntityがRepository寄りのクラスとして定義されており、これまで通りModelのみでもアプリケーションを作ることはできます。

  2. Modelのほうはnew演算子でもインスタンス生成できますが、model()グローバル関数を使うことが推奨されています。model()を使うとModelオブジェクトが共有されるため、生成コストが抑えられます。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?