36

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

这就是我想要的,我有两个表。一个是“Restaurants”,另一个是“Restaurant_Facilities”。

这些表很简单......和一对一的关系。就像有一个restaurant带有id, name, , 等的slug表格和另一个restaurant_facilities带有id, restaurant_id, wifi,parking等的表格

现在我想要做的是..加载所有有 wifi = 1 或 wifi = 0 的餐厅.. 我怎么能用 Eloquent 做到这一点?我尝试过急切加载、数据透视表、with()、collections(),但似乎没有任何效果!

对于多对多关系,我遇到了同样的问题cuisines!我有同restaurant一张桌子和一张cuisine桌子和一张restaurant_cuisine_connection桌子..

但是如何使用它的 ID 加载特定美食中的所有餐厅?

这行得通。

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

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

有什么想法或方法吗?我已经为此苦苦挣扎了 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 和 wifi = 1 的餐厅总数?

请帮助解决这个问题。

谢谢你。

4

5 回答 5

34

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

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

(PS 我会坚持使用 Facility 的单数形式。请注意hasOne('Facilities')阅读不正确?)

于 2013-01-31T12:33:43.810 回答
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];
于 2014-09-30T17:24:00.503 回答
15

用于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,如上例所示。

于 2016-11-10T18:12:53.933 回答
10

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

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

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

漂亮整洁。

于 2015-11-13T18:03:56.453 回答
1

您是否必须从 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();

这会抓取 ID 为 1 的美食对象,其中包含所有拥有该美食的餐厅

于 2013-01-31T13:19:10.617 回答