はじめに
今回はPHPに実装されている
トレイト(Trait)
と呼ばれる仕組みについて記載してみました。
トレイト(Trait)とは
以下、PHP公式ドキュメントでの説明が下記となります
PHP は、コードを再利用するための「トレイト」という仕組みを実装しています。
トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。
トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。
個人的な解釈
ここからは個人的な解釈となります。
トレイト(Trait)
関連のない【複数クラスで使用する可能性のあるメソッドを再利用できるようにする】ための仕組みです。
継承
【複数クラスで使用する可能性のあるメソッドを再利用できるようにする】を実現させるにあたって、トレイト(Trait)以外を使った方法を先に紹介します。
それが 継承 と呼ばれる仕組みを使った方法です。
継承 は超ざっくり言うと、親クラスの構造を引き継いだ(拡張した)子クラスを定義することです。継承した関係にあるクラスであれば基本的に親と同じメソッドを持っているので、メソッドの再利用を実現可能です。
class Hoge{
protected string $name = 'hoge';
public function toArray()
{
return get_object_vars($this);
}
}
class HogeFuga extends Hoge{
protected string $name = 'hoge-fuga';
}
$hogeFuga = new HogeFuga;
var_dump($hogeFuga->toArray());
// 出力
// array(1) {
// ["name"]=>
// string(8) "HogeFuga"
// }
上記コードのサンプルでは
Hoge
クラスを継承したHogeFuga
クラスを用意しました。
このHogeFuga
クラス(子)はHoge
クラス(親)を継承しているので
Hoge
クラス(親)に搭載されているtoArray
メソッドを使用可能です。
親子関係のない別のクラスにもtoArray
を取り入れたい...
ある時、Hoge
クラスのtoArray()
メソッドを
用途としてはまったく関係のない別のColor
クラスにも導入したいとなりました。
この際にHoge
を継承させたColor
クラスを作るやり方だと
Hoge
クラスに変更を加えた際にColor
クラスまで影響を受けることになります。
今回はあくまでメソッドだけを取り入れたいので
トレイト(Trait) を使ってメソッドだけを取り入れることにしました。
<?php
trait toArrayConvertable{
public function toArray()
{
return get_object_vars($this);
}
}
class Hoge{
use toArrayConvertable;
protected string $name = 'hoge';
}
class HogeFuga extends Hoge{
protected string $name = 'hoge-huga';
}
// 関係のないColorクラス
class Color{
use toArrayConvertable;
protected string $name = 'color';
}
$hogeFuga = new HogeFuga;
var_dump($hogeFuga->toArray());
$color = new Color;
var_dump($color->toArray());
// 出力
// array(1) {
// ["name"]=>
// string(9) "hoge-huga"
// }
// array(1) {
// ["name"]=>
// string(5) "color"
// }
?>
Traitを使用することで関係のないクラスに対して
メソッドを提供することができました。
トレイト(Trait)と継承の使い分け
個人的な使い分けの観点ですが
- 親子関係にあるクラス内でのメソッド => 継承
- 親子関係にないクラス間でのメソッド => トレイト(Trait)
というイメージで使い分けています。
ユースケース
トレイト(Trait)クラスの活用例をいくつか載せてみました。
Laravel内で使用されている例や、個人的にLaravelでの実装時に使用した事例となります。
Laravelで特定モデルへ論理削除を適用する
こちらはLaravelで使用されている例となります。
Laravelで特定のテーブルに論理削除を導入したい場合に、
モデルクラスへIlluminate\Database\Eloquent\SoftDeletes
のトレイトを適用することで
簡単に論理削除を導入することができます。
(deleted_at
を基準として論理削除となります)
こちらはLaravel 11でインストール時に作られるUser
モデルクラスへ
論理削除のトレイトを適用する例です。
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes; //★ Use宣言
class User extends Authenticatable
{
use HasFactory, Notifiable, SoftDeletes; //★ SoftDeletesの適用
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
特定機能のアクションメソッドを定義する
こちらは個人的に試した事例となります。
各リソースに対する機能のうち、
CSV形式で出力するルーティングexport/csv
をコントローラに対して設定したケースです。
こちらのCSV出力機能はリソースによっては追加しない想定のため、
アクションメソッドをトレイトで共通化しました。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Http\Controllers\Traits\CsvExportable;
class UserController exuends Controller
{
use CsvExportable;
protected $model = User::class;
}
<?php
namespace App\Http\Controllers\Traits;
use Illuminate\Http\Request;
trait CsvExportable
{
/**
* CSVエクスポート
*/
public function exportCsv(Request $request)
{
if (is_null($this->model)) {
throw new \Exception('modelプロパティが設定されていません');
}
// CSV出力処理
}
}
まとめ
- Trait(トレイト)は実装したメソッドを共通化するための仕組みの1つ
- Traitを実装して、クラス内へ
use トレイト名
でメソッドが可能になる
<?php
trait Greetable {
public function hello()
{
echo 'hello!';
}
}
class Hoge {
use Greetable; // Greetableトレイトのメソッドを使用可能とする
public function hoge()
{
echo 'hoge';
}
}
$hoge = new Hoge;
$hoge->hello(); // hogeが挨拶してくれたようです
// 出力
// hello!
?>
参考
告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。
みなさまからのご応募をお待ちしております。