web-dev-qa-db-ja.com

Laravel、関係の列のwhere条件を使用するには?

私はLaravelを使用しており、Eloquent ORMで小さな問題を抱えています。JOINを使用してSQLクエリで簡単にこれを取得できますが、Eloquentで動作させることはできません。

これは私が欲しいものです、私は2つの表を持っています。 1つは「レストラン」、もう1つは「Restaurant_Facilities」です。

テーブルはシンプルです。そして、一対一のリレーション。 restaurantidnameなどのslugテーブルと、idrestaurant_facilitieswifiparkingなどのrestaurant_idという別のテーブルがあるように

今、私がしたいことは..wifi = 1またはwifi = 0のすべてのレストランをロードすることです。Eloquentでそれを行うにはどうすればよいですか?積極的な読み込み、ピボットテーブル、with()、collections()を試しましたが、何も機能しないようです!

cuisinesの多対多リレーションについても同じ問題があります!同じrestaurantテーブルとcuisineテーブルとrestaurant_cuisine_connectionテーブルがあります。

しかし、IDを使用して特定の料理内のすべてのレストランを読み込むにはどうすればよいですか?

これは動作します。

Cuisine::find(6)->restaurants()->get();

しかし、私はこれをRestaurant :: model from cuisines。からロードしたいのです。多くの条件が連鎖しているためです。検索とフィルタリング/ブラウズページのために。

アイデアや方法はありますか?私はこれに3日間苦労してきましたが、まだ答えはありません。

モデル例:

class Restaurant extends Eloquent {

    protected $table = 'restaurants';

    public function facilities() {
        return $this->hasOne('Facilities'); 
    }
}

class Facilities extends Eloquent {

    protected $table = 'restaurants_facilities';

    public function restaurant() {
        return $this->belongsTo('Restaurant');
    }

}

PS:これは機能しているように見えますが、これは雄弁な方法ではありませんか?

Restaurant::leftJoin(
                'cuisine_restaurant', 
                'cuisine_restaurant.restaurant_id', 
                '=', 'restaurants.id'
             )
             ->where('cuisine_id', 16)
               ->get();

また、別のクエリなしで特定の列の値を持つレストランの数を見つける最良の方法は何ですか?のような..私は駐車場= 1と無線LAN = 1を持っているレストランの合計を見つけなければなりませんか?

これで助けてください。

ありがとうございました。

33

Restaurantモデルからロードする必要がある場合、ここで左結合を実行しても何も問題はありません。次のように、Restaurantモデルのメソッドに抽象化する場合があります。

_class Restaurant extends Eloquent {
    protected $table = 'restaurants'; // will be default in latest L4 beta

    public function facility()
    {
      return $this->hasOne('Facility');
    }

    // Or, better, make public, and inject instance to controller.
    public static function withWifi()
    {
      return static::leftJoin(
        'restaurant_facilities',
        'restaurants.id', '=', 'restaurant_facilities.restaurant_id'
      )->where('wifi', '=', 1);
    }
}
_

そして、あなたのルートから:

_Route::get('/', function()
{
  return Restaurant::withWifi()->get();
});
_

外出先では、そのコードをテストしていませんが、動作するはずです。代わりに、制約付きの積極的な読み込みを使用することもできますが、それはファシリティオブジェクトがnullかどうかのみを指定します。 where句を指定しない限り、すべてのレストランが返されます。

(追伸:ファシリティの単数形に固執します。hasOne('Facilities')が正しく読み取れないことに注意してください。)

32
JeffreyWay

REST新しい共有パラダイムを構築する際のAPIの方法論を改善しようとして、この投稿に出くわしました。使用したい Eager Loading Constraints 。共有アイテムをロードするapiルートと、次のようなサブアイテムのコレクションがあるとします。

/api/shared/{share_id}/subitem/{subitem_id}

GETリクエストでこのルートをヒットするとき、その特定のサブアイテムをロードします。そのIDでそのモデルを読み込むことができますが、そもそもユーザーがその共有アイテムにアクセスできるかどうかを検証する必要がある場合はどうでしょうか。 1つの答えは、逆の関係をロードすることを推奨しましたが、これは非常に迅速に混乱し混乱するコントローラーにつながる可能性があります。積極的な負荷に制約を使用することは、より「雄弁」なアプローチです。したがって、次のようにロードします。

$shared = Shared::where('id', $share_id)
  ->with([ 'subitems' => function($query) use ($subitem_id) {
    $query->where('subitem_id', $subitem_id)
  }]);

そのため、そのIDを持つサブアイテムのみが必要です。次のような操作を行うことで、見つかったかどうかを確認できます。

if ($shared->subitems->isEmpty())

サブアイテムはコレクション(サブアイテムの配列)であるため、次のサブアイテム[0]を返します。

return $shared->subitems[0];
20
Throttlehead

whereHasを使用して、関係でフィルターします。リレーションには参加しませんが、関連するプロパティによって現在のモデルをフィルターします。このような状況に役立つローカルスコープも調べてください https://laravel.com/docs/5.3/eloquent#local-scopes

あなたの例は次のようになります:

Restaurant::whereHas('facilities', function($query) {
    return $query->where('wifi', true);
})->get();


Restaurant::whereHas('cuisines', function($query) use ($cuisineId) {
    return $query->where('id', $cuisineId);
})->get();

ローカルスコープで同じことを実現するには:

class Restaurant extends Eloquent
{
    // Relations here

    public function scopeHasWifi($query)
    {
        return $query->whereHas('facilities', function($query) {
            return $query->where('wifi', true);
        });
    }

    public function scopeHasCuisine($query, $cuisineId)
    {
        return $query->whereHas('cuisines', function($query) use ($cuisineId) {
            return $query->where('id', $cuisineId);
        });
    }
}

ローカルスコープの場合、クエリビルダーの新しいインスタンスが作成され、メソッドのチェーン化が妨げられるため、モデルの静的メソッドとして定義しないでください。ローカルスコープを使用すると、クエリビルダーの現在のインスタンスが挿入されて返されるため、好きなだけスコープを連鎖できます。

Restaurant::hasWifi()->hasCuisine(6)->get();

ローカルスコープは、上記の例のように、メソッド名に接頭辞scopeを付けて定義され、メソッド名にscopeなしで呼び出されます。

14
Eric Tucker

whereHas()関数を主演する別のソリューション:

$with_wifi = function ($query) {
   $query->where('wifi', 1);
};

Facilities::whereHas('restaurant', $with_wifi)

素敵できれい。

10

Restaurantモデルから絶対にロードする必要がありますか?問題を解決するために、私は通常、逆にアプローチします。

Facilities::with('restaurant')->where('wifi' ,'=', 0)->get();

これにより、条件に一致するすべてのレストラン施設が取得され、レストランが熱心にロードされます。

より多くの条件を連鎖して、このように合計をカウントできます。

Facilities::with('restaurant')
  ->where('wifi' ,'=', 1)
  ->where('parking','=', 1)
  ->count();

これは料理にも使用できます

Cuisine::with('restaurant')->where('id','=',1)->get();

これは、この料理を持つすべてのレストランがロードされた、1熱心なIDを持つ料理オブジェクトを取得します

1
Alex Naspo