Laravel,如何使用关系列的条件?

时间:2013-01-31 09:04:49

标签: php laravel eloquent

我正在使用Laravel并且在Eloquent ORM中遇到一个小问题。我可以使用JOIN简单地使用SQL查询,但我似乎无法让它与Eloquent合作!

这就是我想要的,我有两个表格。一个是'餐馆',另一个是'Restaurant_Facilities'。

表格简单..和一对一的关系。就像有一个restaurant表格idnameslug等,以及另一个名为restaurant_facilities的表id,{{1} },restaurant_idwifi

现在我要做的是..加载所有有wifi = 1或wifi = 0的餐馆.. 我怎么能用Eloquent做到这一点?我尝试过急切加载,数据透视表,(),集合(),似乎什么都没有用!

我对parking的多对多关系有同样的问题! 我有相同的cuisines表和restaurant表以及cuisine表..

但是如何使用它的ID加载特定菜肴中的所有餐馆?

这很有效。

restaurant_cuisine_connection

但是我想从Restaurant :: model而不是菜肴加载它...因为我有很多条件链接在一起..它用于搜索和过滤/浏览页面。

任何想法或方法?我已经苦苦挣扎了3天,仍然没有答案。

示例模型:

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

PS: 这似乎有效..但这不是Eloquent的方式吗?

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');
    }

}

在没有其他查询的情况下,找到具有特定列值的餐馆数量的最佳方法是什么?喜欢..我必须找到停车= 1和wifi = 1的餐馆总数?

请帮忙。

谢谢。

5 个答案:

答案 0 :(得分:32)

如果您必须从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();
});

在旅途中 - 尚未测试该代码,但我认为它应该可行。您可以改为使用带有约束的预先加载,但这只会指定设施对象是否为空。它仍将返回所有餐馆,除非您指定where子句。

(P.S。我坚持使用单一形式的设施。请注意hasOne('Facilities')如何无法正确阅读?)

答案 1 :(得分:20)

在构建新的共享范例时,我偶然发现了这篇文章,同时尝试改进我的REST API方法。您想使用Eager Loading Constraints。假设您有一个api路由,您可以在其中加载共享项目以及它的子集合,例如:

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

当使用GET请求命中此路由时,您希望加载该特定子项。当然,您可以通过该ID加载该模型,但是如果我们需要验证用户是否可以首先访问该共享项呢?一个答案建议加载反向关系,但这可能会很快导致混乱和混乱的控制器。使用对急切负载的约束是一种更“雄辩”的方法。所以我们这样加载它:

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

所以只需要具有该id的子项。现在我们可以通过这样做来检查它是否被发现:

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

由于subitems是一个集合(子项目数组),我们返回subitem [0]:

return $shared->subitems[0];

答案 2 :(得分:14)

使用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进行调用,如abover中的示例所示。

答案 3 :(得分:10)

主演whereHas()功能的另一个解决方案:

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

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

干净整洁。

答案 4 :(得分:1)

您是否必须从餐厅模型中加载它?为了解决这个问题,我通常会反过来。

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个热切的美食对象