Eloquent ORMでのモデルの拡張



WebアプリケーションでSQLクエリを手動で記述していた時代から長い道のりを歩んできました。 LaravelのEloquent ORMなどのツールを使用すると、データベースをより高いレベルで操作でき、クエリ構文やセキュリティなどのより低いレベルの詳細から解放されます。







Eloquentでの作業を開始すると、必然的にwhere



join



などの演算子が思い付きます。 より高度なものについては、リクエスト(スコープ)、リーダー(アクセサ)、およびミューテータ(ミューテータ)のスコープがあり、クエリを構築する古い方法に対するより表現力豊かな代替手段を提供します。







よく繰り返されるwhere



句とクエリスタブ(スコープ)の代わりとして使用できる他の代替手段を見てみましょう。 このテクノロジーは、別のモデルを継承する新しいEloquentモデルを作成することです。 このようなモデルは、独自のメソッド、リクエストスタブ(スコープ)、リスナー(リスナー)などを追加する機能を保持しながら、親モデルのすべての機能を継承します。 これは通常「単一テーブル継承」と呼ばれますが、「モデル継承」と呼ぶ方が好きです。









ほとんどのWebアプリケーションには「管理者」という概念があります。 管理者は、昇格された特権とアプリケーションのサービス部分へのアクセス権を持つ通常のユーザーです。 一般ユーザーと管理者を区別するために、次のように記述します。







 $admins = User::where('is_admin', true)->get();
      
      





アプリケーションwhere



式が頻繁に繰り返される場合、それをリクエストのローカルスコープに置き換えると便利です。 User



モデルにisAdmin



リクエストisAdmin



isAdmin



より表現力豊かで再利用されたコードを記述できるようになります。







 $admins = User::isAdmin()->get(); // : class User extends Model { public function scopeIsAdmin($query) { $query->where('is_admin', true); } }
      
      





先に進み、モデルの継承を使用しましょう。 User



モデルから継承してグローバルリクエストスタブを追加すると、以前よりも正確な結果が得られますが、今では完全に新しいオブジェクトが得られます。 このオブジェクト( Admin



)は、独自のメソッド、リクエストスタブ、およびその他の機能を持つことができます。







 $admins = Admin::all(); // : class Admin extends User { protected $table = 'users'; public static function boot() { parent::boot(); static::addGlobalScope(function ($query) { $query->where('is_admin', true); }); } }
      
      





注:クエリが正常に機能するには、 protected $table = 'users'



変数が必要です。 Eloquentはモデルクラス名を使用してテーブル名を決定します。 したがって、Eloquentは、テーブル名が「ユーザー」ではなく「管理者」であると想定します。これにより、 Base table or view not found



エラーが発生します。

Admin



モデルができたので、 User



モデルと機能を簡単に共有できます。 例:







通知



新しいAdmin



モデルにより、すべての管理者に通知を送信するなどの簡単な操作が簡単になりました。







 Notification::send(Admin::all(), NewSignUp($user));
      
      





確認する



User



モデルでの操作が管理者に限定されている場合は常に、 User



が管理者を表していることを確認する必要があります。







 //  if ($admin = User::find($id)->is_admin !== true) { throw new Exception; } $admin->impersonate($user);
      
      





Admin



グローバルリクエストスタブはAdmin



者のみに制限されるため、 Admin



クラスに対してimpersonate



メソッドをすぐに呼び出すことができます。







 Admin::findOrFail($id)->impersonate($user);
      
      





モデル工場



テスト中に、以下の例のようにモデルファクトリを使用して管理者権限を持つUser



モデルを作成する必要がある場合があります。







 $admin = factory(User::class)->create(['is_admin' => true]); // //    $factory->define(User::class, function () { return [ ... 'is_admin' => false, ]; });
      
      





ユーザーが管理者であるものカプセル化することにより、ファクトリに状態を追加することにより、このコードを改善できます。







 $admin = factory(User::class)->states('admin')->create(); //    $factory->state(User::class, 'admin', function () { return ['is_admin' => true]; });
      
      





間違いなく優れていますが、それでもUser



モデルのインスタンスを取得します。 Admin



モデルの新しいファクトリを定義することにより、 Admin



者権限を持つユーザーも取得しますが、ファクトリはAdmin



モデルのインスタンスを返します。







 $admin = factory(Admin::class)->create(); //    $factory->define(Admin::class, function () { return ['is_admin' => true] + factory(User::class)->raw(); });
      
      





関係は機能しません。



Eloquentがテーブル名を定義するように、モデルクラス名は外部キーとステージングテーブルを定義するために使用されます。 したがって、 Admin



モデルからの関係へのアクセスには問題があります。







 Admin::first()->posts; //  : Unknown column 'posts.admin_id' //   : class Admin extends User { // } class User extends Model { public function posts() { return $this->hasMany(Post::class); } }
      
      





Eloquentは、 Post



モデルの各インスタンスにuser_id



フィールドではなくadmin_id



フィールドがあると想定しているため、リレーションにアクセスできません。 User



モデルでuser_id



外部キーを渡すことでこれを修正できます。







 //  : class Admin extends User { // } class User extends Model { public function posts() { return $this->hasMany(Post::class, 'user_id'); } }
      
      





同じ問題が多対多に存在します。 Eloquentは、ステージングテーブルの名前が現在のモデルクラスの名前と一致すると仮定します。







 Admin::first()->tags; //  : Table 'admin_tag' doesn't exist //   : class Admin extends User { // } class User extends Model { public function tags() { return $this->belongsToMany(Tag::class); } ...
      
      





ピボットテーブルの名前とリモートキーの名前を明示的に指定することで、この問題を解決することもできます。







 //  : class Admin extends User { // } class User extends Model { public function tags() { return $this->belongsToMany(Tag::class, 'user_tag', 'user_id'); } ...
      
      





リモートキーとピボットテーブルを明示的に定義すると、 Admin



モデルはUser



モデルの関係にアクセスできますが、このソリューションは理想とはほど遠いものです。 これらの一見不要な定義が存在しても、コードは改善されません。







ただし、この問題を自動的に解決するHasParentModel HasParentModel



を作成できます。 この特性は、モデルクラス名を親モデルのクラス名に置き換えます。 GitHub特性コード。







単一テーブルの継承により、さらに進んでLaravelの動作を改善できます。 Laravelアプリケーションでのモデルの作成を簡素化するパッケージを作成し、日々リリースする準備ができています。 お知らせをお見逃しなくTwitterでフォローしてください!

この特性を使用する新しいAdmin



モデルを見てみましょう。







 use App\Abilities\HasParentModel; class Admin extends User { use HasParentModel; //    : protected $table = 'users' public static function boot() { parent::boot(); static::addGlobalScope(function ($query) { $query->where('is_admin', true); }); } }
      
      





これで、 User



モデルの関係は、デフォルト値に依存した状態に戻ることができます。







 //  : class User extends Model { public function posts() { return $this->hasMany(Post::class); } public function tags() { return $this->belongsToMany(Tag::class); } }
      
      





HasParentModel HasParentModel



モデルをクリアし、開発者がモデル内で何か特別なことが起こっていることを理解できるようにします。







モデルの継承



Eloquentモデルの一般的な特性を特定し、その継承を使用してそれらをクリーンにしました。 このテクノロジーにより、より良いオブジェクト名を作成し、アプリケーションにカプセル化できます。 Users



Admins



だけでなく、すべてのEloquentモデルで継承を使用できることにAdmins



。 可能性は無限です!







作成し、楽しみ、知識を共有してください。 プロジェクトでこのパターンをどのように使用するかを教えてください! (ツイーター@calebporzioおよび@tightenco







頑張って








All Articles