我正在使用Laravel并且在Eloquent ORM中遇到一个小问题。我可以使用JOIN简单地使用SQL查询,但我似乎无法让它与Eloquent合作!
这就是我想要的,我有两个表格。一个是'餐馆',另一个是'Restaurant_Facilities'。
表格简单..和一对一的关系。就像有一个restaurant
表格id
,name
,slug
等,以及另一个名为restaurant_facilities
的表id
,{{1} },restaurant_id
,wifi
等
现在我要做的是..加载所有有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的餐馆总数?
请帮忙。
谢谢。
答案 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个热切的美食对象