Eloquent ORMでのモデルの拡張


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


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


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



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


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

アプリケーションwhere式が頻繁に繰り返される場合、それをリクエストのローカルスコープに置き換えると便利です。 UserモデルにisAdminリクエストisAdminisAdminより表現力豊かで再利用されたコードを記述できるようになります。


 $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モデルの一般的な特性を特定し、その継承を使用してそれらをクリーンにしました。 このテクノロジーにより、より良いオブジェクト名を作成し、アプリケーションにカプセル化できます。 UsersAdminsだけでなく、すべてのEloquentモデルで継承を使用できることにAdmins 。 可能性は無限です!


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


頑張って



Source: https://habr.com/ru/post/J344728/


All Articles